如果想了解RPC框架motan是一个不错的入口。为了加深理解,今天先去了解了整个服务暴露的过程。
先给出一个Demo案例
public static void main(String[] args) throws InterruptedException {
ServiceConfig<MotanDemoService> motanDemoService = new ServiceConfig<MotanDemoService>();
motanDemoService.setInterface(MotanDemoService.class);
motanDemoService.setRef(new MotanDemoServiceImpl());
motanDemoService.setGroup("motan-demo-rpc");
motanDemoService.setVersion("1.0");
RegistryConfig registry = new RegistryConfig();
registry.setRegProtocol("zookeeper");
registry.setAddress("192.168.88.129:2181");
motanDemoService.setRegistry(registry);
ProtocolConfig protocol = new ProtocolConfig();
protocol.setId("motan");
protocol.setName("motan");
motanDemoService.setProtocol(protocol);
motanDemoService.setExport("motan:8004");
motanDemoService.export();
MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true);
System.out.println("server start...");
}
上面的案例使用motan协议,在8004端口上暴露了MotanDemoServiceImpl服务。任何client都可以连接到8004端口,并使用motan协议调用这个服务。
整个流程注释很清楚,这里深入代码了解。
ServiceConfig
需要注意的是这里不是通过Spring的demo,但是需要知道的是,通过spring 的xml 配置,依然会被解析为一个ServiceConfig对象来描述整个配置。
源码分析
***motanDemoService.export()***
这个方法是具体实现暴露的地方。
里面的主要方法:
List<URL> registryUrls = loadRegistryUrls();
doExport(protocolConfig, port, registryUrls);
afterExport();
***doExport方法***:
根据我们的配置组装服务URL地址比如: motan://192.168.174.1:8004/com.weibo.motan.demo.service.MotanDemoService?group=motan-demo-rpc
URL serviceUrl = new URL(protocolName, hostAddress, port, interfaceClass.getName(), map);
给注册中心的URL新增embed属性,保存了服务的URL
for (URL u : urls) {
u.addParameter(URLParamType.embed.getName(), StringTools.urlEncode(serviceUrl.toFullStr()));
registereUrls.add(u.createCopy());
}
获取ConfigHandler
ConfigHandler configHandler = ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);
通过ConfigHandler去继续暴露服务
configHandler.export(interfaceClass, ref, urls)
三个参数的含义:
接口名称
具体的实现类
注册中心url包含embed熟悉(服务的url)
***SimpleConfigHandler***
通过SPI机制获取具体的实现类SimpleConfigHandler可以看到具体的实现
public <T> Exporter<T> export(Class<T> interfaceClass, T ref, List<URL> registryUrls) {
String serviceStr = StringTools.urlDecode(registryUrls.get(0).getParameter(URLParamType.embed.getName()));
URL serviceUrl = URL.valueOf(serviceStr);
String protocolName = serviceUrl.getParameter(URLParamType.protocol.getName(), URLParamType.protocol.getValue());
Protocol orgProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocolName);
Provider<T> provider = getProvider(orgProtocol, ref, serviceUrl, interfaceClass);
Protocol protocol = new ProtocolFilterDecorator(orgProtocol);
Exporter<T> exporter = protocol.export(provider, serviceUrl);
register(registryUrls, serviceUrl);
return exporter;
}
***ProtocolFilterDecorator***
整个流程注释已经很清楚,我们继续看ProtocolFilterDecorator的export方法
public <T> Exporter<T> export(Provider<T> provider, URL url) {
return protocol.export(decorateWithFilter(provider, url), url);
}
执行具体的export方法, 通过前面的代码可以看到 是将DefaultRpcProtocol传递给ProtocolFilterDecorator,因此这里会调用
DefaultRpcProtocol的export方法,而DefaultRpcProtocol是继承AbstractProtocol这个抽象类的,并没有实现export方法,因此我们去AbstractProtocol看具体的export方法。
***DefaultRpcProtocol***
public class DefaultRpcProtocol extends AbstractProtocol {
private ConcurrentHashMap<String, ProviderMessageRouter> ipPort2RequestRouter = new ConcurrentHashMap<String, ProviderMessageRouter>();
@Override
protected <T> Exporter<T> createExporter(Provider<T> provider, URL url) {
return new DefaultRpcExporter<T>(provider, url, this.ipPort2RequestRouter, this.exporterMap);
}
@Override
protected <T> Referer<T> createReferer(Class<T> clz, URL url, URL serviceUrl) {
return new DefaultRpcReferer<T>(clz, url, serviceUrl);
}
}
***AbstractProtocol***
AbstractProtocol的export方法
public <T> Exporter<T> export(Provider<T> provider, URL url) {
------忽略一些代码------
exporter = createExporter(provider, url);
exporter.init();
------忽略一些代码------
}
***接下来我们重点来看看DefaultRpcExporter的init方法***
我们发现它没有init方法,但是看到它继承了AbstractExporter
DefaultRpcExporter<T> extends AbstractExporter<T>
我们去AbstractExporter查找,也没有,但是它继承了AbstractNode
AbstractExporter<T> extends AbstractNode implements Exporter<T>
于是我们去AbstractNode查找
终于我们在AbstractNode中找到了init方法,在这里调用了上面DefaultRpcExporter的doInit方法:
public synchronized void init() {
if (init) {
LoggerUtil.warn(this.getClass().getSimpleName() + " node already init: " + desc());
return;
}
boolean result = doInit();
if (!result) {
LoggerUtil.error(this.getClass().getSimpleName() + " node init Error: " + desc());
throw new MotanFrameworkException(this.getClass().getSimpleName() + " node init Error: " + desc(),
MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
} else {
LoggerUtil.info(this.getClass().getSimpleName() + " node init Success: " + desc());
init = true;
available = true;
}
}
***DefaultRpcExporter的doInit方法***
protected boolean doInit() {
boolean result = server.open();
return result;
}
ok,那这个server代表什么呢?
可以看到在初始化DefaultRpcExporter的时候其实已经返回了一个server,并且是传入了我们服务的url。
server = endpointFactory.createServer(url, requestRouter);
其实这里是返回了一个NettyServer,然后调用open方法绑定端口,可以看到通过我们服务的url获取我们一开始设置的暴露端口
ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(url.getPort()));
***在DefaultRpcProtocol里面进行初始化的DefaultRpcExporter很重要***
public DefaultRpcExporter(Provider<T> provider, URL url, ConcurrentHashMap<String, ProviderMessageRouter> ipPort2RequestRouter,
ConcurrentHashMap<String, Exporter<?>> exporterMap) {
super(provider, url);
this.exporterMap = exporterMap;
this.ipPort2RequestRouter = ipPort2RequestRouter;
ProviderMessageRouter requestRouter = initRequestRouter(url);
endpointFactory =
ExtensionLoader.getExtensionLoader(EndpointFactory.class).getExtension(
url.getParameter(URLParamType.endpointFactory.getName(), URLParamType.endpointFactory.getValue()));
server = endpointFactory.createServer(url, requestRouter);
}
protected ProviderMessageRouter initRequestRouter(URL url) {
String ipPort = url.getServerPortStr();
ProviderMessageRouter requestRouter = ipPort2RequestRouter.get(ipPort);
if (requestRouter == null) {
ipPort2RequestRouter.putIfAbsent(ipPort, new ProviderProtectedMessageRouter());
requestRouter = ipPort2RequestRouter.get(ipPort);
}
requestRouter.addProvider(provider);
return requestRouter;
}
***最后回到SimpleConfigHandler,当绑定端口成功后***
register(registryUrls, serviceUrl);
到这里 整个服务的暴露流程就差不多了。
***ProviderMessageRouter***
上面我们提到了ProviderMessageRouter,这里因为我们不会深入到transport层面
所以直接说出它的作用:
当nettyServer处理客户端请求的hadler会进入这个方法。
主要包括下面几个方法:
addProvider:在DefaultRpcExporter初始化的时候 加入DefaultProvider:
String serviceKey = MotanFrameworkUtil.getServiceKey(provider.getUrl());
if (providers.containsKey(serviceKey)) {
throw new MotanFrameworkException("provider alread exist: " + serviceKey);
}
providers.put(serviceKey, provider);
handle:获取到具体的Provider,调用客户端真正请求的实现类的方法
String serviceKey = MotanFrameworkUtil.getServiceKey(request);
Provider<?> provider = providers.get(serviceKey);
call(request, provider);
我们知道Provider具体的实现是DefaultProvider,在SimpleConfigHandler里面的 export方法就已经被初始化:
proxyImpl是具体实现类
url就是上文的服务url
clz就是服务接口
public DefaultProvider(T proxyImpl, URL url, Class<T> clz) {
super(url, clz);
this.proxyImpl = proxyImpl;
}
ProviderMessageRouter里面最终的call方法其实就是调用了DefaultProvider里面的invoke方法
通过反射调用具体的实现类的方法
Object value = method.invoke(proxyImpl, request.getArguments());
response.setValue(value);
总结
最后总结一下服务暴露的流程
根据我们配置的服务信息,获取注册中心的地址URL,和具体服务的URL,
然后保存provider到ProviderMessageRouter后续处理客户端使用,并启动netty服务监听客户端的请求。
当客户端发出请求后,根据唯一的属性找到具体实现的Provider,然后根据反射调用 具体的实现类方法。
打开App,阅读手记