这是我参与更文挑战的第2天,活动详情查看: 更文挑战
总文档 :文章目录
Github : github.com/black-ant
一 . 前言
这是Feign 系列文章中的一篇 , 主要说明 Feign 关于负载均衡的相关知识点 , 文章包含如下内容 :
- Feign 使用 Ribbon 时的负载均衡处理
二 . Feign 的轮询策略
Feign 其本身有一套轮询的逻辑处理 Server 的调用 , 其主要流程如下 :
- Step 1 : 调用 submit 获取 server 信息
- Step 2 : selectServer 选择 Server
- Step 3 : 回调执行 ServerOperation 中的请求发起
前期调用逻辑
为了方便理解 , 我们来看一下其之前的调用逻辑 , Feign 主要是通过 Invoke 的方式发起方法的代理 :
- Step 1 : 触发 InvoKe (FeignInvocationHandler)
- Step 2 : SynchronousMethodHandler 的调用 , 处理对于方法逻辑
- Step 3 : LoadBalancerFeignClient 调用主逻辑
- Step 4 : AbstractLoadBalancerAwareClient 中间过渡 , 处理 URL , 生成真正的URL
- Step 5 : 最终调用的是 Client 的内部类 (Default)
2.1 获取 Server 入口
可以看到 , 在如上第四步中 , 开始负载均衡的相关处理 :
executeWithLoadBalancer 是 AbstractLoadBalancerAwareClient 中的负载均衡方法 , 该类位于 com.netflix.loadbalancer 包中
PS : OpenFeign 本身就携带了 Ribbon 依赖 !
首先 , 我们来看一下 executeWithLoadBalancer 主逻辑 , 这里 Submit 是一个Function 语法糖调用 :
1 | java复制代码C- AbstractLoadBalancerAwareClient |
接下来看一下 Submit 方法中做了什么事情 :
- Step 1 : 容器准备
- Step 2 : 负载均衡监听器开启
- Step 3 : 重试次数设置
- maxRetrysSame : 在一台服务器上执行的最大重试次数
- maxRetrysNext : 要重试的最大不同服务器的数量
- Step 4 : 使用负载均衡器获得 Server ]
- Step 5 : 重试策略的处理
- Step 6 : 流程异常处理
1 | java复制代码// C- LoadBalancerCommand : 做过魔改 , 仅展示核心的代码 |
重点 : 在Step 4 中 , selectServer 就已经完成负载均衡的选择处理了 , 其后在 concatMap (Function 1)中进行了进一步的操作 :
[Pro] : Function:001 中干了什么 ?
总结: 简单来说 , 就是数据审计 , 监听器触发和方法扩展 , 用于后续的回调
1 | java复制代码C- LoadBalancerCommand |
[Pro] : Function:002 中干了什么 ?
Function 2 是异常处理流程 , 其中主要干了三件事 :
- 通过不同的重试配置 , 构建不同的 ClientException
- listenerInvoker 存在 , 触发事件
- 返回一个观察者对象 , 当观察者订阅了这个Observable时,它会调用Observer的Observer#onError方法
1 | java复制代码C- LoadBalancerCommand |
2.2 selectServer 选择 Server
前面说了 , 负载均衡的核心就是 SelectServer :
1 | java复制代码C- LoadBalancerCommand |
2.3 getServerFromLoadBalancer 主逻辑
这一段逻辑比较长 , 但是核心代码不多 , 我们仅保留其中最核心的几个部分
1 | java复制代码C- LoadBalancerContext |
2.4 chooseServer 中通过 Rule 选择 Server
此处就是最终算法的使用点 , 通过 Rule 调用最终的策略进行处理 :
1 | java复制代码C- BaseLoadBalancer |
Rule 体系中提供了完善的体系 , 来看一下 Rule 体系的完整结构 :
PS : 这里主要使用 PredicateBasedRule
2.5 PredicateBasedRule 案例分析
其中有几个主要的逻辑 :
- lb.getAllServers() : 获取所有的Server
- getEligibleServers(servers, loadBalancerKey) :
- incrementAndGetModulo(eligible.size()) :
Step 1 : choose 选择 Server 可以看到 , 这里是通过 Predicate 进行具体的选择
1 | java复制代码C- PredicateBasedRule |
Step 2 : Server 选择主方法
在该方法中首先获取一个正确的 Server List (Step 3), 在使用算法选择具体的 Server (Step 4)
1 | java复制代码C- PredicateBasedRule |
Step 3 : 获取正确的 Server List
1 | java复制代码C- PredicateBasedRule |
Step 4 : 使用算法选择具体的 Server
1 | java复制代码C- PredicateBasedRule |
2.6 执行 ServerOperation (Function:003)
当Submit 执行Server 处理时(详见 -> UNDO:001) , 就会回调之前准备的观察者对象 , 此处通过 Server 生成自己需要的 URL , 完成负载均衡的处理
这里也是一种观察者的使用 , 在所有的处理完成后 , 通过订阅的方式执行后续逻辑代码 >>
1 | java复制代码C- AbstractLoadBalancerAwareClient |
其他
问题一 : 观察者及 Function 的调用逻辑
Feign 负载均衡中一大麻烦就是观察者的使用及Function 语法糖 , 这种回调模式很容易混淆整个逻辑 :
- Step 1 : LoadBalancerCommand 中取得 Server 对象
- Step 2 : Function:002 中进行 Server 的二次处理
- Step 3 : Observable:002 中执行监听器和计时
- Step 4 : 回到 AbstractLoadBalancerAwareClient 中的 Function:003 生成 Url 及后续逻辑
- Step 5 : Observable:003 中 onNext 触发监听器和设置 entity 实体
- Step 6 : Observable:003 中 onCompleted 对请求完成进行订阅处理
问题二 : ExecutionListener 的使用
- 作用 : 负载均衡器在执行的不同阶段调用的监听器
- 使用 : 提供了如下几种方法
- onExecutionStart : 启动
- onStartWithServer : 当选择服务器并且请求将在服务器上执行时调用
- onExceptionWithServer : Server 出现异常
- onExecutionSuccess : 执行成功
- onExecutionFailed : 执行失败
1 | java复制代码public void onExecutionStart(ExecutionContext<I> context) { |
总结
这篇文章力求把负载均衡的逻辑理清楚 , 现在看基本上满足了要求 , 其实整个过程中还有很多可以思考的地方
例如 :
- Feign 中多观察者对象的使用 , 虽然可读性变差了 ,但是业务能力提升了不少 , 如何更好的设计这套逻辑
- Feign Ribbon Rule 的使用 ,是否可以自行定制一套或者通过扩展的方式来外界算法
这些东西都会在后续的文章中 , 进行分析 , 拭目以待.
本文转载自: 掘金