Подробное объяснение использования WebClient Spring5.
Подробное объяснение использования WebClient Spring5.

Предисловие

Spring5Приносит новую отзывчивостьwebрамки развитияWebFlux,в то же время,Также представлены новыеHttpClientрамкаWebClient。WebClientдаSpring5Казнь введена в HTTP Неблокирующий, реактивный клиент для запросов. Он имеет хорошую поддержку как синхронных, так и асинхронных, а также потоковых сценариев. После выпуска WebClient RestTemplate станет устаревшим в будущих версиях, и никакие важные новые функции не будут добавлены.

Сравнение WEBCLIENT и RESTTEMPLATE

WebClient — это полнофункциональный клиент HTTP-запросов. По сравнению с RestTemplate, WebClient поддерживает следующее:

  • Неблокирующий ввод-вывод.
  • Реактивное противодавление потока (механизм, который активно возвращает производительность для замедления производства, когда потребительская нагрузка слишком высока).
  • Он имеет высокий уровень параллелизма и потребляет меньше аппаратных ресурсов.
  • Плавный дизайн API.
  • Синхронные и асинхронные взаимодействия.
  • Поддержка потоковой передачи

Выбор базовой библиотеки HTTP

И клиент WebClient Spring 5, и сервер WebFlux используют один и тот же неблокирующий кодек для кодирования и декодирования содержимого запросов и ответов. Нижний уровень по умолчанию использует Netty со встроенной поддержкой реактивной реализации Jetty HttpClient. В то же время вы также можете настроить новую базовую библиотеку, реализовав интерфейс ClientHttpConnector посредством кодирования, например, переключив реализацию Jetty:

Язык кода:javascript
копировать
        WebClient.builder()
                .clientConnector(new JettyClientHttpConnector())
                .build();

Конфигурация ВЕБ-КЛИЕНТА

Базовая конфигурация

Конструктор экземпляра WebClient может устанавливать некоторую базовую информацию о конфигурации глобального веб-запроса, например файлы cookie по умолчанию, заголовки, baseUrl и т. д.

Язык кода:javascript
копировать
WebClient.builder()
                .defaultCookie("kl","kl")
                .defaultUriVariables(ImmutableMap.of("name","kl"))
                .defaultHeader("header","kl")
                .defaultHeaders(httpHeaders -> {
                    httpHeaders.add("header1","kl");
                    httpHeaders.add("header2","kl");
                })
                .defaultCookies(cookie ->{
                    cookie.add("cookie1","kl");
                    cookie.add("cookie2","kl");
                })
                .baseUrl("http://www.kailing.pub")
                .build();

Конфигурация библиотеки NETTY

Настраивая базовую библиотеку Netty, вы можете настроить безопасные соединения SSL, тайм-ауты запросов, тайм-ауты чтения и записи и т. д. Здесь следует отметить проблему. Максимальное количество подключений по умолчанию составляет 500. Время ожидания соединения по умолчанию составляет 45 000 мс. Вы можете настроить его как динамический пул соединений, чтобы преодолеть эти конфигурации по умолчанию, или настроить его в соответствии с вашим бизнесом. Включая поток выбора Netty и рабочий поток, вы также можете установить его самостоятельно.

Язык кода:javascript
копировать
       //Конфигурациядинамичныйсоединятьбассейн        //ConnectionProvider provider = ConnectionProvider.elastic("elastic pool");
        //Конфигурация пула фиксированного размера соединения, например, максимальное количество соединений, время ожидания соединения, время простоя соединения и т. д.
        ConnectionProvider provider = ConnectionProvider.fixed("fixed", 45, 4000, Duration.ofSeconds(6));
        HttpClient httpClient = HttpClient.create(provider)
                .secure(sslContextSpec -> {
                    SslContextBuilder sslContextBuilder = SslContextBuilder.forClient()
                            .trustManager(new File("E://server.truststore"));
                    sslContextSpec.sslContext(sslContextBuilder);
                }).tcpConfiguration(tcpClient -> {
                    //Указываем выбор Нетти и количество работы
                    LoopResources loop = LoopResources.create("kl-event-loop", 1, 4, true);
                    return tcpClient.doOnConnected(connection -> {
                        //Чтение и запись настроек таймаута
                        connection.addHandlerLast(new ReadTimeoutHandler(10, TimeUnit.SECONDS))
                                .addHandlerLast(new WriteTimeoutHandler(10));
                    })
                            //соединять настройку таймаута
                            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
                            .option(ChannelOption.TCP_NODELAY, true)
                            .runOn(loop);
                });

        WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();

