要了解服务导出做了什么,需要了解导出的目的是什么?
dubbo是一款面向接口代理的高性能RPC调用,说白了就是提供远程服务。
服务导出需要做的:简单说就是根据服务参数、服务协议构建服务URL,注册到注册中心,并启动Server。其中服务参数可以动态配置也需要监听。
服务导出入口
1、onApplicationEvent
org.apache.dubbo.config.spring.ServiceBean#onApplicationEvent
发布ContextRefreshedEvent,进行导出服务
2、export
org.apache.dubbo.config.spring.ServiceBean#export
调用父类ServiceConfig导出,之后发布一个ServiceBeanExportedEvent
3、export
org.apache.dubbo.config.ServiceConfig#export
①、检查更新配置参数
②、shouldExport检查服务是否需要导出
org.apache.dubbo.config.ServiceConfig#shouldExport
③、shouldDelay检查服务是否需要延迟发布
④、doExport进行服务导出
更新配置
checkAndUpdateSubConfigs
org.apache.dubbo.config.ServiceConfig#checkAndUpdateSubConfigs
1、completeCompoundConfigs
ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、ModuleConfig、ApplicationConfig中获取
①、如果配置了ProviderConfig provider,如果application、module、registries、monitor、protocols、configCenter这些属性为空的情况下,可以从provider中获取信息并赋值
②、同样的道理,如果ModuleConfig module不为空,可以为registries、monitor为空的赋值
③、如果ApplicationConfig application不为空,可以为registries、monitor为空的赋值
2、startConfigCenter
从配置中心获取配置,包括应用配置和全局配置
①、如果ConfigCenterConfig ConfigCenter配置中心配置不为空,则需要从其他位置获取配置中心的相关属性信息进行更新refresh
org.apache.dubbo.config.AbstractConfig#refresh
a、前缀是prefix = "dubbo.config-center"、id = null 在不同的位置获取混合的配置CompositeConfiguration
CompositeConfiguration是一个有序的LinkedList
SystemConfiguration(JVM环境变量系统配置)、EnvironmentConfiguration(操作系统环境变量)、InmemoryConfiguration(配置中心应用配置和全局配置)、PropertiesConfiguration(dubbo.properties中的配置)
b、根据configCenterFirst配置设置config的位置。默认true第四个位置
参数配置及优先级
SystemConfiguration:是系统环境变量,可以在服务启动时通过-D指定参数
AbstractConfig:是通过@Service注解配置的参数
PropertiesConfiguration:是dubbo.properties文件配置的
AppExternalConfiguration和ExternalConfiguration:是在配置中心Dubbo-Admin中配置的,AppXxx是应用级别
配置优先级是:SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
如果放到第二个位置优先级是:SystemConfiguration -->AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> -> PropertiesConfiguration
c、根据setXX()方法和setParameters()方法进行参数值覆盖
②、prepareEnvironment
管理台上的动态配置中心,如果是zookeeper,获取的就是/dubbo/config/dubbo/dubbo.properties节点中的内容。如果是应用级别的则获取的就是/dubbo/config/dubbo-demo-consumer-application/dubbo.properties节点中的内容(dubbo-demo-consumer-application应用名,这里是以dubbo-demo为例)
③、refreshAll
ConfigManager.getInstance().refreshAll()刷新所有配置
org.apache.dubbo.config.context.ConfigManager#refreshAll
3、checkDefault 从配置中心设置Provider配置
org.apache.dubbo.config.ServiceConfig#checkDefault
4、checkProtocol 从配置中心设置Protocol配置
org.apache.dubbo.config.ServiceConfig#convertProtocolIdsToProtocols
①、从配置中心的全局配置获取dubbo.protocols.的配置项值添加到configedProtocols
②、从配置中心的应用配置获取dubbo.protocols.的配置项值添加到configedProtocols
③、用,号join所有的protocol(configedProtocols),并赋值给protocolIds
④、如果protocolIds为空,说明配置中心没有配置协议,就取默认的协议
⑤、如果配置中心配置协议(protocolIds不为空),就把从配置中心配置的协议添加到服务的协议列表中
5、checkApplication
如果ApplicationConfig为空,则构造一个ApplicationConfig
6、checkRegistry
如果协议不是injvm本地协议(isOnlyInJvm),则会从配置中心获取Registry配置
7、refresh 刷新配置中心配置
8、checkMetadataReport 更新MetadataReportConfig中的属性为优先级最高的配置
9、检查当前服务是不是一个泛化服务
10、Local、Stub、Mock
local和stub一样,不建议使用了。如果Local存根为true,则存根类为interfaceName + "Local"。如果Stub本地存根为true,则存根类为interfaceName + "Stub"
a、checkStubAndLocal
org.apache.dubbo.config.AbstractInterfaceConfig#checkStubAndLocal
b、checkMock
服务导出
doExport
org.apache.dubbo.config.ServiceConfig#doExport
unexported表示:当前服务已经被取消了,就不需要再导出了
exported表示:已经导出了,就不再重复导出了
1、doExportUrls
org.apache.dubbo.config.ServiceConfig#doExportUrls
①、registryURLs
注册服务也是一个服务,也会有对应的url,通过调用该url可以完成服务注册
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider1-application&dubbo=2.0.2&logger=log4j&pid=3384®istry=zookeeper&release=2.7.0×tamp=1595994447689
②、遍历protocols协议
pathKey = group/应用名/path服务名:version
例如:mygroup/dubbo-demo/org.apache.dubbo.demo.DemoService:1.0.1
ProviderMethodModel表示某一个方法、方法名所属的服务的,包含实现类,接口,以及接口中的各个方法
ApplicationModel表示应用中有哪些服务提供者和引用了哪些服务
每种协议都会导出一个单独的服务,并注册到各个注册中心
2、doExportUrlsFor1Protocol
org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
protocolConfig表示协议配置,registryURLs表示所有的注册中心URL
①、如果配置的协议没有配置name,则默认为dubbo
②、map表示服务url的参数,包括监控中心、应用、模块、提供者、协议、 服务本身
③、methods方法参数处理
④、METHODS_KEY = methods 参数添加
⑤、Token参数
是为了防止服务被消费者直接调用(伪造http请求)
⑥、获取服务URL 可以通过获取的host和port访问该服务
服务url
dubbo://192.168.7.81:20881/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider1-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.7.81&bind.port=20881&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=3384&release=2.7.0&side=provider×tamp=1595995315282
⑦、scope导出方式(scope=null进行远程导出)
scope取值是:null、remote、 local、none
如果scope为none,则不会进行任何的服务导出,既不会远程,也不会本地
如果scope不是remote,则会进行本地导出,会把当前url的protocol改为injvm,然后进行导出
如果scope不是local,则会进行远程导出
如果是injvm,则不需要进行注册中心注册
⑧、为服务url添加dynamic、monitor、proxy参数,如果存在的话
如果有注册中心,则将服务注册到注册中心
⑨、生成Invoker代理对象
Invoker 是实体域,它是 Dubbo 的核心模型,它代表一个可执行体,可向它发起 invoke 调用。可参考官网架构设计领域模型
a、生成一个当前服务接口的代理对象invoker。使用代理(默认使用javassist)生成一个Invoker,Invoker表示服务提供者的代理,可以使用Invoker的invoke方法执行服务
参数包含:服务的实现者ref=DemoServiceImpl、服务接口类interfaceClass=DemoService、服务的注册地址url是
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider1-application&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.7.81%3A20881%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-provider1-application%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.7.81%26bind.port%3D20881%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26logger%3Dlog4j%26methods%3DsayHello%26pid%3D4824%26release%3D2.7.0%26side%3Dprovider%26timestamp%3D1596073850978&logger=log4j&pid=4824®istry=zookeeper&release=2.7.0×tamp=1596073850955
生成的注册服务URL是在registryURL和export参数是服务url的拼装
b、DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置。是对Invoker的包裹
c、protocol.export(wrapperInvoker)这是导出的核心,使用了SPI。
使用特定的协议来对服务进行导出,这里的协议是registry,导出成功后得到一个Exporter
由于注册地址也是服务,所以会先使用RegistryProtocol进行服务注册(服务导出),然后注册(服务导出)完了之后,再使用DubboProtocol进行导出
自适应SPI会获取url
org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker#getUrl
⑩、当存在注册中心时,是先使用Registy协议注册服务,然后在使用Http协议导出服务。而没有注册中心时,是直接使用Http协议导出服务。根据服务url,讲服务的元信息存入元数据中心MetadataReportService
export
RegistryProtocol被ProtocolFilterWrapper包裹,ProtocolFilterWrapper被ProtocolListenerWrapper包裹(SPI原理)
RegistryProtocol
1、export
org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper#export
如果协议是REGISTRY_PROTOCOL(registry)则会直接走到下一个protocol实现类ProtocolFilterWrapper。
如果不是则会使用ListenerExporterWrapper处理
2、export
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#export
ProtocolFilterWrapper导出同样的道理,如果是registry则会直接走到下一个。如果不是,则会调用buildInvokerChain构建执行代理链
3、export
org.apache.dubbo.registry.integration.RegistryProtocol#export
①、获取registryUrl 将registry://xxx?xx=xx®istry=zookeeper 转为 zookeeper://xx
zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider1-application&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.7.81%3A20881%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-provider1-application%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.7.81%26bind.port%3D20881%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26logger%3Dlog4j%26methods%3DsayHello%26pid%3D1072%26release%3D2.7.0%26side%3Dprovider%26timestamp%3D1596076187605&logger=log4j&pid=1072&release=2.7.0×tamp=1596076187072
②、获取providerUrl 服务提供者url
dubbo://192.168.7.81:20881/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider1-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.7.81&bind.port=20881&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=1072&release=2.7.0&side=provider×tamp=1596076187605
③、生成overrideSubscribeUrl,与监听有关
在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
overrideSubscribeUrl是用来对动态配置监听的,需要监听的服务和监听的类型(configurators, 老版本)
一个overrideSubscribeUrl对应一个OverrideListener,用来监听overrideSubscribeUrl变化事件
provider://192.168.7.81:20881/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider1-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.7.81&bind.port=20881&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=1072&release=2.7.0&side=provider×tamp=1596076187605
④、doLocalExport导出
此时服务协议是dubbo,DubboProtocol被ProtocolFilterWrapper包裹,ProtocolFilterWrapper被ProtocolListenerWrapper包裹
DubboProtocol
1、export
org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper#export
返回结果被ListenerExporterWrapper包裹
2、export
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#export
①、buildInvokerChain
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#buildInvokerChain
根据url获取filter,根据url中的parameters匹配key所对应的filter,也会匹配group
EchoFilter、ClassLoaderFilter、GenericFilter、ContextFilter、TraceFilter、TimeoutFilter、MonitorFilter、ExceptionFilter
返回包裹了Filter的CallbackRegistrationInvoker
3、export
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#export
获取url、获取key、isStubSupportEvent=false、isCallbackservice=false
开启Server。dubbo协议是NettyServer。dubbo协议的mina,netty,http协议的jetty,servlet等,默认为netty
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#createServer
其中创建Server,最重要的是通过url绑定端口,和对应的请求处理器。requestHandler是请求处理器,类型为ExchangeHandler。表示从url的端口接收到请求后,requestHandler来进行处理
4、register
org.apache.dubbo.registry.integration.RegistryProtocol#export
在调用doLocalExport方法进入DubboProtocol协议开启Server之后,便会调用RegistryProtocol的register注册到注册中心zookeeper中
registry表示注册中心,registeredProviderUrl表示存入到注册中心去的providerUrl,会对其中的参数进行简化
providerInvokerWrapper表示服务提供者Invoker、该服务对应的注册中心地址、简化后的服务registeredProviderUrl的包装类
①、register
org.apache.dubbo.registry.integration.RegistryProtocol#register
②、register
org.apache.dubbo.registry.support.FailbackRegistry#register
③、doRegister
调用zkClient.create,注册到zookeeper中
动态配置监听
在服务导出过程中,需要对动态配置中心的数据进行监听。如果发生更改,可以及时做出反映。
这里用到了Zookeeper的Watcher机制。
1、配置中心的路径与动态配置路径是不相同的,这里需要区分开来
配置中心的路径是:
①、应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
②、全局:/dubbo/config/dubbo/dubbo.properties节点的内容
2、对于动态配置的监听有版本的差别
在2.7之前,只可以对单个服务进行监听,不可以对应用监听
/dubbo/org.apache.dubbo.demo.DemoService/configurators/* 只对路径名字监听,不监听内容
在2.7之后,服务和应用都可以监听
服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators 节点的内容
应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators 节点的内容
3、监听时机RegistryProtocol#export
org.apache.dubbo.registry.integration.RegistryProtocol#export
2.6之后版本监听器原理
0、overrideUrlWithConfig
org.apache.dubbo.registry.integration.RegistryProtocol#overrideUrlWithConfig
providerConfigurationListener是RegistryProtocol的一个属性值,在创建RegistryProtocol类时,便会得到这个应用配置的监听器
1、ProviderConfigurationListener 应用监听器
应用配置,providerConfigurationListener是在属性那里直接初始化好的,providerConfigurationListener会监听配置中心的应用配置信息变动
org.apache.dubbo.registry.integration.RegistryProtocol.ProviderConfigurationListener#ProviderConfigurationListener
org.apache.dubbo.registry.integration.AbstractConfiguratorListener#initWith
①、获取dynamicConfiguration
②、指定路径添加监听器addListener
org.apache.dubbo.configcenter.DynamicConfiguration#addListener
org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration#addListener
③、获取路径getPathKey
org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration#getPathKey
所以,key值是dubbo-demo-provider1-application.configurators的监听路径是
/dubbo/config/dubbo/dubbo-demo-provider1-application.configurators
rawConfig表示从配置中心获取当前应用的动态配置数据,如果存在应用配置信息则根据配置信息生成Configurator
④、genConfiguratorsFromRawRule
org.apache.dubbo.registry.integration.AbstractConfiguratorListener#genConfiguratorsFromRawRule
先把应用或服务配置转成url,再根据url生成对应的Configurator
2、ServiceConfigurationListener 服务监听器
ServiceConfigurationListener会监听配置中心的服务信息配置信息变动,每注册一个服务都会生成一个服务监听器
org.apache.dubbo.registry.integration.RegistryProtocol.ServiceConfigurationListener#ServiceConfigurationListener
org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration#getPathKey
key是org.apache.dubbo.demo.DemoService::.configurators,得到的监听路径是/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService::.configurators
3、把服务监听添加到serviceConfigurationListeners集合中
2.6以及之前版本监听器原理
org.apache.dubbo.registry.support.FailbackRegistry#subscribe
org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe
进行订阅,先看父类的subscribe方法。订阅所有服务
单独订阅某一个服务,根据监听地址去拿listeners,如果没有则生成
添加跟zookeeper相关的ChildListener
这里的urls就是从现在所引入的服务的目录下查到的url,比如下面这个三个目录下的路径(这里会在服务引入时,有这些值)
/dubbo/org.apache.dubbo.demo.DemoService/providers
/dubbo/org.apache.dubbo.demo.DemoService/configurators
/dubbo/org.apache.dubbo.demo.DemoService/routers
OverrideListener触发
OverrideListener实现了NotifyListener,OverrideListener订阅的subscribeUrl发生改变OverrideListener将会接收到通知
1、notify
org.apache.dubbo.registry.integration.RegistryProtocol.OverrideListener#notify
对发生了变化的url进行过滤,只获取url是override协议或参数category=onfigurators的url
根据Override协议修改
2、doOverrideIfNecessary
org.apache.dubbo.registry.integration.RegistryProtocol.OverrideListener#doOverrideIfNecessary
根据configurators修改url
修改过的newUrl和目前的currentUrl不相同,则重新按newUrl导出
3、reExport
org.apache.dubbo.registry.integration.RegistryProtocol#reExport
根据newInvokerUrl获取exporter进行导出、得到最新的注册registryUrl。如果新的服务url和这个服务之前的服务url不相等,则需要把新的服务url注册到注册中心。
4、process
ProviderConfigurationListener和ServiceConfigurationListener都实现了AbstractConfiguratorListener,所以如果配置发生了改变将会触发org.apache.dubbo.registry.integration.AbstractConfiguratorListener#process方法
org.apache.dubbo.registry.integration.RegistryProtocol.OverrideListener#doOverrideIfNecessary
会调用子类doOverrideIfNecessary方法
Exporter结构
org.apache.dubbo.registry.integration.RegistryProtocol#doLocalExport调用之后得到的exporter从外到内依次是
RegistryProtocol$ExporterChangeableWrapper、ListenerExporterWrapper、DubboExporter
在org.apache.dubbo.registry.integration.RegistryProtocol#export返回结果又被DestroyableExporter包裹。
1、DestroyableExporter
org.apache.dubbo.registry.integration.RegistryProtocol.DestroyableExporter#unexport
2、ExporterChangeableWrapper
org.apache.dubbo.registry.integration.RegistryProtocol.ExporterChangeableWrapper#unexport
从注册中心删除服务URL、解绑当前服务的监听器Listener
3、ListenerExporterWrapper
org.apache.dubbo.rpc.listener.ListenerExporterWrapper#unexport
在unexport对应服务之后,把服务监听器移除
4、AbstractExporter
org.apache.dubbo.rpc.protocol.AbstractExporter#unexport
这几个Exporter主要在于getInvoker和unexport方法
总结:
dubbo服务导出的核心就是根据各种位置的配置构建服务url,并注册到注册中心。
还有一点是服务导出生成的Export结构是为了服务消费端进行调用的。
其中,开启服务也是为了与服务消费端进行调用信息的传输。