Dubbo服务导出字面意思就是,把生产者的服务,注册到注册中心。加了@Service注解的服务可以被理解为Dubbo的一种服务,这些服务会被转化成ServiceBean(父类是ServiceConfig),一个服务对应一个ServiceBean,里面包装的参数会在dubbo中被解析成一个个Url然后上传到注册中心中去;所以Dubbo服务导出需要考虑以下几个问题
1.服务导出是在什么时候
2.Dubbo底层是netty,是怎么启动的
3.该如何将Url注册到注册中心
4.如果注册中心上的数据发生变更又该如何监听
@Service中可以配置一些参数,以此来确定一个服务,配置参数也不仅仅是通过这个方法,还可以通过在VmOptions中通过-D配置(系统环境变量),dubbo.properties文件,或者Dubbo自己所属的动态配置中心,可以通过这四种方法去确定服务的配置,这些配置如果配置了同一个参数,也有优先级
以下是通过这四种方式确定参数的优先级以及这四种方式在Dubbo中对应的实现类
dubbo.registries.r1.address=zookeeper://127.0.0.1:2181
dubbo.registries.r1.timeout=3000
registries就是上面对应的配置dubbo.registries后面跟着的数量,你配了几个就是几个,然后就会解析这些参数放到一个map里面,最后就是调用parseURLs(address,map),根据你传入的map来构造注册中心的地址(REGISTRY_URL),最后就是放到一个List中然后返回
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider1application&dubbo=2.0.2&logger=log4j&pid=10920®istry=zookeeper&release=2.7.0&timeout=3000×tamp=1703071491179
providerUrl:dubbo://172.16.5.151:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider1-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService:1.0.1:tulings&bind.ip=172.16.5.151&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=tulings&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=10920&release=2.7.0&revision=1.0.1&side=provider×tamp=1703071655680&version=1.0.1
invoker.getUrl():registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider1-application&dubbo=2.0.2&export=dubbo://172.16.5.151:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider1-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService:1.0.1:tulings&bind.ip=172.16.5.151&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=tulings&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=10920&release=2.7.0&revision=1.0.1&side=provider×tamp=1703071655680&version=1.0.1&logger=log4j&pid=10920®istry=zookeeper&release=2.7.0&timeout=3000×tamp=1703071491179
重写url:在overrideUrlWithConfig这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
providerConfigurationListener表示应用级别的动态配置监听器,
serviceConfigurationListener表示服务级别的动态配置监听器
可以看到服务级别的重写会覆盖应用级别的覆盖(因为写在最后一行)
因为应用监听是在属性那边就直接初始化好了,所以不需要再去初始化一遍,这边直接来看服务监听,可以看到调用了initWith方法并且传入了一个path进去(就是我们监听数据变化的地址)
在initWith方法中,便可看到为这个路径添加了监听器(this),所以数据有变动的时候便会触发到你自己本身的ServiceConfigurationListener.notifyOverrides()->RegistryProtocol.doOverrideIfNecessary()然后去重写Invoker重写推送数据至注册中心
启动netty服务
doLocalExport方法下面就会根据你传进来的invokerDelegate.getUrl()中的protocol协议去调用对应的export方法
然后之前的步骤会先根据invoker造出一个exporter,后续调用的时候就可以直接从本地缓存拿,然后接着就会开启openServer下的createSever方法
开启Netty服务的链路比较长,这边就用调用链来代替:
server = Exchangers.bind(url, requestHandler)->HeaderExchanger.bind(URL url, ExchangeHandler handler)->Transporters.bind(URL url, ChannelHandler... handlers)->NettyTransporter.bind(URL url, ChannelHandler listener)->NettyServer.super()->AbstractServer.doOpen()->NettyServer.doOpen():最终启动Netty服务
启动netty服务之后便会调用register(URL registryUrl, URL registeredProviderUrl)->FailbackRegistry.doRegister()->ZookeeperRegistry.doRegister()去注册数据,最后存到注册中心的url是简化后的registeredProviderUrl,而不是整个registryUrl和providerUrl拼起来的,因为有很多参数是不需要的或者重复的,这段简化的url逻辑在RegistryProtocol.getRegisteredProviderUrl(providerUrl, registryUrl)中体现