7.2 服务本地暴露

Posted 赵计刚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了7.2 服务本地暴露相关的知识,希望对你有一定的参考价值。

为了安全:服务启动的ip全部使用10.10.10.10

服务暴露的流程其实就是下边这样(图片来自:http://www.iteye.com/topic/1123039

简单看一下服务暴露的伪代码:

 1 /**
 2  * dubbo 服务暴露伪代码
 3  */
 4 public class DubboProviderSimpleCode {
 5     private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();
 6     /**
 7      * 获取注册中心url列表
 8      * [ registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&pid=2956&registry=zookeeper&timestamp=1507004600231 ]
 9      */
10     List<URL> registryURLs = loadRegistries();//获取注册中心url列表
11     for (ProtocolConfig protocolConfig : protocols) {
12         /**
13          * 创建协议url
14          * dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2956&side=provider&timestamp=1507004625957
15          */
16         URL url = new URL(name, host, port, path, map);
17         /**
18          * 本地暴露
19          */
20         if (<dubbo:service scope!=remote>) {
21             /**
22              * 构造injvm协议的url
23              * injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2956&side=provider&timestamp=1507004625957
24              */
25             URL local = URL.valueOf(url.toFullString())
26                     .setProtocol(Constants.LOCAL_PROTOCOL)
27                     .setHost(NetUtils.LOCALHOST)
28                     .setPort(0);
29             Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);
30             Exporter<?> exporter = protocol.export(invoker);
31             exporters.add(exporter);
32         }
33         /**
34          * 远程暴露
35          */
36         if (<dubbo:service scope!=local>) {
37             for (URL registryURL : registryURLs) {
38                 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
39                 Exporter<?> exporter = protocol.export(invoker);
40                 exporters.add(exporter);
41             }
42         }
43     }
44 }

本地暴露:

  • 通过JavassistProxyFactory(默认)将具体的实现类包装成AbstractProxyInvoker实例
  • InjvmProtocol将上述的AbstractProxyInvoker实例转换成Exporter

远程暴露:

  • 通过JavassistProxyFactory(默认)将具体的实现类包装成AbstractProxyInvoker实例
  • DubboProtocol将上述的AbstractProxyInvoker实例转换成Exporter

 本节来看本地暴露。首先给出整个本地服务暴露的调用链

 1 ServiceBean.onApplicationEvent(ApplicationEvent event)
 2 -->ServiceConfig.export()
 3    -->doExport()
 4       -->doExportUrls()
 5          -->loadRegistries(boolean provider) //
 6          -->doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)
 7             -->Wrapper getWrapper(Class<?> c)
 8                -->makeWrapper(Class<?> c)
 9             -->new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map)
10             
11             <!--   本地暴露   -->
12             -->exportLocal(url)
13                -->构造injvm协议的url:injvmUrl
14                <!--  1 将ref包装成Invoker   -->
15                -->ProxyFactory$Adaptive.getInvoker(ref实例, interfaceClass, injvmUrl)
16                   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")
17                   -->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
18                      -->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
19                         -->Wrapper getWrapper(Class<?> c)
20                            -->makeWrapper(Class<?> c)
21                         -->new AbstractProxyInvoker<T>(ref实例, interfaceClass, injvmUrl)
22                <!--  2 将Invoker暴露为Exporter   -->
23                -->Protocol$Adaptive.export(Invoker<T> invoker)
24                   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension("injvm")
25                   -->ProtocolListenerWrapper.export(Invoker<T> invoker)
26                      -->ProtocolFilterWrapper.export(Invoker<T> invoker)
27                         -->buildInvokerChain(final Invoker<T> invoker, key:"service.filter", group:"provider")
28                            -->ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(injvmUrl, "service.filter", "provider")
29                         -->InjvmProtocol.export(Invoker<T> invoker)
30                            -->new InjvmExporter(Invoker<T> invoker, key:"com.alibaba.dubbo.demo.DemoService", Map<String, Exporter<?>> exporterMap)
31                               -->InjvmExporter.exporterMap({"com.alibaba.dubbo.demo.DemoService" -> "injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3207&side=provider&timestamp=1507009133930"})
32                   -->new ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) : 使用listener包装invoker
33                <!--  3 将Invoker暴露为Exporter   -->
34                -->ServiceConfig#exporters.add(exporter) 