Что касается настройки пула соединений, согласно отзывам друзей группы, они будут выдавать исключение соединения при использовании WebClient в параллельном сценарии. Исключение следующее:

Язык кода:javascript
копировать
Caused by: reactor.netty.internal.shaded.reactor.pool.PoolAcquireTimeoutException: Pool#acquire(Duration) has been pending for more than the configured timeout of 45000ms

После углубленного исследования блоггера было обнаружено, что стратегия инициализации TcpTcpResources по умолчанию базовой библиотеки зависимостей Reacty-netty WebClient различается в разных версиях. Версия реактора-netty, используемая блоггером в системе шлюза, — 0.8.3. и по умолчанию создается динамический пул соединений, это исключение не возникало даже в параллельных сценариях. После версии 0.9.x инициализируется пул соединений фиксированного размера. Этот член группы использует реактор-netty 0.9.1, что приводит к недоступности соединения во время параллелизма. После ожидания 45 секунд выдается исключение. Поэтому при использовании последней версии WebClient вы должны разумно настроить базовые ресурсы в соответствии с вашими собственными бизнес-сценариями и приведенным выше примером конфигурации Netty HttpClient.

默认策略改动的初衷да有人在github提出了默认使用динамичныйсоединятьбассейн的顾虑:https://github.com/reactor/reactor-netty/issues/578

окончательная корректировка кодаpullЗаписывать:https://github.com/reactor/reactor-netty/pull/812

реактор-нетти-0.8.x инициализировать TcpTcpResources

реактор-нетти-0.9.x инициализировать TcpTcpResources

Конфигурация кодека

Для определенных форматов взаимодействия с данными вы можете установить собственный режим кодирования и декодирования следующим образом:

Язык кода:javascript
копировать
        ExchangeStrategies strategies = ExchangeStrategies.builder()
                .codecs(configurer -> {
                    configurer.customCodecs().decoder(new Jackson2JsonDecoder());
                    configurer.customCodecs().encoder(new Jackson2JsonEncoder());
                })
                .build();
        WebClient.builder()
                .exchangeStrategies(strategies)
                .build();

Пример GET-запроса

URI поддерживает заполнители атрибутов при построении, а реальные параметры можно сортировать при вводе параметров. В то же время вы можете установить тип носителя и кодировку через Accept. Окончательное значение результата получается через Mono и Flux, а возвращаемое значение подписывается в методе subscribe.

Язык кода:javascript
копировать
        WebClient client = WebClient.create("http://www.kailing.pub");
         Mono<String> result = client.get()
                .uri("/article/index/arcid/{id}.html", 256)
                .acceptCharset(StandardCharsets.UTF_8)
                .accept(MediaType.TEXT_HTML)
                .retrieve()
                .bodyToMono(String.class);
        result.subscribe(System.err::println);

Если вам нужно передать сложные параметры запроса, вы можете создать адрес запроса uri через UriComponentsBuilder, например:

Язык кода:javascript
копировать
        //Определяем параметры запроса
        MultiValueMap params = new LinkedMultiValueMap<>();
        params.add("name", "kl");
        params.add("age", "19");
        //Определяем параметры URL
        Map uriVariables = new HashMap<>();
        uriVariables.put("id", 200);
        String uri = UriComponentsBuilder.fromUriString("/article/index/arcid/{id}.html")
                .queryParams(params)
                .uriVariables(uriVariables)

При загрузке файлов, поскольку вы не знаете тип MIME, соответствующий файлам различных форматов, вы можете установить для параметра Accept значение MediaType.ALL, а затем использовать ресурс Spring для получения данных, например:

Язык кода:javascript
копировать
        WebClient.create("https://kk-open-public.oss-cn-shanghai.aliyuncs.com/xxx.xlsx")
                .get()
                .accept(MediaType.ALL)
                .retrieve()
                .bodyToMono(Resource.class)
                .subscribe(resource -> {
                    try {
                        File file = new File("E://abcd.xlsx");
                        FileCopyUtils.copy(StreamUtils.copyToByteArray(resource.getInputStream()), file);
                    }catch (IOException ex){}
                });

