第六章 dubbo源码解析目录
从 9.1 客户端发起请求源码 的客户端请求总体流程图中,截取部分如下:
//代理发出请求 proxy0.sayHello(String paramString) -->InvokerInvocationHandler.invoke(Object proxy, Method method, Object[] args) -->new RpcInvocation(method, args) -->MockClusterInvoker.invoke(Invocation invocation)//服务降级的地方
dubbo就是通过MockClusterInvoker来实现服务降级的。
一、示例
1 public interface DemoService { 2 // String sayHello(String name); 3 Car sayHello(String name); 4 }
将dubbo-demo中的服务接口定义一个返回模型Car。提供者实现如下:
1 public class DemoServiceImpl implements DemoService { 2 public Car sayHello(String name) { 3 Car car = new Car(); 4 car.setCarNum("浙A10000"); 5 car.setGoMile(100); 6 return car; 7 } 8 }
消费者使用如下:
1 public class Consumer { 2 public static void main(String[] args) { 3 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"}); 4 context.start(); 5 DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy 6 7 while (true) { 8 try { 9 Thread.sleep(1000); 10 Car hello = demoService.sayHello("world"); // call remote method 11 System.out.println(hello.getCarNum() + "-" + hello.getGoMile()); // get result 12 } catch (Throwable throwable) { 13 throwable.printStackTrace(); 14 } 15 } 16 } 17 }
二、使用方式
实际使用中,会通过直接在dubbo-admin中设置服务降级策略,这里使用dubbo用户手册中的方式来更清晰的看一下服务降级的配置(实际上就是进行配置覆盖)
配置规则:
1、使用自定义mock类(接口名+Mock)
- mock = default => DemoServiceMock
- mock = true => DemoServiceMock
- mock = fail => DemoServiceMock
- mock = force => DemoServiceMock
2、先普通执行,执行失败之后再执行相应的mock逻辑
- mock = fail:throw => throw new RpcException(" mocked exception for Service degradation. ");
- mock = fail:throw XxxException => throw new RpcException(RpcException.BIZ_EXCEPTION, XxxException);
- mock = fail:return => return null
- mock = fail:return xxx => return xxx
- mock = fail:return empty => return new Car()
3、直接执行相应的mock逻辑
- mock = force:throw => throw new RpcException(" mocked exception for Service degradation. ");
- mock = force:throw XxxException => throw new RpcException(RpcException.BIZ_EXCEPTION, XxxException);
- mock = force:return => return null
- mock = force:return xxx => return xxx
- mock = force:return empty => return new Car()
进行配置:
1 public class DegradeTest { 2 public static void main(String[] args) { 3 RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); 4 Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.211.55.5:2181")); 5 // return null; 6 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:return")); 7 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:return+null")); 8 // return 空对象; 9 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:return+empty")); 10 // return value; 11 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:return+hello")); 12 // throw exception 13 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:throw")); 14 // throw custom-msg exception 15 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:throw+com.alibaba.dubbo.Test.MyRuntimeException")); 16 // 执行mock类 17 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:com.alibaba.dubbo.demo.DemoServiceMock")); 18 } 19 }
上述需要注意的是需要配置为“force:return+null”的格式而非“force:return null”。(实际上空格的url encode就是+号),上述代码的执行,实际上是在zk上创建configurators的子节点:
关于覆盖配置:http://dubbo.io/books/dubbo-user-book/demos/config-rule.html
override://
表示数据采用覆盖方式,支持override
和absent
,可扩展,必填。0.0.0.0
表示对所有 IP 地址生效,如果只想覆盖某个 IP 的数据,请填入具体 IP,必填。
表示只对指定服务生效,必填。com.alibaba.dubbo.demo.DemoService
category=configurators
表示该数据为动态配置类型,必填。dynamic=false
表示该数据为持久数据,当注册方退出时,数据依然保存在注册中心,必填。enabled=true
覆盖规则是否生效,可不填,缺省生效。application=demo-consumer
表示只对指定应用生效,可不填,表示对所有应用生效。
表示将满足以上条件的 mock 参数的值覆盖为 force:return+null。如果想覆盖其它参数,直接加在mock=force:return+null
override
的 URL 参数上。
三、源码分析
1 public class MockClusterInvoker<T> implements Invoker<T> { 2 private final Directory<T> directory; //RegistryDirectory:存储invoker列表 3 private final Invoker<T> invoker; //FailoverClusterInvoker:容错策略 4 5 public Result invoke(Invocation invocation) throws RpcException { 6 Result result = null; 7 8 String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim(); 9 if (value.length() == 0 || value.equalsIgnoreCase("false")) { 10 //no mock 11 result = this.invoker.invoke(invocation); 12 } else if (value.startsWith("force")) { 13 ... 14 //force:direct mock 15 result = doMockInvoke(invocation, null); 16 } else { 17 //fail-mock 18 try { 19 result = this.invoker.invoke(invocation); 20 } catch (RpcException e) { 21 if (e.isBiz()) { 22 throw e; 23 } else { 24 ... 25 result = doMockInvoke(invocation, e); 26 } 27 } 28 } 29 return result; 30 } 31 }
首先去获取mock参数,
- 如果没有配置,则直接使用FailoverClusterInvoker去正常的向provider发出请求;
- 如果配置为以force开头的,则直接执行doMockInvoke(Invocation invocation, RpcException e),不再向provider发送请求;
- 如果配置为以fail开头的,则先使用FailoverClusterInvoker去正常的向provider发出请求,如果失败抛出了非业务异常,则执行doMockInvoke(Invocation invocation, RpcException e);
1 private Result doMockInvoke(Invocation invocation, RpcException e) { 2 Result result = null; 3 Invoker<T> minvoker; 4 5 List<Invoker<T>> mockInvokers = selectMockInvoker(invocation); //获取mock类型的Invoker 6 if (mockInvokers == null || mockInvokers.size() == 0) { 7 minvoker = (Invoker<T>) new MockInvoker(directory.getUrl()); //如果没有配置mock类型的Invoker,则自己创建一个MockInvoker 8 } else { 9 minvoker = mockInvokers.get(0); 10 } 11 try { 12 result = minvoker.invoke(invocation); //执行MockInvoker的invoke(Invocation invocation)方法 13 } catch (RpcException me) { 14 if (me.isBiz()) { 15 result = new RpcResult(me.getCause()); 16 } else { //非业务异常 17 throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause()); 18 } 19 } catch (Throwable me) { 20 throw new RpcException(getMockExceptionMessage(e, me), me.getCause()); 21 } 22 return result; 23 }
从RegistryDirectory中获取MockInvoker:
1 /** 2 * Return MockInvoker 3 * Contract: 4 * directory.list() will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return. 5 * if directory.list() returns more than one mock invoker, only one of them will be used. 6 * 7 * @param invocation 8 * @return 9 */ 10 private List<Invoker<T>> selectMockInvoker(Invocation invocation) { 11 List<Invoker<T>> invokers = null; 12 //TODO generic invoker? 13 if (invocation instanceof RpcInvocation) { 14 //Note the implicit contract (although the description is added to the interface declaration, but extensibility is a problem. The practice placed in the attachement needs to be improved) 15 ((RpcInvocation) invocation).setAttachment(Constants.INVOCATION_NEED_MOCK, Boolean.TRUE.toString()); 16 //directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return. 17 try { 18 invokers = directory.list(invocation); 19 } catch (RpcException e) { 20 if (logger.isInfoEnabled()) { 21 logger.info("Exception when try to invoke mock. Get mock invokers error for service:" 22 + directory.getUrl().getServiceInterface() + ", method:" + invocation.getMethodName() 23 + ", will contruct a new mock with 'new MockInvoker()'.", e); 24 } 25 } 26 } 27 return invokers; 28 }
首先使用RegistryDirectory获取出方法名为sayHello的Invoker列表,之后使用MockInvokersSelector(Router)选取出MockInvoker。
1 public class MockInvokersSelector implements Router { 2 3 public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers, 4 URL url, final Invocation invocation) throws RpcException { 5 if (invocation.getAttachments() == null) { 6 return getNormalInvokers(invokers); 7 } else { 8 String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK); 9 if (value == null) 10 return getNormalInvokers(invokers); 11 else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) { 12 return getMockedInvokers(invokers); 13 } 14 } 15 return invokers; 16 } 17 18 private <T> List<Invoker<T>> getMockedInvokers(final List<Invoker<T>> invokers) { 19 if (!hasMockProviders(invokers)) { 20 return null; 21 } 22 List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(1); 23 for (Invoker<T> invoker : invokers) { 24 if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) { 25 sInvokers.add(invoker); 26 } 27 } 28 return sInvokers; 29 } 30 31 private <T> boolean hasMockProviders(final List<Invoker<T>> invokers) { 32 boolean hasMockProvider = false; 33 for (Invoker<T> invoker : invokers) { 34 if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) { 35 hasMockProvider = true; 36 break; 37 } 38 } 39 return hasMockProvider; 40 } 41 }
这里获取到的是空列表。
所以会先创建一个MockInvoker对象,之后执行其invoker方法。
MockInvoker:
1 public Result invoke(Invocation invocation) throws RpcException { 2 String mock = getUrl().getParameter(invocation.getMethodName() + "." + Constants.MOCK_KEY); //sayHello.mock 3 if (invocation instanceof RpcInvocation) { 4 ((RpcInvocation) invocation).setInvoker(this); 5 } 6 if (StringUtils.isBlank(mock)) { 7 mock = getUrl().getParameter(Constants.MOCK_KEY); //mock 8 } 9 10 if (StringUtils.isBlank(mock)) { 11 throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url)); 12 } 13 mock = normallizeMock(URL.decode(mock)); 14 if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mock.trim())) { // return 15 RpcResult result = new RpcResult(); 16 result.setValue(null); 17 return result; 18 } else if (mock.startsWith(Constants.RETURN_PREFIX)) { // return value(包括return null) 19 mock = mock.substring(Constants.RETURN_PREFIX.length()).trim(); 20 mock = mock.replace('`', '"'); 21 try { 22 Type[] returnTypes = RpcUtils.getReturnTypes(invocation); 23 Object value = parseMockValue(mock, returnTypes); 24 return new RpcResult(value); 25 } catch (Exception ew) { 26 throw new RpcException("mock return invoke error. method :" + invocation.getMethodName() + ", mock:" + mock + ", url: " + url, ew); 27 } 28 } else if (mock.startsWith(Constants.THROW_PREFIX)) { // throw xxx 29 mock = mock.substring(Constants.THROW_PREFIX.length()).trim(); 30 mock = mock.replace('`', '"'); 31 if (StringUtils.isBlank(mock)) {// throw 32 throw new RpcException(" mocked exception for Service degradation. "); 33 } else { // user customized class : throw xxx 34 Throwable t = getThrowable(mock); 35 throw new RpcException(RpcException.BIZ_EXCEPTION, t); 36 } 37 } else { //impl mock: 自定义mock类 38 try { 39 Invoker<T> invoker = getInvoker(mock); 40 return invoker.invoke(invocation); 41 } catch (Throwable t) { 42 throw new RpcException("Failed to create mock implemention class " + mock, t); 43 } 44 } 45 }
首先获取到mock配置,例如:mock=force:return+null,之后进行url解码为mock=force:return null,最后进行处理为mock=return null,然后根据规则走分支。
mock参数的处理函数:
1 /** 2 * 一、使用自定义mock类 3 * mock = default => DemoServiceMock 4 * mock = true => DemoServiceMock 5 * mock = fail => DemoServiceMock 6 * mock = force => DemoServiceMock 7 * 8 * 二、先普通执行,执行失败之后再执行相应的mock逻辑 9 * mock = fail:throw => throw new RpcException(" mocked exception for Service degradation. "); 10 * mock = fail:throw XxxException => throw new RpcException(RpcException.BIZ_EXCEPTION, XxxException); 11 * mock = fail:return => return null 12 * mock = fail:return xxx => return xxx 13 * 14 * 三、直接执行相应的mock逻辑 15 * mock = force:throw => throw new RpcException(" mocked exception for Service degradation. "); 16 * mock = force:throw XxxException => throw new RpcException(RpcException.BIZ_EXCEPTION, XxxException); 17 * mock = force:return => return null 18 * mock = force:return xxx => return xxx 19 * 20 * @param mock 21 * @return 22 */ 23 private String normallizeMock(String mock) { 24 if (mock == null || mock.trim().length() == 0) { 25 return mock; 26 } else if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock.trim()) || "force".equalsIgnoreCase(mock.trim())) { 27 mock = url.getServiceInterface() + "Mock"; 28 } 29 if (mock.startsWith(Constants.FAIL_PREFIX)) { 30 mock = mock.substring(Constants.FAIL_PREFIX.length()).trim(); 31 } else if (mock.startsWith(Constants.FORCE_PREFIX)) { 32 mock = mock.substring(Constants.FORCE_PREFIX.length()).trim(); 33 } 34 return mock; 35 }
我们这里来看一下自定义mock类。消费端编写:
1 public class DemoServiceMock implements DemoService { 2 3 @Override 4 public Car sayHello(String name) { 5 Car car = new Car(); 6 car.setCarNum("mock中"); 7 car.setGoMile(666); 8 return car; 9 } 10 }
配置覆盖:
1 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:com.alibaba.dubbo.demo.DemoServiceMock"));
MockInvoker.invoke
1 try { 2 Invoker<T> invoker = getInvoker(mock); 3 return invoker.invoke(invocation); 4 } catch (Throwable t) { 5 throw new RpcException("Failed to create mock implemention class " + mock, t); 6 }
1 private Invoker<T> getInvoker(String mockService) { 2 Invoker<T> invoker = (Invoker<T>) mocks.get(mockService); 3 if (invoker != null) { 4 return invoker; 5 } else { 6 Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface()); 7 if (ConfigUtils.isDefault(mockService)) { 8 mockService = serviceType.getName() + "Mock"; 9 } 10 11 Class<?> mockClass = ReflectUtils.forName(mockService); 12 if (!serviceType.isAssignableFrom(mockClass)) { 13 throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName()); 14 } 15 16 if (!serviceType.isAssignableFrom(mockClass)) { 17 throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName()); 18 } 19 try { 20 T mockObject = (T) mockClass.newInstance(); // 获取自定义mock类实例 21 invoker = proxyFactory.getInvoker(mockObject, (Class<T>) serviceType, url); // 和普通类一样创建Invoker 22 if (mocks.size() < 10000) { 23 mocks.put(mockService, invoker); 24 } 25 return invoker; 26 } catch (InstantiationException e) { 27 throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e); 28 } catch (IllegalAccessException e) { 29 throw new IllegalStateException(e); 30 } 31 } 32 }
上边看了return和自定义mock类,最后来看一下throw异常。
默认抛出RpcException,异常信息:mocked exception for Service degradation. 也可以自定义异常,例如:
1 public class MyRuntimeException extends RuntimeException { 2 private String msg; 3 4 public MyRuntimeException(String msg){ 5 this.msg = msg; 6 } 7 }
自定义异常必须具有单参构造器且参数为String。
配置覆盖:
MockInvoker.invoke
1 registry.register(URL.valueOf("override://0.0.0.0/com.alibaba.dubbo.demo.DemoService?category=configurators&dynamic=false&application=demo-consumer&mock=force:throw+com.alibaba.dubbo.Test.MyRuntimeException"));
1 private Throwable getThrowable(String throwstr) { 2 Throwable throwable = (Throwable) throwables.get(throwstr); 3 if (throwable != null) { 4 return throwable; 5 } else { 6 Throwable t = null; 7 try { 8 Class<?> bizException = ReflectUtils.forName(throwstr); 9 Constructor<?> constructor; 10 constructor = ReflectUtils.findConstructor(bizException, String.class); 11 t = (Throwable) constructor.newInstance(new Object[]{" mocked exception for Service degradation. "}); 12 if (throwables.size() < 1000) { 13 throwables.put(throwstr, t); 14 } 15 } catch (Exception e) { 16 throw new RpcException("mock throw error :" + throwstr + " argument error.", e); 17 } 18 return t; 19 } 20 }
服务降级结束!!!
dubbo提供了三种结果缓存机制:
- lru:基于最近最少使用原则删除多余缓存,保持最热的数据被缓存
- threadlocal:当前线程缓存
- jcache:可以桥接各种缓存实现
一、使用方式
1 <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"> 2 <dubbo:method name="sayHello" timeout="60000" cache="lru"/> 3 </dubbo:reference>
添加cache配置。
注意:dubbo结果缓存有一个bug,https://github.com/alibaba/dubbo/issues/1362,当cache="xxx"配置在服务级别时,没有问题,当配置成方法级别的时候,不管怎么配置,都睡使用LruCache。
二、LRU缓存源码解析
1 /** 2 * CacheFilter 3 * 配置了cache配置才会加载CacheFilter 4 */ 5 @Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY) 6 public class CacheFilter implements Filter { 7 private CacheFactory cacheFactory; 8 9 public void setCacheFactory(CacheFactory cacheFactory) { 10 this.cacheFactory = cacheFactory; 11 } 12 13 public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { 14 if (cacheFactory != null && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.CACHE_KEY))) { 15 // 使用CacheFactory$Adaptive获取具体的CacheFactory,然后再使用具体的CacheFactory获取具体的Cache对象 16 Cache cache = cacheFactory.getCache(invoker.getUrl().addParameter(Constants.METHOD_KEY, invocation.getMethodName())); 17 if (cache != null) { 18 // 缓存对象的key为arg1,arg2,arg3,...,arg4 19 String key = StringUtils.toArgumentString(invocation.getArguments()); 20 // 获取缓存value 21 Object value = cache.get(key); 22 if (value != null) { 23 return new RpcResult(value); 24 } 25 Result result = invoker.invoke(invocation); 26 // 响应结果没有exception信息,则将相应结果的值塞入缓存 27 if (!result.hasException()) { 28 cache.put(key, result.getValue()); 29 } 30 return result; 31 } 32 } 33 return invoker.invoke(invocation); 34 } 35 }
从@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)中我们可以看出,consumer端或provider端配置了cache="xxx",则会走该CacheFilter。
首先获取具体Cache实例:CacheFilter中的cacheFactory属性是CacheFactory$Adaptive实例。
1 public class CacheFactory$Adaptive implements com.alibaba.dubbo.cache.CacheFactory { 2 public com.alibaba.dubbo.cache.Cache getCache(com.alibaba.dubbo.common.URL arg0) { 3 if (arg0 == null) throw new IllegalArgumentException("url == null"); 4 com.alibaba.dubbo.common.URL url = arg0; 5 String extName = url.getParameter("cache", "lru"); 6 if (extName == null) 7 throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.cache.CacheFactory) name from url(" + url.toString() + ") use keys([cache])"); 8 // 获取具体的CacheFactory 9 com.alibaba.dubbo.cache.CacheFactory extension = (com.alibaba.dubbo.cache.CacheFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.cache.CacheFactory.class).getExtension(extName); 10 // 使用具体的CacheFactory获取具体的Cache 11 return extension.getCache(arg0); 12 } 13 }
这里extName使我们配置的lru,如果不配置,默认也是lru。这里获取到的具体的CacheFactory是LruCacheFactory。
1 @SPI("lru") 2 public interface CacheFactory { 3 @Adaptive("cache") 4 Cache getCache(URL url); 5 } 6 7 public abstract class AbstractCacheFactory implements CacheFactory { 8 private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>(); 9 10 public Cache getCache(URL url) { 11 String key = url.toFullString(); 12 Cache cache = caches.get(key); 13 if (cache == null) { 14 caches.put(key, createCache(url)); 15 cache = caches.get(key); 16 } 17 return cache; 18 } 19 20 protected abstract Cache createCache(URL url); 21 } 22 23 public class LruCacheFactory extends AbstractCacheFactory { 24 protected Cache createCache(URL url) { 25 return new LruCache(url); 26 } 27 }
调用LruCacheFactory.getCache(URL url)方法,实际上调用的是其父类AbstractCacheFactory的方法。逻辑是:创建一个LruCache实例,之后存储在ConcurrentMap<String, Cache> caches中,key为url.toFullString()。
再来看LruCache的创建:
1 public interface Cache { 2 void put(Object key, Object value); 3 Object get(Object key); 4 } 5 6 public class LruCache implements Cache { 7 private final Map<Object, Object> store; 8 9 public LruCache(URL url) { 10 final int max = url.getParameter("cache.size", 1000); 11 this.store = new LRUCache<Object, Object>(max); 12 } 13 14 public void put(Object key, Object value) { 15 store.put(key, value); 16 } 17 18 public Object get(Object key) { 19 return store.get(key); 20 } 21 }
默认缓存存储的最大个数为1000个。之后创建了一个LRUCache对象。
1 public class LRUCache<K, V> extends LinkedHashMap<K, V> { 2 private static final long serialVersionUID = -5167631809472116969L; 3 4 private static final float DEFAULT_LOAD_FACTOR = 0.75f; 5 6 private static final int DEFAULT_MAX_CAPACITY = 1000; 7 private final Lock lock = new ReentrantLock(); 8 private volatile int maxCapacity; 9 10 public LRUCache(int maxCapacity) { 11 /** 12 * 注意: 13 * LinkedHashMap 维护着一个运行于所有Entry的双向链表:此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序 14 * 而真正存储的数据结构还是其父类HashMap的那个Entry[]数组,上述的双向链表仅用于维护迭代顺序(帮助实现lru算法等) 15 * 16 * LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 17 * 第三个参数accessOrder:false(插入顺序),true(访问顺序) 18 */ 19 super(16, DEFAULT_LOAD_FACTOR, true); 20 this.maxCapacity = maxCapacity; 21 } 22 23 /** 24 * 是否需要删除最老的数据(即最近没有被访问的数据) 25 * @param eldest 26 * @return 27 */ 28 @Override 29 protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { 30 return size() > maxCapacity; 31 } 32 33 @Override 34 public V get(Object key) { 35 try { 36 lock.lock(); 37 return super.get(key); 38 } finally { 39 lock.unlock(); 40 } 41 } 42 43 @Override 44 public V put(K key, V value) { 45 try { 46 lock.lock(); 47 return super.put(key, value); 48 } finally { 49 lock.unlock(); 50 } 51 } 52 53 @Override 54 public V remove(Object key) { 55 try { 56 lock.lock(); 57 return super.remove(key); 58 } finally { 59 lock.unlock(); 60 } 61 } 62 63 @Override 64 public int size() { 65 try { 66 lock.lock(); 67 return super.size(); 68 } finally { 69 lock.unlock(); 70 } 71 } 72 ... 73 }
注意:
- LinkedHashMap维护着一个运行于所有Entry的双向链表:此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序(真正存储的数据结构还是其父类HashMap的那个Entry[]数组,上述的双向链表仅用于维护迭代顺序)
- 当指定了LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)第三个参数accessOrder=true时,每次执行get(Object key)时,获取出来的Entry都会被放到尾节点,也就是说双向链表的header节点是最久以前访问的,当执行put(Object key, Object value)的时候,就执行removeEldestEntry(java.util.Map.Entry<K, V> eldest)来判断是否需要删除这个header节点。(这些是LinkedHashMap实现的,具体源码分析见 https://yikun.github.io/2015/04/02/Java-LinkedHashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/ http://wiki.jikexueyuan.com/project/java-collection/linkedhashmap.html)
三、ThreadLocal缓存源码解析
根据文章开头提到的bug,cache=""只能配置在服务级别。
1 <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService" cache="threadlocal"/>
1 public class ThreadLocalCacheFactory extends AbstractCacheFactory { 2 protected Cache createCache(URL url) { 3 return new ThreadLocalCache(url); 4 } 5 } 6 7 public class ThreadLocalCache implements Cache { 8 private final ThreadLocal<Map<Object, Object>> store; 9 10 public ThreadLocalCache(URL url) { 11 this.store = new ThreadLocal<Map<Object, Object>>() { 12 @Override 13 protected Map<Object, Object> initialValue() { 14 return new HashMap<Object, Object>(); 15 } 16 }; 17 } 18 19 public void put(Object key, Object value) { 20 store.get().put(key, value); 21 } 22 23 public Object get(Object key) { 24 return store.get().get(key); 25 } 26 }
ThreadLocalCache的实现是HashMap。
监控总体图:
红色:监控中心 - dubbo-simple-monitor
黄色:provider
蓝色:consumer
统计总体流程:
- MonitorFilter向DubboMonitor发送数据
- DubboMonitor将数据进行聚合后(默认聚合1min中的统计数据)暂存到ConcurrentMap<Statistics, AtomicReference<long[]>> statisticsMap,然后使用一个含有3个线程(线程名字:DubboMonitorSendTimer)的线程池每隔1min钟,调用SimpleMonitorService遍历发送statisticsMap中的统计数据,每发送完毕一个,就重置当前的Statistics的AtomicReference<long[]>
- SimpleMonitorService将这些聚合数据塞入BlockingQueue<URL> queue中(队列大写为100000)
- SimpleMonitorService使用一个后台线程(线程名为:DubboMonitorAsyncWriteLogThread)将queue中的数据写入文件(该线程以死循环的形式来写)
- SimpleMonitorService还会使用一个含有1个线程(线程名字:DubboMonitorTimer)的线程池每隔5min钟,将文件中的统计数据画成图表
注意:
- SimpleMonitorService理解为一个服务提供者;而provider和consumer都是一个服务消费者,所以二者的DubboMonitor中的MonitorService实例都是一个代理实例。
- dubbo-monitor计数监控不支持异步调用下的数据监控
一、dubbo-monitor使用
在配置文件中添加:
1 <dubbo:monitor address="10.211.55.5:9090" />
即开启了monitor监控,并且指定了监控中心服务器为“10.211.55.5:9090”。
9090端口是Prometheus的默认端口,dubbo提供的监控中心比较简陋,我们后续会使用Prometheus作为监控中心来存储监控数据。
二、服务端加载monitor配置
doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)中:
1 if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) { 2 ... 3 if (registryURLs != null && registryURLs.size() > 0) { 4 for (URL registryURL : registryURLs) { 5 ... 6 URL monitorUrl = loadMonitor(registryURL); 7 if (monitorUrl != null) { 8 url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString()); 9 } 10 ... 11 } 12 } else { 13 ... 14 } 15 }
其中loadMonitor(URL registryURL)方法主要用于创建MonitorConfig对象(如果monitor配置在dubbo.properties中的话),并且设置属性,之后设置到数据总线Url中。
1 protected URL loadMonitor(URL registryURL) { 2 if (monitor == null) { 3 String monitorAddress = ConfigUtils.getProperty("dubbo.monitor.address"); 4 String monitorProtocol = ConfigUtils.getProperty("dubbo.monitor.protocol"); 5 if ((monitorAddress == null || monitorAddress.length() == 0) && (monitorProtocol == null || monitorProtocol.length() == 0)) { 6 return null; 7 } 8 9 monitor = new MonitorConfig(); 10 if (monitorAddress != null && monitorAddress.length() > 0) { 11 monitor.setAddress(monitorAddress); 12 } 13 if (monitorProtocol != null && monitorProtocol.length() > 0) { 14 monitor.setProtocol(monitorProtocol); 15 } 16 } 17 appendProperties(monitor); 18 ... 19 }
三、消费端加载monitor配置
createProxy(Map<String, String> map)中:
1 List<URL> us = loadRegistries(false); 2 if (us != null && us.size() > 0) { 3 for (URL u : us) { 4 URL monitorUrl = loadMonitor(u); 5 if (monitorUrl != null) { 6 map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString())); 7 } 8 ... 9 } 10 }
四、MonitorFilter收集监控数据
consumer端在发起调用之前会先走filter链;provider端在接收到请求时也是先走filter链,然后才进行真正的业务逻辑处理。默认情况下,在consumer和provider的filter链中都会有Monitorfilter。
1 /** 2 * MonitorFilter. (SPI, Singleton, ThreadSafe) 3 */ 4 @Activate(group = {Constants.PROVIDER, Constants.CONSUMER}) 5 public class MonitorFilter implements Filter { 6 7 private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class); 8 9 // key: 接口名.方法名 value: 当前的并发数 10 private final ConcurrentMap<String, AtomicInteger> concurrents = new ConcurrentHashMap<String, AtomicInteger>(); 11 12 private MonitorFactory monitorFactory;// MonitorFactory$Adaptive 13 14 public void setMonitorFactory(MonitorFactory monitorFactory) { 15 this.monitorFactory = monitorFactory; 16 } 17 18 // intercepting invocation 19 public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { 20 if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {// 开启了monitor监控 21 RpcContext context = RpcContext.getContext(); // provider must fetch context before invoke() gets called 22 String remoteHost = context.getRemoteHost(); 23 long start = System.currentTimeMillis(); // record start timestamp 24 getConcurrent(invoker, invocation).incrementAndGet(); // 并发数+1 25 try { 26 Result result = invoker.invoke(invocation); // proceed invocation chain 27 collect(invoker, invocation, result, remoteHost, start, false);// 收集统计数据 28 return result; 29 } catch (RpcException e) { 30 collect(invoker, invocation, null, remoteHost, start, true);// 发生异常时收集统计数据 31 throw e; 32 } finally { 33 getConcurrent(invoker, invocation).decrementAndGet(); // 并发数-1 34 } 35 } else { 36 return invoker.invoke(invocation); 37 } 38 } 39 40 // collect info 41 private void collect(Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) { 42 try { 43 // ---- service statistics ---- 44 long elapsed = System.currentTimeMillis() - start; // 此次调用花费的时间 45 int concurrent = getConcurrent(invoker, invocation).get(); // current concurrent count 46 String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY); 47 String service = invoker.getInterface().getName(); // service name 48 String method = RpcUtils.getMethodName(invocation); // method name 49 URL url = invoker.getUrl().getUrlParameter(Constants.MONITOR_KEY); 50 Monitor monitor = monitorFactory.getMonitor(url);//根据monitorUrl获取Monitor实现(默认使用DubboMonitor) 51 if (monitor == null) { 52 return; 53 } 54 int localPort; 55 String remoteKey; 56 String remoteValue; 57 if (Constants.CONSUMER_SIDE.equals(invoker.getUrl().getParameter(Constants.SIDE_KEY))) { 58 // ---- for service consumer ---- 59 localPort = 0; 60 remoteKey = MonitorService.PROVIDER; 61 remoteValue = invoker.getUrl().getAddress(); 62 } else { 63 // ---- for service provider ---- 64 localPort = invoker.getUrl().getPort(); 65 remoteKey = MonitorService.CONSUMER; 66 remoteValue = remoteHost; 67 } 68 String input = "", output = ""; 69 if (invocation.getAttachment(Constants.INPUT_KEY) != null) { 70 input = invocation.getAttachment(Constants.INPUT_KEY); 71 } 72 if (result != null && result.getAttachment(Constants.OUTPUT_KEY) != null) { 73 output = result.getAttachment(Constants.OUTPUT_KEY); 74 } 75 monitor.collect(new URL(Constants.COUNT_PROTOCOL, 76 NetUtils.getLocalHost(), localPort, 77 service + "/" + method, 78 MonitorService.APPLICATION, application, 79 MonitorService.INTERFACE, service, 80 MonitorService.METHOD, method, 81 remoteKey, remoteValue, 82 error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1",// 成功失败数 83 MonitorService.ELAPSED, String.valueOf(elapsed),// 调用消耗的时间 84 MonitorService.CONCURRENT, String.valueOf(concurrent),// 并发数 85 Constants.INPUT_KEY, input, 86 Constants.OUTPUT_KEY, output)); 87 } catch (Throwable t) { 88 logger.error("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t); 89 } 90 } 91 92 // concurrent counter 93 private AtomicInteger getConcurrent(Invoker<?> invoker, Invocation invocation) { 94 String key = invoker.getInterface().getName() + "." + invocation.getMethodName(); 95 AtomicInteger concurrent = concurrents.get(key); 96 if (concurrent == null) { 97 concurrents.putIfAbsent(key, new AtomicInteger()); 98 concurrent = concurrents.get(key); 99 } 100 return concurrent; 101 } 102 103 }
调用之前,记录调用开始时间、并发数,之后进行调用,最后进行统计数据收集:
- 获取计算各种统计数据(调用消耗时间、调用成功/错误数等)
- 使用MonitorFactory获取Monitor
- 将统计数据构造成url
- 使用Monitor收集这些统计数据
获取Monitor的源码后续再说。这里获取到的是DubboMonitor实例。
五、DubboMonitor聚合监控数据
1 private static final int LENGTH = 10; 2 private final ConcurrentMap<Statistics, AtomicReference<long[]>> statisticsMap = new ConcurrentHashMap<Statistics, AtomicReference<long[]>>(); 3 4 // 聚合统计数据 5 public void collect(URL url) { 6 // data to collect from url 7 int success = url.getParameter(MonitorService.SUCCESS, 0); 8 int failure = url.getParameter(MonitorService.FAILURE, 0); 9 int input = url.getParameter(MonitorService.INPUT, 0); 10 int output = url.getParameter(MonitorService.OUTPUT, 0); 11 int elapsed = url.getParameter(MonitorService.ELAPSED, 0); 12 int concurrent = url.getParameter(MonitorService.CONCURRENT, 0); 13 // init atomic reference 14 Statistics statistics = new Statistics(url); 15 AtomicReference<long[]> reference = statisticsMap.get(statistics); 16 if (reference == null) { 17 statisticsMap.putIfAbsent(statistics, new AtomicReference<long[]>()); 18 reference = statisticsMap.get(statistics); 19 } 20 // use CompareAndSet to sum 21 long[] current; 22 long[] update = new long[LENGTH]; 23 do { 24 current = reference.get(); 25 if (current == null) { 26 update[0] = success; 27 update[1] = failure; 28 update[2] = input; 29 update[3] = output; 30 update[4] = elapsed; 31 update[5] = concurrent; 32 update[6] = input; 33 update[7] = output; 34 update[8] = elapsed; 35 update[9] = concurrent; 36 } else { 37 update[0] = current[0] + success; 38 update[1] = current[1] + failure; 39 update[2] = current[2] + input; 40 update[3] = current[3] + output; 41 update[4] = current[4] + elapsed; 42 update[5] = (current[5] + concurrent) / 2; 43 update[6] = current[6] > input ? current[6] : input; 44 update[7] = current[7] > output ? current[7] : output; 45 update[8] = current[8] > elapsed ? current[8] : elapsed; 46 update[9] = current[9] > concurrent ? current[9] : concurrent; 47 } 48 } while (!reference.compareAndSet(current, update)); 49 }
实际上这里聚合了1min钟的统计数据到statisticsMap中。
六、Monitor使用MonitorService存储数据到队列
1 private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3, new NamedThreadFactory("DubboMonitorSendTimer", true)); 2 private final ScheduledFuture<?> sendFuture; 3 private final long monitorInterval; 4 5 public DubboMonitor(Invoker<MonitorService> monitorInvoker, MonitorService monitorService) { 6 this.monitorInvoker = monitorInvoker; 7 this.monitorService = monitorService; 8 this.monitorInterval = monitorInvoker.getUrl().getPositiveParameter("interval", 60000); 9 // collect timer for collecting statistics data 10 sendFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() { 11 public void run() { 12 // collect data 13 try { 14 send(); 15 } catch (Throwable t) { 16 logger.error("Unexpected error occur at send statistic, cause: " + t.getMessage(), t); 17 } 18 } 19 }, monitorInterval, monitorInterval, TimeUnit.MILLISECONDS); 20 }
1 public void send() { 2 if (logger.isInfoEnabled()) { 3 logger.info("Send statistics to monitor " + getUrl()); 4 } 5 String timestamp = String.valueOf(System.currentTimeMillis()); 6 for (Map.Entry<Statistics, AtomicReference<long[]>> entry : statisticsMap.entrySet()) { 7 // get statistics data 8 Statistics statistics = entry.getKey(); 9 AtomicReference<long[]> reference = entry.getValue(); 10 long[] numbers = reference.get(); 11 long success = numbers[0]; 12 long failure = numbers[1]; 13 long input = numbers[2]; 14 long output = numbers[3]; 15 long elapsed = numbers[4]; 16 long concurrent = numbers[5]; 17 long maxInput = numbers[6]; 18 long maxOutput = numbers[7]; 19 long maxElapsed = numbers[8]; 20 long maxConcurrent = numbers[9]; 21 22 // send statistics data 23 URL url = statistics.getUrl() 24 .addParameters(MonitorService.TIMESTAMP, timestamp, 25 MonitorService.SUCCESS, String.valueOf(success), 26 MonitorService.FAILURE, String.valueOf(failure), 27 MonitorService.INPUT, String.valueOf(input), 28 MonitorService.OUTPUT, String.valueOf(output), 29 MonitorService.ELAPSED, String.valueOf(elapsed), 30 MonitorService.CONCURRENT, String.valueOf(concurrent), 31 MonitorService.MAX_INPUT, String.valueOf(maxInput), 32 MonitorService.MAX_OUTPUT, String.valueOf(maxOutput), 33 MonitorService.MAX_ELAPSED, String.valueOf(maxElapsed), 34 MonitorService.MAX_CONCURRENT, String.valueOf(maxConcurrent) 35 ); 36 monitorService.collect(url); 37 38 // reset 39 long[] current; 40 long[] update = new long[LENGTH]; 41 do { 42 current = reference.get(); 43 if (current == null) { 44 update[0] = 0; 45 update[1] = 0; 46 update[2] = 0; 47 update[3] = 0; 48 update[4] = 0; 49 update[5] = 0; 50 } else { 51 update[0] = current[0] - success; 52 update[1] = current[1] - failure; 53 update[2] = current[2] - input; 54 update[3] = current[3] - output; 55 update[4] = current[4] - elapsed; 56 update[5] = current[5] - concurrent; 57 } 58 } while (!reference.compareAndSet(current, update)); 59 } 60 }
- 首先从聚合数据存储器statisticsMap中获取相关统计数据并存储到数据总线Url中
- 之后调用MonitorService(这里是SimpleMonitorService),将统计数据存储到一个BlockingQueue中
注意:这里有一个改进点
- 由于monitorService.collect(url)是远程调用,这里在for循环体中执行远程调用,实际上是不合适的,我们可以将所有的url先暂存在一个List<URL>中,最后,使用一次monitorService.collect(urlList)即可 - 此时,可适当缩短数据发送时间。
SimpleMonitorService:
1 private final BlockingQueue<URL> queue; 2 3 public void collect(URL statistics) { 4 queue.offer(statistics); 5 if (logger.isInfoEnabled()) { 6 logger.info("collect statistics: " + statistics); 7 } 8 }
七、MonitorService将数据写入本地文件
1 private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboMonitorTimer", true)); 2 private final ScheduledFuture<?> chartFuture; 3 private final Thread writeThread; 4 private final BlockingQueue<URL> queue; 5 private String statisticsDirectory = "statistics"; 6 private String chartsDirectory = "charts"; 7 private volatile boolean running = true; 8 9 public SimpleMonitorService() { 10 queue = new LinkedBlockingQueue<URL>(Integer.parseInt(ConfigUtils.getProperty("dubbo.monitor.queue", "100000"))); 11 writeThread = new Thread(new Runnable() { 12 public void run() { 13 while (running) { 14 try { 15 write(); // write statistics 16 } catch (Throwable t) { 17 logger.error("Unexpected error occur at write stat log, cause: " + t.getMessage(), t); 18 try { 19 Thread.sleep(5000); // retry after 5 secs 20 } catch (Throwable t2) { 21 } 22 } 23 } 24 } 25 }); 26 writeThread.setDaemon(true); 27 writeThread.setName("DubboMonitorAsyncWriteLogThread"); 28 writeThread.start(); 29 chartFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() { 30 public void run() { 31 try { 32 draw(); // draw chart 33 } catch (Throwable t) { 34 logger.error("Unexpected error occur at draw stat chart, cause: " + t.getMessage(), t); 35 } 36 } 37 }, 1, 300, TimeUnit.SECONDS); 38 statisticsDirectory = ConfigUtils.getProperty("dubbo.statistics.directory"); 39 chartsDirectory = ConfigUtils.getProperty("dubbo.charts.directory"); 40 }
write()将统计数据写入文件,draw()将统计数据画成图片。这两种方式在实际使用中都不会用到。
最后来看一下获取Monitor实例的过程(帮助我们开发自定义的Monitor):
1 Monitor monitor = monitorFactory.getMonitor(url);
MonitorFilter中的monitorFactory实例是:MonitorFactory$Adaptive。
1 package com.alibaba.dubbo.monitor; 2 3 import com.alibaba.dubbo.common.extension.ExtensionLoader; 4 5 public class MonitorFactory$Adaptive implements com.alibaba.dubbo.monitor.MonitorFactory { 6 public com.alibaba.dubbo.monitor.Monitor getMonitor(com.alibaba.dubbo.common.URL arg0) { 7 if (arg0 == null) throw new IllegalArgumentException("url == null"); 8 com.alibaba.dubbo.common.URL url = arg0; 9 String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); 10 if (extName == null) 11 throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.monitor.MonitorFactory) name from url(" + url.toString() + ") use keys([protocol])"); 12 com.alibaba.dubbo.monitor.MonitorFactory extension = (com.alibaba.dubbo.monitor.MonitorFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.monitor.MonitorFactory.class).getExtension(extName); 13 return extension.getMonitor(arg0); 14 } 15 }
首先,根据输入的url中的protocol来获取相关的MonitorFactory(这里protocol默认是dubbo,所以获取的是DubboMonitorFactory,可以通过指定dubbo:monitor标签中的protocol属性来自定义获取XxxMonitorFactory),之后调用DubboMonitorFactory.getMonitor(arg0)。该方法在其父类AbstractMonitorFactory中:
1 /** 2 * AbstractMonitorFactory. (SPI, Singleton, ThreadSafe) 3 */ 4 public abstract class AbstractMonitorFactory implements MonitorFactory { 5 private static final Logger logger = LoggerFactory.getLogger(AbstractMonitorFactory.class); 6 7 // lock for getting monitor center 8 private static final ReentrantLock LOCK = new ReentrantLock(); 9 10 // monitor centers Map<RegistryAddress, Registry> 11 private static final Map<String, Monitor> MONITORS = new ConcurrentHashMap<String, Monitor>(); 12 13 private static final Map<String, ListenableFuture<Monitor>> FUTURES = new ConcurrentHashMap<String, ListenableFuture<Monitor>>(); 14 15 private static final ExecutorService executor = new ThreadPoolExecutor(0, 10, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new NamedThreadFactory("DubboMonitorCreator", true)); 16 17 public static Collection<Monitor> getMonitors() { 18 return Collections.unmodifiableCollection(MONITORS.values()); 19 } 20 21 public Monitor getMonitor(URL url) { 22 url = url.setPath(MonitorService.class.getName()).addParameter(Constants.INTERFACE_KEY, MonitorService.class.getName()); 23 String key = url.toServiceStringWithoutResolving();// dubbo://10.211.55.5:9090/com.alibaba.dubbo.monitor.MonitorService 24 Monitor monitor = MONITORS.get(key); 25 Future<Monitor> future = FUTURES.get(key); 26 if (monitor != null || future != null) { 27 return monitor; 28 } 29 30 LOCK.lock(); 31 try { 32 monitor = MONITORS.get(key); 33 future = FUTURES.get(key); 34 if (monitor != null || future != null) { 35 return monitor; 36 } 37 38 final URL monitorUrl = url; 39 // 使用另外的线程MonitorCreator来创建Monitor实例(原因是:即使Monitor创建失败,也不会影响主流程) 40 final ListenableFutureTask<Monitor> listenableFutureTask = ListenableFutureTask.create(new MonitorCreator(monitorUrl)); 41 listenableFutureTask.addListener(new MonitorListener(key)); 42 executor.execute(listenableFutureTask); 43 FUTURES.put(key, listenableFutureTask); 44 45 return null; 46 } finally { 47 // unlock 48 LOCK.unlock(); 49 } 50 } 51 52 protected abstract Monitor createMonitor(URL url); 53 54 // Callable和Runnable一样,也是创建一个线程去执行,只是Callable有返回值(T call()),而Runnable无返回值(void run()) 55 class MonitorCreator implements Callable<Monitor> { 56 57 private URL url; 58 59 public MonitorCreator(URL url) { 60 this.url = url; 61 } 62 63 @Override 64 public Monitor call() throws Exception { 65 Monitor monitor = AbstractMonitorFactory.this.createMonitor(url);//调用子类的createMonitor方法创建Monitor 66 return monitor; 67 } 68 } 69 70 class MonitorListener implements Runnable { 71 72 private String key; 73 74 public MonitorListener(String key) { 75 this.key = key; 76 } 77 // listenableFutureTask一旦isDone()完成(正常完成、抛出异常、被中断等),就会立即执行该方法 78 @Override 79 public void run() { 80 try { 81 ListenableFuture<Monitor> listenableFuture = AbstractMonitorFactory.FUTURES.get(key); 82 AbstractMonitorFactory.MONITORS.put(key, listenableFuture.get()); 83 AbstractMonitorFactory.FUTURES.remove(key); 84 } catch (InterruptedException e) { 85 logger.warn("Thread was interrupted unexpectedly, monitor will never be got."); 86 AbstractMonitorFactory.FUTURES.remove(key); 87 } catch (ExecutionException e) { 88 logger.warn("Create monitor failed, monitor data will not be collected until you fix this problem. ", e); 89 } 90 } 91 } 92 }
来看DubboMonitorFactory.createMonitor(url):
1 /** 2 * DefaultMonitorFactory 3 */ 4 public class DubboMonitorFactory extends AbstractMonitorFactory { 5 6 private Protocol protocol; 7 8 private ProxyFactory proxyFactory; 9 10 public void setProtocol(Protocol protocol) { 11 this.protocol = protocol; 12 } 13 14 public void setProxyFactory(ProxyFactory proxyFactory) { 15 this.proxyFactory = proxyFactory; 16 } 17 18 @Override 19 protected Monitor createMonitor(URL url) { 20 url = url.setProtocol(url.getParameter(Constants.PROTOCOL_KEY, "dubbo")); 21 if (url.getPath() == null || url.getPath().length() == 0) { 22 url = url.setPath(MonitorService.class.getName()); 23 } 24 String filter = url.getParameter(Constants.REFERENCE_FILTER_KEY); 25 if (filter == null || filter.length() == 0) { 26 filter = ""; 27 } else { 28 filter = filter + ","; 29 } 30 url = url.addParameters(Constants.CLUSTER_KEY, "failsafe", Constants.CHECK_KEY, String.valueOf(false), 31 Constants.REFERENCE_FILTER_KEY, filter + "-monitor"); 32 // 创建Invoker<MonitorService>,内部会构造与MonitorService实现类SimpleMonitorService所在的检测中心dubbo-simple-monitor的长连接 33 Invoker<MonitorService> monitorInvoker = protocol.refer(MonitorService.class, url); 34 // 获取MonitorService的代理 35 MonitorService monitorService = proxyFactory.getProxy(monitorInvoker); 36 return new DubboMonitor(monitorInvoker, monitorService); 37 } 38 39 }
注意:这里的SimpleMonitorService其实会部署在dubbo-simple-monitor中,被provider和consumer中的DubboMonitor调用。