服务暴露实际上就是启动server本地监听,并且将服务信息注册到注册中心上。在dubbo:service上的export可以指定是否暴露,同时provider也可以指定延迟暴露的时间。
ServiceConfig#doExport -> ServiceConfig#doExportUrls
serviceConfig#doExportUrlsFor1Protocol
这个方法主要就是将服务的配置信息包装成一个url,并且本地启动服务,并将服务信息发布到注册中心。代码贴出来会比较多,我这边按重点贴出部分
代码前端包括了用Map存储该协议的所有配置参数,包括协议名称、dubbo版本、当前系统时间戳、进程ID、application配置、module配置、默认服务提供者参数(ProviderConfig)、协议配置、服务提供Dubbo:service的属性。
将service 里面的 method 里面的 argument 也加到 map中,其中就包括了callback。
然后泛化信息添加。另外将所有的方法名也添加进map,以逗号分隔,这边使用javassiat字节码生成器获取所有的方法具体详解代码 String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
之后有token的处理,还有获取服务的host和port
然后再一次封装服务的暴露URL
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
大体内容如下:
接着获取 动态配置中心,并将url写到配置中心。接着往下
根据scope来暴露服务,如果scope不配置,则默认本地与远程都会暴露,如果配置成local或remote,那就只能是二选一。
如果scope不为remote,则先在本地暴露(injvm):,具体暴露服务的具体实现,将在remote 模式中详细分析。
如果scope不为local,则将服务暴露在远程。
remote方式,检测当前配置的所有注册中心,如果注册中心不为空,则遍历注册中心,将服务依次在不同的注册中心进行注册。
如果dubbo:service的dynamic属性未配置, 尝试取dubbo:registry的dynamic属性,该属性的作用是否启用动态注册,如果设置为false,服务注册后,其状态显示为disable,需要人工启用,当服务不可用时,也不会自动移除,同样需要人工处理,此属性不要在生产环境上配置。
根据注册中心url(注册中心url),构建监控中心的URL,如果监控中心URL不为空,则在服务提供者URL上追加monitor,其值为监控中心url(已编码)。
下面就是服务暴露的细节了
我们debug到invoke这边,对与invoke 后续会出专门的章节来介绍。这边只要了解到Invoke中的URL
registryURL = registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=33205&registry=zookeeper&timestamp=1639897064350
invoke.url = registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.0.102%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-api-provider%26bind.ip%3D192.168.0.102%26bind.port%3D20880%26default.deprecated%3Dfalse%26default.dynamic%3Dfalse%26default.register%3Dtrue%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dfalse%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D33205%26register%3Dtrue%26release%3D%26side%3Dprovider%26timestamp%3D1639897069365&pid=33205&registry=zookeeper&timestamp=1639897064350 其中协议就是register
在dubbo的扩展中,对于扩展点接口的方法上有@Adaptive注解的话,会去参数里面获取URL,然后取对应的key,这边的key就是register。那么很好了解下面的方法了Exporter<?> exporter = protocol.export(wrapperInvoker);
实际上调用的RegistryProtocol的exporter。
在上面的URL providerUrl = getProviderUrl(originInvoker);方法中,把invoke中的url获取后,替换了前面的register协议,变成了dubbo协议,即dubbo://开头的URL。
接下来看下面的执行doLocalExport方法
注意这里面使用了一个invoke的委托类,将url改成了providerUrl,即dubbo协议开头。所以根据dubbo的扩展实现,拿到了protocal实现类就是DubboProtocal。
DubboProtocal#export
DubboProtocal#openServer
同一个主机上上不同服务,使用同一个address,所以共享一个ExchangeServer,如果内存中找不到,则create
为服务提供者url增加channel.readonly.sent属性,默认为true,表示在发送请求时,是否等待将字节写入socket后再返回,默认为true。
为服务提供者url增加heartbeat属性,表示心跳间隔时间,默认为60*1000,表示60s。
为服务提供者url增加server属性,可选值为netty,mina等等,默认为netty。
根据SPI机制,判断server属性是否支持。
为服务提供者url增加codec属性,默认值为dubbo,协议编码方式。
根据服务提供者URI,服务提供者命令请求处理器requestHandler构建ExchangeServer实例。requestHandler的实现具体在以后详细分析Dubbo服务调用时再详细分析。
验证客户端类型是否可用。
在上文的RegisterProtocal#export -> RegisterProtocal#getRegistry
根据注册中心URL,从注册中心工厂中获取指定的注册中心实现类:zookeeper注册中心的实现类为:ZookeeperRegistry
这里面registryFactory的实现类是啥呢?这个就在之前章节讲到了扩展点的IOC注入,实际上registryFactory也是有ExtensionLoader去加载的。
获取服务提供者URL中的register属性,如果为true,则调用注册中心的ZookeeperRegistry#register方法向注册中心注册服务(实际由其父类FailbackRegistry实现,ZookeeperRegistry extends FailbackRegistry)。
FailbackRegistry#register -> ZookeeperRegistry#doRegister
最后服务提供者向注册中心订阅自己,主要是为了服务提供者URL发送变化后重新暴露服务,当然,会将dubbo:reference的check属性设置为false。