本地暴露的代码如下:

 1     /**
 2      * 本地暴露
 3      * 1 根据传进来的url(例如dubbo协议的url)构造injvm协议的url
 4      * injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2999&side=provider&timestamp=1507005507343
 5      *
 6      * 2 将ref包装为AbstractProxyInvoker实例
 7      * 3 将AbstractProxyInvoker实例转换为InjvmExporter
 8      *
 9      * @param url dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2999&side=provider&timestamp=1507005507343
10      */
11     @SuppressWarnings({"unchecked", "rawtypes"})
12     private void exportLocal(URL url) {
13         if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
14             URL local = URL.valueOf(url.toFullString()).setProtocol(Constants.LOCAL_PROTOCOL).setHost(NetUtils.LOCALHOST).setPort(0);
15             Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);
16             Exporter<?> exporter = protocol.export(invoker);
17             exporters.add(exporter);
18             logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
19         }
20     }

为了清晰,这里对exportLocal(URL url)做了稍稍的改动。整体流程如下:

1 首先将dubbo协议的url,改成了injvm协议的url:local;

2 将具体服务类ref通过proxyFactory包装成AbstractProxyInvoker实例;

3 将AbstractProxyInvoker实例转化为Exporter实例;

4 最后将生成的Exporter实例存放在ServiceConfig的List<Exporter> exporters中。

 

一 具体服务包装成AbstractProxyInvoker实例

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);

具体服务:com.alibaba.dubbo.demo.provider.DemoServiceImpl。调用链如下:

1                <!--  1 将ref包装成Invoker   -->
2                -->ProxyFactory$Adaptive.getInvoker(ref实例, interfaceClass, injvmUrl)
3                   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")
4                   -->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
5                      -->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
6                         -->Wrapper getWrapper(Class<?> c)
7                            -->makeWrapper(Class<?> c)
8                         -->new AbstractProxyInvoker<T>(ref实例, interfaceClass, injvmUrl)

 

1 ProxyFactory$Adaptive.getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2)

 1     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 {
 2         if (arg2 == null)
 3             throw new IllegalArgumentException("url == null");
 4         com.alibaba.dubbo.common.URL url = arg2;
 5         String extName = url.getParameter("proxy", "javassist");
 6         if(extName == null)
 7             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
 8         com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
 9         return extension.getInvoker(arg0, arg1, arg2);
10     }
  • arg0: com.alibaba.dubbo.demo.provider.DemoServiceImpl 实例
  • arg1: interface com.alibaba.dubbo.demo.DemoService Class对象
  • arg2: injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3275&side=provider&timestamp=1507010529605

这里,首先是参数检查和赋值。之后获取key为javassist的ProxyFactory实现类:JavassistProxyFactory,该类会在spi进行aop的时候包裹在StubProxyFactoryWrapper中,最终调用链为:

ProxyFactory$Adaptive -> StubProxyFactoryWrapper -> JavassistProxyFactory

 

2 StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)

1     public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
2         return proxyFactory.getInvoker(proxy, type, url);
3     }

这里的proxyFactory就是JavassistProxyFactory实例了。

 

3 JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)

 1     public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
 2         // TODO Wrapper类不能正确处理带$的类名
 3         final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf(\'$\') < 0 ? proxy.getClass() : type);
 4         return new AbstractProxyInvoker<T>(proxy, type, url) {
 5             @Override
 6             protected Object doInvoke(T proxy, String methodName,
 7                                       Class<?>[] parameterTypes,
 8                                       Object[] arguments) throws Throwable {
 9                 return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
10             }
11         };
12     }

