dubbo 3.0服务调用者调用第三方接口源码主流程分析
我们在上一篇已经分析了生产者服务是怎么进行实例发布的,我们再来分析一下消费者是怎么进行调用的;
这里分为2步分析:
第一步:分析如何基于需要调用的服务接口动态生成代理类的
第二步:如何根据动态生成的代理进行调用的
第三步:服务端处理请求
第四步:如何接受返回的数据
一、构建动态代理类
在get的方法里面这一行代码和之前生产者服务实例发布里面的代码逻辑是一样的,这里就不过多分析了
getScopeModel().getDeployer().start();
我们直接看init方法就好
我们其余的分支代码就不分析了,只分析最主要的,因为毕竟是文字,我怕跳来跳去太混乱了,我看了上一篇感觉全部贴代码不太好理解,这里我们就根据createProxy方法作为入口进行分析;
这里不会进行本地服务调用,而是直接会调用createInvokerForRemote方法创建Invoker;
在通过protocolSPI的refer方法一层层去调用创建了Invoker,相当于链式调用,会比较深;
注意后续方法调用的类,这里会调用RegistryProtocol的refer方法,里面会获取一些构建所需的参数,实际构建则是调用doRefer方法进行构建;
对参数进行了一些处理在调用interceptInvoker方法,这里会调用许多的监听器;
这里会循环调用监听器的onRefer方法,这里有一个MigrationRuleListener的监听器比较重要,会通过DynamicDirectory去zk动态获取目标服务实例的集群地址;
构建MigrationRuleHandler并调用其doMigrate方法;
这里会调用refreshInvoker方法去构建一个Invoker,重要,注意看
调用MigrationInvoker类的migrateToApplicationFirstInvoker方法
这里我们主要看MigrationInvoker的refreshInterfaceInvoker方法,refreshServiceDiscoveryInvoker方法和refreshInterfaceInvoker方法逻辑差不多,在calcPreferredInvoker方法就计算一下优先级,选择出来一个优先级比较高的Invoker而已;
这里会调用RegistryProtocol的getInvoker方法,在调用doCreateInvoker方法
这里会调用ListenerRegistryWrapper的register方法进行服务注册,并且还会调用DynamicDirectory的subscribe方法进行服务的发现及注册监听器进行订阅,之后就调用join方法构建了一个MockClusterInvoker进行返回;
我们先看一下调用ListenerRegistryWrapper的register方法之后是如何进行服务注册的,我们点进去之后会在调用org.apache.dubbo.registry.support.FailbackRegistry的register方法
这里会调用ZookeeperRegistry的doRegister方法
里面比较核心的是会利用zk的api去创建一个节点
创建完之后就会返回,在调用DynamicDirectory的subscribe方法进行服务的发现及注册监听器进行订阅
最终会调用到ZookeeperRegistry的doSubscribe方法施加监听器,并返回服务实例的地址集合
获取到目标服务实例的地址集合之后会调用notify方法进行服务实例注册表缓存刷新
这里的doNotify方法,对节点施加的监听器在节点发生变动时也会调用这个方法进行注册表的刷新,我们点击进去看一下
这个方法里面有一块比较重要的逻辑,就是会通过Netty进行网络连接,我们点进去看一下
会调用到RegistryDirectory的notify方法,这个里面会做一些逻辑处理,最终会调用到DubboProtocol的refer方法中去
DubboProtocol在处理时会new一个DubboInvoker出来,在构建的时候会调用getClients方法进行Netty网络连接
最终会通过Exchangers.connect方法发起连接
具体的逻辑会调用到NettyTransporter的connect进行实现,其实就是构建了一个NettyClient,在构建的时候会发起网络连接请求
NettyClient构建时父类的AbstractClient会有2个比较重要的方法,一个是doOpen方法,会初始化一些参数,而connect方法则会建立连接;
我们跳到netty4的NettyClient的doOpen实现里面,发现是对Bootstrap类做一些赋值而已
连接的具体实现也需要到netty4的NettyClient的doConnect方法里面
网络连接建立好之后再将修改信息缓存在本地缓存中
从注释中我们可以发现Properties文件是一个本地磁盘缓存,里面就存储了服务注册表的信息
根据createInvokerForRemote方法创建出来了一个invoker,我们在来看一下是如何基于invoker来生成一个动态代理的,在之后如何根据这个动态代理去进行调用的
这里会基于StubProxyFactoryWrapper的getProxy进行调用
之后会调用到AbstractProxyFactory的getProxy方法中去
在调用JavassistProxyFactory的getProxy方法,这里会先通过Proxy.getProxy方法获取出来一个Proxy动态代理,在将我们之前创建的invoker封装成一个InvokerInvocationHandler给生成的动态代理类
获取Proxy动态代理类里面的话做了一些前置处理,在从缓存中获取,缓存中没有就直接创建了一个Proxy,其中buildProxyClass方法是最核心的方法
我们观察一下,发现这个方法会基于我们传递进来的参数动态的去生成和拼接一个动态代理类的代码,这个类会去实现一些接口以及一些方法;最后再返回这个动态生成的代理类;
private static Class<?> buildProxyClass(ClassLoader cl, Class<?>[] ics, ProtectionDomain domain) {
ClassGenerator ccp = null;
try {
ccp = ClassGenerator.newInstance(cl);
Set<String> worked = new HashSet<>();
List<Method> methods = new ArrayList<>();
String pkg = ics[0].getPackage().getName();
Class<?> neighbor = ics[0];
for (Class<?> ic : ics) {
String npkg = ic.getPackage().getName();
if (!Modifier.isPublic(ic.getModifiers())) {
if (!pkg.equals(npkg)) {
throw new IllegalArgumentException("non-public interfaces from different packages");
}
}
ccp.addInterface(ic);
for (Method method : ic.getMethods()) {
String desc = ReflectUtils.getDesc(method);
if (worked.contains(desc) || Modifier.isStatic(method.getModifiers())) {
continue;
}
worked.add(desc);
int ix = methods.size();
Class<?> rt = method.getReturnType();
Class<?>[] pts = method.getParameterTypes();
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for (int j = 0; j < pts.length; j++) {
code.append(" args[").append(j).append("] = ($w)#34;).append(j + 1).append(";");
}
code.append(" Object ret = handler.invoke(this, methods[").append(ix).append("], args);");
if (!Void.TYPE.equals(rt)) {
code.append(" return ").append(asArgument(rt, "ret")).append(';');
}
methods.add(method);
ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
}
}
// create ProxyInstance class.
String pcn = neighbor.getName() + "DubboProxy" + PROXY_CLASS_COUNTER.getAndIncrement();
ccp.setClassName(pcn);
ccp.addField("public static java.lang.reflect.Method[] methods;");
ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{InvocationHandler.class}, new Class<?>[0], "handler=$1;");
ccp.addDefaultConstructor();
Class<?> clazz = ccp.toClass(neighbor, cl, domain);
clazz.getField("methods").set(null, methods.toArray(new Method[0]));
return clazz;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
// release ClassGenerator
if (ccp != null) {
ccp.release();
}
}
}
二、基于动态生成的代理类进行调用
这里调用时就会根据我们之前动态生成的代理类进行调用;
因为是动态生成的,最后会调用到InvokerInvocationHandler的invoke方法中去
做了一些前置处理之后会调用到MigrationInvoker的invoke方法中
这个方法基本没做什么处理就调用了MockClusterInvoker的invoke方法
在调用到ClusterFilterInvoker的invoke方法去,这里主要是会通过一个过滤链条进行处理
这里会调用到FilterChainNode的invoke方法去执行具体的过滤逻辑
在这之前会经过很多的过滤组件进行过滤,最终会到MonitorFilter的invoke方法中去,这里会调用Invoker的invoke方法,这里的Invoker实现类是AbstractClusterInvoker
其实会调用到AbstractClusterInvoker的invoke方法,做了一些前置处理之后会调用到doInvoke方法中去,但是在这之前我们可以看一下Invoker集合是怎么获取到的
其实这里最终会调用到DynamicDirectory的doList方法中去,之前在生成动态代理类的时候我们就知道获取目标实例服务集群地址就是这个类处理的,并且对zk施加了监听器,地址发生变化也可以感知到,及时修改,而他内部也会缓存一份注册表,所以在获取的时候直接找他去获取就好了
在获取的时候会有一些路由策略,不一定注册上去的服务实例你都是可以用的
服务实例列表有了,那怎么挑选一个出来了?默认为随机算法进行选择
前置条件准备好之后在调用FailoverClusterInvoker的doInvoke方法进行处理实际调用第三方接口
默认发起rpc请求是异步化的操作,结果层层处理之后会调用NettyChannel将数据发送出去
经过层层处理之后,最后调用channel.writeAndFlush方法将数据发送出去
三、服务端处理请求
NettyServerHandler监听到有请求时会调用channelRead处理
在利用线程池去执行处理线程
之后ChannelEventRunnable的run方法被执行
调用DubboProtocol实现的reply方法,其中会利用Invoker调用其invoke方法
这里会经过一连串的过滤器进行处理
最后会调用到TraceFilter的invoke方法,整个过滤链条调用完毕,最后在调用AbstractProxyInvoker的invoke方法调用我们实现的方法
这里会通过反射技术实际去调用我们实现类的方法
返回结果之后调用channel.send方法将数据返回
四、如何接受返回的数据
threadlessExecutor.waitAndDrain方法会等待请求结果返回,在调用run方法进行执行
这里我们有超时时间,所以调用timedGet方法
注释:等待后返回原始结果,如果中断则返回null,或超时时抛出TimeoutException
请先 后发表评论~