Soul源碼閱讀 http代理是如何進(jìn)行注冊(cè)自己的服務(wù)的【第六天】

http代理 是如何進(jìn)行注冊(cè)自己的服務(wù)的

// 注解在Controller上
@SoulSpringMvcClient(path = "/order")
// 注解在方法上
@SoulSpringMvcClient(path = "/findById", desc = "Find by id")

首先被代理的服務(wù)啟動(dòng)的時(shí)候會(huì)根據(jù)上面的注解加載對(duì)應(yīng)的接口信息,這個(gè)注解在 soul-client-springmvc-2.2.1.jar 下面
初始化加載的時(shí)候,使用的是SpringMvcClientBeanPostProcessor,這個(gè)類實(shí)現(xiàn)了BeanPostProcessor接口,BeanPostProcessor的作用主要是在Spring 容器完成 Bean 的實(shí)例化、配置和其他的初始化前后添加一些自己的邏輯處理,然后注冊(cè)到容器中。

疑問:當(dāng)admin沒有啟動(dòng)的時(shí)候,注冊(cè)失敗后是如何處理的

先啟動(dòng)SoulTestHttpApplication,再依次啟動(dòng)SoulAdminBootstrap和SoulBootstrapApplication,執(zhí)行調(diào)用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
}

無法訪問,等待一會(huì)后會(huì)重新調(diào)用url,訪問成功

{
    "id": "1",
    "name": "hello world findById"
}

是哪里發(fā)起的重試?

先看一下注冊(cè)服務(wù)調(diào)用的地方,/soul-client/springmvc-register
好像只有這里可以注冊(cè),且其他地方?jīng)]有可以注冊(cè)的地方,也就是意味著被代理的服務(wù)如果先起的話是無法進(jìn)行訪問的,目前是沒有重試注冊(cè)的。

如果是已經(jīng)啟動(dòng)SoulAdminBootstrap和SoulBootstrapApplication,這個(gè)時(shí)候啟動(dòng)被代理的服務(wù),admin是否會(huì)自動(dòng)同步消息到bootstrap?

第一次請(qǐng)求的時(shí)候:

{
    "code": -107,
    "message": "Can not find selector, please check your configuration!",
    "data": null
}

后續(xù)請(qǐng)求也是沒有進(jìn)行同步的,那我們手動(dòng)點(diǎn)擊同步試試


image.png

還是未找到selector,跟上面同樣的錯(cuò)誤

那如果是已注冊(cè)的服務(wù),先起是否是沒問題的呢?

如果是已經(jīng)注冊(cè)過的,順序上面則沒什么要求,很順利地請(qǐng)求到了

注冊(cè)后是的同步流程是怎樣的?

注冊(cè)的代碼
被代理端注冊(cè)在SpringMvcClientBeanPostProcessor

    // 初始化結(jié)束后要做的操作
    @Override
    public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
        // 如果isFull是true,則提供所有服務(wù)的代理
        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;
                    // 注冊(cè)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;
                    // 注冊(cè)Method
                    executorService.execute(() -> RegisterUtils.doRegister(buildJsonParams(soulSpringMvcClient, finalPrePath), url,
                            RpcTypeEnum.HTTP));
                }
            }
        }
        return bean;
    }

上面這段代碼我們可以看到,注冊(cè)請(qǐng)求的是/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) {
        // 默認(rèn)非元數(shù)據(jù),這個(gè)有可能是元數(shù)據(jù)類型嗎? cutie 20200120
        if (dto.isRegisterMetaData()) {
            MetaDataDO exist = metaDataMapper.findByPath(dto.getPath());
            if (Objects.isNull(exist)) {
                saveSpringMvcMetaData(dto);
            }
        }
        // 注冊(cè)選擇器
        String selectorId = handlerSpringMvcSelector(dto);
        // 注冊(cè)規(guī)則
        handlerSpringMvcRule(selectorId, dto);
        return SoulResultMessage.SUCCESS;
    }

