当前位置: 首页 > news >正文

gateway配置自定义转发

需求背景:

项目上想要在线查看日志,但是又不想搭建elk服务组件。咋解决呢? 所有的项目都引入一个日志模块,能拉取当前项目下的日志文件目录和提供在线查看和下载的功能。

对外暴露出来的服务只有一个,那么就需要根据标识信息进行转发了,我们又两个路由转发负载均衡的组件,一个是gateway,一个是nginx。 本文重点讲一下gateway。

核心思路就是header中携带机器标识,当负载均衡进行转发时,如果能匹配到对应的header就转发到当前服务器。

功能验证

项目搭建:

第一步肯定是搭建一个demo项目,验证方案的可行性。
关键包引入:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 确保引入 Spring Cloud LoadBalancer -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

找到路由转发的处理类:

第一步定位当前服务谁在进行服务列表的筛选,我们去更改这个类做增强。

我们的项目是gateway项目,肯定首先去看GlobalFilter,gateway通过它的拦截器来实现地址转换,lb://xxx转换成http://xxxx.
查询GlobalFilter的实现类,发现一个带有关键字:ReactiveLoadBalancerClientFilter(loadBalancer=负载均衡),断点打在这儿,去看一下业务处理。里面有lb的转换,就是它了。

找到进行节点选择的处理类:

翻译一下源码或者顺着断点多点几次,发现一个choose方法,入参是serviceId返回是ServiceInstance, serviceInstance里面盛放的需要转发到的ip端口等关键信息。这个核心的功能是谁在实现呢?看到注入的类是ReactorLoadBalancer,看看里面有两个实现一个是随机数,一个是轮询。通常默认的是轮询。

自己写负载均衡类:

copy轮询的类(RoundRobinLoadBalancer)出来改一下名字,当做我们自己的类。里面逻辑很简单,通过AtomicInteger+1来趋于节点数进行获取。

我们更改为根据header的关键信息进行获取。核心代码如下。

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request<RequestDataContext> request) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + serviceId);}return new EmptyResponse();}if (instances.size() == 1) {return new DefaultResponse(instances.get(0));}// 新增根据header 进行路由选择--startList<String> targetServiceList = request.getContext().getClientRequest().getHeaders().get("X-Target-Server");if(targetServiceList!=null && targetServiceList.size()>0){Optional<ServiceInstance> first = instances.stream().filter(action -> targetServiceList.contains(String.format("%s:%s", action.getHost(), action.getPort()))).findFirst();if(first.isPresent()){return new DefaultResponse(first.get());}}// 新增根据header 进行路由选择--endint pos = this.position.incrementAndGet() & Integer.MAX_VALUE;ServiceInstance instance = instances.get(pos % instances.size());return new DefaultResponse(instance);}

你说它的入参列表只有instances,没有header?这个是我们自己创建的类这些内部方法随便改出入参,从choose里面延续的传递下来就行。

我以为我把核心功能完成了,剩下的注入到容器让他生效就行了,没想到我还是太年轻了。

----------------------------------------------------华丽的分割线-----------------------------------------------------------

让自己写的负载均衡类生效

看一下框架里面的轮询类怎么生效的?哦,通过类(LoadBalancerClientConfiguration),注入进来的。而且他写的是@ConditionalOnMissingBean,见下图:

我们仿照它这段写一个,去掉@ConditionalOnMissingBean注解,写@Bean创建我们自己的类,让我们的类优先加载。

问题分析:

报错:空指针

ClientFactoryObjectProvider 对应的name为空,这个name从哪儿来的呢?我们自己写的类初始化的时候传递的。上面截图的name. name从哪儿来的呢?从环境变量里面获取的字段:loadbalancer.client.name, 
先在配置文件写上这个字段验证一下,嗯,写的我测试的这个serviceId能正常访问。这个东西不应该是在配置文件写死的啊?我可能路由不通的项目,写死一个固定的servcieId肯定不行。试试别的项目呢?

嗯, 不出意外的果然不行,访问的时候找不到服务列表,servcieId传递的值错误了。一直传递我配置文件的servcieId。这样肯定是不行的,删除配置文件,回到了原点。

额,遇到难题了,我尝试网上搜索一下。照着这个样例写的也是不生效,待会再说异常原因。
以下截图是踩坑的错误例子,不要这样写,正确写法,看最后结论。

呼,平心静气看一下代码逻辑。

逻辑二次梳理