这里首先会创建一个class com.alibaba.dubbo.demo.provider.DemoServiceImpl的包装类Wrapper:Wrapper.getWrapper(Class<DemoServiceImpl>)。该类记录了DemoServiceImpl的属性名称,方法名称等信息。具体代码如下:

 1 package com.alibaba.dubbo.demo;
 2 
 3 import com.alibaba.dubbo.common.bytecode.Wrapper;
 4 import java.util.HashMap;
 5 
 6 public class Wrapper1 extends Wrapper {
 7 
 8     public static String[] pns;//property name array
 9     public static java.util.Map pts = new HashMap();//<property key, property value>
10     public static String[] mns;//method names
11     public static String[] dmns;//
12     public static Class[] mts0;
13 
14     public Wrapper1() {
15     }
16 
17     public String[] getPropertyNames() {
18         return pns;
19     }
20 
21     public boolean hasProperty(String n) {
22         return pts.containsKey(n);
23     }
24 
25     public Class getPropertyType(String n) {
26         return (Class) pts.get(n);
27     }
28 
29     public String[] getMethodNames() {
30         return mns;
31     }
32 
33     public String[] getDeclaredMethodNames() {
34         return dmns;
35     }
36 
37     public void setPropertyValue(Object o, String n, Object v) {
38         com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
39         try {
40             w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl) o);
41         } catch (Throwable e) {
42             throw new IllegalArgumentException(e);
43         }
44         throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \\"" + n + "\\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
45     }
46 
47     public Object getPropertyValue(Object o, String n) {
48         com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
49         try {
50             w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl) o);
51         } catch (Throwable e) {
52             throw new IllegalArgumentException(e);
53         }
54         throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \\"" + n + "\\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
55     }
56 
57     /**
58      * @param o  实现类
59      * @param n  方法名称
60      * @param p  参数类型
61      * @param v  参数名称
62      * @return
63      * @throws java.lang.reflect.InvocationTargetException
64      */
65     public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
66         com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
67         try {
68             w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl) o);
69         } catch (Throwable e) {
70             throw new IllegalArgumentException(e);
71         }
72         try {
73             if ("sayHello".equals(n) && p.length == 1) {
74                 return ($w) w.sayHello((java.lang.String) v[0]);
75             }
76         } catch (Throwable e) {
77             throw new java.lang.reflect.InvocationTargetException(e);
78         }
79         throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \\"" + n + "\\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
80     }
81 }

之后创建一个AbstractProxyInvoker实例。

 1 package com.alibaba.dubbo.rpc.proxy;
 2 
 3 import com.alibaba.dubbo.common.URL;
 4 import com.alibaba.dubbo.rpc.Invocation;
 5 import com.alibaba.dubbo.rpc.Invoker;
 6 import com.alibaba.dubbo.rpc.Result;
 7 import com.alibaba.dubbo.rpc.RpcException;
 8 import com.alibaba.dubbo.rpc.RpcResult;
 9 
10 import java.lang.reflect.InvocationTargetException;
11 
12 public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
13     private final T proxy;
14     private final Class<T> type;
15     private final URL url;
16 
17     public AbstractProxyInvoker(T proxy, Class<T> type, URL url) {
18         if (proxy == null) {
19             throw new IllegalArgumentException("proxy == null");
20         }
21         if (type == null) {
22             throw new IllegalArgumentException("interface == null");
23         }
24         if (!type.isInstance(proxy)) {
25             throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
26         }
27         this.proxy = proxy;
28         this.type = type;
29         this.url = url;
30     }
31 
32     public Class<T> getInterface() {
33         return type;
34     }
35 
36     public URL getUrl() {
37         return url;
38     }
39 
40     public boolean isAvailable() {
41         return true;
42     }
43 
44     public void destroy() {
45     }
46 
47     public Result invoke(Invocation invocation) throws RpcException {
48         try {
49             return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
50         } catch (InvocationTargetException e) {
51             return new RpcResult(e.getTargetException());
52         } catch (Throwable e) {
53             throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
54         }
55     }
56 
57     /**
58      * 由创建的实例来复写
59      * @param proxy
60      * @param methodName
61      * @param parameterTypes
62      * @param arguments
63      * @return
64      * @throws Throwable
65      */
66     protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
67 
68     @Override
69     public String toString() {
70         return getInterface() + " -> " + getUrl() == null ? " " : getUrl().toString();
71     }
72 }

