Dubbo/Dubbox的服务消费(一)- 服务代理的创建
来源:互联网 发布:java自带的观察者模式 编辑:程序博客网 时间:2024/05/20 05:57
dubbo的consumer的初始过程
一个常见的consumer配置是这样的
<dubbo:reference id="dubboDemo" interface="com.company.dsp.adcenter.protocol.dubbo.DubboDemo" protocol="dubbo"/>
这样spring会创建一个beanName为dubboDemo的bean,使用上是这样的
@Resource(name = "dubboDemo")private DubboDemo dubboDemo;
那么问题来了why why why? dubbo是怎么创建这样的bean的?全程都没有要求我们使用bean标签,也没有要求使用Componment和Service注解,即使使用显示的方式声明一个Bean,那我们也没有相关的实现类啊。
既然我们已经拥有上面的配置项,拍脑袋一想,已知dubbo启动时会利用spring对自定义配置项的spring.handles文件加载对应的handler解析自定义节点,且上文书已经分析
dubbo:reference节点的配置项存储在com.alibaba.dubbo.config.spring.ReferenceBean
中,该类的uml图如下。
可见该类集成自com.alibaba.dubbo.config.ReferenceConfig
类,并实现了下面四个接口
org.springframework.beans.factory.FactoryBeanorg.springframework.context.ApplicationContextAwareorg.springframework.beans.factory.InitializingBeanorg.springframework.beans.factory.DisposableBean
InitializingBean和ApplicationContextAware我们都很熟悉了,还没详细了解过FactoryBean,不过这里有一个道友的文章可以看看《FactoryBean的实现原理与作用》
其中提到了以下两句
OK,那么这个时候我们getBean(“personFactory”)得到的就是Person对象而不是PersonFactoryBean对象。具体原理参考上面在IOC的应用,我们通过bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)这个方法,具体调用到了getObject方法,所以结果很明显。
通过上面的小案例的代码,我们可以看到如果一个类实现了FactoryBean接口,那么getBean得到的不是他本身了,而是它所产生的对象,如果我们希望得到它本身,只需要加上&符号即可。至于FactoryBean的实际应用,需要大家去发现理解,后面如果有机会会继续聊聊这个东西。
那把目光转向:com.alibaba.dubbo.config.spring.ReferenceBean
,开启找寻getObject方法之旅
/** * ReferenceFactoryBean * * @author william.liangf * @export */ public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean { //...... public Object getObject() throws Exception { return get(); } //...... }
其中get()方法 继承自com.alibaba.dubbo.config.ReferenceConfig
public synchronized T get() { if (destroyed){ throw new IllegalStateException("Already destroyed!"); } if (ref == null) { init(); } return ref; }
可见get方法内部调用了一个init()方法
private void init() { if (initialized) { return; } initialized = true; //初始化标记,防止重复创建对象 if (interfaceName == null || interfaceName.length() == 0) { throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!"); } // 获取消费者全局配置,检查consumer是否有配置,如果该属性为null,则new一个ConsumerConfig对象 // 方法内部调用appendProperties(consumer)方法,该方法内部会拼装一个 dubbo.tagName.属性名的key,在配置文件中查找值,如果有值则调用属性的setter方法,设置属性值。 checkDefault(); // 上边注释已经说明appendProperties方法用途 appendProperties(this); if (getGeneric() == null && getConsumer() != null) { //判断是不是泛化调用 setGeneric(getConsumer().getGeneric()); } if (ProtocolUtils.isGeneric(getGeneric())) { interfaceClass = GenericService.class; } else { try { interfaceClass = Class.forName(interfaceName, true, Thread.currentThread() .getContextClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } //继承自com.alibaba.dubbo.config.AbstractInterfaceConfig //检查接口类中是否存在指定的方法,如果dubbo:service->dubbo:method 没有配置的情况下,methods为null,该方法不会执行方法校验。 //如果有相关的配置,该方法会检查name属性对应的方法是否存在,不存在会抛IllegalStateException异常。 checkInterfaceAndMethods(interfaceClass, methods); } //用-Ddubbo.resolve.file指定映射文件路径,此配置优先级高于<dubbo:reference>中的配置,1.0.15及以上版本支持 //......省略-Ddubbo.resolve.file的解析代码行......// //........省略进一步初始化相关配置的代码行 .......// //检查应用配置,如果没有配置会创建默认对象。其内部会调用appendProperties(AbstractConfig config) 填充属性。 //检查失败会抛出IllegalStateException异常 checkApplication(); // checkStubAndMock(interfaceClass); //看到Map就应当想到dubbo又要初始化一个URL对象了 Map<String, String> map = new HashMap<String, String>(); Map<Object, Object> attributes = new HashMap<Object, Object>(); map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE); map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion()); map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis())); if (ConfigUtils.getPid() > 0) { map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid())); } if (! isGeneric()) { //非泛化调用 String revision = Version.getVersion(interfaceClass, version); if (revision != null && revision.length() > 0) { map.put("revision", revision); } String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); if(methods.length == 0) { logger.warn("NO method found in service interface " + interfaceClass.getName()); map.put("methods", Constants.ANY_VALUE); } else { map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ",")); } } map.put(Constants.INTERFACE_KEY, interfaceName); appendParameters(map, application); appendParameters(map, module); appendParameters(map, consumer, Constants.DEFAULT_KEY); appendParameters(map, this); String prifix = StringUtils.getServiceKey(map); //如果指定了接口暴露哪些方法时才有效 if (methods != null && methods.size() > 0) { for (MethodConfig method : methods) { appendParameters(map, method, method.getName()); String retryKey = method.getName() + ".retry"; if (map.containsKey(retryKey)) { String retryValue = map.remove(retryKey); if ("false".equals(retryValue)) { map.put(method.getName() + ".retries", "0"); } } appendAttributes(attributes, method, prifix + "." + method.getName()); checkAndConvertImplicitConfig(method, map, attributes); } } //attributes通过系统context进行存储. StaticContext.getSystemContext().putAll(attributes); //创建当前服务的消费代理(十分重要) ref = createProxy(map); }
@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" }) private T createProxy(Map<String, String> map) { URL tmpUrl = new URL("temp", "localhost", 0, map); final boolean isJvmRefer; if (isInjvm() == null) { if (url != null && url.length() > 0) { //指定URL的情况下,不做本地引用 isJvmRefer = false; } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) { //默认情况下如果本地有服务暴露,则引用本地服务. isJvmRefer = true; } else { isJvmRefer = false; } } else { isJvmRefer = isInjvm().booleanValue(); } if (isJvmRefer) { URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map); invoker = refprotocol.refer(interfaceClass, url); if (logger.isInfoEnabled()) { logger.info("Using injvm service " + interfaceClass.getName()); } } else { if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL //......省略点对点直连的代码行......// } else { // 通过注册中心配置拼装URL //该方法继承自com.alibaba.dubbo.config.AbstractInterfaceConfig //在http://blog.csdn.net/weiythi/article/details/78467094 有相关介绍 List<URL> us = loadRegistries(false); if (us != null && us.size() > 0) { //遍历注册中心地址 for (URL u : us) { URL monitorUrl = loadMonitor(u); if (monitorUrl != null) { map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString())); } urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))); } } if (urls == null || urls.size() == 0) { throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config."); } } //以下的代码反映一个主题,即创建一个invoker实例(com.alibaba.dubbo.rpc.Invoker) if (urls.size() == 1) { //这里refprotocol又是一个自适应的扩展点 invoker = refprotocol.refer(interfaceClass, urls.get(0)); } else { List<Invoker<?>> invokers = new ArrayList<Invoker<?>>(); URL registryURL = null; for (URL url : urls) { //注意这里refprotocol是一个自适应扩展点,根据url的protocol判断,这里返回的是一个ProtocolListenerWrapper包装类实例,并包装com.alibaba.dubbo.registry.integration.RegistryProtocol,refer方法涉及服务发现。独立章节说明 invokers.add(refprotocol.refer(interfaceClass, url)); if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { registryURL = url; // 用了最后一个registry url } } if (registryURL != null) { // 有 注册中心协议的URL // 对有注册中心的Cluster 只用 AvailableCluster URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); invoker = cluster.join(new StaticDirectory(u, invokers)); } else { // 不是 注册中心的URL invoker = cluster.join(new StaticDirectory(invokers)); } } } Boolean c = check; if (c == null && consumer != null) { c = consumer.isCheck(); } if (c == null) { c = true; // default true } if (c && ! invoker.isAvailable()) { throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion()); } if (logger.isInfoEnabled()) { logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl()); } // 创建服务代理, return (T) proxyFactory.getProxy(invoker); }
注意这里refprotocol是一个自适应扩展点,根据url的protocol判断,这里返回的是一个ProtocolListenerWrapper包装类实例,并包装com.alibaba.dubbo.registry.integration.RegistryProtocol,refer方法涉及服务发现。有时间独立章节说明
createProxy方法的最后一行return (T) proxyFactory.getProxy(invoker); 那么这个proxyFactory又是啥?
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
好吧,他又是一个自适应的扩展点,看一下ProxyFactory的接口定义,结合我们对dubbo中扩展点机制的掌握,看一看这个proxyFactory创建了一个怎样的代理 。com.alibaba.dubbo.rpc.ProxyFactory
类的定义如下
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.extension.Adaptive; import com.alibaba.dubbo.common.extension.SPI; /** * ProxyFactory. (API/SPI, Singleton, ThreadSafe) * * @author william.liangf */ @SPI("javassist") public interface ProxyFactory { /** * create proxy. * * @param invoker * @return proxy */ @Adaptive({Constants.PROXY_KEY}) <T> T getProxy(Invoker<T> invoker) throws RpcException; /** * create invoker. * * @param <T> * @param proxy * @param type * @param url * @return invoker */ @Adaptive({Constants.PROXY_KEY}) <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException; }
Constants.PROXY_KEY 常量的值为proxy,在\META-INF\dubbo\internal\com.alibaba.dubbo.rpc.ProxyFactory文件中找到扩展点的配置如下:
stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
其中@SPI注解的值为javassist 所以使用com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory 类作为扩展点的实现,但是这里还有com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper包装类
所以这里会使用StubProxyFactoryWrapper实例包装JavassistProxyFactory,调用getProxy(invoker)的化学反应;方法会调用继承自com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory 的 getProxy(invoker); 方法,其内部实现如下
public <T> T getProxy(Invoker<T> invoker) throws RpcException { Class<?>[] interfaces = null; //config 一般配置下这里会返回 null String config = invoker.getUrl().getParameter("interfaces"); if (config != null && config.length() > 0) { String[] types = Constants.COMMA_SPLIT_PATTERN.split(config); if (types != null && types.length > 0) { interfaces = new Class<?>[types.length + 2]; interfaces[0] = invoker.getInterface(); interfaces[1] = EchoService.class; for (int i = 0; i < types.length; i ++) { interfaces[i + 1] = ReflectUtils.forName(types[i]); } } } if (interfaces == null) { interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class}; } return getProxy(invoker, interfaces); }
可见dubbo 加进来了一个奇怪的接口 com.alibaba.dubbo.rpc.service.EchoService,这里先暂时不管这个EchoService
看一下 getProxy(invoker, interfaces);的实现
@SuppressWarnings("unchecked") public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); }
com.alibaba.dubbo.common.bytecode.Proxy 是一个生成代理对象的工具类
1. 遍历所有入参接口,以;分割连接起来, 以它为key以map为缓存查找如果有,说明代理对象已创建返回
2. 利用AtomicLong对象自增获取一个long数组来作为生产类的后缀,防止冲突
3. 遍历接口获取所有定义的方法,加入到一个集合Set worked中 ,用来判重,
Proxy.getProxy(interfaces),会返回一个代理对象,该方法内部操作复杂,会返回一个如下所示的代理类
package com.alibaba.dubbo.common.bytecode;import java.lang.reflect.InvocationHandler;public class Proxy0 extends com.alibaba.dubbo.common.bytecode.Proxy { @Override public Object newInstance(InvocationHandler h) { return new proxy0(h); }}class proxy0 implements 服务接口,com.alibaba.dubbo.rpc.service.EchoService{ /** * 被代理的方法 */ public static java.lang.reflect.Method[] methods; private java.lang.reflect.InvocationHandler handler; public proxy0() { } /** * 构造函数 * * @param handler */ public proxy0(java.lang.reflect.InvocationHandler handler) { this.handler = handler; } /** * 被代理方法(有返回值,有参数) * * @return */ com.company.project.common.dto.Result methodName0(com.company.project.module.protocol.request.AdGroupRequest adGroupRequest) throws Throwable { //args数组长度为参数列表长度 Object[] args = new Object[1]; args[0] = adGroupRequest; Object ret = handler.invoke(this, methods[0], args); return (com.ksyun.dsp.common.dto.Result) ret; } /** * 被代理方法(无返回值,有参数) * * @return */ void methodName1(com.company.project.module.protocol.request.AdGroupRequest adGroupRequest) throws Throwable { Object[] args = new Object[1]; args[0] = adGroupRequest; Object ret = handler.invoke(this, methods[1], args); } /** * 被代理方法(无返回值,无参数) * * @return */ void methodName2() throws Throwable { Object[] args = new Object[0]; Object ret = handler.invoke(this, methods[2], args); }}
从以上示例代码可见,剩下的操作都是通过
handler.invoke(this, method, args); 这种方式来实现代理方法的调用的
dubbo内部对InvocationHandler的实现如下,可见其还是实现java.lang.reflect.InvocationHandler这个接口的,但是以dubbo的代理生成方式来说,
好像没什么必要实现InvocationHandler接口,这里可能是为了维护动态代理的语义吧
/** * InvokerHandler * * @author william.liangf */public class InvokerInvocationHandler implements InvocationHandler { private final Invoker<?> invoker; public InvokerInvocationHandler(Invoker<?> handler){ this.invoker = handler; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Class<?>[] parameterTypes = method.getParameterTypes(); if (method.getDeclaringClass() == Object.class) { return method.invoke(invoker, args); } if ("toString".equals(methodName) && parameterTypes.length == 0) { return invoker.toString(); } if ("hashCode".equals(methodName) && parameterTypes.length == 0) { return invoker.hashCode(); } if ("equals".equals(methodName) && parameterTypes.length == 1) { return invoker.equals(args[0]); } return invoker.invoke(new RpcInvocation(method, args)).recreate(); }}
最后一行 return invoker.invoke(new RpcInvocation(method, args)).recreate();,为便于理解拆分为以下两行
Result rpcResult = invoker.invoke(new RpcInvocation(method, args)); return rpcResult.recreate();
其中RpcInvocation如图
总结:
1.dubbo通过实现org.springframework.beans.factory.FactoryBean接口的方式实现对服务的代理
2.Dubbo将所有的服务都实现了一个EchoService(回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否畅通,可用于监控。所有服务自动实现EchoService接口,只需要将任意服务引用强制转换为EchoService,即可使用。)
3.服务消费最终会调用com.alibaba.dubbo.rpc.Invoker.invoke(new RpcInvocation(method, args)); 方法
- Dubbo/Dubbox的服务消费(一)- 服务代理的创建
- Dubbo/Dubbox的服务消费(二)- 服务发现
- Dubbo/Dubbox的服务暴露(一)
- Dubbo/Dubbox的服务暴露(三)- 服务的注册
- 分布式服务框架 dubbo/dubbox 的搭建
- Dubbo/Dubbox的服务暴露(二)-扩展点机制
- dubbo2.5.3消费dubbox提供的服务报错
- dubbo 创建服务代理
- dubbox为dubbo提供REST服务(dubbox + springmvc)
- Dubbo/Dubbox的dubbo协议实现(一)-服务端启动
- dubbo-服务消费端类图
- 分布式服务框架 dubbo/dubbox 入门示例
- 分布式服务框架 dubbo/dubbox 入门示例
- 分布式服务框架 dubbo/dubbox 入门示例
- 分布式服务框架 dubbo/dubbox 入门示例
- 分布式服务框架Dubbo/Dubbox入门示例
- 分布式服务框架 dubbo/dubbox 入门示例
- 分布式服务框架 dubbo/dubbox 入门示例
- ubuntu17.10卸载自带的jdk从
- NOIP复赛复习(十三)图论算法巩固与提高
- Java多线程工具包java.util.concurrent---LinkedBlockingQueue
- Swift基础 错误处理
- Git的17条基本用法
- Dubbo/Dubbox的服务消费(一)- 服务代理的创建
- Faster-RCNN使用时Error parsing text-format caffe.NetParameter
- hello.ko---Makefile
- Line论文中的Alias Sampling Algorithm 分析
- 回首Java——堆排序
- 会场安排问题
- UE4实现纹理缩放(将纹理坐标进行缩放)
- 自己动手实现一个Android断点下载
- 数据库一表分两表导入数据的SQL 语句