之前简要地过了一遍Motan服务的启动过程,感觉理解得还不够清楚,所以接下来会就有疑惑的几个点来做一次分析。
和之前一样,以Motan服务的启动为依托,分析启动过程中的几个重要节点以及类,以此来窥探Motan服务端的设计架构和原则。
- ExtensionLoader
- URL
- ConfigHandler
- Exporter
- Provider
- Protocol
- EndpointFactory
ExtensionLoader
Motan自己做了一套类加载机制实现,默认加载META-INF/services
下的配置,支持加载以SPI方式配置的扩展实现。
配置文件 配合 Spi注解、SpiMeta注解(或者Activition注解)可以实现动态扩展的功能,比如:Registry、Protocol等等。
URL
URL是motan框架的核心对象,保存了很多配置信息,ip、端口、服务接口、协议等等。
分为服务URL
和注册URL
。
服务URL:motan://10.39.72.34:8002/com.weibo.motan.demo.service.MotanDemoService2?maxContentLength=1048576&module=motan-demo-rpc&nodeType=service&accessLog=false&minWorkerThread=20&protocol=motan&isDefault=true&application=myMotanDemo&maxWorkerThread=800&shareChannel=true&refreshTimestamp=1516604423329&id=com.weibo.api.motan.config.springsupport.ServiceConfigBean2&export=demoMotan:8002&maxServerConnection=80000&group=motan-demo-rpc&
注册URL:
zookeeper://127.0.0.1:2181/com.weibo.api.motan.registry.RegistryService?group=default_rpc
Provider
可以理解为真正的服务提供者,它持有一个服务serviceImpl对象,提供从DefaultRequest中提取调用信息,并且以反射的方式触发客户端的调用,返回DefaultResponse对象。
ConfigHandler
构建完URL之后,通过ExtensionLoader加载出本次export的ConfigHandler对象。默认为SimpleConfigHandler
@Spi(scope = Scope.SINGLETON)
public interface ConfigHandler {
<T> ClusterSupport<T> buildClusterSupport(Class<T> interfaceClass, List<URL> registryUrls);
<T> T refer(Class<T> interfaceClass, List<Cluster<T>> cluster, String proxyType);
<T> Exporter<T> export(Class<T> interfaceClass, T ref, List<URL> registryUrls);
<T> void unexport(List<Exporter<T>> exporters, Collection<URL> registryUrls);
}
ConfigHandler是用来处理构建出来的URL的。从接口的定义可以得知它的功能:构建集群的支持、创建到服务的引用、发布服务(到注册中心)、撤销发布服务。从架构的层面来看,它完成了配置和协议层的转化。
具体分析下它的export方法:
@Override
public <T> Exporter<T> export(Class<T> interfaceClass, T ref, List<URL> registryUrls) {
// 将URLEncoder编码过的注册URL取出,解码(注册url的embed参数中,存储着所要发布的serviceURL字符串)
String serviceStr = StringTools.urlDecode(registryUrls.get(0).getParameter(URLParamType.embed.getName()));
//将service URL 字符串转成对象
URL serviceUrl = URL.valueOf(serviceStr);
// export service
// 利用protocol decorator来增加filter特性
//根据serviceURL提取出协议名
String protocolName = serviceUrl.getParameter(URLParamType.protocol.getName(), URLParamType.protocol.getValue());
//根据协议名加载出最 基本的协议的实现对象(没有经过Filter配置的)
//此处的实现为DefaultRpcProtocol
Protocol orgProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocolName);
//返回DefaultProvider对象
Provider<T> provider = getProvider(orgProtocol, ref, serviceUrl, interfaceClass);
Protocol protocol = new ProtocolFilterDecorator(orgProtocol);
//发布服务
Exporter<T> exporter = protocol.export(provider, serviceUrl);
// register service
register(registryUrls, serviceUrl);
return exporter;
}
Exporter
查看Exporter<T> exporter = protocol.export(provider, serviceUrl);
的实现:
...
String protocolKey = MotanFrameworkUtil.getProtocolKey(url);
synchronized (exporterMap) {
//同一个Exporter只能在同一个protocol中发布一次
Exporter<T> exporter = (Exporter<T>) exporterMap.get(protocolKey);
if (exporter != null) {
throw new MotanFrameworkException(this.getClass().getSimpleName() + " export Error: service already exist, url=" + url,
MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
}
exporter = createExporter(provider, url);
exporter.init();
exporterMap.put(protocolKey, exporter);
LoggerUtil.info(this.getClass().getSimpleName() + " export Success: url=" + url);
return exporter;
}
exporter = createExporter(provider, url);
最终把provider和服务url提交到了NettyEndpointFactory中:
public Server createServer(URL url, MessageHandler messageHandler) {
//为MessageHandler增加心跳功能,这是个内置的服务
messageHandler = getHeartbeatFactory(url).wrapMessageHandler(messageHandler);
synchronized (ipPort2ServerShareChannel) {
String ipPort = url.getServerPortStr();
String protocolKey = MotanFrameworkUtil.getProtocolKey(url);
boolean shareChannel =
url.getBooleanParameter(URLParamType.shareChannel.getName(), URLParamType.shareChannel.getBooleanValue());
if (!shareChannel) { // 独享一个端口
LoggerUtil.info(this.getClass().getSimpleName() + " create no_share_channel server: url={}", url);
// 如果端口已经被使用了,使用该server bind 会有异常
return innerCreateServer(url, messageHandler);
}
LoggerUtil.info(this.getClass().getSimpleName() + " create share_channel server: url={}", url);
Server server = ipPort2ServerShareChannel.get(ipPort);
if (server != null) {
// can't share service channel
if (!MotanFrameworkUtil.checkIfCanShallServiceChannel(server.getUrl(), url)) {
throw new MotanFrameworkException(
"Service export Error: share channel but some config param is different, protocol or codec or serialize or maxContentLength or maxServerConnection or maxWorkerThread or heartbeatFactory, source="
+ server.getUrl() + " target=" + url, MotanErrorMsgConstant.FRAMEWORK_EXPORT_ERROR);
}
saveEndpoint2Urls(server2UrlsShareChannel, server, protocolKey);
return server;
}
url = url.createCopy();
url.setPath(""); // 共享server端口,由于有多个interfaces存在,所以把path设置为空
server = innerCreateServer(url, messageHandler);
ipPort2ServerShareChannel.put(ipPort, server);
saveEndpoint2Urls(server2UrlsShareChannel, server, protocolKey);
return server;
}
}
只是在这之前,把provider做了再次封装,变成了MessageHandler对象(ProviderProtectedMessageRouter)
可以看到,它关联上了provider,并且提供了一些其他的功能,比如说根据服务地址查找provider。
并且EndpointFactory维护了一个
public void register(URL url) {
if (url == null) {
LoggerUtil.warn("[{}] register with malformed param, url is null", registryClassName);
return;
}
LoggerUtil.info("[{}] Url ({}) will register to Registry [{}]", registryClassName, url, registryUrl.getIdentity());
//在ZookeeperRegitry中有实现
doRegister(removeUnnecessaryParmas(url.createCopy()));
registeredServiceUrls.add(url);
// available if heartbeat switcher already open
if (MotanSwitcherUtil.isOpen(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER)) {
available(url);
}
}
注册的代码中有一点值得注意,会将url从ZK中删除并添加到unavailableServer
节点下,
然而,只有在设置MotanConstants.REGISTRY_HEARTBEAT_SWITCHER为true的应用,他们的服务才会被添加到
server
节点下,才能被客户端发现到。
在ZookeeperRegitry中的注册实现如下:
@Override
protected void doRegister(URL url) {
try {
serverLock.lock();
// 防止旧节点未正常注销
removeNode(url, ZkNodeType.AVAILABLE_SERVER);
removeNode(url, ZkNodeType.UNAVAILABLE_SERVER);
createNode(url, ZkNodeType.UNAVAILABLE_SERVER);
} catch (Throwable e) {
throw new MotanFrameworkException(String.format("Failed to register %s to zookeeper(%s), cause: %s", url, getUrl(), e.getMessage()), e);
} finally {
serverLock.unlock();
}
}
这样服务发布完成
-EOF-