HTTP 服务探活
目的
最终目的是搞清楚网关中服务节点缓存在各种情况下的变动, 这样在请求网关转发 HTTP 服务时, 才能做到心中有数.
拆分任务
看到最终目的, 我会有几个想法:
-
网关的缓存节点信息应该在何时变动 ?
-
后台数据会同步网关, 那后台数据如何变动, 如何通知网关 ?
根据这些问题, 就得到了要进行的任务 ==>
-
HTTP 服务注册时
- 后台缓存的服务节点如何变动
- 后台缓存加入新注册信息后, 如何通知网关
- 网关如何新增服务缓存
-
HTTP 服务下线时 (探活)
- 后台如何得知服务下线并更新缓存
- 后台缓存更新后, 如何通知网关
- 网关如何更新服务缓存
服务注册
后台缓存信息新增
UpstreamCheckService 的 UPSTREAM_MAP 中存放着后台服务节点信息
public class UpstreamCheckService {
private static final Map<String, List<DivideUpstream>> UPSTREAM_MAP = Maps.newConcurrentMap();
}
UPSTREAM_MAP 中的数据有两个来源, 一是启动时从数据库拿, 二是服务注册时传入.
数据库获取方式相对简单, 在 UpstreamCheckService#setup 中有体现.
服务注册路径图:
SpringMvcClientBeanPostProcessor(http服务端): 收集Controller层信息, 请求后台的路径: /soul-client/springmvc-register
SoulController: 后台对外暴露的 Http api, 供请求服务注册
SoulClientRegisterServiceImpl: 直接调用 UpstreamCheckService#submit 传入要新增的节点数据
后台缓存信息新增 通知网关
后台通知路径图 (Websocket模式):
SoulClientRegisterServiceImpl: 发送自定义事件, 由订阅事件中心处理 (Spring 发布订阅模式)
DataChangedEventDispatcher: 事件接收和分发类, 根据事件类型调用监听类的对应方法
WebsocketCollector: 管理 Websocket 通信, 维护连接的 session 会话.
网关缓存信息新增
UpstreamCheckService 的 UPSTREAM_MAP 中存放着网关的 divide 插件服务节点信息
public final class UpstreamCacheManager {
private static final Map<String, List<DivideUpstream>> UPSTREAM_MAP = Maps.newConcurrentMap();
}
服务信息变动路径图 (Websocket模式):
SoulWebsocketClient: 后台 wesocket 信息在这里被监听, 并发送给 WebsocketDataHandler 处理.
WebsocketDataHandler: 根据事件类型, 选择对应处理器 (PluginDataHandler、RuleDataHandler等)
AbstractDataHandler: 根据事件变动类型(refresh、update等), 调用处理器对应方法, 具体实现类会调用到 CommonPluginDataSubscriber 订阅器
CommonPluginDataSubscriber: 这里存有所有注册为 Bean 的事件处理器, 这些处理器来自各个扩展插件, 会调用他们的 handlerXXX() 方法
DividePluginDataHandler: 更新或移除缓存管理器中服务节点信息
服务下线
后台缓存信息变动
探活路径图:
后台缓存管理器在初始化时, 会启动定时器 (间隔10秒) 检测缓存中节点服务的活性, 方式是尝试 Socket#connect 连接.
后台缓存信息变动 通知网关
通知路径图 (Websocket模式):
这块与 HTTP 服务注册时后台通知网关很相似, 最终都会流入到 DataChangedEventDispatcher 事件分发器, 并通过 Websocket 通信发出.
不同的是发起点, HTTP 服务注册时, 由具体的 HTTP 服务发起后台请求, 而 HTTP 服务下线时, 是由后台缓存管理器自身的定时探活模块发起.
网关缓存信息更新
网关的缓存更新有两种方式, 一个是接收到后台信息变更通知, 这点与服务注册时一致, 不再赘述.
第二种是 divide 的缓存管理器, 可开启如同后台的定时探活, 也是通过 Socket#connect 去判断服务是否可用, 但这块默认配置是关闭的, 且检测间隔时间为 30S.
TIPS
HTTP 服务注册时, 后台会将服务信息写入数据库.
public class SoulClientRegisterServiceImpl implements SoulClientRegisterService {
private String registerSelector(final String contextPath, final String rpcType, final String appName, final String uri) {
// ...
if (RpcTypeEnum.DUBBO.getName().equals(rpcType)) {
} else {
//... is divide
// 通知缓存管理器节点信息
upstreamCheckService.submit(selectorDTO.getName(), divideUpstream);
}
// ...
// 这里会将数据写入数据库
return selectorService.register(selectorDTO);
}
}
总结
引用 “进击的巨人” 里常看见的一句话, 根据现在可以公开的情报:
- 后台的 UpstreamCheckService 类以及网关的 UpstreamCacheManager 类持有各自的服务节点缓存信息
- UpstreamCheckService -> UpstreamCacheManager 可以通过框架提供的数据同步传输 (Websocket、Zookeeper 等)
- 后台HTTP服务的探活是定时尝试 Socket 连接来完成, 而网关端可以接收到后台数据同步来的最新信息, 不用自己完成探活
- 网关有可选项, 开启定时 Socket 连接完成探活 (肯定是不建议开的, 网关太累了…)
一点思考🤔 : Socket 连接有些过重, 改进的话参考一些注册中心的实现, 比如 Eureka 会让服务节点引入 EurekaClient , 与服务端 EurekaServer 做心跳检测, 是一种比较轻的探活方式.