Dubbo服务提供端核心模型之Invoker

服务提供端主要是利用ServiceAnnotationPostProcessor对@DubboService注解的解析。

DubboClassPathBeanDefinitionScanner扫描指定路径下dubbo涉及的bean。如果@EnableDubbo没有显式指定路径,则默认为启动类所在的包路径,此时意味着Dubbo需要遍历应用中所有类是否为候选类,所以显示指定路径可以提升应用启动速率。

Dubbo中目标接口的多个实现类必须通过version版本号区别。针对每个候选类在Spring IOC中存在两种形式,一种是beanName + Class属性(候选类真实Class类型),另一种是 beanName:version + Class属性为ServiceBean。最后一种形式也是后置处理器ServiceAnnotationPostProcessor类涉及的核心功能。

public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPostProcessor {
 
    private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
        // @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
        DubboService.class,
        // @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
        Service.class,
        // @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
        com.alibaba.dubbo.config.annotation.Service.class
    );


    private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

        scaned = true;
        ...
        // resourceLoader:AnnotationConfigServletWebApplicationContext
        DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
        scanner.setBeanNameGenerator(beanNameGenerator);
        for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
            // 通过 已知注解类型 对候选类进行过滤是否满足条件
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        }

        ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
        // 通过 对候选类进行过滤 判断是否需要排除 候选类
        scanner.addExcludeFilter(scanExcludeFilter);

        for (String packageToScan : packagesToScan) {
            ...
            // 扫描 packagesToScan路径下所有候选类,并且利用 AnnotationTypeFilter 过滤得到 当前注解所关注的候选类
            // 候选类在IOC容器中正常普通注册【不需要代理】
            scanner.scan(packageToScan);
            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            Set<BeanDefinitionHolder> beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {                
                 // 将候选类包装为 ServiceBean 类型,以便代理处理
                 processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
                 servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
            } 

            servicePackagesHolder.addScannedPackage(packageToScan);
        }
    }

    private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,DubboClassPathBeanDefinitionScanner scanner) {

        Class<?> beanClass = resolveClass(beanDefinitionHolder);
        // 获取目标类存在的注解 @DubboService
        Annotation service = findServiceAnnotation(beanClass);

        // The attributes of @Service annotation
        Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
        // 目标类实现的接口全限定名
        String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
        // annotatedServiceBeanName:目标类原始IOC种bean name
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
        // ServiceBean Bean name
        // beanName:ServiceBean:serviceInterface:version:group
        //例如:ServiceBean:common.service.CountryService:1.0
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);
        // 将目标类的 @DubboService 注解相关属性 抽象为一个类 ~ ServiceBean
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
        // IOC容器中创建 beanName 对应的实例,这个实例其实就是 ServiceBean
        registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);

    }
}

1.ServiceBean之本地注册服务

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean{
    @Override
    public void afterPropertiesSet() throws Exception {
        if (StringUtils.isEmpty(getPath())) {
            if (StringUtils.isNotEmpty(getInterface())) {
                setPath(getInterface());
            }
        }
        //register service bean
        //从IOC容器中获取ModuleModel
        ModuleModel moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
        // ModuleConfigManager
        moduleModel.getConfigManager().addService(this);
        moduleModel.getDeployer().setPending();
    }
}

通过上述分析得知在IOC容器中存在两种类型的候选类,一种是候选类真实类型,另一种即为需要代理处理的ServiceBean。

在Spring刷新容器过程中会遍历注册表中全部候选类,其中包括InitializingBean类型的ServiceBean。在接口核心方法内部将候选类注册为dubbo服务,即dubbo:service


2.DubboDeployApplicationListener实现服务发布

注册监听器DubboDeployApplicationListener,并由此监听器发送ContextRefreshedEvent事件。

public class DubboDeployApplicationListener implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {
    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        // DefaultModuleDeployer
        ModuleDeployer deployer = moduleModel.getDeployer();
        Future future = deployer.start();
        future.get();
    }
}


