欢迎你们关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思惟、职场分享、产品思考等等,同时欢迎你们加我我的微信「java_front」一块儿交流学习java
1 泛化调用实例
对于JAVA服务端开发者而言在使用Dubbo时并不常常使用泛化调用,一般方法是在生产者发布服务以后,消费者能够经过引入生产者提供的client进行调用。那么泛化调用使用场景是什么呢?面试
第一种场景是消费者不但愿引入生产者提供的client依赖,只但愿关注调用哪一个方法,须要传什么参数便可。第二种场景是消费者不是使用Java语言,而是使用例如Python语言,那么如何调用使用Java语言生产者提供的服务呢?这时咱们能够选择泛化调用。spring
泛化调用使用方法并不复杂,下面咱们编写一个泛化调用实例。首先生产者发布服务,这与普通服务发布没有任何区别。apache
package com.java.front.dubbo.demo.provider; public interface HelloService { public String sayHelloGeneric(Person person, String message); } public class HelloServiceImpl implements HelloService { @Override public String sayHelloGeneric(Person person, String message) throws Exception { String result = "hello[" + person + "],message=" + message; return result; } }
Person类声明:设计模式
package com.java.front.dubbo.demo.provider.model; public class Person implements Serializable { private String name; }
provider.xml文件内容:微信
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- 提供方应用信息,用于计算依赖关系 --> <dubbo:application name="java-front-provider" /> <!-- 链接注册中心 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 生产者9999在端口暴露服务 --> <dubbo:protocol name="dubbo" port="9999" /> <!-- Bean --> <bean id="helloService" class="com.java.front.dubbo.demo.provider.HelloServiceImpl" /> <!-- 暴露服务 --> <dubbo:service interface="com.java.front.dubbo.demo.provider.HelloService" ref="helloService" /> </beans>
消费者代码有所不一样:架构
import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.service.GenericService; public class Consumer { public static void testGeneric() { ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); reference.setApplication(new ApplicationConfig("java-front-consumer")); reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); reference.setInterface("com.java.front.dubbo.demo.provider.HelloService"); reference.setGeneric(true); GenericService genericService = reference.get(); Map<String, Object> person = new HashMap<String, Object>(); person.put("name", "微信公众号「JAVA前线」"); String message = "你好"; Object result = genericService.$invoke("sayHelloGeneric", new String[] { "com.java.front.dubbo.demo.provider.model.Person", "java.lang.String" }, new Object[] { person, message }); System.out.println(result); } }
2 Invoker
咱们经过源码分析讲解泛化调用原理,咱们首先须要了解Invoker这个Dubbo重量级概念。在生产者暴露服务流程整体分为两步,第一步是接口实现类转换为Invoker,第二步是Invoker转换为Exporter并放入ExporterMap,咱们看看生产者暴露服务流程图:app

