Dubbo-发布服务执行的流程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo-发布服务执行的流程相关的知识,希望对你有一定的参考价值。
参考技术A 我们以dubbo 的xml配置为例:dubbo服务发布只需在spring.xml中如下配置即可:
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
通过dubbo于spring的融合可以了解到<dubbo:service>标签是通过ServiceBean解析封装。
ServiceBean这个类继承 了ServiceConfig实现了spring的5个接口
InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware 来融入到spring的启动过程。
ServiceBean实现了 ApplicationListener 接口,当spring容器触发了ContextRefreshedEvent事件时,
就会调用 ServiceConfig 中的export()方法发布申明的dubbo服务,
ServiceConfig 中的export()方法部分源码如下,如果申明了delay(延迟多少),那么延迟调用doExport()发布这个服务,如果没有设置则直接调用doExport()发布服务:
接下来看ServiceConfig的doExport()方法
1,检查中是否配置了interface, 如果为空,那么抛出异常:
if (interfaceName == null || interfaceName.length() == 0) throw new IllegalStateException("interface not allow null!");
2,检查接口类型必需为接口
if(! interfaceClass.isInterface())
throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
3,检查方法是否在接口中存在
4,检查引用不为空,并且引用必需实现接口 interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"
5,检查checkApplication(); checkRegistry(); checkProtocol();有效性。
6,调用ServiceConfig.doExportUrls()发布dubbo服务
ServiceConfig.doExportUrls()如下:
通过调用loadRegistries(true)得到所有registry的url地址,例如配置了
<dubbo:registry address="zookeeper://127.0.0.1:2181">
配置结果为dubbo.registry.address=zookeeper://127.0.0.1:2181;
protocols就是将要发布服务的协议集合(dubbo服务可以同时暴露多种协议),例如配置了
<dubbo:protocol name="dubbo" port="20880">
dubbo.protocol.name=dubbo , dubbo.protocol.port=20880
ServiceConfig.doExportUrlsFor1Protocol()
先把application、module、provider、protocol、exporter、registries、monitor所有属性封装到Map中例如protocol=dubbo,host=10.0.0.1,port=20880,path=com.alibaba.dubbo.demo.TestService等,然后构造dubbo定义的统一数据模型URL:
URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
这个url非常重要,贯穿整个dubbo服务的发布和调用过程,可以在服务发布后在dubbo-monitor中看到;
ServiceConfig.doExportUrlsFor1Protocol()中根据scope判断服务的发布范围:
如果配置scope = none, 那么不需要发布这个dubbo服务;
没有配置scope = none,且配置的scope != remote, 那么本地暴露 这个dubbo服务;
没有配置scope = none,且配置的scope != remote且配置的scope != local,那么远程暴露这个dubbo服务(例如远程暴露这个服务到zk上,默认情况下scope没有配置,就是在这里发布服务);
以上如果执行成功,会把dubbo服务到zookeeper上,invoker.getUrl()的值为
registry://10.0.53.87:2188/com.alibaba.dubbo.registry.RegistryService?application=dubbo-test&dubbo=2.0.0&export=dubbo%3A%2F%2F10.52.16.218%3A20886%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-test%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26loadbalance%3Droundrobin%26methods%3DsayHello%26owner%3Dafei%26pid%3D2380%26side%3Dprovider%26timestamp%3D1509953019382&owner=afei&pid=2380®istry=zookeeper×tamp=150995301934:
接下来我们分析
Protocol.export()暴露服务接口:
然后调用RegistryProtocol.export():
核心调用registry.register(registedProviderUrl)。
调用AbstractRegistry.register(URL),把这次需要注册的URL加到Set registered中,即本地缓存新的注册URL;
在ZookeeperRegistry.doRegister(URL)调用AbstractZookeeperClient.create(),toUrlPath将URL形式的地址转换成zookeeper路径,最终在AbstractZookeeperClient中把需要发布的服务的URL保存到zookeeper:
ZookeeperRegistry.doRegister(url)注册服务如果失败:
如果开启了启动检查check=true,那么直接抛出异常;
如果没有开启启动检查,那么将失败的注册请求记录到失败列表,定时重试;
核心调用registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener):
对发布的dubbo服务的这个url进行监听, 当服务变化有时通知重新暴露服务, 以zookeeper为例,暴露服务会在zookeeper生成一个节点,当节点发生变化的时候会触发overrideSubscribeListener的notify方法重新暴露服务
注册服务失败的重试机制:
注册服务失败后,会将url加入重试url集合中,failedRegistered.add(url);重试任务在FailbackRegistry中实现:
注册的监听机制:
订阅并设置监听registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
--> FailbackRegistry.subscribe(URL url, NotifyListener listener)
--> ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener),部分实现源码如下:
当服务有变化的时候:
doNotify(url, listener, urls);
AbstractRegistry.notify(URL url, NotifyListener listener, List urls)
--> RegistryDirectory.notify(List urls)
--> RegistryDirectory.refreshInvoker(List invokerUrls),这里调用toMethodInvokers(Map> invokersMap)的实现比较重要,将invokers列表转成与方法的映射关系,且每个方法对应的List需要通过Collections.sort(methodInvokers, InvokerComparator.getComparator());排序,然后,还要将其转为unmodifiable的map
其中 InvokerComparator 的定义如下,即直接根据url进行比较排序
dubbo协议发布服务会调用DubboProtocol.export()的过程:
从Invoker中获取URL: URL url = invoker.getUrl();
根据URL得到key, 由暴露的服务接口+端口组成,例如com.alibaba.dubbo.demo.DemoService:20886 ; String key = serviceKey(url);
构造DubboExporter存到Map中local cache化:
DubboExporter exporter = new DubboExporter(invoker, key, exporterMap); exporterMap.put(key, exporter);
调用DubboProtocol.openServer()开启netty(默认)服务保持通信,并设置requestHandler处理consumer对provider的调用请求;
DubboProtocol.openServer():
key的值就是IP:Port,例如10.52.17.167:20886,根据key从serverMap中如果取不到ExchangeServer,表示还没绑定服务端口,需要调用createServer(url)-->Exchangers.bind(url, requestHandler)-->Transporters.getTransporter().bind(url, handler)(dubbo支持mina,netty,grizzly,默认实现是netty) --> NettyTransporter.bind(URL, ChannelHandler) --> NettyServer.open();
dubbo默认调用的是netty
Netty服务几个重要的地方
构造 ChannelPipeline 时指定了编码&解码,其中编码为NettyCodecAdapter.getEncoder(),解码为NettyCodecAdapter.getDncoder();
指定了handler为final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);处理请求;
Dubbo工作流程
一、dubbo整体架构
- 其中Service 和 Config 层为 API,对应服务提供方来说是使用ServiceConfig来代表一个要发布的服务配置对象,对应服务消费方来说ReferenceConfig代表了一个要消费的服务的配置对象。可以直接初始化配置类,也可以通过 spring 解析配置生成配置类。
- proxy 服务代理层:扩展接口为 ProxyFactory,dubbo实现的SPI主要JavassistProxyFactory(默认使用)和JdkProxyFactory,用来对服务提供方和服务消费方的服务进行代理。
- registry 注册中心层:封装服务地址的注册与发现,扩展接口为 Registry , RegistryService,Dubbo提供的扩展接口实现为ZookeeperRegistry,RedisRegistry,MulticastRegistry,DubboRegistry。
- 扩展接口RegistryFactory,dubbo提供的扩展接口实现DubboRegistryFactory,DubboRegistryFactory,RedisRegistryFactory,ZookeeperRegistryFactory。
- cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,
- 扩展接口为 Cluster , Directory , Router ,LoadBalance。
- monitor 监控层:RPC 调用次数和调用时间监控,扩展接口为 MonitorFactory , Monitor , MonitorService。
- protocol 远程调用层:封将 RPC 调用,扩展接口为 Protocol , Invoker , Exporter。
- exchange 信息交换层:封装请求响应模式,同步转异步,扩展接口为 Exchanger , ExchangeChannel ,ExchangeClient , ExchangeServer
- transport 网络传输层:抽象 mina 和 netty 为统一接口扩展接口为 Channel , Transporter , Client , Server , Codec
- serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization ,ObjectInput , ObjectOutput , ThreadPool
二、dubbo服务发布原理
首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:UserServiceImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。
接下来就是 Invoker 转换到 Exporter 的过程。Dubbo 处理服务暴露的关键就在 Invoker 转换到 Exporter 的过程,上图中的红色部分。
Dubbo 协议的 Invoker 转为 Exporter 发生在 DubboProtocol 类的 export 方法,它主要是打开创建一个Netty Server 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现,然后注册服务到服务注册中心
三、dubbo消费原理
首先 ReferenceConfig 类的 init 方法调用 Protocol 的 refer 方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。接下来把Invoker 转换为客户端需要的接口。
dubbo协议的invoker转换为客户端需要的接口是发生在DubboProtocol的refer方法,他主要是创建一个netty client 链接服务提供者,通讯细节由 Dubbo 自己实现。
四、dubbo原理总结
节点角色说明:
- Provider: 暴露服务的服务提供方。
- Consumer: 调用远程服务的服务消费方。
- Registry: 服务注册与发现的注册中心。
- Monitor: 统计服务的调用次调和调用时间的监控中心。
- Container: 服务运行容器。
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
registry(configServer)
注册中心,和每个Server/Client之间会作一个实时的心跳检测(因为它们都是建立的Socket长连接),比如几秒钟检测一次。收集每个Server提供的服务的信息,每个Client的信息,整理出一个服务列表
当某个Server不可用,那么就更新受影响的服务对应的serverAddressList,即把这个Server从serverAddressList中踢出去(从地址列表中删除),同时将推送serverAddressList给这些受影响的服务的clientAddressList里面的所有Client
consumer(client)
Client在使用服务的时候根据服务名称去ConfigServer中获取服务提供者信息(这样ConfigServer就知道某个服务是当前哪几个Client在使用),Client拿到这些服务提供者信息后,与它们都建立连接,后面就可以直接调用服务了,当有多个服务提供者的时候,Client根据一定的规则来进行负载均衡,如轮询,随机,按权重等。
一旦Client使用的服务它对应的服务提供者有变化(服务提供者有新增,删除的情况),ConfigServer就会把最新的服务提供者列表推送给Client,Client就会依据最新的服务提供者列表重新建立连接,新增的提供者建立连接,删除的提供者丢弃连接
provider(server)
优点:
- 只要在Client和Server启动的时候,ConfigServer是好的,服务就可调用了,如果后面ConfigServer挂了,那只影响ConfigServer挂了以后服务提供者有变化,而Client还无法感知这一变化。
- Client每次调用服务是不经过ConfigServer的,Client只是与它建立联系,从它那里获取提供服务者列表而已
- 调用服务-负载均衡:Client调用服务时,可以根据规则在多个服务提供者之间轮流调用服务。
- 服务提供者-容灾:某一个Server挂了,Client依然是可以正确的调用服务的,当前提是这个服务有至少2个服务提供者,Client能很快的感知到服务提供者的变化,并作出相应反应。
- 服务提供者-扩展:添加一个服务提供者很容易,而且Client会很快的感知到它的存在并使用它。
以上是关于Dubbo-发布服务执行的流程的主要内容,如果未能解决你的问题,请参考以下文章