面试官从Dubbo泛化调用问到设计模式,我们聊了三十分钟
欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习 1 泛化调用实例 对于JAVA服务端开发者而言在使用Dubbo时并不经常使用泛化调用,通常方法是在生产者发布服务之后,消费者可以通过引入生产者提供的client进行调用。那么泛化调用使用场景是什么呢? 第一种场景是消费者不希望引入生产者提供的client依赖,只希望关注调用哪个方法,需要传什么参数即可。第二种场景是消费者不是使用Java语言,而是使用例如Python语言,那么如何调用使用Java语言生产者提供的服务呢?这时我们可以选择泛化调用。 泛化调用使用方法并不复杂,下面我们编写一个泛化调用实例。首先生产者发布服务,这与普通服务发布没有任何区别。 packagecom.java.front.dubbo.demo.provider; publicinterfaceHelloService{ publicStringsayHelloGeneric(Personperson,Stringmessage); } publicclassHelloServiceImplimplementsHelloService{ @Override publicStringsayHelloGeneric(Personperson,Stringmessage)throwsException{ Stringresult="hello["+person+"],message="+message; returnresult; } } Person类声明: packagecom.java.front.dubbo.demo.provider.model; publicclassPersonimplementsSerializable{ privateStringname; } provider.xml文件内容: <beansxmlns="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:applicationname="java-front-provider"/> <!--连接注册中心--> <dubbo:registryaddress="zookeeper://127.0.0.1:2181"/> <!--生产者9999在端口暴露服务--> <dubbo:protocolname="dubbo"port="9999"/> <!--Bean--> <beanid="helloService"class="com.java.front.dubbo.demo.provider.HelloServiceImpl"/> <!--暴露服务--> <dubbo:serviceinterface="com.java.front.dubbo.demo.provider.HelloService"ref="helloService"/> </beans> 消费者代码有所不同: importorg.apache.dubbo.config.ApplicationConfig; importorg.apache.dubbo.config.ReferenceConfig; importorg.apache.dubbo.config.RegistryConfig; importorg.apache.dubbo.rpc.RpcContext; importorg.apache.dubbo.rpc.service.GenericService; publicclassConsumer{ publicstaticvoidtestGeneric(){ ReferenceConfig<GenericService>reference=newReferenceConfig<GenericService>(); reference.setApplication(newApplicationConfig("java-front-consumer")); reference.setRegistry(newRegistryConfig("zookeeper://127.0.0.1:2181")); reference.setInterface("com.java.front.dubbo.demo.provider.HelloService"); reference.setGeneric(true); GenericServicegenericService=reference.get(); Map<String,Object>person=newHashMap<String,Object>(); person.put("name","微信公众号「JAVA前线」"); Stringmessage="你好"; Objectresult=genericService.$invoke("sayHelloGeneric",newString[]{"com.java.front.dubbo.demo.provider.model.Person","java.lang.String"},newObject[]{person,message}); System.out.println(result); } } 2 Invoker 我们通过源码分析讲解泛化调用原理,我们首先需要了解Invoker这个Dubbo重量级概念。在生产者暴露服务流程总体分为两步,第一步是接口实现类转换为Invoker,第二步是Invoker转换为Exporter并放入ExporterMap,我们看看生产者暴露服务流程图: 生产者通过ProxyFactory.getInvoker方法创建Invoker(AbstractProxyInvoker): publicclassJdkProxyFactoryextendsAbstractProxyFactory{ @Override public<T>Invoker<T>getInvoker(Tproxy,Class<T>type,URLurl){ returnnewAbstractProxyInvoker<T>(proxy,type,url){ @Override protectedObjectdoInvoke(Tproxy,StringmethodName, Class<?>[]parameterTypes, Object[]arguments)throwsThrowable{ //proxy为被代理对象->com.java.front.dubbo.demo.provider.HelloServiceImpl Methodmethod=proxy.getClass().getMethod(methodName,parameterTypes); returnmethod.invoke(proxy,arguments); } }; } } 我们再看看消费者引用服务流程图: 消费者Invoker通过显示实例化创建,例如本地暴露和远程暴露都是通过显示初始化的方法创建Invoker(AbstractInvoker): newInjvmInvoker<T>(serviceType,url,url.getServiceKey(),exporterMap) newDubboInvoker<T>(serviceType,url,getClients(url),invokers) 再通过ProxyFactory.getProxy创建代理: publicclassJdkProxyFactoryextendsAbstractProxyFactory{ @Override public<T>TgetProxy(Invoker<T>invoker,Class<?>[]interfaces){ InvokerInvocationHandlerinvokerInvocationHandler=newInvokerInvocationHandler(invoker); return(T)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),interfaces,invokerInvocationHandler); } } 无论是生产者还是消费者的Invoker都实现自org.apache.dubbo.rpc.Invoker: publicabstractclassAbstractInvoker<T>implementsInvoker<T>{ } publicabstractclassAbstractProxyInvoker<T>implementsInvoker<T>{ } 3 装饰器模式 为什么生产者和消费者都要转换为Invoker而不是不直接调用呢?我认为Invoker正是Dubbo设计精彩之处:真实调用都转换为Invoker,Dubbo就可以通过装饰器模式增强Invoker功能。我们看看什么是装饰器模式。 装饰器模式可以动态将责任附加到对象上,在不改变原始类接口情况下,对原始类功能进行增强,并且支持多个装饰器的嵌套使用。实现装饰器模式需要以下组件: Component(抽象构件) 核心业务抽象:可以使用接口或者抽象类 ConcreteComponent(具体构件) 实现核心业务:最终执行的业务代码 Decorator(抽象装饰器) 抽象装饰器类:实现Component并且组合一个Component对象 ConcreteDecorator(具体装饰器) 具体装饰内容:装饰核心业务代码 我们分析一个装饰器实例。有一名足球运动员要去踢球,我们用球鞋和球袜为他装饰一下,这样可以使战力值增加。 (1) Component /** *抽象构件(可以用接口替代) */ publicabstractclassComponent{ /** *踢足球(业务核心方法) */ publicabstractvoidplayFootBall(); } (2) ConcreteComponent /** *具体构件 */ publicclassConcreteComponentextendsComponent{ @Override publicvoidplayFootBall(){ System.out.println("球员踢球"); } } (3) Decorator /** *抽象装饰器 */ publicabstractclassDecoratorextendsComponent{ privateComponentcomponent=null; publicDecorator(Componentcomponent){ this.component=component; } @Override publicvoidplayFootBall(){ this.component.playFootBall(); } } (4) ConcreteDecorator /** *球袜装饰器 */ publicclassConcreteDecoratorAextendsDecorator{ publicConcreteDecoratorA(Componentcomponent){ super(component); } /** *定义球袜装饰逻辑 */ privatevoiddecorateMethod(){ System.out.println("换上球袜战力值增加"); } /** *重写父类方法 */ @Override publicvoidplayFootBall(){ this.decorateMethod(); super.playFootBall(); } } /** *球鞋装饰器 */ publicclassConcreteDecoratorBextendsDecorator{ publicConcreteDecoratorB(Componentcomponent){ super(component); } /** *定义球鞋装饰逻辑 */ privatevoiddecorateMethod(){ System.out.println("换上球鞋战力值增加"); } /** *重写父类方法 */ @Override publicvoidplayFootBall(){ this.decorateMethod(); super.playFootBall(); } } (5) 测试代码 publicclassTestDecoratorDemo{ publicstaticvoidmain(String[]args){ Componentcomponent=newConcreteComponent(); component=newConcreteDecoratorA(component); component=newConcreteDecoratorB(component); component.playFootBall(); } } //换上球鞋战力值增加 //换上球袜战力值增加 //球员踢球 4 过滤器链路 Dubbo为Invoker增强了哪些功能?过滤器链是我认为增强的最重要的功能之一,我们继续分析源码: publicclassProtocolFilterWrapperimplementsProtocol{ @Override public<T>Exporter<T>export(Invoker<T>invoker)throwsRpcException{ if(Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())){ returnprotocol.export(invoker); } //增加过滤器链 Invoker<T>invokerChain=buildInvokerChain(invoker,Constants.SERVICE_FILTER_KEY,Constants.PROVIDER); returnprotocol.export(invokerChain); } @Override public<T>Invoker<T>refer(Class<T>type,URLurl)throwsRpcException{ if(Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())){ returnprotocol.refer(type,url); } //增加过滤器链 Invoker<T>invoker=protocol.refer(type,url); Invoker<T>result=buildInvokerChain(invoker,Constants.REFERENCE_FILTER_KEY,Constants.CONSUMER); returnresult; } } 无论是生产者还是消费者都会创建过滤器链,我们看看buildInvokerChain这个方法: publicclassProtocolFilterWrapperimplementsProtocol{ privatefinalProtocolprotocol; publicProtocolFilterWrapper(Protocolprotocol){ if(protocol==null){ thrownewIllegalArgumentException("protocol==null"); } this.protocol=protocol; } privatestatic<T>Invoker<T>buildInvokerChain(finalInvoker<T>invoker,Stringkey,Stringgroup){ 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(inti=filters.size()-1;i>=0;i--){ finalFilterfilter=filters.get(i); finalInvoker<T>next=last; //构造一个简化Invoker last=newInvoker<T>(){ @Override publicClass<T>getInterface(){ returninvoker.getInterface(); } @Override publicURLgetUrl(){ returninvoker.getUrl(); } @Override publicbooleanisAvailable(){ returninvoker.isAvailable(); } @Override publicResultinvoke(Invocationinvocation)throwsRpcException{ //构造过滤器链路 Resultresult=filter.invoke(next,invocation); if(resultinstanceofAsyncRpcResult){ AsyncRpcResultasyncResult=(AsyncRpcResult)result; asyncResult.thenApplyWithContext(r->filter.onResponse(r,invoker,invocation)); returnasyncResult; }else{ returnfilter.onResponse(result,invoker,invocation); } } @Override publicvoiddestroy(){ invoker.destroy(); } @Override publicStringtoString(){ returninvoker.toString(); } }; } } returnlast; } } 加载所有包含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) publicclassGenericImplFilterimplementsFilter{ @Override publicResultinvoke(Invoker<?>invoker,Invocationinvocation)throwsRpcException{ //方法名=$invoke //invocation.getArguments()=("sayHelloGeneric",newString[]{"com.java.front.dubbo.demo.provider.model.Person","java.lang.String"},newObject[]{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(Objectarg:args){ if(!(byte[].class==arg.getClass())){ error(generic,byte[].class.getName(),arg.getClass().getName()); } } }elseif(ProtocolUtils.isBeanGenericSerialization(generic)){ for(Objectarg:args){ if(!(arginstanceofJavaBeanDescriptor)){ error(generic,JavaBeanDescriptor.class.getName(),arg.getClass().getName()); } } } //附加参数generic值设置为true ((RpcInvocation)invocation).setAttachment(Constants.GENERIC_KEY,invoker.getUrl().getParameter(Constants.GENERIC_KEY)); } //继续执行过滤器链路 returninvoker.invoke(invocation); } } (2) GenericFilter @Activate(group=Constants.PROVIDER,order=-20000) publicclassGenericFilterimplementsFilter{ @Override publicResultinvoke(Invoker<?>invoker,Invocationinv)throwsRpcException{ //RpcInvocation[methodName=$invoke,parameterTypes=[classjava.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 Stringname=((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=[classcom.java.front.dubbo.demo.provider.model.Person,classjava.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}] RpcInvocationrpcInvocation=newRpcInvocation(method,args,inv.getAttachments()); Resultresult=invoker.invoke(rpcInvocation); } } } 6 文章总结 本文首先介绍了如何使用泛化调用,并引出泛化调用为什么生效这个问题。第二点介绍了重量级概念Invoker,并引出为什么Dubbo要创建Invoker这个问题。第三点介绍了装饰器模式如何增强功能。最后我们通过源码分析知道了过滤器链增强了Invoker功能并且是实现泛化调用的核心,希望本文对大家有所帮助。 欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习