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®istry=zookeeper×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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专题-服务暴露总结(本地暴露+远程暴露时序图)