http代理 是如何进行注册自己的服务的
// 注解在Controller上
@SoulSpringMvcClient(path = "/order")
// 注解在方法上
@SoulSpringMvcClient(path = "/findById", desc = "Find by id")
首先被代理的服务启动的时候会根据上面的注解加载对应的接口信息,这个注解在 soul-client-springmvc-2.2.1.jar 下面
初始化加载的时候,使用的是SpringMvcClientBeanPostProcessor,这个类实现了BeanPostProcessor接口,BeanPostProcessor的作用主要是在Spring 容器完成 Bean 的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,然后注册到容器中。
疑问:当admin没有启动的时候,注册失败后是如何处理的
先启动SoulTestHttpApplication,再依次启动SoulAdminBootstrap和SoulBootstrapApplication,执行调用http://localhost:9195/http/order/findById?id=1
{
"code": 500,
"message": "Internal Server Error",
"data": "Did not observe any item or terminal signal within 3000ms in 'peekTerminal' (and no fallback has been configured)"
}
{
"code": -106,
"message": "Can not find url, please check your configuration!",
"data": null
}
无法访问,等待一会后会重新调用url,访问成功
{
"id": "1",
"name": "hello world findById"
}
是哪里发起的重试?
先看一下注册服务调用的地方,/soul-client/springmvc-register
好像只有这里可以注册,且其他地方没有可以注册的地方,也就是意味着被代理的服务如果先起的话是无法进行访问的,目前是没有重试注册的。
如果是已经启动SoulAdminBootstrap和SoulBootstrapApplication,这个时候启动被代理的服务,admin是否会自动同步消息到bootstrap?
第一次请求的时候:
{
"code": -107,
"message": "Can not find selector, please check your configuration!",
"data": null
}
后续请求也是没有进行同步的,那我们手动点击同步试试
还是未找到selector,跟上面同样的错误
那如果是已注册的服务,先起是否是没问题的呢?
如果是已经注册过的,顺序上面则没什么要求,很顺利地请求到了
注册后是的同步流程是怎样的?
注册的代码
被代理端注册在SpringMvcClientBeanPostProcessor
// 初始化结束后要做的操作
@Override
public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
// 如果isFull是true,则提供所有服务的代理
if (soulSpringMvcConfig.isFull()) {
return bean;
}
// @Controller
Controller controller = AnnotationUtils.findAnnotation(bean.getClass(), Controller.class);
// @RestController
RestController restController = AnnotationUtils.findAnnotation(bean.getClass(), RestController.class);
// @RequestMapping("/order")
RequestMapping requestMapping = AnnotationUtils.findAnnotation(bean.getClass(), RequestMapping.class);
if (controller != null || restController != null || requestMapping != null) {
// @SoulSpringMvcClient(path = "/order") Controller上的soul注解
SoulSpringMvcClient clazzAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), SoulSpringMvcClient.class);
String prePath = "";
if (Objects.nonNull(clazzAnnotation)) {
if (clazzAnnotation.path().indexOf("*") > 1) {
String finalPrePath = prePath;
// 注册Controller
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) {
// @SoulSpringMvcClient(path = "/findById", desc = "Find by id") Method上的soul注解
SoulSpringMvcClient soulSpringMvcClient = AnnotationUtils.findAnnotation(method, SoulSpringMvcClient.class);
if (Objects.nonNull(soulSpringMvcClient)) {
String finalPrePath = prePath;
// 注册Method
executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(soulSpringMvcClient, finalPrePath), url,
RpcTypeEnum.HTTP));
}
}
}
return bean;
}
上面这段代码我们可以看到,注册请求的是/soul-client/springmvc-register
@PostMapping("/springmvc-register")
public String registerSpringMvc(@RequestBody final SpringMvcRegisterDTO springMvcRegisterDTO) {
return soulClientRegisterService.registerSpringMvc(springMvcRegisterDTO);
}
@Override
@Transactional
public String registerSpringMvc(final SpringMvcRegisterDTO dto) {
// 默认非元数据,这个有可能是元数据类型吗? cutie 20200120
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;
}
注册选择器
// 注册选择器
private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) {
// 获取访问前缀
String contextPath = dto.getContext();
// 根据访问前缀获取选择器
SelectorDO selectorDO = selectorService.findByName(contextPath);
// 选择器id
String selectorId;
// 拼uri 主机ip:端口
String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort()));
if (Objects.isNull(selectorDO)) {
// 选择器不存在的话则进行注册
selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri);
} else {
// 选择器存在的话则获取信息并进行分发
selectorId = selectorDO.getId();
//update upstream
String handle = selectorDO.getHandle();
String handleAdd;
// 根据uri创建DivideUpstream对象
DivideUpstream addDivideUpstream = buildDivideUpstream(uri);
// 根据访问前缀创建选择器对象
SelectorData selectorData = selectorService.buildByName(contextPath);
// handle字段即DivideUpstream对象的json串
if (StringUtils.isBlank(handle)) {
handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream));
} else {
// 如果handle字段存在,就遍历看看是否已经保存过了
List exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class);
for (DivideUpstream upstream : exist) {
if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) {
// 找到了的话就返回选择器id
return selectorId;
}
}
// 添加到存在列表中
exist.add(addDivideUpstream);
// 转成json串
handleAdd = GsonUtils.getInstance().toJson(exist);
}
// 更新用
selectorDO.setHandle(handleAdd);
// 发布通知用
selectorData.setHandle(handleAdd);
// update db
selectorMapper.updateSelective(selectorDO);
// submit upstreamCheck 定时更新提交
upstreamCheckService.submit(contextPath, addDivideUpstream);
// publish change event.
// ApplicationEventPublisher是ApplicationContext的父接口之一。这接口的作用是:Interface that encapsulates event publication functionality. 功能就是发布事件,也就是把某个事件告诉的所有与这个事件相关的监听器。
// 把当前选择器发布给监听选择器发布更新的服务,解耦用不错
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
Collections.singletonList(selectorData)));
}
return selectorId;
}
注册规则
// 注册规则
private void handlerSpringMvcRule(final String selectorId, final SpringMvcRegisterDTO dto) {
// 查询下规则是否存在
RuleDO ruleDO = ruleMapper.findByName(dto.getRuleName());
if (Objects.isNull(ruleDO)) {
// 不存在的话就注册规则
registerRule(selectorId, dto.getPath(), dto.getRpcType(), dto.getRuleName());
}
}
不存在的话就注册规则
// 不存在的话就注册规则
private void registerRule(final String selectorId, final String path, final String rpcType, final String ruleName) {
// 包装规则
RuleHandle ruleHandle = RuleHandleFactory.ruleHandle(RpcTypeEnum.acquireByName(rpcType), path);
RuleDTO ruleDTO = RuleDTO.builder()
.selectorId(selectorId)
.name(ruleName)
.matchMode(MatchModeEnum.AND.getCode())
.enabled(Boolean.TRUE)
.loged(Boolean.TRUE)
.sort(1)
.handle(ruleHandle.toJson())
.build();
RuleConditionDTO ruleConditionDTO = RuleConditionDTO.builder()
.paramType(ParamTypeEnum.URI.getName())
.paramName("/")
.paramValue(path)
.build();
if (path.indexOf("*") > 1) {
ruleConditionDTO.setOperator(OperatorEnum.MATCH.getAlias());
} else {
ruleConditionDTO.setOperator(OperatorEnum.EQ.getAlias());
}
ruleDTO.setRuleConditions(Collections.singletonList(ruleConditionDTO));
// 注册规则
ruleService.register(ruleDTO);
}