深入掌握Dubbo服务提供者发布与注册原理

简介: 该文章主要介绍了Dubbo服务提供者发布与注册的原理,包括服务发布的流程、多协议发布、构建Invoker、注册到注册中心等过程。

服务发布和注册是Dubbo非常核心的能力之一,也是Rpc技术非常关键的一步。本文将从源码层面掌握Dubbo的服务发布和注册。

先看下整体步骤

我们接着看下源码是不是按这个流程来处理的。

暴露服务的源码入口

com.alibaba.dubbo.config.spring.ServiceBean#onApplicationEvent spring容器启动完成后触发ContextRefreshedEvent事件 。

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,        ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,        ApplicationEventPublisherAware {     @Override    public void onApplicationEvent(ContextRefreshedEvent event) {        if (!isExported() && !isUnexported()) {            if (logger.isInfoEnabled()) {                logger.info("The service ready on spring started. service: " + getInterface());            }            //执行发布            export();        }    }}

多协议发布

private void doExportUrls() {        List<URL> registryURLs = loadRegistries(true);        //遍历协议列表,分开发布服务        for (ProtocolConfig protocolConfig : protocols) {            String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);            ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);            ApplicationModel.initProviderModel(pathKey, providerModel);            doExportUrlsFor1Protocol(protocolConfig, registryURLs);        }    }

组织Url,需要说明的是Dubbo是根据组装的URL来驱动发布注册的。Url信息如下:

public class URL implements Serializable {​    private static final long serialVersionUID = -1985165475234910535L;​    private final String protocol;​    private final String username;​    private final String password;​    // by default, host to registry    private final String host;​    // by default, port to registry    private final int port;​    private final String path;​    private final Map<String, String> parameters;}

构建Invoker\

public class JavassistProxyFactory extends AbstractProxyFactory {​    @Override    @SuppressWarnings("unchecked")    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));    }​    @Override    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'        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) throws Throwable {                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);            }        };    }​}

发布本地tcp端口

@Override    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
   
           URL url = invoker.getUrl();// export service.        String key = serviceKey(url);        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);        exporterMap.put(key, exporter);//export an stub service for dispatching event        Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);        Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);        if (isStubSupportEvent && !isCallbackservice) {
   
               String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);            if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
   
                   if (logger.isWarnEnabled()) {
   
                       logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +                            "], has set stubproxy support event ,but no stub methods founded."));                }} else {
   
                   stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);            }        }​        openServer(url);        optimizeSerialization(url);return exporter;    }
public class HeaderExchanger implements Exchanger {
   
   public static final String NAME = "header";@Override    public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
   
           return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);    }@Override    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
   
           return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));    }}
org.apache.dubbo.remoting.transport.netty4#doOpen 开启端口

并绑定请求处理器

@Override    protected void doOpen() throws Throwable {
   
           bootstrap = new ServerBootstrap();​        bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));        workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),                new DefaultThreadFactory("NettyServerWorker", true));​        final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);        channels = nettyServerHandler.getChannels();​        bootstrap.group(bossGroup, workerGroup)                .channel(NioServerSocketChannel.class)                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)                .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)                .childHandler(new ChannelInitializer<NioSocketChannel>() {
   
                       @Override                    protected void initChannel(NioSocketChannel ch) throws Exception {
   
                           // FIXME: should we use getTimeout()?                        int idleTimeout = UrlUtils.getIdleTimeout(getUrl());                        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);                        ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug                                .addLast("decoder", adapter.getDecoder())                                .addLast("encoder", adapter.getEncoder())                                .addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))                                .addLast("handler", nettyServerHandler);                    }                });        // bind        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());        channelFuture.syncUninterruptibly();        channel = channelFuture.channel();}

注册到注册中心,将url注册到zk上 路径是/dubbo/服务名/providers

@Override    public void doRegister(URL url) {
   
           try {
   
               zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));        } catch (Throwable e) {
   
               throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);        }    }

看完源码,服务发布和注册流程确实是按如下图,可以debug再验证下。