注冊(cè)選擇器

    // 注冊(cè)選擇器
    private String handlerSpringMvcSelector(final SpringMvcRegisterDTO dto) {
        // 獲取訪問前綴
        String contextPath = dto.getContext();
        // 根據(jù)訪問前綴獲取選擇器
        SelectorDO selectorDO = selectorService.findByName(contextPath);
        // 選擇器id
        String selectorId;
        // 拼uri 主機(jī)ip:端口
        String uri = String.join(":", dto.getHost(), String.valueOf(dto.getPort()));
        if (Objects.isNull(selectorDO)) {
            // 選擇器不存在的話則進(jìn)行注冊(cè)
            selectorId = registerSelector(contextPath, dto.getRpcType(), dto.getAppName(), uri);
        } else {
            // 選擇器存在的話則獲取信息并進(jìn)行分發(fā)
            selectorId = selectorDO.getId();
            //update upstream
            String handle = selectorDO.getHandle();
            String handleAdd;
            // 根據(jù)uri創(chuàng)建DivideUpstream對(duì)象
            DivideUpstream addDivideUpstream = buildDivideUpstream(uri);
            // 根據(jù)訪問前綴創(chuàng)建選擇器對(duì)象
            SelectorData selectorData = selectorService.buildByName(contextPath);
            // handle字段即DivideUpstream對(duì)象的json串
            if (StringUtils.isBlank(handle)) {
                handleAdd = GsonUtils.getInstance().toJson(Collections.singletonList(addDivideUpstream));
            } else {
                // 如果handle字段存在,就遍歷看看是否已經(jīng)保存過了
                List<DivideUpstream> exist = GsonUtils.getInstance().fromList(handle, DivideUpstream.class);
                for (DivideUpstream upstream : exist) {
                    if (upstream.getUpstreamUrl().equals(addDivideUpstream.getUpstreamUrl())) {
                        // 找到了的話就返回選擇器id
                        return selectorId;
                    }
                }
                // 添加到存在列表中
                exist.add(addDivideUpstream);
                // 轉(zhuǎn)成json串
                handleAdd = GsonUtils.getInstance().toJson(exist);
            }
            // 更新用
            selectorDO.setHandle(handleAdd);
            // 發(fā)布通知用
            selectorData.setHandle(handleAdd);
            // update db
            selectorMapper.updateSelective(selectorDO);
            // submit upstreamCheck 定時(shí)更新提交
            upstreamCheckService.submit(contextPath, addDivideUpstream);
            // publish change event.
            // ApplicationEventPublisher是ApplicationContext的父接口之一。這接口的作用是:Interface that encapsulates event publication functionality. 功能就是發(fā)布事件,也就是把某個(gè)事件告訴的所有與這個(gè)事件相關(guān)的監(jiān)聽器。
            // 把當(dāng)前選擇器發(fā)布給監(jiān)聽選擇器發(fā)布更新的服務(wù),解耦用不錯(cuò)
            eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
                    Collections.singletonList(selectorData)));
        }
        return selectorId;
    }

注冊(cè)規(guī)則

    // 注冊(cè)規(guī)則
    private void handlerSpringMvcRule(final String selectorId, final SpringMvcRegisterDTO dto) {
        // 查詢下規(guī)則是否存在
        RuleDO ruleDO = ruleMapper.findByName(dto.getRuleName());
        if (Objects.isNull(ruleDO)) {
            // 不存在的話就注冊(cè)規(guī)則
            registerRule(selectorId, dto.getPath(), dto.getRpcType(), dto.getRuleName());
        }
    }

不存在的話就注冊(cè)規(guī)則

    // 不存在的話就注冊(cè)規(guī)則
    private void registerRule(final String selectorId, final String path, final String rpcType, final String ruleName) {
        // 包裝規(guī)則
        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));
        // 注冊(cè)規(guī)則
        ruleService.register(ruleDTO);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容