【深入解析spring cloud gateway】13 Reactive Feign的使用

问题引入

在gateway中如果使用feignClient的话,会报如下错误

java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.4.15.jar:3.4.15]Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]*__checkpoint ⇢ HTTP GET "/get" [ExceptionHandlingWebHandler]

其报错的原因是:网关的reactive线程模型,并不支持像openfeign这样的阻塞IO的操作。
网上给出了一种解决方案

解决方案之一

方案1:自定义一个BlockingLoadBalancerClient.java Bean覆盖原有Bean

step1:创建自定义类CustomBlockingLoadBalancerClient.java
CustomBlockingLoadBalancerClient.java继承BlockingLoadBalancerClient.java,并重写方法BlockingLoadBalancerClient#choose(java.lang.String, org.springframework.cloud.client.loadbalancer.Request)

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import reactor.core.publisher.Mono;import java.util.concurrent.CompletableFuture;/*** @Author: ekko* @Description: 自定义CustomBlockingLoadBalancerClient.java* @Date: 2023/9/20 16:16*/
public class CustomBlockingLoadBalancerClient extends BlockingLoadBalancerClient {private final ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory;public CustomBlockingLoadBalancerClient(LoadBalancerClientFactory loadBalancerClientFactory, LoadBalancerProperties properties) {super(loadBalancerClientFactory, properties);this.loadBalancerClientFactory = loadBalancerClientFactory;}@Overridepublic <T> ServiceInstance choose(String serviceId, Request<T> request) {ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);if (loadBalancer == null) {return null;}CompletableFuture<Response<ServiceInstance>> f = CompletableFuture.supplyAsync(() -> {Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();return loadBalancerResponse;});Response<ServiceInstance> loadBalancerResponse = null;try {loadBalancerResponse = f.get();} catch (InterruptedException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}if (loadBalancerResponse == null) {return null;}return loadBalancerResponse.getServer();}
}

step2: 创建BlockingLoadBalancerClient类
将自定义CustomBlockingLoadBalancerClient注入到容器中

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Author: ekko* @Description: BlockingLoadBalancerClient配置类,创建自定义的BlockingLoadBalancerClient Bean* @Date: 2023/9/20 16:31*/
@Configuration
public class BlockingLoadBalancerClientConfig {@AutowiredLoadBalancerClientFactory loadBalancerClientFactory;@AutowiredLoadBalancerProperties properties;@Beanpublic LoadBalancerClient BlockingLoadBalancerClient() {return new CustomBlockingLoadBalancerClient(loadBalancerClientFactory, properties);}
}

这种方式可以解决报错的问题。BUT,在gateway网关中强行使用feignClient,同步调用,其实是有风险的一个事情。假设feignClient的下游服务,由于某些原因导致性能变慢。而gateway是同步阻塞式的调用。那么gateway的主线程也会被阻塞。由于gateway底层实际上就是netty的线程池,有两个线程池(主从多线程模型)这种模型使用一个独立的线程池来处理连接请求(Acceptor),而I/O操作则由另一个线程池处理。这种方式可以减少连接请求处理对I/O操作的干扰,提高系统并发性能。
在这里插入图片描述

更优雅的解决方案-feign-reactive

好了,切入正题,优雅的解决方案,当然还是回归到使用Reactive 异步调用的feign了。
看看官网是怎么说的:
https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html

As the OpenFeign project does not currently support reactive clients, such as Spring WebClient, neither does Spring Cloud OpenFeign.SinceSpring Cloud OpenFeign project is now considered feature-complete, we’re not planning on adding support even if it becomes available in the upstream project. We suggest migrating over to Spring Interface Clients instead. Both blocking and reactive stacks are supported there.Until that is done, we recommend using feign-reactive for Spring WebClient support.

翻译一下就是,open-feign并不支持reactive异步客户端。如果要使用reactive 异步客户端,请移步 feign-reactive
网上关于feign-reactive这方面的介绍实在太少,这里做一下介绍吧。

引入依赖

 <artifactId>gateway</artifactId><properties><reactor.feign.version>3.2.6</reactor.feign.version></properties><dependencies><!--reactivefeign--><dependency><groupId>com.playtika.reactivefeign</groupId><artifactId>feign-reactor-spring-configuration</artifactId><version>${reactor.feign.version}</version></dependency><dependency><groupId>com.playtika.reactivefeign</groupId><artifactId>feign-reactor-webclient</artifactId><version>${reactor.feign.version}</version></dependency><dependency><groupId>com.playtika.reactivefeign</groupId><artifactId>feign-reactor-cloud</artifactId><version>${reactor.feign.version}</version></dependency><!--reactivefeign-->

定义reactivefeign的client接口

@ReactiveFeignClient(name = "hello-service")
public interface TestFeignClient {@RequestMapping(value = "/hello1", method = RequestMethod.GET)Mono<String> hello(@RequestParam("name") String name);
}

返回结果一定要用Mono包装。

其下游对应着一个hello-service的接口,如下所示

    @RequestMapping(value = "/hello1", method = RequestMethod.GET)public String hello(@RequestParam("name") String name) {return helloService.hello(name);}

启用reactivefeign的client接口

@Configuration(proxyBeanMethods = false)
@EnableReactiveFeignClients(clients = {TestFeignClient.class})
public class FeignClientsConfig {}

在filter中调用feignClient

