WebClient
大约 2 分钟
Spring WebFlux 包含一个用于执行 HTTP 请求的客户端。WebClient 具有基于 Reactor 的功能性、流畅的 API,请参阅 Reactive Libraries,它支持异步逻辑的声明式组合,而无需处理线程或并发。它是完全非阻塞的,它支持流式传输,并且依赖于同样用于在服务器端对请求和响应内容进行编码和解码的编解码器。
必要依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
注意
目前 @HttpExchange
功能不是很完善,此方式仅供参考。可以使用客户端模式完成第三方调用。模块调用推荐使用 Spring Cloud OpenFeign
配合 @HttpExchange 注解使用
创建 Client 接口
以 SkillClient
为例,该接口下可以实现全部 skill
相关的远程调用,下面是查询技能树列表的样例
@HttpExchange("http://basic-paper-config")
public interface SkillClient {
@GetExchange("skill/query_skill_tree")
ResultVO<List<SkillTreeDTO>> querySkillTree();
}
创建 Client 配置文件
ReactorLoadBalancerExchangeFilterFunction
为负载均衡过滤器、@LoadBalanced
为负载均衡注解。如果不需要使用服务名进行模块调用可以不配置。
@Configuration
public class SkillClientConfiguration {
@Resource
private ReactorLoadBalancerExchangeFilterFunction loadBalancerExchangeFilter;
private static final Logger logger = LoggerFactory.getLogger(SkillClientConfiguration.class);
@Bean
@LoadBalanced
public SkillClient skillClient() {
WebClient webClient = WebClient.builder()
// 此过滤器中包含 RequestContextHolder,需要保持最先进入,如果配置再 loadBalancerExchangeFilter 之后,会导致无法获取父线程的 RequestAttributes
.filter((request, next) -> {
// 此处为获取用户信息过滤器的 demo,可以酌情处理,不需要可以删除,或替换为第三方的 token
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest servletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
String userSessionId = servletRequest.getHeader(UserHeaderConsts.USER_SESSION_ID);
String tenantSn = servletRequest.getHeader(UserHeaderConsts.TENANT_SN);
ClientRequest clientRequest = ClientRequest.from(request)
.header(UserHeaderConsts.TENANT_SN, tenantSn)
.header(UserHeaderConsts.USER_SESSION_ID, userSessionId)
.build();
return next.exchange(clientRequest);
}).filter(loadBalancerExchangeFilter)
// 配置输出请求详细日志
.codecs(configure -> configure.defaultCodecs().enableLoggingRequestDetails(true))
.build();
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).build();
return proxyFactory.createClient(SkillClient.class);
}
}
独立使用
WebClient
是可以使用单例的,可以 WebClient.builder().baseUrl("http://localhost/").build()
将其注入一个 bean 后使用。
GET 请求
GET 请求参数使用参数形式传输,这里不要使用 URLEncoder.encode
来编码,否则可能会出现数据传输错误。
String block = WebClient.builder().baseUrl("http://localhost/").build()
.get().uri("auth/token?ticket={ticket}", ticket).header("header","header")
.retrieve().bodyToMono(String.class).block();