其中:

  • proxy: com.alibaba.dubbo.demo.provider.DemoServiceImpl 实例
  • type: interface com.alibaba.dubbo.demo.DemoService Class对象
  • url: injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3275&side=provider&timestamp=1507010529605

这样,具体服务就被包装成AbstractProxyInvoker实例了。

 

二 AbstractProxyInvoker实例转换为Exporter

Exporter<?> exporter = protocol.export(invoker);

调用链如下:

 1                <!--  2 将Invoker暴露为Exporter   -->
 2                -->Protocol$Adaptive.export(Invoker<T> invoker)
 3                   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension("injvm")
 4                   -->ProtocolListenerWrapper.export(Invoker<T> invoker)
 5                      -->ProtocolFilterWrapper.export(Invoker<T> invoker)
 6                         -->buildInvokerChain(final Invoker<T> invoker, key:"service.filter", group:"provider")
 7                            -->ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(injvmUrl, "service.filter", "provider")
 8                         -->InjvmProtocol.export(Invoker<T> invoker)
 9                            -->new InjvmExporter(Invoker<T> invoker, key:"com.alibaba.dubbo.demo.DemoService", Map<String, Exporter<?>> exporterMap)
10                               -->InjvmExporter.exporterMap({"com.alibaba.dubbo.demo.DemoService" -> "injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3207&side=provider&timestamp=1507009133930"})
11                   -->new ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) : 使用listener包装invoker

 

1 Protocol$Adaptive.export(Invoker<T> invoker)

 1     public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
 2         if (arg0 == null)
 3             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
 4         if (arg0.getUrl() == null)
 5             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
 6         com.alibaba.dubbo.common.URL url = arg0.getUrl();
 7         String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
 8         if(extName == null)
 9             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
10         com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
11         return extension.export(arg0);
12     }
  • arg0:上边创建出来的AbstractProxyInvoker实例。

这里,首先是参数检查和赋值。之后获取key为injvm的Protocol实现类:InjvmProtocol,该类会在spi进行aop的时候被ProtocolFilterWrapper和ProtocolListenerWrapper递归包裹,最终调用链为:

ProxyFactory$Adaptive -> ProtocolListenerWrapper -> ProtocolFilterWrapper -> InjvmProtocol

 

2 ProtocolListenerWrapper.export(Invoker<T> invoker)

1     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
2         if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
3             return protocol.export(invoker);
4         }
5         return new ListenerExporterWrapper<T>(protocol.export(invoker),
6                                               Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
7     }

这里先调用ProtocolFilterWrapper.export(Invoker<T> invoker),之后获取listener,最后进行递归包裹。(这里没有listener)

 

3 ProtocolFilterWrapper.export(Invoker<T> invoker)

1     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
2         if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
3             return protocol.export(invoker);
4         }
5         return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
6     }

这里首先使用filter对invoker进行了递归包裹,之后使用InjvmProtocol将包裹后的invoker转化为InjvmExporter。

buildInvokerChain(final Invoker<T> invoker, String key, String group)

 1     /**
 2      * 1 根据key从url中获取相应的filter的values,再根据这个values和group去获取类上带有@Active注解的filter集合
 3      * 2 之后将这些filter对传入的invoker进行递归包装层invoker(就是一个链表)
 4      */
 5     private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
 6         Invoker<T> last = invoker;
 7         List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
 8         if (filters.size() > 0) {
 9             for (int i = filters.size() - 1; i >= 0; i--) {
10                 final Filter filter = filters.get(i);
11                 final Invoker<T> next = last;
12                 last = dubbo专题-服务暴露总结(本地暴露+远程暴露时序图)

服务暴露

Dubbo服务暴露分析

Dubbo如何支持本地调用?InJvm方式解析

FOXMAIL 7.2 本地邮件恢复

暴露多个本地运行的 docker 容器