public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> implements ModuleDeployer {

    // DefaultApplicationDeployer
    private ApplicationDeployer applicationDeployer;

    @Override
    public synchronized Future start() throws IllegalStateException {

        ...
        // 不管是 DefaultApplicationDeployer 还是DefaultModuleDeployer的initialize方法,都是处理相关配置文件
        // 其功能等价于监听器 DubboConfigApplicationListener
        applicationDeployer.initialize();
        initialize();
        // 真正触发 服务注册功能
        exportServices();
        ...
        return startFuture;
    }

    private void exportServices() {
        // 从ModuleConfigManager遍历每一个ServiceBean,即dubbo.service
        for (ServiceConfigBase sc : configManager.getServices()) {
            exportServiceInternal(sc);
        }
    }

    private void exportServiceInternal(ServiceConfigBase sc) {
        ServiceConfig<?> serviceConfig = (ServiceConfig<?>) sc;
        ...
        if (sc.isExported()) {return;}
        ...
        sc.export();
    }
}
public class ServiceConfig<T> extends ServiceConfigBase<T> {
    // 服务接口
    protected Class<?> interfaceClass;
    // 服务接口的实现类
    protected T ref;

    private Protocol protocolSPI;
    private ProxyFactory proxyFactory;

    private void exportUrl(URL url, List<URL> registryURLs) {
        String scope = url.getParameter(SCOPE_KEY);
        url = exportRemote(url, registryURLs);
        if (!isGeneric(generic) && !getScopeModel().isInternal()) {
            // 利用zookeeper 客户端注册服务
            MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel());
        }
        this.urls.add(url);
    }

    private URL exportRemote(URL url, List<URL> registryURLs) {
        // 服务接口 javassist 代理
        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
        if (withMetaData) {
            invoker = new DelegateProviderMetaDataInvoker(invoker, this);
        }
        Exporter<?> exporter = protocolSPI.export(invoker);
        exporters.add(exporter);
    }
}
  1. 通过ProxyFactory创建Invoker。
  2. 利用NettyTransporter启动服务提供端提供rpc功能的NettyServer。监听端口20880注册的客户端连接Socket。
  3. 利用Protocol将服务提供方的服务export出去。

发布服务过程中涉及两种类型的Protocol链:

  1. ServiceConfig:ProtocolSerializationWrapper->ProtocolFilterWrapper->ProtocolListenerWrapper->RegistryProtocol。
  2. RegistryProtocol内部生成本地Exportor:ProtocolSerializationWrapper->ProtocolFilterWrapper->ProtocolListenerWrapper->QosProtocolWrapper->DubboProtocol

 2.1.Invoker的创建

public class JavassistProxyFactory extends AbstractProxyFactory {
    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,Class<?>[] parameterTypes,Object[] arguments){
                // 利用反射调用目标接口实现类的目标方法
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }
}
  1. 对目标接口的实现类proxy做javaassit代理。
  2. javaassit代理的类被包装为Wrapper,最终返回Invoker为AbstractProxyInvoker

2.2.RegistryProtocol

RegistryProtocol类核心功能:

  1. 主要是创建LocalExport。
  2. 将服务提供端服务其本身注册在本地。 
public class RegistryProtocol implements Protocol, ScopeModelAware {
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        // service-discovery-registry://192.168.50.89:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-provider&dubbo=2.0.2&pid=57555&qos.enable=false&register-mode=instance&registry=zookeeper&release=3.0.7&timestamp=1708673397005
        URL registryUrl = getRegistryUrl(originInvoker);

        //dubbo://192.168.80.36:20880/image/common.service.CountryService?anyhost=true&application=dubbo-provider&background=false&bind.ip=192.168.80.36&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=common.service.CountryService&methods=getCountry&pid=57555&qos.enable=false&register-mode=instance&release=3.0.7&revision=1.0&scope=remote&service-name-mapping=true&side=provider&timestamp=1708674621496&version=1.0
        URL providerUrl = getProviderUrl(originInvoker);

