前文探索了soul-admin后台对配置服务项自动探活以及同步给网关的机制。今天主要来探究在服务启动时,是如何注册服务到soul-admin的。
准备工作
启动后台(soul-admin)和网关(soul-bootstrap)两个项目。
源码探秘
服务注册
服务启动注册时,也是通过websocket机制同步到soul-admin后台的。猜测也会经过前文所述WebsocketDataChangedListener
类中有onSelectorChanged
方法。所以先在这个方法上打上断点。然后启动一个soul-examples-http
服务。
跟期望的一样,soul-admin进入了断点位置。此时找到此次请求进入soul-admin最开始的入口。
@RequestMapping("/soul-client")
public class SoulClientController {
@PostMapping("/springmvc-register")
public String registerSpringMvc(@RequestBody final SpringMvcRegisterDTO springMvcRegisterDTO) {
return soulClientRegisterService.registerSpringMvc(springMvcRegisterDTO);
}
}
可以看到这是soul-admin后台暴露出来的一个API接口。在服务启动的过程中,肯定调用了这个接口完成了服务注册。
全局检索/springmvc-register
,找到疑似类SpringMvcClientBeanPostProcessor
。
在postProcessAfterInitialization
方法中SoulSpringMvcClient clazzAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), SoulSpringMvcClient.class);
打上断点,然后以另一个端口再启动另一个soul-examples-http
服务。此时顺利进入断点处。
public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
// ...
if (controller != null || restController != null || requestMapping != null) {
// 收集soul自定义SoulSpringMvcClient注解使用于controller中的部分
SoulSpringMvcClient clazzAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), SoulSpringMvcClient.class);
String prePath = "";
if (Objects.nonNull(clazzAnnotation)) {
if (clazzAnnotation.path().indexOf("*") > 1) {
String finalPrePath = prePath;
// 另起一个线程向soul-admin后台发送请求注册服务
executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(clazzAnnotation, finalPrePath), url,
RpcTypeEnum.HTTP));
return bean;
}
prePath = clazzAnnotation.path();
}
final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(bean.getClass());
for (Method method : methods) {
// 遍历收集soul自定义SoulSpringMvcClient注解使用于method中的部分
SoulSpringMvcClient soulSpringMvcClient = AnnotationUtils.findAnnotation(method, SoulSpringMvcClient.class);
if (Objects.nonNull(soulSpringMvcClient)) {
String finalPrePath = prePath;
// 另起一个线程向soul-admin后台发送请求注册服务
executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(soulSpringMvcClient, finalPrePath), url,
RpcTypeEnum.HTTP));
}
}
}
return bean;
}
服务启动时,所有Bean会经过这个postProcessAfterInitialization
方法,然后收集所有使用了soul自定义注解SoulSpringMvcClient
的controller和method。之后向soul-admin网关请求接口注册服务。在RegisterUtils
的doRegister
中使用OkHttpClient
最终请求接口。
public static void doRegister(final String json, final String url, final RpcTypeEnum rpcTypeEnum) {
try {
String result = OkHttpTools.getInstance().post(url, json);
if (AdminConstants.SUCCESS.equals(result)) {
log.info("{} client register success: {} ", rpcTypeEnum.getName(), json);
} else {
log.error("{} client register error: {} ", rpcTypeEnum.getName(), json);
}
} catch (IOException e) {
log.error("cannot register soul admin param, url: {}, request body: {}", url, json, e);
}
}
至此,在服务启动时,服务自己做的工作已经了解清楚了。在回到soul-admin后台的入口registerSpringMvc
那里。继续往下追踪。
public class SoulClientRegisterServiceImpl implements SoulClientRegisterService {
@Transactional
public String registerSpringMvc(final SpringMvcRegisterDTO dto) {
if (dto.isRegisterMetaData()) {
MetaDataDO exist = metaDataMapper.findByPath(dto.getPath());
if (Objects.isNull(exist)) {
saveSpringMvcMetaData(dto);
}
}
// 处理选择器配置信息
String selectorId = handlerSpringMvcSelector(dto);
// 处理规则配置信息
handlerSpringMvcRule(selectorId, dto);
return SoulResultMessage.SUCCESS;
}
}
在registerSpringMvc
方法中对注册中的元数据、选择器、规则信息进行了处理。选择处理选择器配置信息的方法handlerSpringMvcSelector
继续往下追踪。
private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) {
// ...
// 通过接收到的服务请求构建soul适用的DivideUpstream
DivideUpstream addDivideUpstream = buildDivideUpstream(uri);
// ...
// 更新数据库
selectorMapper.updateSelective(selectorDO);
// 将新的配置信息提交到soul-admin后台的缓存中(UpstreamCheckService的UPSTREAM_MAP)
upstreamCheckService.submit(contextPath, addDivideUpstream);
// 发布数据变更事件广播给所有监听者
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
Collections.singletonList(selectorData)));
}
return selectorId;
}
至此,服务启动向后台注册服务信息,接着由后台向网关同步配置信息的过程就理清楚了。
总结
服务启动
服务启动时,所有的Bean生成后都会去判断是否在其上使用了soul自定义的注解SoulSpringMvcClient
。若直接在controller中使用了且设置中包含*
,则其中全部method都符合要求。其余的controller都会遍历所有method进行单独判断。所有符合要求的部分都会向soul-admin发送请求注册服务信息。
后台接收
soul-admin接收到请求后会处理接收到的配置信息;更新到数据库;将新的配置信息提交到后台缓存中,由后台自己的探活机制去检查是否可用;最后发布数据变更事件广播给所有监听者,网关接收到数据变更事件后执行前文所述的逻辑。