• 第六章 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调用。

  • 相关阅读:
    01背包问题需要找出相应路径
    单链表的正序输出和逆序输出
    二叉树之叶子节点个数
    01背包问题
    STL之map和multimap(关联容器)
    python的tips:字符和字符串的问题
    postman的使用(转载)
    python tips(3);import的机制
    python每日一类(5):itertools模块
    python每日一类(4):slice
  • 原文地址:https://www.cnblogs.com/hanease/p/16295228.html
Copyright © 2020-2023  润新知