要实现的功能是轮询的负载均衡,根据什么进行轮询?根据服务标识(servcieId)进行轮询,类里面的全局变量定义了轮询的位置信息,所以每个servcieId应该是对应的是一个类。这样当这个servcieId进行下次进行访问的时候,才能在上次访问的基础上去访问下一个机器。所以我把自己写的策略类,直接扔到spring容器中是不对的。

线上验证, 去掉我的实现,走默认实现,查看spring容器中是否有注入默认的实现类(RoundRobinLoadBalancer)

断点分析

断点打到RoundRobinLoadBalancer的构造方法,看看是哪个地方触发的类创建?项目启动的时候没有进入断点,访问时进入断点,我们看看怎么触发过来的。找找为什么会调用这个类(LoadBalancerClientConfiguration)来创建对象(RoundRobinLoadBalancer),我们着重关心关键词LoadBalancerClientConfiguration,因为这个是构建这个bean的关键类。我们会看到factoryBeanName就是这个名称,用这个类当做工厂来创建轮询负载均衡的类。入口类是LoadBalancerClientFactory

分析一下工厂类

LoadBalancerClientFactory类在进行getInstances方法的时候触发的类加载,contexts很熟悉的上下文,还是一个本地的map,看一下这个属性所在的类(NamedContextFactory)。根据名称来创建的上下文工厂。和我们刚才的想法是一致的,根据不同的servcieId创建这个servcieId对应的上下文。LoadBalancerClientFactory被spring管理,我们找一下它的构建类。有了新的发现。

构建工厂类,根据配置项构建,断点打上看看配置项有没有对应的关键词:LoadBalancerClientConfiguration
下面一个setConfigurations信息,看看里面有没有关键词:LoadBalancerClientConfiguration
这个configurations就是我们要找的类。这个地方注入了类:LoadBalancerClientSpecification声明了这个工厂类加载的时候根据哪些类进行加载。 

找一下类LoadBalancerClientSpecification加载

找啊找找到了我们之前错误截图的注解@LoadBalancerClients
这个能决定要根据那个配置进行加载。它能创建LoadBalancerClientSpecification。
我们指向谁?指向刚才写的配置加载类,但是配置加载类被spring管理,导致我们自定义的负载类被加载产生异常,

正确结论

拆分成为两个类。 一个被spring管理的类,添加注解@LoadBalancerClients指向另外一个普通类。

普通类内注入我们自定义的负载类,见下图。

http://www.dtcms.com/a/553767.html

相关文章:

  • 【洛谷】枚举专题-普通枚举 经典题解之铺地毯、回文日期、扫雷
  • GPU机器-显卡占用
  • 网站关键词抓取id wordpress
  • 学校网站 建设措施西地那非片能延时多久每次吃多少
  • Adobe Lightroom Classic 2026 v15.0 更新详解:AI加持下的全新摄影工作流
  • 蚂蚁S19j XP 117T矿机技术分析:适合BTC与BCH挖矿的高效选择
  • 单元测试、集成测试和系统测试的联系和区别是什么?
  • 做旅行社的都是在哪网站拿票办一个网站要多少钱
  • 青岛网站推广方案查看网站是否做百度推广
  • IDEA多实例项目启动模拟负载均衡
  • maven进阶了解
  • Android 嵌入h5顶部状态栏空白
  • 网页制作可以用手机吗江门网站排名优化
  • 营销型网站核心要素有哪些网站建设费税率是多少钱
  • IDEA的安装与设置
  • Fuzzing 工具来一波
  • 10.31
  • 网站后台更新 前台不显示金蝶财务软件
  • Spring Boot项目的核心依赖
  • ollama本地化部署deepseek/大模型及其api流式调用
  • 向华为学习——53页华为制造行业数字化转型工业互联网智能制造解决方案【附全文阅读】
  • 基于电鱼 ARM 工控机的煤矿主控系统高可靠运行方案——让井下控制系统告别“死机与重启”
  • 顶尖网站建设国内大宗商品交易平台有哪些
  • 用langchain搭建简单agent
  • 在 Windows 11 中安装 VirtualBox 7.2.4
  • 【开题答辩全过程】以 基于Java的社交健身系统的设计与实现为例,包含答辩的问题和答案
  • Ubuntu20.04升级autoconf
  • 网站名注册最佳商城ui网站设计
  • R包kuenm和ENMeval--你用对了吗?
  • 接口自动化测试项目框架详解