Подробное объяснение использования 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
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода