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
```