Soul网关源码探秘《十四》 - 数据同步(后台接收与同步篇二)

前文探索了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网关请求接口注册服务。在RegisterUtilsdoRegister中使用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接收到请求后会处理接收到的配置信息;更新到数据库;将新的配置信息提交到后台缓存中,由后台自己的探活机制去检查是否可用;最后发布数据变更事件广播给所有监听者,网关接收到数据变更事件后执行前文所述的逻辑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rughru

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值