生产者经过ProxyFactory.getInvoker方法建立Invoker(AbstractProxyInvoker):jvm
public class JdkProxyFactory extends AbstractProxyFactory { @Override public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { return new AbstractProxyInvoker<T>(proxy, type, url) { @Override protected Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable { // proxy为被代理对象 -> com.java.front.dubbo.demo.provider.HelloServiceImpl Method method = proxy.getClass().getMethod(methodName, parameterTypes); return method.invoke(proxy, arguments); } }; } }
咱们再看看消费者引用服务流程图:async

消费者Invoker经过显示实例化建立,例如本地暴露和远程暴露都是经过显示初始化的方法建立Invoker(AbstractInvoker):
new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap) new DubboInvoker<T>(serviceType, url, getClients(url), invokers)
再经过ProxyFactory.getProxy建立代理:
public class JdkProxyFactory extends AbstractProxyFactory { @Override public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { InvokerInvocationHandler invokerInvocationHandler = new InvokerInvocationHandler(invoker); return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, invokerInvocationHandler); } }
不管是生产者仍是消费者的Invoker都实现自org.apache.dubbo.rpc.Invoker:
public abstract class AbstractInvoker<T> implements Invoker<T>{ } public abstract class AbstractProxyInvoker<T> implements Invoker<T> { }
3 装饰器模式
为何生产者和消费者都要转换为Invoker而不是不直接调用呢?我认为Invoker正是Dubbo设计精彩之处:真实调用都转换为Invoker,Dubbo就能够经过装饰器模式加强Invoker功能。咱们看看什么是装饰器模式。
装饰器模式能够动态将责任附加到对象上,在不改变原始类接口状况下,对原始类功能进行加强,而且支持多个装饰器的嵌套使用。实现装饰器模式须要如下组件:
Component(抽象构件) 核心业务抽象:可使用接口或者抽象类 ConcreteComponent(具体构件) 实现核心业务:最终执行的业务代码 Decorator(抽象装饰器) 抽象装饰器类:实现Component而且组合一个Component对象 ConcreteDecorator(具体装饰器) 具体装饰内容:装饰核心业务代码
咱们分析一个装饰器实例。有一名足球运动员要去踢球,咱们用球鞋和球袜为他装饰一下,这样可使战力值增长。
(1) Component
/** * 抽象构件(能够用接口替代) */ public abstract class Component { /** * 踢足球(业务核心方法) */ public abstract void playFootBall(); }
(2) ConcreteComponent
/** * 具体构件 */ public class ConcreteComponent extends Component { @Override public void playFootBall() { System.out.println("球员踢球"); } }
(3) Decorator
/** * 抽象装饰器 */ public abstract class Decorator extends Component { private Component component = null; public Decorator(Component component) { this.component = component; } @Override public void playFootBall() { this.component.playFootBall(); } }
(4) ConcreteDecorator
/** * 球袜装饰器 */ public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } /** * 定义球袜装饰逻辑 */ private void decorateMethod() { System.out.println("换上球袜战力值增长"); } /** * 重写父类方法 */ @Override public void playFootBall() { this.decorateMethod(); super.playFootBall(); } } /** * 球鞋装饰器 */ public class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); } /** * 定义球鞋装饰逻辑 */ private void decorateMethod() { System.out.println("换上球鞋战力值增长"); } /** * 重写父类方法 */ @Override public void playFootBall() { this.decorateMethod(); super.playFootBall(); } }
(5) 测试代码
public class TestDecoratorDemo { public static void main(String[] args) { Component component = new ConcreteComponent(); component = new ConcreteDecoratorA(component); component = new ConcreteDecoratorB(component); component.playFootBall(); } } // 换上球鞋战力值增长 // 换上球袜战力值增长 // 球员踢球
4 过滤器链路
Dubbo为Invoker加强了哪些功能?过滤器链是我认为加强的最重要的功能之一,咱们继续分析源码:
public class ProtocolFilterWrapper implements Protocol { @Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { return protocol.export(invoker); } // 增长过滤器链 Invoker<T> invokerChain = buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER); return protocol.export(invokerChain); } @Override public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { return protocol.refer(type, url); } // 增长过滤器链 Invoker<T> invoker = protocol.refer(type, url); Invoker<T> result = buildInvokerChain(invoker, Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER); return result; } }
不管是生产者仍是消费者都会建立过滤器链,咱们看看buildInvokerChain这个方法:
public class ProtocolFilterWrapper implements Protocol { private final Protocol protocol; public ProtocolFilterWrapper(Protocol protocol) { if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; } private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) { Invoker<T> last = invoker; // (1)加载全部包含Activate注解的过滤器 // (2)根据group过滤获得过滤器列表 // (3)Invoker最终被放到过滤器链尾部 List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); if (!filters.isEmpty()) { for (int i = filters.size() - 1; i >= 0; i--) { final Filter filter = filters.get(i); final Invoker<T> next = last; // 构造一个简化Invoker last = new Invoker<T>() { @Override public Class<T> getInterface() { return invoker.getInterface(); } @Override public URL getUrl() { return invoker.getUrl(); } @Override public boolean isAvailable() { return invoker.isAvailable(); } @Override public Result invoke(Invocation invocation) throws RpcException { // 构造过滤器链路 Result result = filter.invoke(next, invocation); if (result instanceof AsyncRpcResult) { AsyncRpcResult asyncResult = (AsyncRpcResult) result; asyncResult.thenApplyWithContext(r -> filter.onResponse(r, invoker, invocation)); return asyncResult; } else { return filter.onResponse(result, invoker, invocation); } } @Override public void destroy() { invoker.destroy(); } @Override public String toString() { return invoker.toString(); } }; } } return last; } }
加载全部包含Activate注解的过滤器,根据group过滤获得过滤器列表,Invoker最终被放到过滤器链尾部,生产者最终生成链路:
EchoFilter -> ClassloaderFilter -> GenericFilter -> ContextFilter -> TraceFilter -> TimeoutFilter -> MonitorFilter -> ExceptionFilter -> AbstractProxyInvoker
消费者最终生成链路:
ConsumerContextFilter -> FutureFilter -> MonitorFilter -> GenericImplFilter -> DubboInvoker
5 泛化调用原理
咱们终于即将看到泛化调用核心原理,咱们在生产者链路看到GenericFilter过滤器,消费者链路看到GenericImplFilter过滤器,正是这两个过滤器实现了泛化调用。
(1) GenericImplFilter
@Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY, order = 20000) public class GenericImplFilter implements Filter { @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { // 方法名=$invoke // invocation.getArguments()=("sayHelloGeneric", new String[] { "com.java.front.dubbo.demo.provider.model.Person", "java.lang.String" }, new Object[] { person, "你好" }); if (invocation.getMethodName().equals(Constants.$INVOKE) && invocation.getArguments() != null && invocation.getArguments().length == 3 && ProtocolUtils.isGeneric(generic)) { // 第一个参数表示方法名 // 第二个参数表示参数类型 // 第三个参数表示参数值 -> [{name=微信公众号「JAVA前线」},你好] Object[] args = (Object[]) invocation.getArguments()[2]; if (ProtocolUtils.isJavaGenericSerialization(generic)) { for (Object arg : args) { if (!(byte[].class == arg.getClass())) { error(generic, byte[].class.getName(), arg.getClass().getName()); } } } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { for (Object arg : args) { if (!(arg instanceof JavaBeanDescriptor)) { error(generic, JavaBeanDescriptor.class.getName(), arg.getClass().getName()); } } } // 附加参数generic值设置为true ((RpcInvocation) invocation).setAttachment(Constants.GENERIC_KEY, invoker.getUrl().getParameter(Constants.GENERIC_KEY)); } // 继续执行过滤器链路 return invoker.invoke(invocation); } }
(2) GenericFilter
@Activate(group = Constants.PROVIDER, order = -20000) public class GenericFilter implements Filter { @Override public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException { // RpcInvocation[methodName=$invoke, parameterTypes=[class java.lang.String, class [Ljava.lang.String;, class [Ljava.lang.Object;], arguments=[sayHelloGeneric, [Ljava.lang.String;@14e77f6b, [Ljava.lang.Object;@51e5f393], attachments={path=com.java.front.dubbo.demo.provider.HelloService, input=451, dubbo=2.0.2, interface=com.java.front.dubbo.demo.provider.HelloService, version=0.0.0, generic=true}] if (inv.getMethodName().equals(Constants.$INVOKE) && inv.getArguments() != null && inv.getArguments().length == 3 && !GenericService.class.isAssignableFrom(invoker.getInterface())) { // sayHelloGeneric String name = ((String) inv.getArguments()[0]).trim(); // [com.java.front.dubbo.demo.provider.model.Person, java.lang.String] String[] types = (String[]) inv.getArguments()[1]; // [{name=微信公众号「JAVA前线」}, 你好] Object[] args = (Object[]) inv.getArguments()[2]; // RpcInvocation[methodName=sayHelloGeneric, parameterTypes=[class com.java.front.dubbo.demo.provider.model.Person, class java.lang.String], arguments=[Person(name=JAVA前线), abc], attachments={path=com.java.front.dubbo.demo.provider.HelloService, input=451, dubbo=2.0.2, interface=com.java.front.dubbo.demo.provider.HelloService, version=0.0.0, generic=true}] RpcInvocation rpcInvocation = new RpcInvocation(method, args, inv.getAttachments()); Result result = invoker.invoke(rpcInvocation); } } }
6 文章总结
本文首先介绍了如何使用泛化调用,并引出泛化调用为何生效这个问题。第二点介绍了重量级概念Invoker,并引出为何Dubbo要建立Invoker这个问题。第三点介绍了装饰器模式如何加强功能。最后咱们经过源码分析知道了过滤器链加强了Invoker功能而且是实现泛化调用的核心,但愿本文对你们有所帮助。
欢迎你们关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思惟、职场分享、产品思考等等,同时欢迎你们加我我的微信「java_front」一块儿交流学习