@Slf4j
public class FeignTestFilter implements GlobalFilter, Ordered {private final TestFeignClient testFeignClient;public FeignTestFilter(TestFeignClient testFeignClient) {this.testFeignClient = testFeignClient;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {Mono<String> mono = testFeignClient.hello("zhangsan");return mono.doOnNext(e -> {log.info("feignClient请求结果是:" + e);}).then(chain.filter(exchange));}@Overridepublic int getOrder() {return 0;}
}

这里由于是reactive的写法。如果想先处理返回值 ,处理完后,再执行后续的filter,那么写法就如上面代码。
如果不太会这种写法,请参考Reactive 官网相关的文档。

调用效果

通过网关访问任意下游的接口,日志输出如下
在这里插入图片描述

总结

  • 从使用方式来看,其实reactivefeign和openFeign很相似,RestFull的接口和我们平常写的注解一样。
  • 注意返回值需要用Mono封装。在处理请求结果时,也要用reactive的写法。
  • 通过这种reactive的写法,当我们下游feign调用的微服务变慢,并不会影响gateway的主线程,并不会拖垮网关

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/630401.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git 删除本地分支 删除远程仓库中的分支

语法&#xff1a; 删除本地分支 git branch -D <分支名>删除远程分支 git push <remote名称> <分支名> --delete 示例&#xff1a; 删除本地分支 git branch -D feature/test_listview删除远程分支 git push origin feature/test_listview --delete 两个…

OpenHarmony南向开发案例:【智能照相机】

样例简介 本Demo是基于Hi3516开发板&#xff0c;使用OpenHarmony3.0-LTS开发的应用。通过获取摄像头数据&#xff0c;实现预览拍照以及路视频等功能。并且通过后台AI服务识别唤醒词来进行语音控制拍照及录制视频。 应用运行效果图&#xff1a; 此为相机的预览界面。 样例原理…

【MATLAB源码-第46期】基于matlab的OFDM系统多径数目对比,有无CP(循环前缀)对比,有无信道均衡对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OFDM&#xff08;正交频分复用&#xff09;是一种频域上的多载波调制技术&#xff0c;经常用于高速数据通信中。以下是关于多径数目、有无CP&#xff08;循环前缀&#xff09;以及有无信道均衡在OFDM系统中对误码率的影响&am…

聊聊binlog是什么

1. 上一讲思考題解答:redo日志刷盘策略的选择建议 先给大家解释一下上一讲的思考題&#xff0c;我给大家的一个建议&#xff0c;其实对于redo日志的三种刷盘策略&#xff0c;我们通常建议是设置为1 也就是说&#xff0c;提交事务的时候&#xff0c;redo日志必须是刷入磁盘文件…

Golang入门基础

文章目录 Golang的背景知识Golang的发展历程Golang的特点Golang的应用领域 开发环境搭建下载并安装SDK包设置环境变量Go项目目录结构 注释变量标识符命名输入和输出运算符算术运算符关系运算符逻辑运算符赋值运算符位运算符其他运算符 Golang的背景知识 Golang的发展历程 Gola…

postgresql数据库pg_dirtyread插件闪回技术 —— 筑梦之路

闪回查询&#xff08;Flashback Query&#xff09;是一种在数据库中执行时间点查询的技术。它允许查询数据库中过去某个时间点的数据状态&#xff0c;并返回相应的查询结果。通常闪回查询分为表级以及行级的闪回查询。PostgreSQL数据库由于MVCC的机制&#xff0c;对于DML的操作…

[生活][杂项] 上班党的注意事项

前言 目前是上班已经接近两年了&#xff0c;目前的状态是&#xff0c;一个人租了一个单间在上班。对于这种情况有以下几点需要注意。 钥匙问题&#xff0c;一定不要陷入钥匙丢失的情况&#xff01;一定不要陷入钥匙丢失的情况&#xff01;一定不要陷入钥匙丢失的情况&#xff…

密码学 | 椭圆曲线密码学 ECC 入门(三)

目录 7 这一切意味着什么&#xff1f; 8 椭圆曲线密码学的应用 9 椭圆曲线密码学的缺点 10 展望未来 ⚠️ 原文地址&#xff1a;A (Relatively Easy To Understand) Primer on Elliptic Curve Cryptography ⚠️ 写在前面&#xff1a;本文属搬运博客&#xff0c;自己留…

解决VirtualBox虚拟机启动失败的问题

一.出现的问题&#xff08;未能启动虚拟电脑&#xff0c;由于物理网卡未找到&#xff09; 一、错误信息分析 “未能启动虚拟电脑&#xff0c;由于物理网卡未找到”&#xff1a;这个错误通常是由于VirtualBox无法识别或连接到物理网卡造成的。可能是由于驱动程序问题、网络设置错…

用一个寓言故事讲明白KMP算法

在讲寓言故事之前&#xff0c;先讲一下理论的前提知识&#xff0c;避免有些0基础而无法理解 前提知识 首先KMP算法是指在串中&#xff0c;想要快速找出主串里跟我们的模板串一样的位置的一种算法&#xff0c;其主要是解决最普通的BF算法中主串指针回溯的问题。 BF算法就是一…

Windows Quick Suffix Classification 文件快速分拣工具 v1.0 绿色便携版

在电脑中使用的文件自动分拣软件&#xff0c;该软件支持自定义的配置&#xff0c;自动按照文件扩展名自动分拣移动文件到指定的文件夹等&#xff0c;本站提供的是这款软件的绿色版本。 使用说明 1.指定后缀处理并复制&#xff1a; 将第一个输入框输入的目录&#xff0c;查找第…

Linux shell 脚本基础与部署SpringCloud实战

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…