如果想了解RPC框架motan是一个不错的入口。为了加深理解,今天先去了解了整个服务暴露的过程。
先给出一个Demo案例
public static void main(String[] args) throws InterruptedException {
//1-初始化一个服务的配置
ServiceConfig<MotanDemoService> motanDemoService = new ServiceConfig<MotanDemoService>();
//2-设置接口及实现类
motanDemoService.setInterface(MotanDemoService.class);
motanDemoService.setRef(new MotanDemoServiceImpl());
// 配置服务的group以及版本号
motanDemoService.setGroup("motan-demo-rpc");
motanDemoService.setVersion("1.0");
//3-配置注册中心zk
RegistryConfig registry = new RegistryConfig();
registry.setRegProtocol("zookeeper");
registry.setAddress("192.168.88.129:2181");
motanDemoService.setRegistry(registry);
//4-配置RPC协议
ProtocolConfig protocol = new ProtocolConfig();
protocol.setId("motan");
protocol.setName("motan");
motanDemoService.setProtocol(protocol);
//5-设置暴露的端口号
motanDemoService.setExport("motan:8004");
//6-暴露服务,等待客户端调用
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()***
这个方法是具体实现暴露的地方。
里面的主要方法:
//获取注册中心的URL,代表注册中心服务的地址 zookeeper://192.168.88.129:2181/com.weibo.api.motan.registry.RegistryService?group=default_rpc
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) {
//根据embed属性获取我们服务的URL地址
String serviceStr = StringTools.urlDecode(registryUrls.get(0).getParameter(URLParamType.embed.getName()));
URL serviceUrl = URL.valueOf(serviceStr);
// export service
String protocolName = serviceUrl.getParameter(URLParamType.protocol.getName(), URLParamType.protocol.getValue());
//根据 SPI 获取协议的具体实现类是 DefaultRpcProtocol
Protocol orgProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocolName);
//DefaultProvider 创建 provider ref是具体实现类,serviceUrl就是上文的服务url,interfaceClass就是服务接口, 返回 具体 实现类 DefaultProvider
Provider<T> provider = getProvider(orgProtocol, ref, serviceUrl, interfaceClass);
//把DefaultRpcProtocol包装到ProtocolFilterDecorator里面
Protocol protocol = new ProtocolFilterDecorator(orgProtocol);
//暴露服务,调用ProtocolFilterDecorator 里面的export方法 进行fliter处理
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) {
//这里会先做 decorateWithFilter 进行 filter的处理
return protocol.export(decorateWithFilter(provider, url), url);
}
执行具体的export方法, 通过前面的代码可以看到 是将DefaultRpcProtocol传递给ProtocolFilterDecorator,因此这里会调用
DefaultRpcProtocol的export方法,而DefaultRpcProtocol是继承AbstractProtocol这个抽象类的,并没有实现export方法,因此我们去AbstractProtocol看具体的export方法。
***DefaultRpcProtocol***
public class DefaultRpcProtocol extends AbstractProtocol {
// 多个service可能在相同端口进行服务暴露,因此来自同个端口的请求需要进行路由以找到相应的服务,同时不在该端口暴露的服务不应该被找到
private ConcurrentHashMap<String, ProviderMessageRouter> ipPort2RequestRouter = new ConcurrentHashMap<String, ProviderMessageRouter>();
//AbstractProtocol里面的具体实现
@Override
protected <T> Exporter<T> createExporter(Provider<T> provider, URL url) {
//传入DefaultProvider和服务的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) {
------忽略一些代码------
//调用 DefaultRpcProtocol 里面具体实现的方法,返回一个 DefaultRpcExporter
exporter = createExporter(provider, url);
//调用DefaultRpcExporter的init方法
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;
}
//调用DefaultRpcExporter里面的doInit方法
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;
//将DefaultProvider加入到ProviderMessageRouter,在处理客户端请求的时候会用到
ProviderMessageRouter requestRouter = initRequestRouter(url);
endpointFactory =
ExtensionLoader.getExtensionLoader(EndpointFactory.class).getExtension(
url.getParameter(URLParamType.endpointFactory.getName(), URLParamType.endpointFactory.getValue()));
//根据服务的url创建NettyServer
server = endpointFactory.createServer(url, requestRouter);
}
protected ProviderMessageRouter initRequestRouter(URL url) {
String ipPort = url.getServerPortStr();
//处理客户端请求的时候会用到ProviderMessageRouter
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,调用客户端真正请求的实现类的方法
//根据具体的请 获取 group/interface/version 来唯一标示一个服务
String serviceKey = MotanFrameworkUtil.getServiceKey(request);
//获取具体处理请求的Provider
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,然后根据反射调用 具体的实现类方法。
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