探活机制分析
探活即用来和后端代理的相关服务进行心跳检测,以确定其是否能够进行正常服务,由于该部分功能不在整个调用链上,因此首先进行分析
- 探活服务默认在soul-admin开启,相关类为UpstreamCheckService,在该类被spring自动装配后,会进行启动线程对后端服务进行检查,默认每隔10s检查一次
检查时,如果发现服务不可用时,则会从数据库将该服务剔除,并且发布相关事件,通过数据同步至网关,网关收到后,会同步缓存中相关服务,相关关键代码如下:
private void check(final String selectorName, final List<DivideUpstream> upstreamList) {
List<DivideUpstream> successList = Lists.newArrayListWithCapacity(upstreamList.size());
for (DivideUpstream divideUpstream : upstreamList) {//会逐个判断代理的服务
final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl());//通过socket探活
if (pass) {
//前后代码省略
successList.add(divideUpstream);
}
//代码省略,
if (successList.size() == upstreamList.size()) {
return;
}
if (successList.size() > 0) {
UPSTREAM_MAP.put(selectorName, successList);
updateSelectorHandler(selectorName, successList);
} else {
UPSTREAM_MAP.remove(selectorName);
updateSelectorHandler(selectorName, null);//更新数据库,并发布相关事件
}
}
在网关侧,通过CommonPluginDataSubscriber中的subscribeDataHandler方法根据相关操作事件分发至DividePluginDataHandler中的相关方法中,
关键类UpstreamCacheManager,该类主要作用是更新或删除网关内存中对应的代理服务,该类中同样也存在探活检测,默认30s,但是不开启,探活过程大致与soul-admin一致,不同的是,采用了双map缓存机制,一个保存全量代理,一个保存活的代理放在临时map中,这样设计应该是保证网络有问题时,摘除服务,故障排除后会再次加入。soul-admin如遇故障必须重启后才能加入。一般情况下探活机制只用在soul-admin开启就行,减轻网关压力
调用链分析
url请求过来后,在soul中的相关入口处为SoulWebHandler类,soul-admin默认会引入大部分插件,插件通过boot-starter最终会被注入进SoulWebHandler类中,关键代码
前后加入的是监控相关的代码,暂时不分析,主要看DefaultSoulPluginChain类,为内部类,插件执行链在该类的方法中
可以看出,该方法是按照调用链根据相关条件会一直往下调用,插件调用时是具有顺序的,插件顺序在PluginEnum类中
public enum PluginEnum {
GLOBAL(1, 0, "global"),
SIGN(2, 0, "sign"),
WAF(10, 0, "waf"),
RATE_LIMITER(20, 0, "rate_limiter"),
CONTEXTPATH_MAPPING(25, 0, "context_path"),
REWRITE(30, 0, "rewrite"),
REDIRECT(40, 0, "redirect"),
HYSTRIX(45, 0, "hystrix"),
SENTINEL(45, 0, "sentinel"),
RESILIENCE4J(45, 0, "resilience4j"),
DIVIDE(50, 0, "divide"),
SPRING_CLOUD(50, 0, "springCloud"),
根据插件顺序可以发现,先走的是global插件,全局插件,该插件会做一些通用处理,区分请求的协议,并存储在上下文中,供后续插件使用,代码不再贴出,现在直接分析divide插件,注意divide插件和springclcoud插件的顺序是一致的,这点很关键,先看divide插件处理链
该方法为divide插件入口核心方法,主要作用是从缓存中拿到后端代理,通过负载均衡拿到对应的url,并将相关信息put到exchange中,供后续插件使用,那么是哪个插件来负责发起代理请求,经过分析可知下一个插件是soul-plugin-httpcient插件,有两种实现,WebClientPlugin和NettyHttpClient,默认使用WebClientPlugin,主要该插件的顺序:
会紧跟着divide插件,该插件负责具体发起代理请求,由于springcloud和divide插件都需要被代理请求,这也就解释了springcloud和divide插件的顺序为什么是一致的,而且需要在soul-plugin-httpcient插件前边。后续流程之后再分析
负载均衡算法
divide插件提供了三种负载均衡算法
- HashLoadBalance(hash算法)
- RandomLoadBalance(随机算法)
- RoundRobinLoadBalance(轮询算法)
通过spi机制注入,soul自定义了加载spi的相关类ExtensionLoader,具体相关实现过程后续分析
与springcloud调用对比
springcloud与divide插件处理过程基本一致,这里贴出相关代码
不同的点是画红线处,divide插件的后端代理直接注册在soul-admin上,网关可以直接拿到并通过均衡负载算出,springcloud的网关拿不到,只能通过springcloud提供的相关负载均衡客户端方法去相关注册中心去拿到相关的服务,并获取相关url等信息放到exchange中,后续流程与divide插件一致