六Dubbo核心技术归纳--3Dubbo中Invoker的作用及转换
六Dubbo核心技术归纳--3Dubbo中Invoker的作用及转换
6.3 dubbo的核心模型Invoker
Dubbo中invoker是provider和consumer实现RPC调用的关键,invoker的构建过程,是Dubbo服务的init初始化过程;invoker.invoke的调用是Dubbo服务的call调用过程。因此,这里分析invoker模型,主要是provider和consumer端的初始化过程。
6.3.1 Invoker接口及主要实现类
该部分内容主要见[7.3.2.1 Invoker的接口及主要实现类](#7.3.2.1 Invoker的接口及主要实现类)
public interface Invoker<T> extends Node {
// 服务的interface
Class<T> getInterface();
//调用服务
Result invoke(Invocation invocation) throws RpcException;
}
invoke方法就是通过动态代理的机制,来调用远程服务。
dubbo中常用的Invoker类,如下:
- AbstractProxyInvoker 本地执行类的Invoker,实际通过Java反射的方式执行原始对象的方法。为proxyFactory.getInvoker生成的被wrapper包装过的Invoker-------InvokerWrapper。
- AbstractInvoker: 远程通信类的Invoker,实际通过通信协议发起远程调用请求,并接收响应。
- AbstraceClusterInvoker 多个远程通信类的Invoker聚合成的集群Invoker,加入了集群容错和负载均衡策略。cluster集群下的Invoker实现,采用模板方法设计模式,abstract定义方法框架,具体方法实现由子类实现。
6.3.2 Dubbo中Invoker的转换(todo)
Invoker是Dubbo中实体类,rpc的server端服务提供和client端服务调用,都要由invoker实现。因此,其作为一个可执行体,在server端,用于调用provider的本地服务调用;在client端,其内包含远程通信的NettyClient,用于远程调用。
provider端和consumer端,在Config配置类中,分别持有ProxyFactory和Protocol实例,通过??????(写invoke怎么从config启动初始化)
在Dubbo框架图中标志出来,provider端和consumer端涉及到Invoker的构建、转换等过程的模块和执行流程。分析过程中,对应源码和框架调用流程图去理解。
6.3.2.1 Provider端Invoker
(这里是provider端的init过程)
provider端的invoker,由ProxyFactory.getInvoker()创建。而ProxyFactory默认实现类是JavassistProxyFactory。
@SPI("javassist")
public interface ProxyFactory {
/**
* create proxy.
*
* @param invoker
* @return proxy
*/
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException;
/**provider端创建invoker
* create invoker.
*
* @param <T>
* @param proxy
* @param type
* @param url
* @return invoker
*/
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
从入口方法分析:
public class JavassistProxyFactory extends AbstractProxyFactory {
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper类不能正确处理带$的类名
//TAG1
//创建provider端服务类的wrapper(javassist创建)
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
//创建匿名invoker对象
//TAG2
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
//invoker.doInvoke方法,会调用wrapper.invokeMethod,最终会调用包装类内目标service方法
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
}
getInvoker(T proxy, Class
proxy参数为服务端service的实现类serviceImpl;type为provider提供的服务接口。
getInvoker主要做两件事:
//TAG1 Wrapper.getWrapper(Class)——Class为提供服务的service实现类。
provider端,根据服务实现类,构建服务的proxy代理类invoker。而对目标服务的调用,是对wrapper对象的调用(wrapper是javassist技术创建的包装类)
创建目标service的包装类。仅可以通过Wrapper静态方法getWrapper(Class)创建。创建过程,首先对传入Class对象解析,分别对属性、方法等信息。这个包装类,持有service服务接口的真实实现类proxy,把公共逻辑创建字节码追加append,然后javassist创建class对象,最后反射创建wrapper实例。wrapper实例方法的调用,通过wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments),通过方法名和参数类型,选择调用的方法。
// TAG2 创建匿名类AbstractProxyInvoker
实现doInvoke方法。该方法,将对服务的调用,转发给wrapper.invokeMethod。
TAG1 getWrapper()
该部分内容详见[2.2 Dubbo中Wrapper](#2.2 Dubbo中Wrapper)
上述过程,通过对传入provider服务类,重新构建字节码,通过Dubbo自己的ClassGenerator,其初始化通过获取javassist的ClassPool工具类,然后在getWrapper中,对构建的字节码转换为Class对象,然后反射创建实例对象wrapper类。(wrapper类中,把proxy目标类中所有方法,放在invokeMethod中,调用时候,if判断methodName选择方法的调用执行)。
TAG2 创建匿名AbstractProxyInvoker类
返回主流程,创建完wrapper后,provider端会返回匿名AbstractProxyInvoker类,实现doInvoker方法,其调用wrapper,实现对服务实现类的调用逻辑。(该处实际是AOP的思路)
总结provider的invoker获取过程:
6.3.2.2 Consumer端Invoker(todo config启动)
在consumer端,Invoker用于执行远程调用(所以consumer端invoker带有通信能力,内部传入NettyClient)。
consumer端初始化创建Invoker。
ReferenceConfig内启动初始化
public class ReferenceConfig<T> extends AbstractReferenceConfig {
private static final long serialVersionUID = -5864351140409987595L;
//静态参数,初始化时创建protocol.class的自适应类DubboProtocol
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
//FailoverCluster
private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
//JavassistProxyFactory
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
Protocol实现类中,常用的有DubboProtocol和RegistryProtocol。
1 DubboProtocol.refer
dubboProtocol.refer执行中参数
serviceType为调用服务的接口,URL以dubbo开头,后续跟了dubbo服务地址,以及服务名称DemoService,该url描述了一个dubbo协议的服务。因此,refer此处逻辑,是调用远程host处dubbo协议的demoService的服务。
public class DubboProtocol extends AbstractProtocol {
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
// 创建RPC功能的invoker
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
//将创建好的invoker加入集合
invokers.add(invoker);
return invoker;
}
上述创建RPC功能的invoker。参数中,getClients(url)获取客户端实例,为ExchangeClient。
DubboProtocol
private ExchangeClient[] getClients(URL url){
//是否共享连接
boolean service_share_connect = false;
//从URL获取连接数的参数,如果为0,表示未配置
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
//如果connections不配置,则共享连接,否则每服务每连接
if (connections == 0){
//未配置连接数,采用共享
service_share_connect = true;
connections = 1;
}
ExchangeClient[] clients = new ExchangeClient[connections];
for (int i = 0; i < clients.length; i++) {
if (service_share_connect){
//获取共享客户端
clients[i] = getSharedClient(url);
} else {
//init新客户端
clients[i] = initClient(url);
}
}
return clients;
}
getClients(URL)方法,根据url中参数,判定是否需要共享连接,获取客户端。
此处,跟入getSharedClient获取共享客户端方法
//DubboProtocol
private ExchangeClient getSharedClient(URL url) {
String key = url.getAddress();
//检查缓存
ReferenceCountExchangeClient client = referenceClientMap.get(key);
//1 当本地缓存中有客户端,返回
if (client != null) {
if (!client.isClosed()) {
//如果dubboProtocol的exchangeClient缓存中有address对应的客户端,则返回客户端共享,并增加引用计数(“引用计数”功能的 ExchangeClient)
client.incrementAndGetCount();
return client;
} else {
//当client关闭,从缓存移除
referenceClientMap.remove(key);
}
}
//2 当缓存没有client,新建
locks.putIfAbsent(key, new Object());
//对当前address加锁,
synchronized (locks.get(key)) {
if (referenceClientMap.containsKey(key)) {
return referenceClientMap.get(key);
}
// 创建 ExchangeClient 客户端
ExchangeClient exchangeClient = initClient(url);
// 将 ExchangeClient 实例传给 ReferenceCountExchangeClient,这里使用了装饰模式
client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
//加锁,将key-client入缓存
referenceClientMap.put(key, client);
ghostClientMap.remove(key);
locks.remove(key);
return client;
}
}
getSharedClient(URL url)方法获取共享客户端,首先会先从DubboProtocol本地缓存获取;如果没有,加锁创建新客户端initClient,并存入缓存。继续跟入创建新客户端代码:
private ExchangeClient initClient(URL url) {
// 获取客户端类型,默认为 netty
String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
// 添加编解码和心跳包参数到 url 中
url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
// 检测客户端类型,是否是Dubbo所支持的(transport为底层通信,目前支持Mina、Netty、Grizzly)
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported client type: ...");
}
ExchangeClient client;
try {
// 获取 lazy 配置
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
// 创建懒加载 ExchangeClient 实例
client = new LazyConnectExchangeClient(url, requestHandler);
} else {
// 创建普通 ExchangeClient 实例
client = Exchangers.connect(url, requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service...");
}
return client;
}
initClient(URL)中,通过向url中添加parm参数,添加心跳、编解码等处理器。然后通过Exchangers.connect(url, requestHandler)创建ExchangeClient。
跟入connect连接:
public class Exchangers {
public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
//加入CODEC_KEY:exchange的url上的参数对
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
// SPI加载,获取 Exchange 实例,默认为 HeaderExchanger
return getExchanger(url).connect(url, handler);
}
public static Exchanger getExchanger(URL url) {
String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
return getExchanger(type);
}
public static Exchanger getExchanger(String type) {
return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
}
public class HeaderExchanger implements Exchanger {
//connect创建客户端
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
// 这里包含了多个调用,分别如下:
// 1. 创建 HeaderExchangeHandler 对象
// 2. 创建 DecodeHandler 对象
// 3. 通过 Transporters 构建 Client 实例
// 4. 创建 HeaderExchangeClient 对象
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);}
public class Transporters {
//通过 Transporters 构建 Client 实例
public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
ChannelHandler handler;
if (handlers == null || handlers.length == 0) {
//handler为channel.pipeline上事件处理器,定义了一系列inbound和outbound事件触发方法
handler = new ChannelHandlerAdapter();
} else if (handlers.length == 1) {
handler = handlers[0];
} else {
// 如果 handler 数量大于1,则创建一个 ChannelHandler 分发器
handler = new ChannelHandlerDispatcher(handlers);
}
// 获取 Transporter 自适应拓展类,并调用 connect 方法生成 Client 实例
return getTransporter().connect(url, handler);
}
public static Transporter getTransporter() {
return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
public class NettyTransporter implements Transporter {
public Client connect(URL url, ChannelHandler listener) throws RemotingException {
// 创建 NettyClient 对象
return new NettyClient(url, listener);
}
DubboProtocol.refer构建invoker流程如下:
总结:
1 consumer端,DubboProtocol.refer创建的是DubboInvoker的实例,在init初始化构建后,底层有NettyClient的客户端,具备远程通信功能;
2 ExchangeClient没有通信能力,需要对底层NettyClient层层封装;
3 DubboProtocol中,refer方法创建一个DubboInvoker实例,每次创建实例加入invokers的集合中。
2 RegistryProtocol.refer
当集群中一个服务有多个实现,且注册在集群搭建的注册中心时,对应有多个invoker,因此需要将多个同一服务的invoker逻辑合并位一个。暴露出来的就是个集群模式的invoker。
这部分provider端invoker初始化流程图,如下:
registryProtocol类内属性
public class RegistryProtocol implements Protocol {
private Cluster cluster;
//用来设置new RegistryDirectory.setProtocol属性
private Protocol protocol;
//registry工厂类,用来获取registry实例(提供服务注册、订阅服务)
private RegistryFactory registryFactory;
//refer方法中,用proxyFactory.getInvoker(registry,type,url)创建invoker实例
private ProxyFactory proxyFactory;
}
从refer跟入源码:
执行refer的参数,
URL以zookeeper开头,后面是localhost:2181,服务名称为RegistryService,该URL描述一个zookeeper注册中心。因此,此处refer方法,是调用地址为localhost:2181的zookeeper注册中心上的type服务。
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 取出url上registry的键值对,设置为url的protocol协议头,并移除url中registry键值对
//设置url的协议头为zookeeper
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY,Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
//TAG1 获取注册中心Registry实例
Registry registry = registryFactory.getRegistry(url);
//如果调用服务类型type是RegistryService
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
//将url参数转为 Map
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
//获取 group 配置
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
|| "*".equals(group)) {
// 通过 SPI 加载 MergeableCluster 实例,并调用 doRefer 继续执行服务引用逻辑
return doRefer(getMergeableCluster(), registry, type, url);
}
}
// 调用 doRefer,引用注册中心为zookeeperRegistry上的服务
return doRefer(cluster, registry, type, url);
}
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
//TAG2 创建 RegistryDirectory 实例
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
// 设置directory的注册中心和协议
directory.setRegistry(registry);
directory.setProtocol(protocol);
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
//TAG3 构建服务消费者的URL,,如consumer://
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
&& url.getParameter(Constants.REGISTER_KEY, true)) {
//TAG4 registry.register(subscribeUrl)向注册中心注册
//首先,会向consumer消费者url中添加category=consumer的键值对参数
// 然后,registry.register注册服务消费者,在 consumers 目录下创建新节点
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
//TAG5 directory.subscribe(subscribeURL)订阅节点信息
// 首先,会向consumer消费者URL中添加键值对category=,来订阅 providers、configurators、routers 等节点数据;
//然后,调用directory.subscribe订阅节点信息
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
//TAG6 cluster.join(derectory)
// 一个注册中心可能有多个服务提供者,因此这里需要将多个服务提供者合并为一个
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
RegistryProtocol.refer(Class
RegistryProtocol.refer(Class<T> type, URL url){
//TAG1 获取注册中心Registry实例(将consumer注册到注册中心)
Registry registry = registryFactory.getRegistry(url);
//TAG2 创建 RegistryDirectory 实例(用于订阅注册中心下providers、configurators、routers 等节点数据)
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
// 设置directory的注册中心和协议
directory.setRegistry(registry);
directory.setProtocol(protocol);
//TAG3 构建服务消费者的URL,,如consumer://
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
//TAG4 registry.register(subscribeUrl)向注册中心注册
//首先,会向consumer消费者url中添加category=consumer的键值对参数
// 然后,registry.register注册服务消费者,在 consumers 目录下创建新节点
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
//TAG5 directory.subscribe(subscribeURL)订阅节点信息
// 首先,会向consumer消费者URL中添加键值对category=,来订阅 providers、configurators、routers 等节点数据;
//然后,调用directory.subscribe订阅节点信息
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
//TAG6 cluster.join(derectory)
// 一个注册中心可能有多个服务提供者,因此这里需要将多个服务提供者合并为一个
Invoker invoker = cluster.join(directory);
return invoker;
}
directory.subscribe中参数URL为如下,根据添加的category,订阅对应的节点下的数据。
当前invoker底层仍是NettyClient,注册中心是集群搭建模式,一个服务对应多个invoker,需要将多个invoker逻辑合并为一个。执行cluster.join(directory),返回一个集群模式管理的invoker。
跟入cluster.join(directory)
//进入到集群创建Invoker模式
@SPI(FailoverCluster.NAME)
public interface Cluster {
//合并其中Directory的Invoker为一个Invoker
@Adaptive
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
}
cluster是一个SPI拓展点,通过ExtensionLoader拓展点加载机制,默认实现为failoverCluster。但是,dubbo的[Wrapper机制](#2.2.2 Wrapper机制),得到的是MockClusterWrapper。
配置文件中有wrapper类,且MockeClusterWrapper实现Cluster类,且存在有参构造函数,因此,会自动注入cluster实现类FailoveCluster,得到的cluster拓展点实例为wrapper类
public class FailoverCluster implements Cluster {
public final static String NAME = "failover";
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
return new FailoverClusterInvoker<T>(directory);
}
}
//进入到MockerClusterWrapper实现类中
public class MockClusterWrapper implements Cluster {
private Cluster cluster;
public MockClusterWrapper(Cluster cluster) {
//wrapper机制,此处cluster注入FailoverCluster
this.cluster = cluster;
}
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
//先调用FailoverCluster.join,返回FailoverClusterInvoker
//然后,返回一个mock过的invoker
return new MockClusterInvoker<T>(directory, this.cluster.join(directory));
}
}
集群模式下的[mock机制](#2.3.1 Mock机制),在获取到的FailoverClusterInvoker包装为MockClusterInvoker后,使得invoker可以实现服务降级等一系列服务治理中的操作。调用invoker.invoke时,dubbo内部的负载均衡机制,会从多个invoker中选择一个,进行调用。
public class MockClusterInvoker<T> implements Invoker<T>{
private static final Logger logger = LoggerFactory.getLogger(MockClusterInvoker.class);
private final Directory<T> directory ;
//mock的invoker内,持有一个invoker对象
private final Invoker<T> invoker;
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
//获取URL中的mock参数值
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(),
Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || value.equalsIgnoreCase("false")){
//没有mock时,直接调用invoker.invoke
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
if (logger.isWarnEnabled()) {
logger.info("force-mock: " + invocation.getMethodName() +
" force-mock enabled , url : " + directory.getUrl());
}
//force:不发起远程调用,直接降级
result = doMockInvoke(invocation, null);
} else {
//fail-mock:该情况下,远程调用正常,出现异常时,降级操作
try {
result = this.invoker.invoke(invocation);
}catch (RpcException e) {
if (e.isBiz()) {
throw e;
} else {
if (logger.isWarnEnabled()) {
logger.info("fail-mock: " + invocation.getMethodName() +
" fail-mock enabled , url : " + directory.getUrl(), e);
}
//远程调用失败时,降级操作
result = doMockInvoke(invocation, e);
}
}
}
return result;
}
当注册中心为集群模式,会在FailoverInvoker包装在MockClusterInvoker中,实现mock的降级逻辑实现。分别可根据URL传来的mock参数,针对是mock=no、force、fail-mock的的情况,分别进行远程调用或者时降级处理。
总结RegistryProtocol.refer获取invoker的初始化流程图,如下: