1、服務發佈 - 原理:java
首先看Dubbo日誌,截取重要部分:spring
1)暴露本地服務bootstrap
Export dubbo service com.alibaba.dubbo.demo.DemoService to local registry, dubbo version: 2.0.0, current host: 10.165.2.47
2)暴露遠程服務tomcat
Export dubbo service com.alibaba.dubbo.demo.DemoService to url dubbo://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.165.2.47&bind.port=20880&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&qos.port=22222&side=provider×tamp=1520303067433, dubbo version: 2.0.0, current host: 10.165.2.47 Register dubbo service com.alibaba.dubbo.demo.DemoService url dubbo://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.165.2.47&bind.port=20880&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&qos.port=22222&side=provider×tamp=1520303067433 to registry registry://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=3880&qos.port=22222®istry=multicast×tamp=1520303067418, dubbo version: 2.0.0, current host: 10.165.2.47
3)啓動Netty網絡
Start NettyServer bind /0.0.0.0:20880, export /10.165.2.47:20880, dubbo version: 2.0.0, current host: 10.165.2.47
4)打開Zookeeperapp
INFO zookeeper.ClientCnxn: Opening socket connection to server /192.168.48.117:2181
5)註冊到Zookeeper異步
Register: dubbo://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&side=provider×tamp=1520303067433, dubbo version: 2.0.0, current host: 10.165.2.47
6)監聽Zookeeperjvm
Subscribe: provider://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&side=provider×tamp=1520303067433, dubbo version: 2.0.0, current host: 10.165.2.47
7)頻繁發送廣播包,註冊中心利用廣播包監聽provider健康情況socket
[DUBBO] Send broadcast message: subscribe provider://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&side=provider×tamp=1520303067433 to /224.5.6.7:1234, dubbo version: 2.0.0, current host: 10.165.2.47 [DUBBO] Receive multicast message: subscribe provider://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&side=provider×tamp=1520303067433 from /10.165.2.47:1234, dubbo version: 2.0.0, current host: 10.165.2.47
2、根據原理分析源碼tcp
1)首先看Provider註冊文件:
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/> <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
能夠看到,是經過dubbo的schema service進行注入的,那咱們找到DubboNameSpaceHandler,Dubbo命名空間處理器,找到<dubbo:service>標籤解析行:
public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); //這裏就是<dubbo:service>標籤的解析注入入口 registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); }
2)上面new DubboBeanDefinitionParser是<dubbo:service>解析注入入口,那ServiceBean.class就是<dubbo:service>標籤的發佈訂閱入口,進入ServiceBean能夠看到一個核心方法onApplicationEvent(),其中export()就是發佈方法:
public void onApplicationEvent(ContextRefreshedEvent event) { if (isDelay() && !isExported() && !isUnexported()) { if (logger.isInfoEnabled()) { logger.info("The service ready on spring started. service: " + getInterface()); } //這裏是發佈方法 export(); } }
3)發佈代碼處理路徑
ServiceBean.onApplicationEvent -->export() -->ServiceConfig.export() -->doExport() -->doExportUrls() //裏面有個for循環,表明一個服務能夠有多個通訊協議,例如tcp、http、dubbo等協議,默認是tcp協議 -->loadRegistries(true) //加載註冊信息,組裝註冊中心url信息,如源碼中config.properties中讀取dubbo.registry.address = zookeeper://192.168.99.100:32770連接信息,組裝Provider註冊連接串 -->doExportUrlsFor1Protocol(protocolConfig, registryURLs)
// export to local if the config is not remote (export to remote only when config is remote)遠程暴露
-->exportLocal(url)
-->proxyFactory.getInvoker(ref, (Class) interfaceClass, local))
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist");
-->extension.getInvoker(arg0, arg1, arg2)
-->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
-->proxyFactory.getInvoker(proxy, type, url)
-->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
-->Wrapper.getWrapper(com.alibaba.dubbo.demo.provider.DemoServiceImpl)
-->makeWrapper(Class<?> c)
-->return new AbstractProxyInvoker<T>(proxy, type, url)
-->protocal.export //本地暴露
-->Protocal$Adaptive.export
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocal.class).getExtension("injvm");
-->extension.export(arg0)
-->ProtocalFilterWrapper.export
-->buildInvokerChain //建立8個Filter
-->ProtocalListenerWrapper.export
-->InjvmProtocal.export
-->return new InjvmExporter
-->InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {super(invoker);
this.key = key;
this.exporterMap = exporterMap;
exporterMap.put(key, this); //key=com.alibaba.dubbo.demo.DemoService this=InjvmExporter
}
// export to local if the config is not remote (export to remote only when config is remote)遠程暴露
-->proxyFactory.getInvoker()原理和本地暴露同樣都是爲了獲取一個Invoker對象
-->protocal.export
-->Protocal$Adaptive.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocal.class).getExtension("register");
-->extension.export(arg0)
-->ProtocalFilterWrappter.export
-->ProtocalListenerWrapper.export
-->RegistryProtocal.export
-->doLocalExport(originInvoker)
-->getCacheKey(originInvoker) //讀取dubbo://192.168.100.51:20880/
-->protocal.export
-->Protocol$Adaptive.export
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocl.class).getExtension("dubbo");
-->extension.export(arg0)
-->ProtocolFilterWrapper.export
-->buildInvokerChain//建立8個Filter
-->ProtocalListenerWrapper.export
-------一、netty服務暴露的開始-------- -->DubboProtocal.export
-->serviceKey(url) //組裝key=com.alibaba.dubbo.demo.DemoService:20880
-->目的:exporterMap.put(key, this); //key=com.alibaba.dubbo.demo.DemoService:20880 this=
-->openServer(url)
-->createServer(url)
--------二、信息交換層exchange 開始-------- -->Exchanges.bind(url, requestHandler) //exchanger是一個信息交換層
-->getExchanger(url)
-->getExchange(type)
-->ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension("header")
-->HeaderExchanger.bind
-->Transporters.bind(url, new DecoderHandler(new HeaderExchangeHandler(handler)))
-->new HeaderExchangeHandler(handler) //this.handler = handler
-->new DecoderHandler
-->new AbstractChannelHandlerDelegate //this.handler = handler;
------------>三、網絡傳輸層 transporter ------------ -->Transporters.bind
-->getTransporter
-->ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
-->Transporter$Adaptive.bind
//netty服務暴露
-->ExtensionLoader.getExtensionLoader(com.alibaba.remoting.Transporter.class).getExtension("netty")
-->extension.bind(arg0, arg1)
-->NettyTransporter.bind
-->new NettyServer(url, listener)
-->AbstractPeer //this.url = url; this.handler = handler;
-->AbstractEndpoint //codec timeout=1000 connectTimeout=3000
-->AbstractServer //bindAddress accepts=0 idleTimeout=600000
----------->四、打開端口,暴露netty服務 -------------- -->doOpen()
-->設置NioServerSocketChannelFactory boss worker的線程池 線程個數爲3
-->設置編碼handler
-->bootstrap.bind(getBindAddress())
-->new HeaderExchangeServer()
-->this.server = NettyServer
-->this.heartbeat=600000
-->heartbeatTimeout=180000
-->startHeatbeatTimer() //這是一個心跳定時器,採用線程池ScheduledExecutePool,若是斷開了就心跳重連
-->
說說上述這些類及方法的概念做用:」
一、proxyFactory:就是爲了獲取一個接口的代理類,例如獲取一個遠程接口的代理。
它有2個方法,表明2個做用
a、getInvoker:針對server端,將服務對象,例如DemoServiceImpl包裝成一個Wrapper對象。
b、getProxy:針對client端,建立接口的代理對象,例如DemoService的接口。
二、makeWrapper:它相似spring的BeanWrapper,它就是包裝了一個接口或一個類,能夠經過Wrapper對實例對象進行賦值取值以及指定方法的調用。
三、Invoker:它是一個可執行的對象,可以根據方法名稱、參數獲得相應的執行結果。
它裏面有一個很重要的方法Result invoke(Invocation invocation),Invocation是包含了須要執行的方法和參數的重要信息,目前它只有2個實現類RpcInvocation MockInvocation
它有3種類型的Invoker
一、本地執行類的Invoker
二、遠程通訊類的Invoker
三、多個遠程通訊執行類的Invoker聚合成集羣版的Invoker
四、Protocol:
1)export暴露遠程服務(用語服務端),就是將proxyFactory.getInvoker建立的代理類invoker對象,經過協議暴露給外部。
2)refer:引用遠程服務(用於客戶端)
五、Exporter:維護invoker的生命週期
六、exchanger:信息交換層,封裝請求響應模式同步轉異步
七、transporter:網絡傳輸層,用來抽象Netty(dubbo默認)或者Mina的統一接口
4)暴露本地服務與暴露遠程服務的區別是什麼?
a、暴露本地服務:指暴露在同一個JVM裏面,不用經過zk來進行遠程通訊,例如在同一個服務裏面(tomcat),本身調用本身的接口,就無需進行網絡IP鏈接通訊。
b、暴露遠程服務:指暴露給遠程客戶端的IP和端口號,經過網絡來實現通訊。