社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
使用Spring MVC进行微服务化网上有很多博客可以参考,但是关于Spring WebFlux进行微服务的可参考资料还是很少,这也和Spring WebFlux上手比较难,学习曲线比较陡峭有一些关系。本系列会带来一些响应式微服务的文章让大家更快上手响应式微服务。
在系统进行微服务化后,第一个需要解决的就是服务间的通信问题。是使用HTTP,GRPC,还是比较新的RSocket。服务间调用的效率对整个系统的性能是有很大影响的。在网上也有看到过阿里有想做将两个想要通信的服务部署在一个JVM,两个服务之间的通信就好像在调用自己本身的方法一样。举整个例子只是想说明一下服务间的通信是很重要的。
本文要讲述不是最新的RSocket,而是Feign。使用Feign进行响应式微服务系统的构建。Open Feign作为Spring MVC下构建微服务的组件也是阻塞模型,用于构建响应式微服务也是可以的。但是服务间调用就没有办法享受到响应式带来的益处,就好像在使用Spring WebFlux时使用JDBC进行数据库的访问一样。在前文中我也提到了服务间调用的性能问题,服务间的调用是很典型的IO操作,调用会阻塞直到服务响应,这个过程中调用线程一直阻塞等待结果的返回。这个过程中是很浪费计算资源的。所以,在构建响应式微服务时有更好的选择,尽量不要去使用阻塞模型的组件。
我们先去Open Feign的官网看看有没有响应式的Feign。
官网在Reactive Support模块写到它现在还不支持响应式客户端。但是它推荐了另外一个开源的项目——feign-reactive。这个项目是一个叫Playtika的游戏公司受到了Open Feign启发开源的。
Playtika是一家用人工智能技术手段去改造游戏的公司,拥有核心技术壁垒。由Robert Antokol (Playtika CEO)和Uri Shahak联合创建于2010年,总部设在以色列,全球员工超过1300人,主打产品是棋牌社交类手游,类似“海外版的QQ棋牌社交游戏平台”。
--来自百度百科
先说说这个项目的缺点:
在生产使用Reactive Feign首先要有足够的信心解决随时可能出现的问题,在项目中一些特定的需求可能需要阅读源码才能获取到解决方案。
看到这里可能大家会对这个项目有些畏惧了,不过reactive feign的优点也很明显,从open feign迁移到reactive feign成本很低,两者的使用方法基本一致。虽然reactive feign提供了很多语言的整合,由于本文讲述的响应式微服务,所以只讨论reactive feign和Spring Cloud的整合。reactive feign官方推荐使用cloud2整合Spring Cloud,之前的可能后续不再维护了。推荐cloud2的原因也是因为它整合的是新一代的断路器和负载均衡组件CircuitBreaker和LoadBalancer。
本文举例涉及两个服务模块。一个gateway,一个account。
<dependency>
<groupId>com.playtika.reactivefeign</groupId>
<artifactId>feign-reactor-cloud2</artifactId>
</dependency>
<dependency>
<groupId>com.playtika.reactivefeign</groupId>
<artifactId>feign-reactor-spring-configuration</artifactId>
</dependency>
<dependency>
<groupId>com.playtika.reactivefeign</groupId>
<artifactId>feign-reactor-webclient</artifactId>
</dependency>
首先看gateway模块在启动类。上面使用的注解是@EnableReactiveFeignClients,多了Reactive,区别不大。
//gateway模块
@SpringBootApplication
@EnableDiscoveryClient
@EnableReactiveFeignClients(basePackages = {"com.gateway.client"})
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
可以看出来和open feign基本没有区别,只是在原来请求和返回的参数类型上加上了Mono。
//gateway模块
@ReactiveFeignClient(name = "account-service")
public interface TestClient {
@PostMapping(name = "测试", value = "/test/save")
Mono<Test> save(@RequestBody Mono<Test> test);
}
和一般的控制层写法一样。
//account模块
@PostMapping(name = "测试", value = "/save")
public Mono<Test> save(@RequestBody Mono<Test> testMono) {
log.info("hahah");
return testService.add(testMono).log("", Level.INFO, true).flatMap(test -> {
return Mono.subscriberContext().map(ctx -> {
log.info("context:" + ctx.get(SystemConstant.JWT_ACCOUNT));
return test;
});
});
}
reactive feign 整合了CircuitBreaker,LoadBalancer,可以使用下面的配置进行控制开关。(配置不区分模块)
reactive.feign.loadbalancer.enabled=true
reactive.feign.circuit.breaker.enabled=false
使用类进行配置。
//设置一些超时时间
@Bean
public ReactiveOptions reactiveOptions() {
return new WebReactiveOptions.Builder()
.setWriteTimeoutMillis(10000)
.setReadTimeoutMillis(10000)
.setConnectTimeoutMillis(10000)
.build();
}
//重试机制
@Bean
public ReactiveRetryPolicies retryOnNext() {
//不进行重试,retryOnSame是控制对同一个实例的重试策略,retryOnNext是控制对不同实例的重试策略。
return new ReactiveRetryPolicies.Builder()
.retryOnSame(BasicReactiveRetryPolicy.retryWithBackoff(0, 10))
.retryOnNext(BasicReactiveRetryPolicy.retryWithBackoff(0, 10))
.build();
}
在前文中有提到reactive feign不支持Decode和Encode。在github的issue中也有人提到关于提供Decode/Encode支持的问题。官方给的回应是不提供。
feign.codec.Decoder
doesn't meant for reactive decoding.
I suggest that exact implementation ofReactiveHttpClient
should implement encoding/decoding.
In case of implementation based on SpringWebClient
I don't need encoding/decoding as Spring takes full responsibility.
他说的实现自己的 ReactiveHttpClient以提供编码和解码具体没有去研究过。不过对于请求时修改请求内容/添加请求头reactive feign提供了ReactiveHttpRequestInterceptor。下面的例子就是从上下文中取到登陆的Token信息,然后放置到请求头中。
@Bean
public ReactiveHttpRequestInterceptor kuaidiInterceptor() {
return reactiveHttpRequest ->
Mono.subscriberContext().map(ctx -> {
if (ctx.isEmpty()) {
return reactiveHttpRequest;
}
reactiveHttpRequest.headers().put(SystemConstant.TOKEN,
Arrays.asList(ctx.get(SystemConstant.TOKEN)));
return reactiveHttpRequest;
});
}
关于解码。一般项目服务都会有包装返回结果的操作,让响应的结果格式一致。例如:代码1-1。在以前,我们可以使用Decode,在Decode中将data的内容拿出来然后返回。在reactive feigin中没有Decode就比较难办了。笔者这里有一个实现方案,也有实际操作。但是觉得不够优雅就只提供一下思路,不具体贴代码了。笔者是有一个标记是Feign结果的接口,继承Spring的Jackson2JsonDecoder重写decode方法。在这个方法中处理,如果结果是Feign调用返回的结果,则做一些额外的处理。
//代码1-1
class Result<T>{
String code;
String message;
T data;
}
在写这篇文章的时候,不管是在国内还是国外,都没有关于Spring cloud reactive feign整合的博客。所以希望这篇博客能帮助你快速整合Spring Cloud和reactive feign。笔者在参考资料极度匮乏的情况下总结出这些经验希望后面的人少走弯路。如果文中有错误或不足的地方,希望能在评论区指出,大家共同进步。
转载请注明出处
https://blog.csdn.net/LCBUSHIHAHA/article/details/113817966
参考资料
https://github.com/OpenFeign/feign-reactive
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!