http服务探活
官方文档写的是(官方文档地址):
ip + port 检测
在soul-admin 会有一个定时任务来扫描 配置的ip端口,如果发现下线,则会去除该 ip + port
还有两个配置可以控制是否检测和检测间隔时间
soul.upstream.check:true 默认为 ture,设置为false,不检测
soul.upstream.scheduledTime:10 定时检测时间间隔,默认10秒
admin探活
我们找到 soul.upstream.check 配置的地方,发现是 UpstreamCheckService
// UpstreamCheckService.java
public void setup() {
PluginDO pluginDO = pluginMapper.selectByName(PluginEnum.DIVIDE.getName());
// 判断divide插件是否存在
if (pluginDO != null) {
// 从数据库查询是否有http服务已注册
List<SelectorDO> selectorDOList = selectorMapper.findByPluginId(pluginDO.getId());
...
if (CollectionUtils.isNotEmpty(divideUpstreams)) {
// 把http服务节点存到本地缓存,已selector的name为key
UPSTREAM_MAP.put(selectorDO.getName(), divideUpstreams);
}
}
if (check) {
// 如果设置了探活,就新建一个每10s执行一次任务的线程池去检测http服务是否存活
new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), SoulThreadFactory.create("scheduled-upstream-task", false))
.scheduleWithFixedDelay(this::scheduled, 10, scheduledTime, TimeUnit.SECONDS);
}
}
scheduled会循环调用check()
// UpstreamCheckService.java
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());
}
...
}
// 探活
public static boolean checkUrl(final String url) {
...
if (checkIP(url)) {
return isHostConnector(hostPort[0], Integer.parseInt(hostPort[1]));
} else {
return isHostReachable(url);
}
}
我们在上面的 setup() 打上断点,然后启动 admin
发现,断点确实走进来了,因为 http 服务没启动,所以本地缓存为空。
服务注册
启动 soul-examples-http 服务
断点到 check 里面,一会就发现本地缓存里面又启动的 http 服务节点了,然后我们去找更新缓存的地方。
找到了 submit()
往上找,是 SoulClientRegisterServiceImpl#handlerSpringMvcSelector
继续往上是 SoulClientRegisterServiceImpl#registerSpringMvc
再往上就是一个接口
// SoulClientController.java
@PostMapping("/springmvc-register")
public String registerSpringMvc(@RequestBody final SpringMvcRegisterDTO springMvcRegisterDTO) {
return soulClientRegisterService.registerSpringMvc(springMvcRegisterDTO);
}
看到这里,大家就知道了,http 服务在启动之后,会调用这个接口把自己注册到 admin。
我们继续去找 http 服务注册自己的入口。
发现 SpringCloudClientBeanPostProcessor#postProcessAfterInitialization
这个函数里面会在每个 bean 初始化之后,去判断这个 bean 是不是 http 服务
// SpringCloudClientBeanPostProcessor.java
Controller controller = AnnotationUtils.findAnnotation(bean.getClass(), Controller.class);
RestController restController = AnnotationUtils.findAnnotation(bean.getClass(), RestController.class);
RequestMapping requestMapping = AnnotationUtils.findAnnotation(bean.getClass(), RequestMapping.class);
if (controller != null || restController != null || requestMapping != null) {
...
executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(clazzAnnotation, finalPrePath), url,
RpcTypeEnum.SPRING_CLOUD));
return bean;
}
...
然后向 admin 注册,下面是注册的代码。
public static void doRegister(final String json, final String url, final RpcTypeEnum rpcTypeEnum) {
String result = OkHttpTools.getInstance().post(url, json);
if (AdminConstants.SUCCESS.equals(result)) {
log.info("{} client register success: {} ", rpcTypeEnum.getName(), json);
}
...
我们启动http服务后也可以看到以下日志
2021-01-29 01:16:49.503 INFO 14500 --- [pool-1-thread-1] o.d.s.client.common.utils.RegisterUtils : http client register success: {"appName":"http","context":"/http","path":"/http/order/path/**","pathDesc":"","rpcType":"http","host":"172.17.250.209","port":8189,"ruleName":"/http/order/path/**","enabled":true,"registerMetaData":false}
2021-01-29 01:16:54.213 INFO 14500 --- [pool-1-thread-1] o.d.s.client.common.utils.RegisterUtils : http client register success: {"appName":"http","context":"/http","path":"/http/order/findById","pathDesc":"Find by id","rpcType":"http","host":"172.17.250.209","port":8189,"ruleName":"/http/order/findById","enabled":true,"registerMetaData":false}
服务下线
接下来我们看下,如果 http 服务关闭了,admin 怎么感知
关闭 http 服务之后,等一会,admin 这边会再次走到 UpstreamCheckService#check 里
里面回去检查节点是否还活着,如果不存活,就从内存里删除。
if (successList.size() > 0) {
...
} else {
UPSTREAM_MAP.remove(selectorName);
updateSelectorHandler(selectorName, null);
}
总结
soul 的 http 探活,就是 admin 在启动的时候会去数据库捞一遍数据,看有没有服务之前已经注册过,然后根据配置的检测开关, 开启一个每隔10s检测一次节点是否存活的线程池。
然后 http 服务启动后会主动把自己注册到 admin,admin 会把节点缓存起来,然后每隔10s检测是否存活。
服务下线后,admin 检测到下线状态,会从内存里删除这个节点。