五、调用模块源码分析

tech2022-08-17  120

概要:

一、Dubbo 调用非典型使用场景

二、调用内部实现源码分析

一、Dubbo 调用非典型使用场景


泛化提供&引用

泛化提供

是指不通过接口的方式直接将服务暴露出去。通常用于Mock框架或服务降级框架实现。 public static void doExportGenericService() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("demo-provider"); // 注册中心 RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setProtocol("zookeeper"); registryConfig.setAddress("192.168.0.147:2181"); ProtocolConfig protocol=new ProtocolConfig(); protocol.setPort(-1); protocol.setName("dubbo"); GenericService demoService = new MyGenericService(); ServiceConfig<GenericService> service = new ServiceConfig<GenericService>(); // 弱类型接口名 service.setInterface("com.tuling.teach.service.DemoService"); // 指向一个通用服务实现 service.setRef(demoService); service.setApplication(applicationConfig); service.setRegistry(registryConfig); service.setProtocol(protocol); // 暴露及注册服务 service.export(); }

泛化引用 是指不通过常规接口的方式去引用服务,通常用于测试框架。

ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("demo-provider"); // 注册中心 RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setProtocol("zookeeper"); registryConfig.setAddress("192.168.0.147:2181"); // 引用远程服务 ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); // 弱类型接口名 reference.setInterface("com.tuling.teach.service.DemoService"); // 声明为泛化接口 reference.setGeneric(true); reference.setApplication(applicationConfig); reference.setRegistry(registryConfig); // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用 GenericService genericService = reference.get(); Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});

隐示传参

是指通过非常方法参数传递参数,类似于http 调用当中添加cookie值。通常用于分布式追踪框架的实现。使用方式如下 : //客户端隐示设置值 RpcContext.getContext().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐 //服务端隐示获取值 String index = RpcContext.getContext().getAttachment("index");

令牌验证

通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者

使用:

<!--随机token令牌,使用UUID生成--><dubbo:provider interface="com.foo.BarService" token="true" />

**过滤器 **

类似于 WEB 中的Filter ,Dubbo本身提供了Filter 功能用于拦截远程方法的调用。其支持自定义过滤器与官方的过滤器使用**:**

#TODO 演示添加日志访问过滤:

<dubbo:provider filter="accesslog" accesslog="logs/dubbo.log"/>

以上配置 就是 为 服务提供者 添加 日志记录过滤器, 所有访问日志将会集中打印至 accesslog 当中 自定义过滤器:

1、编写过滤器

package com.tuling.dubbo; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.*; @Activate(group = {CommonConstants.PROVIDER}) public class ProviderHelloFilter implements Filter { @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { System.out.println("hello ok====================================>>>>>"); return invoker.invoke(invocation); } }

添加扩展点: 创建文件路径如下:

# 文件路径 META-INF/dubbo/org.apache.dubbo.rpc.Filter #内容: helloFilter=com.tuling.dubbo.ProviderHelloFilter

二 、调用内部实现源码分析


知识点

分析代理类分析类结构初始化过程分析代理类

在调用服务端时,是接口的形式进行调用,该接口是Duboo 动态代理之后的实现,通过反编译工具可以查看到其具体实现:

因为类是代理生成,所以采用arthas工具来反编译,具体操作如下:

#运行 arthas java -jar arthas-boot.jar #扫描类 sc *.proxy0 #反编译代理类 jad com.alibaba.dubbo.common.bytecode.proxy0

反编译的代码如下:

/* * Decompiled with CFR. * * Could not load the following classes: * com.tuling.client.User * com.tuling.client.UserService */ package org.apache.dubbo.common.bytecode; import com.alibaba.dubbo.rpc.service.EchoService; import com.tuling.client.User; import com.tuling.client.UserService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.List; import org.apache.dubbo.common.bytecode.ClassGenerator; public class proxy0 implements ClassGenerator.DC, EchoService, UserService { public static Method[] methods; private InvocationHandler handler; public List findUser(String string, String string2) { Object[] arrobject = new Object[]{string, string2}; Object object = this.handler.invoke(this, methods[0], arrobject); return (List)object; } public User getUser(Integer n) { Object[] arrobject = new Object[]{n}; Object object = this.handler.invoke(this, methods[1], arrobject); return (User)object; } @Override public Object $echo(Object object) { Object[] arrobject = new Object[]{object}; Object object2 = this.handler.invoke(this, methods[2], arrobject); return object2; } public proxy0() { } public proxy0(InvocationHandler invocationHandler) { this.handler = invocationHandler; } }

可看出其代理实现了 UserService 接口。并且基于InvocationHandler 进行代理。实际类是 InvokerInvocationHandler 并且其中之属性为Invoker.。也就是说最终会调用Invoker进行远程调用。

2.Dubbo调用流程:

基础流程:

客户端调用

透明代理

负载均衡

容错

异步转同步

获取结果

服务端响应

分析类结构关系

prxoy$: 代理类Invoker: 执行器Invocation: 执行参数与环境Result:返回结果Protocol:协议

调用过程

调用堆栈

//------ 6协议 调用 doInvoke:77, DubboInvoker {org.apache.dubbo.rpc.protocol.dubbo} invoke:155, AbstractInvoker {org.apache.dubbo.rpc.protocol} //------ 5异步转同步 invoke:52, AsyncToSyncInvoker {org.apache.dubbo.rpc.protocol} // 异步转同步 ,返回结果之前进行阻塞调用线程 //----- 4过滤器链 invoke:92, MonitorFilter {org.apache.dubbo.monitor.support} // 过滤链-> 监控器 invoke:54, FutureFilter {org.apache.dubbo.rpc.protocol.dubbo.filter} //过滤链-> 回调参数 invoke:14, ProviderHelloFilter {com.tuling.dubbo} // 过滤链-> 自定义过滤器 invoke:60, ConsumerContextFilter {org.apache.dubbo.rpc.filter} // 过滤链-> 消费者环境初始化 //------3集群处理 doInvoke:82, FailoverClusterInvoker {org.apache.dubbo.rpc.cluster.support} // 集服-失败重试 invoke:248, AbstractClusterInvoker {org.apache.dubbo.rpc.cluster.support} // //----- Mock服务 invoke:78, MockClusterInvoker {org.apache.dubbo.rpc.cluster.support.wrapper} // mock 服务 //----- 2动态代理 --透明化 invoke:55, InvokerInvocationHandler {org.apache.dubbo.rpc.proxy}// 代理的中间接口 getUser:-1, proxy0 {org.apache.dubbo.common.bytecode} // 代理对象 //----- 1调用客户端 main:53, DubboClient {com.tuling.dubbo} // 客户端

协议—>(注册协议)—>MockClusterInvoker —>ClusterInvoker—> RegistryDirectory —>(DubboProtcol)->FilterChain–>DubboInvoker

最新回复(0)