相关文章
|
1月前
|
负载均衡 监控 Dubbo
Dubbo 原理和机制详解(非常全面)
本文详细解析了 Dubbo 的核心功能、组件、架构设计及调用流程,涵盖远程方法调用、智能容错、负载均衡、服务注册与发现等内容。欢迎留言交流。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Dubbo 原理和机制详解(非常全面)
|
2月前
|
监控 Dubbo Java
dubbo学习三:springboot整合dubbo+zookeeper,并使用dubbo管理界面监控服务是否注册到zookeeper上。
这篇文章详细介绍了如何将Spring Boot与Dubbo和Zookeeper整合,并通过Dubbo管理界面监控服务注册情况。
102 0
dubbo学习三:springboot整合dubbo+zookeeper,并使用dubbo管理界面监控服务是否注册到zookeeper上。
|
4月前
|
JSON Dubbo Java
【Dubbo协议指南】揭秘高性能服务通信,选择最佳协议的终极攻略!
【8月更文挑战第24天】在分布式服务架构中,Apache Dubbo作为一款高性能的Java RPC框架,支持多种通信协议,包括Dubbo协议、HTTP协议及Hessian协议等。Dubbo协议是默认选择,采用NIO异步通讯,适用于高要求的内部服务通信。HTTP协议通用性强,利于跨语言调用;Hessian协议则在数据传输效率上有优势。选择合适协议需综合考虑性能需求、序列化方式、网络环境及安全性等因素。通过合理配置,可实现服务性能最优化及系统可靠性提升。
60 3
|
4月前
|
负载均衡 Dubbo 应用服务中间件
Dubbo服务调用过程原理
该文章主要介绍了Dubbo服务调用过程的原理,包括服务调用的主要阶段和服务调用的具体步骤。
Dubbo服务调用过程原理
|
4月前
|
缓存 Dubbo Java
Dubbo服务消费者启动与订阅原理
该文章主要介绍了Dubbo服务消费者启动与订阅的原理,包括服务消费者的启动时机、启动过程以及订阅和感知最新提供者信息的方式。
Dubbo服务消费者启动与订阅原理
|
4月前
|
C# 开发者 Windows
勇敢迈出第一步:手把手教你如何在WPF开源项目中贡献你的第一行代码,从选择项目到提交PR的全过程解析与实战技巧分享
【8月更文挑战第31天】本文指导您如何在Windows Presentation Foundation(WPF)相关的开源项目中贡献代码。无论您是初学者还是有经验的开发者,参与这类项目都能加深对WPF框架的理解并拓展职业履历。文章推荐了一些适合入门的项目如MvvmLight和MahApps.Metro,并详细介绍了从选择项目、设置开发环境到提交代码的全过程。通过具体示例,如添加按钮点击事件处理程序,帮助您迈出第一步。此外,还强调了提交Pull Request时保持专业沟通的重要性。参与开源不仅能提升技能,还能促进社区交流。
47 0
|
4月前
|
缓存 负载均衡 Dubbo
Dubbo服务集群容错原理(重要)
该文章主要介绍了Dubbo服务集群容错的原理,包括集群容错技术的概念、Dubbo中使用的集群容错技术种类及其原理。
|
7月前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
2月前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
66 2
|
4月前
|
Dubbo Java 应用服务中间件
💥Spring Cloud Dubbo火爆来袭!微服务通信的终极利器,你知道它有多强大吗?🔥
【8月更文挑战第29天】随着信息技术的发展,微服务架构成为企业应用开发的主流模式,而高效的微服务通信至关重要。Spring Cloud Dubbo通过整合Dubbo与Spring Cloud的优势,提供高性能RPC通信及丰富的生态支持,包括服务注册与发现、负载均衡和容错机制等,简化了服务调用管理并支持多种通信协议,提升了系统的可伸缩性和稳定性,成为微服务通信领域的优选方案。开发者仅需关注业务逻辑,而无需过多关心底层通信细节,使得Spring Cloud Dubbo在未来微服务开发中将更加受到青睐。
87 0