开发者学堂课程【阿里巴巴分布式服务框架 Dubbo 快速入门:应用的架构演变】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/624/detail/9479
服务暴露流程
内容介绍
一、内容回顾与引入
二、具体操作演示
三、总结
一、内容回顾与引入
在 loc 容器启动加载配置文件时,标签处理器来解析每一个的标签,会封装成对应的组件,而解析 service 标签会将 service 标签的信息封装成我们这个 ServiceBean。
而 ServiceBean 有点儿特殊,它实现了两个重要的机制,一个是实现了 InitializingBean,这是 spring 里面的一个接口,当组件创建完对象以后,会调用 InitializingBean 里面的 afterPropertiesSet,在属性设置完以后会来回调这个方法。
InitializingBean 还实现了另外一个接口,叫 ApplicationListener,被叫做应用的监听器,这也是 spring 原理里面核心的一部分。
但是它监听的事件是 ContextRefreshEvent,当 Loc 容器整个刷新完成,比如说 LoC 容器里边所有对象都创建完以后,会来回调一个 onApplicationEvent 方法。
相当于 servicBean 会在容器在创建完对象以后,调一个 afterPropertiesSet 方法,还会在 LOC 容器启动完成以后,调用 onApplicationEvent 。这是 spring 里边面的原理。
将用 Debug 的方式对 afterPropertiesSet
onApplicationEvent 方法进行演示
二、具体操作演示
1.经过放行(包括每一个标签的解析),首先看 serviceBean 的 afterPropertiesSet,它获取 provider,获取了一些信息,这个其实就是在标签解析的时候,解析的每一个标签的属性值,封装成的组件。
如果说等于空,在这个位置,就来获取相应的内容。
获取到相应的内容以后,主要的核心在下一步:
被叫做 SetProvider,相当于把标签里面配的 double provider 这个相关的信息保存起来。还有很多,包括 application 的信息,包括 Module 的信息,包括我们配的注册中心的信息,它是保存在当前的 serviceBean 里。相当于把以前配置的信息都保存起来了。
2.下一步发现又回调了 onApplicationEvent 事件。
当 loc 容器刷新了以后,它来调一个方法,如果不是延迟的,既要暴露还没暴露,不是延迟暴露的话,然后它就调一个叫 export(),这是关键。
export方法翻译过来就是暴露服务。
相当于来到了这一步
当 service 标签解析完成,最关键就是 loc 容器完成以后会触发一个事件回调,事件回调里面会进行服务的暴露。
3、服务如何暴露
Stepinto 进来以后,前面依旧是都是一大堆的获取信息判断,而核心在,有一个叫 doExport() 的方法,那就是执行暴露。直接放行到 doExportUrls,step into 进入来看暴露做了什么。
Private void doExportUrls() {
List<URL> registryURLs=loadRegistries(ture);
For (protocolConfig protocolConfig:protocols) {
doExportUrlsFor1protocol(protocolConfig,registryURLS);
}
}
Private void doExportUrls() {List<URL> registryURLs=loadRegistries(ture);For (protocolConfig protocolConfig:protocols) {doExportUrlsFor1protocol(protocolConfig,registryURLS);}}
第一步加载注册中心的一些信息,相当于读取到注册中心的这个地址,配置里有写道。接下来看到有一个 protocols,这个协议,
这个 protocolConfig 相当于要把当前服务在20082端口来暴露。这个暴露是怎样暴露的?
如果要暴露在多个端口,其实能配多个标签,相当于既能用 dubbo 协议,还能用其他协议,都能暴露。这是一个 for 循环,遍历整个数组的。
3.就一个 protocol,是来暴露 dubbo 协议的方式,它调用 do exportUrlsFor1Protocol 这一个协议,这一块如何暴露的?
4.Stepinto 进入,进入以后注意观察注册中心,直接放行到有意义的方法。
在这一块儿有一些方法,叫做 proxyFactory ,用这个代理工厂,它要获取一些 invoker,获取执行器,然后 protocol 还要暴露。此处放行,来到 Invoker<?>invoker=proxyFactory.getInvoker
这一处先是利用 proxy 工厂,
这个代理工厂获取到 Invoker,相当于有一个 UserServiceImpl 实现对象和 user service 接口,来获取到这个接口。
这个对象的执行者其实就是把 UserServiceImpl 实现对象包装了,包括把 UPL 地址也包装了,组合成为了一个 invoker。就认为这个 invoker 是执行者的真正的信息,要执行哪个对象,哪个方法,包括它的 URL 地址是什么,URL 相当于是要给注册中心注册的东西。那这一块得到了一个 invoker 执行器,然后把执行器再一次包装,就是层层包装。继续放行,包装完以后注意会调用 protocol.export ,在这要暴露 Invoker。
Exporter<?>exporter=protocol.export(wrapperInvoker)
如何暴露Invoker?
点击进入查看 protocol,protocol 是通过
getExtensionLoader(Protocol.class).getAdaptiveExtension()
得到的,也就是说 protocol.class 获取扩展的类加载器,包括得到
施配,这是基于 Java 的 SPI 机制,会得到 protocol 里面当前要用
的。同时由于用的是 dubbo 协议暴露的,所以用 DubboProtocol ,
包括也会把它注册到注册中心,所以用 RegistryProtocol。
5、Dubbo 中的 Exporter 方法
看一下 Dubbo 中的 Exporter 和 Registy 中的 export 这
两个都做了什么。
想要 export 导出 invoker (暴露invoker),一暴露就会生成 exporter,exporter 就会保存起来那。
暴露是怎么进行的,放行,放行到第一个叫 registry protocol,先
来到注册中心里面来暴露。它会传一个叫 originInvoker,也就是原
生的执行者。这个执行者里面,其实就是封装了服务的实现 user
service implement,以及服务的接口及服务的 url 地址等等信息。
然后它的第一步是 dolocalExport 来做本地的暴露。
然后
FinalExporterChangeableWrapper<T>exporter=doLocalExpo
rt(originInvoker)
;是重要的一步。
再来看下一步,有一个叫 register consumerRegtable,叫 provider,
就是提供者和消费者的注册表,这个注册表里面,会来注册提供者,
所谓的注册就是把提供者传进来,注册中心的地址,包括提供者的 url 地址。
6、ProviderConsumerRegTable.registerProvider(originInvoker,registryUrl,registedProviderUrll);
If (register){
Register(registyUrl,registedProderUrl);
ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true)
;
在这两步打上断点,此处也有注册方法。
看第一步做了什么: stepinto,进 local,在暴露本地服务的时候,暴露器 exporter 第一次是没有的,它就负责帮助制作一个暴露器。
拿到要暴露的执行者,会看到一个 RegistryProtocol 和 Invoker 都是需要暴露的。它依旧是调用 protocol 基因暴露,直接放行就可以。这是一个 protocol 里面封装 protocol,实际会发现它来到了 dubboprotocol,比如用 double 协议进行服务暴露。
先拿到 Invoker 的 url 地址,这个地址相要在注册中心里要调用,然后,把这个地址各种转化,包装成 DubboExporter,又包装成一个暴露器,这个暴露器来往下查看都是一些属性设置。
接下来暴露器 Dubboprotocol 要做一件事,叫 openServer,打开服务器,
openServer(Url);
optimizeSerialization(url);
return exporter;
}
打开服务器先拿到 url 地址,就是相当于20882,服务暴露的所在端口,它创建一个 ExchangeServer,相当于信息交换的这个服务器,这个服务器开始肯定创不出来,遇到特殊,它就调用一个 creatserver。
7、创建服务器
在以下部分会创建一个服务器:
ExchangeServer server;
Try{
Server=Exchangers.bind(url,requestHandler);
}catch(RemotingException e){
调用这个方法的 bind 来绑定服务器,并绑定请求的处理器。而 bind 打开就可以。
bind点进去绑定一个 url 地址,获取 exchanger 的信息的交换器服务器,然后调用 bind。再点进来,调用看到的是 transporters,这个叫传输器。
再来 bind,而这个传输器 bind,再点入,就看到 channel handler,包括 transporter().bind,都是 Netty 的底层。再点一个 bind,就会让你选择使用哪个 transporter。使用 netty 呢,相当于创建一个 netty 的服务器。
比如上一步创建服务器
openServer(Url);
optimizeSerialization(url);
return exporter;
}
要暴露服务,创建服务器,其实就是启动 ninety 服务器监听20880端口。
那么 dubbo 就完了。再来放行,来到 dubbo 暴露出来,相当于 Netty 服务器在底层启动2080端口进行监听,注册提供者。这个注册是用来保存一些信息。其中 providerInvokers 有一个叫服务提供,提供者的执行器。
与此同时,有一个 consumerInvokers,就是这两个属性:ConcurrentHashMap<String,set<ProviderInvokerWrapper>>
ConcurrentHashMap<String,set<ConsumerInvokerWrapper>>
它相当于保存了每一个url的地址对应的服务提供者,包括对应的消费者的执行器,执行器里才有真正的服务的对象,比如 Service 的实现。相当于把这个关联关系维护着。
Invokers.add(wrapperinvoker)
那么 Invoker,就添加真正的服务,要暴露的地址是 dubbo://192.168.1.101:20882/com.我们的真正的服务是 user service 的实现。
比如最终运行完以后,Invoker 就会来添加上数值,至此服务就暴露出来了。
三、总结
共分为两步。首先 dubbo 在底层, dubbo 的 protocol 会来启动 netty 服务器来监听20880端口。还有一个叫 registry 注册中心,会帮助进行注册服务呢,那就是用这个注册器
其实就是来把注册中心注册好的地址,包括利用的客户端,把注册服务的地址保存在注册中心,控制台其实就已经有打印了。还有将
注册中心的信息缓存。
最重要的就是每一个 url 地址的调用会来保存实际的 Invoker 执行器。
整个过程如图所示
当暴露服务的时候,要获取到 invoker,获取到执行器。是通过用 protocol 来暴露执行器。Protocol 会用两个,一个是 dubbo 协议
的 protocol,还有一个是 registry 的 protocol,两个都对应两个
暴露者。
暴露器 dubbo 的 protocol 会来开启服务器。而注册中心的 exporter的暴露器会帮忙把每一个服务以及它的 url 地址的对应信
息都保存在注册表里。注册表里相当于缓存了一个 url 地址,对应
哪个服务的执行器,执行器里有真正的服务。这样做的好处是什么
呢?
前面的 W20880端口已经启动,接下来远程时要调,要调哪个 url 地址的服务,怎么知道原来地址对应的哪个服务是什么呢?
因为把这个 url 地址跟服务的执行器已经保存在注册表,就按照 url
地址对应哪个执行器,这个执行器拿到就能调用。
这是我们整个服务暴露的过程,核心也看到了 netty 底层再来开服务器。