Пример POST-запроса

Пример почтового запроса демонстрирует более сложный сценарий, включающий как параметры формы, так и данные файлового потока. Если это обычный почтовый запрос, просто установите экземпляр объекта непосредственно через bodyValue. Не используйте конструктор FormInserter.

Язык кода:javascript
копировать
        WebClient client = WebClient.create("http://www.kailing.pub");
        FormInserter formInserter = fromMultipartData("name","kl")
                .with("age",19)
                .with("map",ImmutableMap.of("xx","xx"))
                .with("file",new File("E://xxx.doc"));
       Mono<String> result = client.post()
                .uri("/article/index/arcid/{id}.html", 256)
                .contentType(MediaType.APPLICATION_JSON)
                .body(formInserter)
                //.bodyValue(ImmutableMap.of("name","kl"))
                .retrieve()
                .bodyToMono(String.class);
        result.subscribe(System.err::println);

Синхронно возвращать результаты

Все приведенные выше демонстрации асинхронно подписываются на значения ответа посредством подписки mono. Конечно, если вы хотите заблокировать и получить результат синхронно, вы также можете заблокировать текущий поток, чтобы получить возвращаемое значение через .block().

Язык кода:javascript
копировать
      WebClient client =  WebClient.create("http://www.kailing.pub");
      String result = client .get()
                .uri("/article/index/arcid/{id}.html", 256)
                .retrieve()
                .bodyToMono(String.class)
                .block();
        System.err.println(result);

Однако если требуется несколько вызовов, более эффективно не блокировать каждый ответ по отдельности и вместо этого дождаться объединенного результата, например:

Язык кода:javascript
копировать
      WebClient client =  WebClient.create("http://www.kailing.pub");
         Mono<String> result1Mono = client .get()
                .uri("/article/index/arcid/{id}.html", 255)
                .retrieve()
                .bodyToMono(String.class);
         Mono<String> result2Mono = client .get()
                .uri("/article/index/arcid/{id}.html", 254)
                .retrieve()
                .bodyToMono(String.class);
        Map  map = Mono.zip(result1Mono, result2Mono, (result1, result2) -> {
            Map arrayList = new HashMap<>();
            arrayList.put("result1", result1);
            arrayList.put("result2", result2);
            return arrayList;
        }).block();
        System.err.println(map.toString());

ФИЛЬТР фильтр

Вы можете единообразно изменять запросы перехвата, устанавливая фильтры-перехватчики, например сценарии проверки подлинности. В следующем примере фильтр регистрирует один перехватчик, а фильтры могут регистрировать несколько перехватчиков. встроенный перехватчик, используемый для ограничения размера значения ответа в байтах.

Язык кода:javascript
копировать
        WebClient.builder()
                .baseUrl("http://www.kailing.pub")
                .filter((request, next) -> {
                    ClientRequest filtered = ClientRequest.from(request)
                            .header("foo", "bar")
                            .build();
                    return next.exchange(filtered);
                })
                .filters(filters ->{
                    filters.add(ExchangeFilterFunctions.basicAuthentication("username","password"));
                    filters.add(ExchangeFilterFunctions.limitResponseSize(800));
                })
                .build().get()
                .uri("/article/index/arcid/{id}.html", 254)
                .retrieve()
                .bodyToMono(String.class)
                .subscribe(System.err::println);

Поддержка ВЕБСОКЕТА

WebClient не поддерживает запросы веб-сокетов. Вам необходимо использовать WebSocketClient при запросе интерфейса веб-сокета, например:

Язык кода:javascript
копировать
WebSocketClient client = new ReactorNettyWebSocketClient();
URI url = new URI("ws://localhost:8080/path");
client.execute(url, session ->
        session.receive()
                .doOnNext(System.out::println)
                .then());

Заключение

Мы использовали WebClient во многих проектах, таких как бизнес-шлюз API и платформа SMS. Производительность и стабильность WebClient можно увидеть по трафику и стабильности шлюза. Модель реактивного программирования — это будущая тенденция веб-программирования. RestTemplate будет постепенно запрещен и устранен, и он больше не будет официально обновляться и поддерживаться. WebClient очень хорошо поддерживает адаптивную модель, а дизайн API удобен. Блоггеры настоятельно рекомендуют новую библиотеку HttpClient. Попробуйте сейчас.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose