🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
provider端当`scope`不为`remote`时,都需要将服务发布到本地,发布到本地时使用的时injvm协议,是指服务器发布方和引用方的程序在同一个jvm中执行。 而consumer端中,从2.2.0开始,每个服务默认都会在本地暴露。在引用服务的时候,默认优先引用本地服务。如果希望引用远程服务可以使用一下配置强制引用远程服务。 ``` <dubbo:reference ... scope="remote" /> ``` ## 流程 injvm协议的过程是dubbo中最简单的协议,但除了没有注册中心外,还是可以对其基本流程进行全面的了解。 ![injvm流程](http://www.uxiaowo.com/dubbo/Injvm.png) ## 1.发布到injvm协议 发布到invjm协议时,会先修改url的协议,host和port ``` URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(LOCALHOST) .setPort(0); ``` 然后,ExtensionLoader动态获取Protocol接口时,使用的是InjvmProtocol,会被包装为 ``` |- ProtocolFilterWrapper |- ProtocolListenerWrapper |- InjvmProtocol ``` ProtocolFilterWrapper:主要用来生成调用链,内部的buildInvokerChain方法会查找Filter的实现类,查找group为provider的,并根据order排序,将这些Filter连接成一个调用链 InvokerChain,最终调用已经生成的Invoker ``` EchoFilter -> ClassloaderFilter -> GenericFilter -> ContextFilter -> TraceFilter -> TimeoutFilter -> MonitorFilter -> ExceptionFilter -> AbstractProxyInvoker类 ``` ProtocolListenerWrapper:主要用来添加监听事件。首先调用InjvmProtocol的export,直接返回InjvmExporter;之后查找ExporterListener的实现类,最后将这两者封装为ListenerExporterWrapper并返回。 InjvmProtocol中定义了一个exporterMap,内部保存了服务名与Exporter的对应关系 到此为止,就已经发布到本地了。 ## 2. 引用 如果未强制指定远程服务,会首先从本地查找服务,查找的入口是Spring的context的getBean方法获取实例,其会通过两个步骤获取指定接口的实例:创建Invoker和获取proxy代理对象。 ``` Invoker<DemoService> consuerInvoker = protocol.refer(DemoService.class, url); DemoService service = proxyFactory.getProxy(consuerInvoker); ``` 在引用服务流程中,我们已经分析了获取Proxy对象的过程,每个协议获取Invoker的方式不同,对于injvm协议来说,protocol被包装为: ``` |- ProtocolFilterWrapper |- ProtocolListenerWrapper |- InjvmProtocol ``` ProtocolFilterWrapper和ProtocolListenerWrapper的功能还是构建Filter链和添加监听事件,不同的是,对ProtocolFilterWrapper来说,consumer使用的refer方法,引用`group = Constants.CONSUMER`,而provider端使用的是export,使用了`group = Constants.PROVIDER`,这些都是在实现类的注解中进行配置。 对ProtocolListenerWrapper来说,provider使用的`exporter.listener`,而consumer使用的`invoker.listener` InjvmProtocol的refer中会创建一个InjvmInvoker并返回 ``` public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException { return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap); } ``` InjvmInvoker内部记录了Class类型,url,服务名和exporterMap(保存了服务名与exporter对象的映射) ``` public Result doInvoke(Invocation invocation) throws Throwable { Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl()); if (exporter == null) { throw new RpcException("Service [" + key + "] not found."); } RpcContext.getContext().setRemoteAddress(NetUtils.LOCALHOST, 0); return exporter.getInvoker().invoke(invocation); } ``` ## 测试程序 了解了injvm协议的发布和引用过程后,我们可以对这个过程进行测试,在测试中,我们加入一对Listener和一对Filter。 PExporterListener实现了ExporterListener,在provider发布服务时使用 ``` public class PExporterListener implements ExporterListener { public void exported(Exporter<?> exporter) throws RpcException { System.out.println("[PExporterListener][exported]:" + exporter.getInvoker().getUrl()); } public void unexported(Exporter<?> exporter) { System.out.println("[PExporterListener][unexported]" + exporter.getInvoker().getUrl()); } } ``` CExporterListener实现了InvokerListener,在consumer调用时使用 ``` public class CExporterListener implements InvokerListener { public void referred(Invoker<?> invoker) throws RpcException { System.out.println("[CExporterListener][referred]:" + invoker.getUrl()); } public void destroyed(Invoker<?> invoker) { System.out.println("[CExporterListener][destroyed]:" + invoker.getUrl()); } } ``` PFilter实现了Filter接口,注解@Activate指定了在provider端使用 ``` @Activate(group = Constants.PROVIDER, order = -120000) public class PFilter implements Filter{ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { System.out.println("[PFilter][invoke]" + invocation.getMethodName()); return invoker.invoke(invocation); } } ``` CFilter实现了Filter接口,注解@Activate指定了在consumer端使用 ``` @Activate(group = Constants.CONSUMER, order = -120000) public class CFilter implements Filter{ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { System.out.println("[CFilter][invoke]" + invocation.getMethodName()); return invoker.invoke(invocation); } } ``` 下面是测试程序: ``` public static void main(String[] args) { ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getDefaultExtension(); Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); // 创建url URL url = new URL("injvm","127.0.0.1",0); url= url.setPath(DemoService.class.getName()); url = url.addParameter(Constants.EXPORTER_LISTENER_KEY, "provider.listener"); url = url.addParameter(Constants.INVOKER_LISTENER_KEY, "consumer.listener"); // 发布服务1.创建export invoker Invoker<DemoService> invoker = proxyFactory.getInvoker(new DemoServiceImpl(),DemoService.class,url); // 发布服务2.发布到injvm协议 Exporter<DemoService> exporter = protocol.export(invoker); // 可以取消发布,会抛出异常 // exporter.unexport();//Service [com.alibaba.dubbo.demo.DemoService] not found. // 引用服务1.创建ref Invoker Invoker<DemoService> consuerInvoker = protocol.refer(DemoService.class, url); // 引用服务2.创建代理对象 DemoService service = proxyFactory.getProxy(consuerInvoker); System.out.println(service.sayHello("world")); // 执行调用 } ``` 输出为: ``` [04/12/17 09:44:15:015 CST] main INFO logger.LoggerFactory: using logger: com.alibaba.dubbo.common.logger.log4j.Log4jLoggerAdapter [PExporterListener][exported]:injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?exporter.listener=provider.listener&invoker.listener=consumer.listener [CExporterListener][referred]:injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?exporter.listener=provider.listener&invoker.listener=consumer.listener [CFilter][invoke]sayHello [PFilter][invoke]sayHello Hello world ```