官网服务发布示意图,其中包含几个关键类,文中会涉及到。
官网服务发布主要流程
从一个最简单例子开始
通常发布服务我们需要指定如下信息
-
注册中心信息
-
服务接口类型
-
服务实现类型
-
服务版本
-
其他配置信息
从这个简单例子可以看出服务导出的入口ServiceConfig.export() ,接下来顺藤摸瓜,从ServiceConfig.export() 开始解读服务发布大体流程。
ServiceConfig<DemoService> serviceConfig = new ServiceConfig<>();
serviceConfig.setRegistry(new RegistryConfig("127.0.0.1:2181"));
serviceConfig.setApplication(new ApplicationConfig("dubbo"));
serviceConfig.setInterface(DemoService.class);
serviceConfig.setRef(new DemoServiceImpl());
serviceConfig.setVersion("1.0");
serviceConfig.export();
服务发布源码解读
1. 是否延迟发布
如果需要延迟 ScheduledExecutorService shedule 进行延迟发布;
否则正常发布
2. 加载注册中心信息
一个服务可以被注册到多个服务中心 ,每个注册中心对应一个Invoker 需要遍历所有注册中心,进行注册服务。
3. 所有参数封装成URL
Dubbo中所有扩展点参数都包含 URL 参数,URL 作为上下文信息贯穿整个扩展点设计体系
4. 服务发布的Scope
- scope = none,不导出服务
- scope != remote,导出到本地
- scope != local,导出到远程
5. 注册中心注册 or ip直连方式
一般实际应用时都会使用注册中心,但开发或者教学时为了简单会用ip 直连方式。
我们重点讲注册中心注册方式。
我们需要循环遍历所有注册中心URL ,将服务向各个注册中心发布。
一个注册中心URL 对应 一个Invoker, 这个Invoker ProxyFactory
生成对应的代理对象,执行实际的业务逻辑。
最终会调用Protocol.export
6. Protocol
Protocol 是一个SPI接口,默认使用DubboProtoco
其openServer 方法真正开启端口, 从openServer 方法可以看出,对应Server会用一个Map缓存起来, 其Key 是 url.getAddress ,也就是同一个ip : port ,换句话说同一个机器不同服务IO是多路复用的(默认使用NettyServer)。
Protocol
DubboProtocol
openServer
7. createServer
前面 openServer方法会通过createServer () 开启NettyServer(默认用netty通信,也可以是MinaServer等,这里为了方便描述)
Exchangers.bind(url, requestHandler); 中requestHandler 对应请求处理 (包含TCP连接相关回调处理)
reply、received、connected、disconnected、invoke 。具体处理细节后续深入学习。
有点可以肯定 处理业务请求最终找到对应的Invoker, 通过Invoker内部会真正调用具体实现类逻辑。
ExchangeServer server;
try {
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
数据是如何注册到注册中心的
你或许疑惑上面代码没有看到将服务信息写入注册中心。其实相关逻辑在 RegistryProtocol。
RegistryProtocol 也实现了Protocol接口,同时它还有一个属性 Protocol protocol;这不就是Dubbo 的 SPI wrapper机制,也就是AOP(后面补充Dubbo SPI 知识)
我们看看RegistryProtocol#export关键代码,这就是将服务信息注册到注册中心
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
我们通常使用的Zookeeper,本文以 ZookeeperRegistry为例继续解析。
ZookeeperRegistry
doRegister(URL url) 会通过zkClient将信息写入到 zookeeper 中。终于将核心发布流程串起来了
public void doRegister(URL url) {
try {
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
总结
本文主要梳理服务发布的核心流程,以及关键代码,其中关键 Dubbo SPI Wrap 机制,部分读者可能无法将注册中心串起来。这只是开始,后面继续深入服务调用处理等等。