【Soul源码阅读-03】dubbo插件SoulDubboClient注解解析

目标

  • 运行 examples下面的 apache-dubbo 服务
  • 学习文档,结合 dubbo 插件,发起 http 请求 soul 网关,体验 dubbo 代理

soul 网关接入 Dubbo 应用

  • 参考官方文档:https://dromara.org/zh-cn/doc...

    • 引入相关依赖(官方示例默认开启的alibaba-dubbo,具体参考:soul-examples-dubbo

      soul-bootstrap新增如下依赖:

            
               
                  org.dromara
                  soul-spring-boot-starter-plugin-apache-dubbo
                  ${project.version}
              
              
                  org.apache.dubbo
                  dubbo
                  2.7.5
              
              
              
            
                  org.apache.curator
                  curator-client
                  4.0.1
              
              
                  org.apache.curator
                  curator-framework
                  4.0.1
              
              
                  org.apache.curator
                  curator-recipes
                  4.0.1
              
               

      soul-examples-apache-dubbo-service新增如下依赖:

      
                 
                 
                     org.dromara
                     soul-spring-boot-starter-plugin-apache-dubbo
                     2.2.1
                 
                 
                 
                 
                 
                     org.apache.dubbo
                     dubbo
                     2.7.5
                 
                 
                
                 
                  org.apache.zookeeper
                  zookeeper
                  3.5.6
                  
                      
                          org.slf4j
                          slf4j-log4j12
                      
                      
                          log4j
                          log4j
                      
                  
              
             
    • application.yml添加相关配置

        soul:
           dubbo:
             adminUrl: http://localhost:9095
             contextPath: /dubbo
             appName: dubbo
            # adminUrl: 为你启动的soul-admin 项目的ip + 端口,注意要加 http://
            # contextPath: 为你的这个项目在soul网关的路由前缀,比如/order ,/product 等等,网关会根据你的这个前 缀来进行路由.
            # appName:你的应用名称,不配置的话,会默认取 dubbo配置中application 中的名称
  • dubbo 插件设置

    soul-admin 插件管理中,dubbo 插件设置为开启
    【Soul源码阅读-03】dubbo插件SoulDubboClient注解解析_第1张图片

  • 接口注册到网关

    • dubbo 服务实现类的方法上加上 @SoulDubboClient 注解,表示该接口方法注册到网关
  • 先启动zk,然后再启动TestApacheDubboApplication,输出日志 dubbo client register success,表示dubbo接口已经发布到 soul 网关

    【Soul源码阅读-03】dubbo插件SoulDubboClient注解解析_第2张图片

@SoulDubboClient 注解解析

服务在启动时会将类方法上的 @SoulDubboClient 注解注册到网关,接下来分析一下@SoulDubboClient 注册逻辑。

soul-examples-apache-dubbo-service添加了soul-spring-boot-starter-plugin-apache-dubbo依赖,注册逻辑应该就在这个自定义的spring-boot-starter中。

首先注释掉DubboTestServicel类中findById方法上的@SoulDubboClient注解

    @Override
    //@SoulDubboClient(path = "/findById", desc = "Query by Id")
    public DubboTest findById(final String id) {
        DubboTest dubboTest = new DubboTest();
        dubboTest.setId(id);
        dubboTest.setName("hello world Soul Apache, findById");
        return dubboTest;
    }

soul-spring-boot-starter-client-apache-dubbo 自定义spring-boot-starter通过 spring.factories 加载了SoulApacheDubboClientConfiguration类:

@Configuration
public class SoulApacheDubboClientConfiguration {
    
    /**
     * Apache dubbo service bean post processor alibaba dubbo service bean post processor.
     *
     * @param dubboConfig the dubbo config
     * @return the alibaba dubbo service bean post processor
     */
    @Bean
    public ApacheDubboServiceBeanPostProcessor apacheDubboServiceBeanPostProcessor(final DubboConfig dubboConfig) {
        return new ApacheDubboServiceBeanPostProcessor(dubboConfig);
    }
    
    /**
     * Dubbo config dubbo config.
     *
     * @return the dubbo config
     */
    @Bean
    @ConfigurationProperties(prefix = "soul.dubbo")
    public DubboConfig dubboConfig() {
        return new DubboConfig();
    }
}

SoulApacheDubboClientConfiguration加载了ApacheDubboServiceBeanPostProcessor实例,该实例实现了ApplicationListener 接口,该类中的主要方法:

    public ApacheDubboServiceBeanPostProcessor(final DubboConfig dubboConfig) {
        //获取 application.yml 文件中的配置信息
        String contextPath = dubboConfig.getContextPath();
        String adminUrl = dubboConfig.getAdminUrl();
        if (StringUtils.isEmpty(contextPath)
                || StringUtils.isEmpty(adminUrl)) {
            throw new RuntimeException("apache dubbo client must config the contextPath, adminUrl");
        }
        this.dubboConfig = dubboConfig;
        url = dubboConfig.getAdminUrl() + "/soul-client/dubbo-register";
        executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
    }
    @Override
    public void onApplicationEvent(final ContextRefreshedEvent contextRefreshedEvent) {
        if (Objects.nonNull(contextRefreshedEvent.getApplicationContext().getParent())) {
            return;
        }
        // Fix bug(https://github.com/dromara/soul/issues/415), upload dubbo metadata on ContextRefreshedEvent
        
        //根据 beanType 获取所有的 bean,这里获取的是 spring-dubbo.xml 中 中的DubboTestService和               //DubboMultiParamService         
        //ServiceBean.class:org.apache.dubbo.config.spring.ServiceBean.class
        Map serviceBean = contextRefreshedEvent.getApplicationContext().getBeansOfType(ServiceBean.class);
        for (Map.Entry entry : serviceBean.entrySet()) {
            executorService.execute(() -> handler(entry.getValue()));
        }
    }

处理类中的每个方法

private void handler(final ServiceBean serviceBean) {
        Class clazz = serviceBean.getRef().getClass();
        if (ClassUtils.isCglibProxyClass(clazz)) {
            String superClassName = clazz.getGenericSuperclass().getTypeName();
            try {
                clazz = Class.forName(superClassName);
            } catch (ClassNotFoundException e) {
                log.error(String.format("class not found: %s", superClassName));
                return;
            }
        }
       // 获取类中每个方法
        final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(clazz);
        for (Method method : methods) {
            //获取类上的 @SoulDubboClient 注解
            SoulDubboClient soulDubboClient = method.getAnnotation(SoulDubboClient.class);
            if (Objects.nonNull(soulDubboClient)) {
                //远程调用,注册方法信息到 soul-admin
                RegisterUtils.doRegister(buildJsonParams(serviceBean, soulDubboClient, method), url, RpcTypeEnum.DUBBO);
            }
        }
    }

当获取到DubboTestServicel类中findById的方法时,由于我们上面注释了 @SoulDubboClient注解,所有以这里获取 SoulDubboClient 的值为 null。

【Soul源码阅读-03】dubbo插件SoulDubboClient注解解析_第3张图片

至此,@SoulDubboClient注解分析完毕

问题

先记录一个问题,明天再看:soul 的数据同步是将数据库中的信息同步到本地缓存中,如果是已经通过 @SoulDubboClient注解注册到soul-admin的方法被删除了,数据库中的信息什么时候删除?例如上面findById方法已经注释掉了 @SoulDubboClient注解,服务重启时输出的注册信息已经没有了findById方法,但是soul-admin中dubbo 插件列表和元数据列表中依然存在findById方法信息。

【Soul源码阅读-03】dubbo插件SoulDubboClient注解解析_第4张图片

【Soul源码阅读-03】dubbo插件SoulDubboClient注解解析_第5张图片

你可能感兴趣的:(Soul)