响应式微服务Spring Cloud与Spring WebFlux(一)之 Reactive Feign
发布日期:2021-05-06 01:36:25 浏览次数:26 分类:原创文章

本文共 5251 字,大约阅读时间需要 17 分钟。

使用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模块写到它现在还不支持响应式客户端。但是它推荐了另外一个开源的项目——。这个项目是一个叫Playtika的游戏公司受到了Open Feign启发开源的。

Playtika是一家用人工智能技术手段去改造游戏的公司,拥有核心技术壁垒。由Robert Antokol (Playtika CEO)和Uri Shahak联合创建于2010年,总部设在以色列,全球员工超过1300人,主打产品是棋牌社交类手游,类似“海外版的QQ棋牌社交游戏平台”。

--来自百度百科

先说说这个项目的缺点:

  • 使用这个项目的人应该很少,不管是Google还是百度都很难搜到相关的资料(所以才决定写一些关于响应式微服务的文章)。
  • 文档几乎等于没有。
  • github上列举的例子也比较简单,它比较推荐的cloud2连例子也没有看到,只有比较老的cloud的例子。
  • 没有Open Feign的Decode/Encode。

在生产使用Reactive Feign首先要有足够的信心解决随时可能出现的问题,在项目中一些特定的需求可能需要阅读源码才能获取到解决方案。

看到这里可能大家会对这个项目有些畏惧了,不过reactive feign的优点也很明显,从open feign迁移到reactive feign成本很低,两者的使用方法基本一致。虽然reactive feign提供了很多语言的整合,由于本文讲述的响应式微服务,所以只讨论reactive feign和Spring Cloud的整合。reactive feign官方推荐使用cloud2整合Spring Cloud,之前的可能后续不再维护了。推荐cloud2的原因也是因为它整合的是新一代的断路器和负载均衡组件CircuitBreaker和LoadBalancer。

本文举例涉及两个服务模块。一个gateway,一个account。

Maven依赖

<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=truereactive.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();    }

 

Decode/Encode

在前文中有提到reactive feign不支持Decode和Encode。在github的issue中也有人提到关于提供支持的问题。官方给的回应是不提供。

feign.codec.Decoder doesn't meant for reactive decoding.
I suggest that exact implementation of ReactiveHttpClient should implement encoding/decoding.
In case of implementation based on Spring WebClient 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-1class Result<T>{    String code;    String message;    T data;}

最后

在写这篇文章的时候,不管是在国内还是国外,都没有关于Spring cloud reactive feign整合的博客。所以希望这篇博客能帮助你快速整合Spring Cloud和reactive feign。笔者在参考资料极度匮乏的情况下总结出这些经验希望后面的人少走弯路。如果文中有错误或不足的地方,希望能在评论区指出,大家共同进步。

转载请注明出处

 

 

参考资料

 

上一篇:Spring Cloud Gateway整合服务发现(遇到的一点坑)
下一篇:为什么使用Spring Reactor和Spring WebFlux?什么情况下需要使用?

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2025年03月13日 03时52分53秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章