Dubbo 服务暴露之服务本地暴露
服务暴露的流程如下图所示:
简单看一下服务暴露的伪代码:
/** dubbo 服务暴露伪代码 */
public class DubboProviderServiceExportSimpleCode {
private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();
// 获取注册中心url列表
// [ registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&cellinvokemode=sharing&client=zkclient&dubbo=2.0.0&group=dubbo_test&pid=74378®istry=zookeeper×tamp=1550318759960 ]
List<URL> registryURLs = loadRegistries(); // 获取注册中心url列表
for (ProtocolConfig protocolConfig : protocols) {
// 创建dubbo协议的url
// dubbo://172.16.132.166:20881/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider×tamp=1550318831051
URL url = new URL(name, host, port, path, map);
// 本地暴露
if (<dubbo:service scope!=remote>) {
// 构造injvm协议的url
// injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider×tamp=1550318831051
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(NetUtils.LOCALHOST)
.setPort(0);
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
// 远程暴露
if (<dubbo:service scope!=local>) {
for (URL registryURL : registryURLs) {
// registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())--
// registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&cellinvokemode=sharing&client=zkclient&dubbo=2.0.0&export=dubbo%3A%2F%2F172.16.132.166%3A20881%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D172.16.132.166%26bind.port%3D20881%26cellinvokemode%3Dsharing%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D74378%26side%3Dprovider%26timestamp%3D1550318831051&group=dubbo_test&pid=74378®istry=zookeeper×tamp=1550318759960
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
}
}
}
本地暴露:
- 通过
JavassistProxyFactory
(默认),将具体的实现类包装成AbstractProxyInvoker
实例 InjvmProtocol
将上述的AbstractProxyInvoker
实例转换成Exporter
(导出流程:ProtocolListenerWrapper-->ProtocolFilterWrapper-->InjvmProtocol
)
远程暴露:
- 通过
JavassistProxyFactory
(默认),将具体的实现类包装成AbstractProxyInvoker
实例 DubboProtocol
将上述的AbstractProxyInvoker
实例转换成Exporter
(导出流程:ProtocolListenerWrapper-->ProtocolFilterWrapper-->RegistryProtocol-->DubboProtocol
)
本文先来看本地暴露。首先给出本地服务暴露的整个调用链。
ServiceBean.onApplicationEvent(ContextRefreshedEvent event)
-->ServiceConfig.export()
-->doExport()
-->doExportUrls()
-->loadRegistries(boolean provider) // 生成注册中心URL
-->doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)
-->Wrapper.getWrapper(Class<?> c)
-->makeWrapper(Class<?> c)
-->new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map)
<!-- 本地暴露 -->
-->exportLocal(url)
-->构造injvm协议的url: injvmUrl
<!-- 1 将ref包装成Invoker -->
-->ProxyFactory$Adaptive.getInvoker(ref实例, interfaceClass, injvmUrl)
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")
-->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
-->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
-->Wrapper getWrapper(Class<?> c)
-->makeWrapper(Class<?> c)
-->new AbstractProxyInvoker<T>(ref实例, interfaceClass, injvmUrl)
<!-- 2 将Invoker暴露为Exporter -->
-->Protocol$Adaptive.export(Invoker<T> invoker)
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension("injvm")
-->ProtocolListenerWrapper.export(Invoker<T> invoker)
-->ProtocolFilterWrapper.export(Invoker<T> invoker)
-->buildInvokerChain(final Invoker<T> invoker, key:"service.filter", group:"provider")
-->ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(injvmUrl, "service.filter", "provider")
-->InjvmProtocol.export(Invoker<T> invoker)
-->new InjvmExporter(Invoker<T> invoker, key:"com.alibaba.dubbo.demo.DemoService", Map<String, Exporter<?>> exporterMap)
-->InjvmExporter.exporterMap({"com.alibaba.dubbo.demo.DemoService" -> "injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider×tamp=1550318831051"})
-->new ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners)
<!-- Exporter添加到List<Exporter<?>> exporters -->
-->exporters.add(exporter)
本地暴露的方法及代码如下:
// 本地暴露
// 1 根据传进来的url(例如dubbo协议的url)构造injvm协议的url
// injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider×tamp=1550318831051
// 2 将ref实现类实例包装为AbstractProxyInvoker实例
// 3 将AbstractProxyInvoker实例转换为ListenerExporterWrapper实例,包装InjvmExporter实例
@SuppressWarnings({"unchecked", "rawtypes"})
private void exportLocal(URL url) {
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
// local url: injvm://ip....
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(LOCALHOST)
.setPort(0);
// Protocol$Adaptive.export()代码片段:
// com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
// 包装: ProtocolListenerWrapper-->ProtocolFilterWrapper-->InjvmProtocol
// exporter变量为ListenerExporterWrapper类型,包装InjvmExporter实例
Exporter<?> exporter = protocol.export(
// proxyFactory为Adaptive类实例,proxyFactory.getInvoker()的调用: StubProxyFactoryWrapper --> JavassistProxyFactory
// 返回AbstractProxyInvoker实现类实例
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
}
}
本地暴露的整体流程如下:
-
首先将
dubbo
协议的url
,改成injvm
协议的url
:local
; -
将服务实现类实例
ref
通过proxyFactory
包装成AbstractProxyInvoker
实例; -
将
AbstractProxyInvoker
实例转化为Exporter
实例; -
最后将生成的
Exporter
实例存放在ServiceConfig
的List<Exporter> exporters
中。
一、具体的服务实现类实例包装成AbstractProxyInvoker实例
proxyFactory.getInvoker(ref, (Class) interfaceClass, local)
具体服务实现类:com.alibaba.dubbo.demo.provider.DemoServiceImpl2
。调用链如下:
<!-- 1 将ref包装成Invoker -->
-->ProxyFactory$Adaptive.getInvoker(ref实例, interfaceClass, injvmUrl)
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")
-->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
-->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
-->Wrapper getWrapper(Class<?> c)
-->makeWrapper(Class<?> c)
-->new AbstractProxyInvoker<T>(ref实例, interfaceClass, injvmUrl)
1.ProxyFactory$Adaptive.getInvoker(Object arg0, Class arg1, URL arg2)
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
if (arg2 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
- arg0:
com.alibaba.dubbo.demo.provider.DemoServiceImpl2
实例 - arg1:
interface com.alibaba.dubbo.demo.DemoService
Class
对象 - arg2:
injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider×tamp=1550318831051
这里首先是参数检查和赋值。之后获取key为javassist
的ProxyFactory
实现类:JavassistProxyFactory
,该类会在spi进行aop的时候包裹在StubProxyFactoryWrapper
中,最终调用链为:
ProxyFactory$Adaptive -> StubProxyFactoryWrapper -> JavassistProxyFactory
2.StubProxyFactoryWrapper.getInvoker(T proxy, Class<T>
type, URL url)
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
// 这个类先执行,后执行JavassistProxyFactory.getInvoker() -- 包装
return proxyFactory.getInvoker(proxy, type, url);
}
这里的proxyFactory
就是JavassistProxyFactory
实例。
3.JavassistProxyFactory.getInvoker(T proxy, Class<T>
type, URL url)
// 将具体服务转为Invoker
// @param proxy 对外提供服务的实现类实例
// @param type 服务接口
// @param url URL
// @param <T> 服务类型
// @return Invoker
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper类不能正确处理带$的类名
// 包装类
final Wrapper wrapper = Wrapper
.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
这里首先会创建一个class com.alibaba.dubbo.demo.provider.DemoServiceImpl2
的包装类Wrapper:Wrapper.getWrapper(Class<DemoServiceImpl2>)
。该类记录了DemoServiceImpl2
的属性名称,方法名称等信息。具体代码如下:
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.Wrapper;
import java.util.Map;
// 这里类名为Wrapper1是因为之前ServiceConfig.doExportUrlsFor1Protocol()方法中
// 已使用过Wrapper进行包装
public class Wrapper1 extends Wrapper {
public static String[] pns; // property name array
public static java.util.Map pts = new HashMap();// <property name, property types>
public static String[] mns;// all method name array.
public static String[] dmns;// declared method name array.
public static Class[] mts0; // 方法参数类型数组
// 默认构造器
public Wrapper1() {
}
public String[] getPropertyNames() {
return pns;
}
public boolean hasProperty(String n) {
return pts.containsKey(n);
}
public Class getPropertyType(String n) {
return (Class) pts.get(n);
}
public String[] getMethodNames() {
return mns;
}
public String[] getDeclaredMethodNames() {
return dmns;
}
public void setPropertyValue(Object o, String n, Object v) {
com.alibaba.dubbo.demo.provider.DemoServiceImpl2 w;
try {
w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl2) o);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl2.");
}
public Object getPropertyValue(Object o, String n) {
com.alibaba.dubbo.demo.provider.DemoServiceImpl2 w;
try {
w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl2) o);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl2.");
}
// 调用方法
// @param o 实现类
// @param n 方法名称
// @param p 参数类型
// @param v 参数值
// @return 方法调用结果
// @throws java.lang.reflect.InvocationTargetException
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
com.alibaba.dubbo.demo.provider.DemoServiceImpl2 w;
try {
w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl2) o);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
try {
if ("sayHello".equals(n) && p.length == 1) {
return ($w) w.sayHello((java.lang.String) v[0]);
}
} catch (Throwable e) {
throw new java.lang.reflect.InvocationTargetException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl2.");
}
}
之后创建一个AbstractProxyInvoker
实例。AbstractProxyInvoker
类定义如下:
public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
// 服务接口的实现类实例
private final T proxy;
// 服务接口类
private final Class<T> type;
// 注册中心url 或者 提供者url(injvm协议)
private final URL url;
public AbstractProxyInvoker(T proxy, Class<T> type, URL url) {
if (proxy == null) {
throw new IllegalArgumentException("proxy == null");
}
if (type == null) {
throw new IllegalArgumentException("interface == null");
}
if (!type.isInstance(proxy)) {
throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
}
this.proxy = proxy;
this.type = type;
this.url = url;
}
@Override
public Class<T> getInterface() {
return type;
}
@Override
public URL getUrl() {
return url;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void destroy() {
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
try {
return new RpcResult(doInvoke(proxy, invocation.getMethodName(),
invocation.getParameterTypes(), invocation.getArguments()));
} catch (InvocationTargetException e) {
return new RpcResult(e.getTargetException());
} catch (Throwable e) {
throw new RpcException("Failed to invoke remote proxy method "
+ invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
// 方法实际调用
// @param proxy 对外提供服务的实现类实例
// @param methodName 方法名
// @param parameterTypes 参数类型数组
// @param arguments 具体参数数组
// @return 调用结果
protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
@Override
public String toString() {
return getInterface() + " -> " + (getUrl() == null ? " " : getUrl().toString());
}
}
其中:
- proxy:
com.alibaba.dubbo.demo.provider.DemoServiceImpl2
实例 - type: ` com.alibaba.dubbo.demo.DemoService Class`对象
- url:
injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider×tamp=1550318831051
这样,具体服务就被包装成AbstractProxyInvoker
实例了。
二、AbstractProxyInvoker实例转换为Exporter实例
Exporter<?> exporter = protocol.export(AbstractProxyInvoker实例);
调用链如下:
<!-- 2 将Invoker暴露为Exporter -->
-->Protocol$Adaptive.export(Invoker<T> invoker)
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension("injvm")
-->ProtocolListenerWrapper.export(Invoker<T> invoker)
-->ProtocolFilterWrapper.export(Invoker<T> invoker)
-->buildInvokerChain(final Invoker<T> invoker, key:"service.filter", group:"provider")
-->ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(injvmUrl, "service.filter", "provider")
-->InjvmProtocol.export(Invoker<T> invoker)
-->new InjvmExporter(Invoker<T> invoker, key:"com.alibaba.dubbo.demo.DemoService", Map<String, Exporter<?>> exporterMap)
-->InjvmExporter.exporterMap({"com.alibaba.dubbo.demo.DemoService" -> "injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider×tamp=1550318831051"})
-->new ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners)
<!-- Exporter添加到List<Exporter<?>> exporters -->
-->exporters.add(exporter)
1.Protocol$Adaptive.export(Invoker<T>
invoker)
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
- arg0: 上边创建出来的
AbstractProxyInvoker
实例。
这里首先是参数检查和赋值。之后获取key为injvm
的Protocol
实现类:InjvmProtocol
,该类会在spi进行aop的时候被ProtocolFilterWrapper
和ProtocolListenerWrapper
递归包裹,最终调用链为:
Protocol$Adaptive -> ProtocolListenerWrapper -> ProtocolFilterWrapper -> InjvmProtocol
2.ProtocolListenerWrapper.export(Invoker<T>
invoker)
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
// 注册中心协议
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
// 协议为registry,通过ProtocolFilterWrapper继续导出服务
return protocol.export(invoker);
}
// 1.调用ProtocolFilterWrapper.export()方法导出服务,返回InjvmExporter实例
// 2.找到所有激活的ExporterListener (可自定义导出监听器)(这里没有listener)
// 3.回调所有监听器的void exported(Exporter<?> exporter)方法
// 4.返回包装InjvmExporter实例的ListenerExporterWrapper实例给Protocol$Adaptive
return new ListenerExporterWrapper<T>(protocol.export(invoker), Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
这里首先调用ProtocolFilterWrapper.export()
方法导出服务,返回InjvmExporter
实例;找到所有激活的ExporterListener
(可自定义导出监听器);回调所有监听器的void exported(Exporter<?> exporter)
方法;最后返回包装InjvmExporter
实例的ListenerExporterWrapper
实例给Protocol$Adaptive
。
3.ProtocolFilterWrapper.export(Invoker<T>
invoker)
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
// 若协议为registry,直接通过RegistryProtocol注册并暴露服务
return protocol.export(invoker);
}
// 构建invoker的拦截器,调用InjvmProtocol.export(Invoker<T> invoker)方法
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY,
Constants.PROVIDER));
}
这里首先使用Filter
对invoker
进行了递归包裹,之后使用InjvmProtocol
将包裹后的invoker
转化为InjvmExporter
。
buildInvokerChain(final Invoker
invoker, String key, String group)
// 构建服务调用拦截链。
1.根据key从url中获取相应的filter的values,再根据这个values和group去获取类上带有@Active注解的filter集合
2.之后将这些filter对传入的invoker进行递归包装成invoker(就是一个链表)
// @param invoker AbstractProxyInvoker实例
// @param key service.filter
// @param group provider
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker; // 最后调用的invoker,原invoker最后执行
// 拦截器列表
// 根据order从小到大排序
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class)
.getActivateExtension(invoker.getUrl(), key, group);
if (filters.size() > 0) {
// 构建调用链,从filter(0)开始执行到filter(size()-1),最后到原invoker
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 {
// 进行调用拦截
return filter.invoke(next, invocation);
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
这里:
- invoker:之前创建出来的
AbstractProxyInvoker
实例; - key:
service.filter
- group:
provider
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
这句代码是根据key
从url
中获取相应的Filter
的values
,再根据这个values
和group
去获取类上带有@Active
注解的满足要求的Filter
集合。这里具体的代码逻辑可以查看Dubbo SPI @Activate注解分析这篇文章。最终会获取到8个Filter
,关于Filter
,后续阐述。
4.InjvmProtocol.export(Invoker<T>
invoker)
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
// 返回InjvmExporter实例. 调用过程为: ProtocolListenerWrapper-->ProtocolFilterWrapper-->InjvmProtocol
return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
class InjvmExporter<T> extends AbstractExporter<T> {
// 服务配置的键
// 服务关键字([group/]interface[:version])
private final String key;
// 服务关键字([group/]interface[:version]) ---> InjvmExporter实例
private final Map<String, Exporter<?>> exporterMap;
// 创建InjvmExporter实例
// @param invoker 被Filter进行递归包裹后的Invoker
// @param key 服务关键字
// @param exporterMap 服务关键字([group/]interface[:version]) ---> InjvmExporter实例
InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
super(invoker);
this.key = key;
this.exporterMap = exporterMap;
exporterMap.put(key, this);
}
@Override
public void unexport() {
super.unexport();
exporterMap.remove(key);
}
}
最终的InjvmExporter
实例:
key = "com.alibaba.dubbo.demo.DemoService"
exporterMap: { "com.alibaba.dubbo.demo.DemoService" -> 当前的InjvmExporter实例 }
Invoker<T> invoker = 被Filter进行递归包裹后的Invoker
最终的ServiceConfig
的exporters
列表:
List<Exporter<?>> exporters = [ ListenerExporterWrapper实例(包装InjvmExporter实例) ]
三、需要本地暴露的原因
同一个jvm进程中的服务提供者和消费者,消费者调用提供者不需要通过远程注册中心,但是又想使用Filter
链,可以使用本地暴露。
http://dubbo.incubator.apache.org/zh-cn/docs/user/demos/local-call.html:
“本地调用使用了 injvm 协议,是一个伪协议,它不开启端口,不发起远程调用,只在 JVM 内直接关联,但执行 Dubbo 的 Filter 链。”