You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
4.1 KiB
93 lines
4.1 KiB
5 years ago
|
# 原理分析之预发布灰度发布
|
||
|
|
||
|
SOP网关采用`自定义负载均衡策略`来实现对预发布/灰度发布服务器实例的选择。
|
||
|
|
||
|
spring cloud gateway默认的负载均衡实现类在:`org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.java`中
|
||
|
|
||
|
这个类主要做了几件事情:
|
||
|
|
||
|
1. 解析出请求路径中的scheme
|
||
|
2. 如果scheme不是以`lb`协议开头直接跳过
|
||
|
3. 如果scheme以`lb`协议开头,则说明需要进行负载均衡,选出一台微服务实例
|
||
|
4. 将`lb`协议解析成`http://ip:port`,继续向下请求
|
||
|
|
||
|
其中第4步是由`org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient`来完成的,
|
||
|
我们只要分别继承`LoadBalancerClientFilter`和`RibbonLoadBalancerClient`,然后重写其中的方法就能完成自定义负载均衡。
|
||
|
|
||
|
SOP中的重写类是:`SopLoadBalancerClientFilter`和`SopLoadBalancerClient`,核心代码委托给了`LoadBalanceServerChooser`处理,核心代码如下:
|
||
|
|
||
|
```java
|
||
|
/**
|
||
|
* 选择服务器
|
||
|
*
|
||
|
* @param serviceId serviceId,仅gateway网关有作用
|
||
|
* @param exchange 请求上下文
|
||
|
* @param loadBalancer loadBalancer
|
||
|
* @param superChooser 父类默认的选择
|
||
|
* @param serverChooserFunction 执行选择操作
|
||
|
* @return 返回服务器实例,没有选到则返回null
|
||
|
*/
|
||
|
public R choose(
|
||
|
String serviceId
|
||
|
, T exchange
|
||
|
, ILoadBalancer loadBalancer
|
||
|
, Supplier<R> superChooser
|
||
|
, Function<List<Server>, R> serverChooserFunction) {
|
||
|
// 获取所有服务实例
|
||
|
List<Server> servers = loadBalancer.getReachableServers();
|
||
|
|
||
|
// 存放预发服务器
|
||
|
List<Server> preServers = new ArrayList<>(4);
|
||
|
// 存放灰度发布服务器
|
||
|
List<Server> grayServers = new ArrayList<>(4);
|
||
|
// 存放非预发服务器
|
||
|
List<Server> notPreServers = new ArrayList<>(4);
|
||
|
|
||
|
for (Server server : servers) {
|
||
|
// 获取实例metadata
|
||
|
Map<String, String> metadata = getMetadata(serviceId, server);
|
||
|
// 是否开启了预发模式
|
||
|
if (this.isPreServer(metadata)) {
|
||
|
preServers.add(server);
|
||
|
} else if (this.isGrayServer(metadata)) {
|
||
|
grayServers.add(server);
|
||
|
} else {
|
||
|
notPreServers.add(server);
|
||
|
}
|
||
|
}
|
||
|
notPreServers.addAll(grayServers);
|
||
|
// 如果没有开启预发布服务和灰度发布,直接用默认的方式
|
||
|
if (preServers.isEmpty() && grayServers.isEmpty()) {
|
||
|
return superChooser.get();
|
||
|
}
|
||
|
// 如果是从预发布域名访问过来,则认为是预发布请求,选出预发服务器
|
||
|
if (this.isRequestFromPreDomain(exchange)) {
|
||
|
return serverChooserFunction.apply(preServers);
|
||
|
}
|
||
|
// 如果是灰度请求,则认为是灰度用户,选出灰度服务器
|
||
|
if (this.isRequestGrayServer(exchange)) {
|
||
|
return serverChooserFunction.apply(grayServers);
|
||
|
}
|
||
|
|
||
|
// 到这里说明不能访问预发/灰度服务器,则需要路由到非预发服务器
|
||
|
// 注意:这里允许走灰度服务器,如果不允许走,注释notPreServers.addAll(grayServers);这行
|
||
|
return serverChooserFunction.apply(notPreServers);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
其业务逻辑如下:
|
||
|
|
||
|
1. 选出`serviceId`对应的所有服务器实例
|
||
|
2. 将服务器实例进行分类,分别放进`预发布List`,`灰度List`,`非预发布List`中
|
||
|
3. 如果`预发布List`,`灰度List`都为空,表示没有开启任何预发/灰度服务,直接使用父类的负载均衡策略
|
||
|
4. 如果是从预发布域名访问过来,则认为是预发布请求,选出预发服务器
|
||
|
5. 如果是灰度请求,则认为是灰度用户,选出灰度服务器
|
||
|
6. 最后剩下的是正常用户,正常用户不能走预发环境
|
||
|
|
||
|
## 参考类
|
||
|
|
||
|
- com.gitee.sop.gatewaycommon.gateway.filter.SopLoadBalancerClientFilter
|
||
|
- com.gitee.sop.gatewaycommon.gateway.loadbalancer.SopLoadBalancerClient
|
||
|
- com.gitee.sop.gatewaycommon.gateway.loadbalancer.GatewayLoadBalanceServerChooser
|
||
|
|