        //provider://192.168.80.36:20880/image/common.service.CountryService?anyhost=true&application=dubbo-provider&background=false&bind.ip=192.168.80.36&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=common.service.CountryService&methods=getCountry&pid=57555&qos.enable=false&register-mode=instance&release=3.0.7&revision=1.0&scope=remote&service-name-mapping=true&side=provider&timestamp=1708674621496&version=1.0
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
        ...
        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
        //export invoker
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        // url to registry
        final Registry registry = getRegistry(registryUrl);
        final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

        boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true);
        if (register) {
            // 此处注册只是 在本地缓存注册信息
            register(registry, registeredProviderUrl);
        }
        registerStatedUrl(registryUrl, registeredProviderUrl, register);
        exporter.setRegisterUrl(registeredProviderUrl);
        exporter.setSubscribeUrl(overrideSubscribeUrl);
        ...
        return new DestroyableExporter<>(exporter);
    }

    private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
        String key = getCacheKey(originInvoker);
        return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
            Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
            // 最终返回的是 DubboProtocol 生成的 DubboExporter
            return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
        });
    }
}

创建本地Exporter时涉及originInvoker是指最原始的Invoker之DelegateProviderMetaDataInvoker,通过该Invoker利用反射可以直接调用目标类的目标方法。


2.2.1.DubboProtocol

ExchangeHandler其实就是Netty涉及的channelHandler,服务提供端接收到消费端相关请求后通过该Handler得到原始的Invoker,触发目标类的目标方法。

public class DubboProtocol extends AbstractProtocol {

    protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<>();

    private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {

        @Override
        public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {

            Invocation inv = (Invocation) message;
            // 从集合属性 exporterMap 获取 原始的 invoker
            Invoker<?> invoker = getInvoker(channel, inv);
            inv.setServiceModel(invoker.getUrl().getServiceModel());
            // need to consider backward-compatibility if it's a callback
            if (Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
                String methodsStr = invoker.getUrl().getParameters().get("methods");
                boolean hasMethod = false;
                if (methodsStr == null || !methodsStr.contains(",")) {
                    hasMethod = inv.getMethodName().equals(methodsStr);
                } else {
                    String[] methods = methodsStr.split(",");
                    for (String method : methods) {
                        if (inv.getMethodName().equals(method)) {
                            hasMethod = true;
                            break;
                        }
                    }
                }
            }
            RpcContext.getServiceContext().setRemoteAddress(channel.getRemoteAddress());
            // 利用原始的 invoker 调用目标类的目标方法
            Result result = invoker.invoke(inv);
            return result.thenApply(Function.identity());
        }
    };

    

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();
        // export service.
        String key = serviceKey(url);
        // 将原始invoker 添加到 集合属性 exporterMap
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        ...
        // 此处触发提供端 Netty Server 的初始化,监听20880端口
        openServer(url);
        optimizeSerialization(url);
        return exporter;
    }
}

利用当前Protocol启动NettyServer。


3.zk客户端

zk注册中心注册的客户端:CuratorZookeeperClient。

public class ServiceConfig<T> extends ServiceConfigBase<T> {
    private void exportUrl(URL url, List<URL> registryURLs) {
        String scope = url.getParameter(SCOPE_KEY);
        if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
            if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
                exportLocal(url);//直接注册在本地
            }

            // export to remote if the config is not local (export to local only when config is local)
            if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
                //核心:根据相关协议徐序列化
                url = exportRemote(url, registryURLs);
                if (!isGeneric(generic) && !getScopeModel().isInternal()) {
                    // 真正通过zk客户度注册provider服务
                    MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel());
                }
            }
        }
        this.urls.add(url);
    }
}

dubbo系列五:Dubbo服务导出源码解析

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值