Soul源码阅读 http代理是如何进行注册自己的服务的【第六天】

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
}

后续请求也是没有进行同步的,那我们手动点击同步试试


image.png

还是未找到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<DivideUpstream> 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);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容