Spring5Приносит новую отзывчивостьwebрамки развитияWebFlux,в то же время,Также представлены новыеHttpClientрамкаWebClient。WebClientдаSpring5Казнь введена в HTTP Неблокирующий, реактивный клиент для запросов. Он имеет хорошую поддержку как синхронных, так и асинхронных, а также потоковых сценариев. После выпуска WebClient RestTemplate станет устаревшим в будущих версиях, и никакие важные новые функции не будут добавлены.
WebClient — это полнофункциональный клиент HTTP-запросов. По сравнению с RestTemplate, WebClient поддерживает следующее:
И клиент WebClient Spring 5, и сервер WebFlux используют один и тот же неблокирующий кодек для кодирования и декодирования содержимого запросов и ответов. Нижний уровень по умолчанию использует Netty со встроенной поддержкой реактивной реализации Jetty HttpClient. В то же время вы также можете настроить новую базовую библиотеку, реализовав интерфейс ClientHttpConnector посредством кодирования, например, переключив реализацию Jetty:
WebClient.builder()
.clientConnector(new JettyClientHttpConnector())
.build();
Конструктор экземпляра WebClient может устанавливать некоторую базовую информацию о конфигурации глобального веб-запроса, например файлы cookie по умолчанию, заголовки, baseUrl и т. д.
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, вы можете настроить безопасные соединения SSL, тайм-ауты запросов, тайм-ауты чтения и записи и т. д. Здесь следует отметить проблему. Максимальное количество подключений по умолчанию составляет 500. Время ожидания соединения по умолчанию составляет 45 000 мс. Вы можете настроить его как динамический пул соединений, чтобы преодолеть эти конфигурации по умолчанию, или настроить его в соответствии с вашим бизнесом. Включая поток выбора Netty и рабочий поток, вы также можете установить его самостоятельно.
//Конфигурациядинамичныйсоединятьбассейн //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 в параллельном сценарии. Исключение следующее:
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
Для определенных форматов взаимодействия с данными вы можете установить собственный режим кодирования и декодирования следующим образом:
ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(configurer -> {
configurer.customCodecs().decoder(new Jackson2JsonDecoder());
configurer.customCodecs().encoder(new Jackson2JsonEncoder());
})
.build();
WebClient.builder()
.exchangeStrategies(strategies)
.build();
URI поддерживает заполнители атрибутов при построении, а реальные параметры можно сортировать при вводе параметров. В то же время вы можете установить тип носителя и кодировку через Accept. Окончательное значение результата получается через Mono и Flux, а возвращаемое значение подписывается в методе subscribe.
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, например:
//Определяем параметры запроса
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 для получения данных, например:
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){}
});
Пример почтового запроса демонстрирует более сложный сценарий, включающий как параметры формы, так и данные файлового потока. Если это обычный почтовый запрос, просто установите экземпляр объекта непосредственно через bodyValue. Не используйте конструктор FormInserter.
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().
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);
Однако если требуется несколько вызовов, более эффективно не блокировать каждый ответ по отдельности и вместо этого дождаться объединенного результата, например:
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());
Вы можете единообразно изменять запросы перехвата, устанавливая фильтры-перехватчики, например сценарии проверки подлинности. В следующем примере фильтр регистрирует один перехватчик, а фильтры могут регистрировать несколько перехватчиков. встроенный перехватчик, используемый для ограничения размера значения ответа в байтах.
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 при запросе интерфейса веб-сокета, например:
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. Попробуйте сейчас.