[TOC] 概要: 一、Dubbo 调用非典型使用场景 二、调用内部实现源码分析 ## 一、Dubbo 调用非典型使用场景 --- ### **泛化提供&引用** **泛化提供** 是指不通过接口的方式直接将服务暴露出去。通常用于Mock框架或服务降级框架实现。 #TODO 示例演示 ``` 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");  ``` ### **令牌验证** 通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者 ![](https://img.kancloud.cn/93/18/9318b99fb74891459b49f2ffc1d17590_400x340.png) 使用: ``` <!--随机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 ``` ## 二 、调用内部实现源码分析 --- **知识点** 1. 分析代理类 2. 分析类结构 3. 初始化过程 1. 分析代理类 在调用服务端时,是接口的形式进行调用,该接口是Duboo 动态代理之后的实现,通过反编译工具可以查看到其具体实现: 因为类是代理生成,所以采用[arthas](https://github.com/alibaba/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调用流程: 基础流程: ![](https://img.kancloud.cn/f0/0a/f00a65674f1ac7df915723cb410fdab4_838x413.png) 1. 客户端调用 2. 透明代理 3. 负载均衡 4. 容错 5. 异步转同步 6. 获取结果 7. 服务端响应 1. 分析类结构关系 * prxoy$: 代理类 * Invoker: 执行器 * Invocation: 执行参数与环境 * Result:返回结果 * Protocol:协议 ## ![](https://img.kancloud.cn/f7/48/f7483e2be7a275650e5397477ab73a33_1079x845.png)