使用dubbo时候要尽量了解源码,不然会很容易入坑。

一、服务消费端ReferenceConfig需要自行缓存

ReferenceConfig实例是个很重的实例,每个ReferenceConfig实例里面都维护了与服务注册中心的一个长链,并且维护了与所有服务提供者的的长链。假设有一个服务注册中心和N个服务提供者,那么每个ReferenceConfig实例里面维护了N+1个长链,如果频繁的生成ReferenceConfig实例,可能会造成性能问题,甚至产生内存或者连接泄露的风险。特别是使用dubbo api编程时候容易忽略这个问题。

为了解决这个问题,之前都是自行缓存,但是自从dubbo2.4.0版本后,dubbo 提供了简单的工具类 ReferenceConfigCache 用于缓存ReferenceConfig 实例。使用如下:

  1. /创建服务消费实例
  2. ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>();
  3. reference.setInterface(XxxService.class);
  4. reference.setVersion("1.0.0");
  5. ......
  6. //获取dubbo提供的缓存
  7. ReferenceConfigCache cache = ReferenceConfigCache.getCache();
  8. // cache.get方法中会缓存 reference对象,并且调用reference.get方法启动ReferenceConfig,并返回经过代理后的服务接口的对象
  9. XxxService xxxService = cache.get(reference);
  10.  
  11. // 使用xxxService对象
  12. xxxService.sayHello();

需要注意的是 Cache内持有ReferenceConfig对象的引用,不要在外部再调用ReferenceConfig的destroy方法了,这会导致Cache内的ReferenceConfig失效!

如果要销毁 Cache 中的 ReferenceConfig ,将销毁 ReferenceConfig 并释放对应的资源,具体使用下面方法来销毁

  1. ReferenceConfigCache cache = ReferenceConfigCache.getCache();
  2. cache.destroy(reference);

另外以服务 Group、接口、版本为缓存的 Key,ReferenceConfig实例为对应的value。如果你需要使用自定义的key,可以在创建cache时候调用ReferenceConfigCache cache = ReferenceConfigCache.getCache(keyGenerator );方法传递自定义的keyGenerator。

二、 并发控制

2.1 服务消费方并发控制 在服务消费方法进行并发控制需要设置actives参数,如下:

  1. <dubbo:reference id="userService" interface="com.test.UserServiceBo"
  2. group="dubbo" version="1.0.0" timeout="3000" actives="10"/>

设置com.test.UserServiceBo接口中所有方法,每个方法最多同时并发请求10个请求。

也可以使用下面方法设置接口中的单个方法的并发请求个数,如下:

  1. <dubbo:reference id="userService" interface="com.test.UserServiceBo"
  2. group="dubbo" version="1.0.0" timeout="3000">
  3. <dubbo:method name="sayHello" actives="10" />
  4. </dubbo:reference>

如上设置sayHello方法的并发请求数量最大为10,如果客户端请求该方法并发超过了10则客户端会被阻塞,等客户端并发请求数量少于10的时候,该请求才会被发送到服务提供方服务器。在dubbo中客户端并发控制是使用ActiveLimitFilter过滤器来控制的,代码如下:

  1. public class ActiveLimitFilter implements Filter {
  2.  
  3. public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
  4. URL url = invoker.getUrl();
  5. String methodName = invocation.getMethodName();
  6. //获取设置的acvites的值,默认为0
  7. int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
  8. //获取当前方法目前并发请求数量
  9. RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
  10. if (max > 0) {//说明设置了actives变量
  11. long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);
  12. long start = System.currentTimeMillis();
  13. long remain = timeout;
  14. int active = count.getActive();
  15. //如果该方法并发请求数量大于设置值,则挂起当前线程。
  16. if (active >= max) {
  17. synchronized (count) {
  18. while ((active = count.getActive()) >= max) {
  19. try {
  20. count.wait(remain);
  21. } catch (InterruptedException e) {
  22. }
  23. //如果等待时间超时,则抛出异常
  24. long elapsed = System.currentTimeMillis() - start;
  25. remain = timeout - elapsed;
  26. if (remain <= 0) {
  27. throw new RpcException("Waiting concurrent invoke timeout in client-side for service: "
  28. + invoker.getInterface().getName() + ", method: "
  29. + invocation.getMethodName() + ", elapsed: " + elapsed
  30. + ", timeout: " + timeout + ". concurrent invokes: " + active
  31. + ". max concurrent invoke limit: " + max);
  32. }
  33. }
  34. }
  35. }
  36. }
  37. //没有限流时候,正常调用
  38. try {
  39. long begin = System.currentTimeMillis();
  40. RpcStatus.beginCount(url, methodName);
  41. try {
  42. Result result = invoker.invoke(invocation);
  43. RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);
  44. return result;
  45. } catch (RuntimeException t) {
  46. RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);
  47. throw t;
  48. }
  49. } finally {
  50. if (max > 0) {
  51. synchronized (count) {
  52. count.notify();
  53. }
  54. }
  55. }
  56. }
  57.  
  58. }

可知客户端并发控制,是如果当并发量达到指定值后,当前客户端请求线程会被挂起,如果在等待超时期间并发请求量少了,那么阻塞的线程会被激活,然后发送请求到服务提供方,如果等待超时了,则直接抛出异常,这时候服务根本都没有发送到服务提供方服务器。

三、 改进的广播策略

前面我们讲解集群容错时候谈到有一个广播策略,该策略主要用于对所有服务提供者进行广播消息,那么有个问题需要思考,广播是是说你在客户端调用接口一次,内部就是轮询调用所有服务提供者的机器的服务,那么你调用一次该接口,返回值是什么那?比如内部轮询了10台机器,每个机器应该都有一个返回值,那么你调用的这一次返回值是10个返回值的组成?其实不是,返回的是轮询调用的最后一个机器结果,那么如果我们想把所有的机器返回的结果聚合起来如何做的?

结合源码分析 bubble 使用注意事项的更多相关文章

  1. jQuery源码分析系列(36) : Ajax - 类型转化器

    什么是类型转化器? jQuery支持不同格式的数据返回形式,比如dataType为 xml, json,jsonp,script, or html 但是浏览器的XMLHttpRequest对象对数据的 ...

  2. jQuery-1.9.1源码分析系列(二)jQuery选择器

    1.选择器结构 jQuery的选择器根据源码可以分为几块 init: function( selector, context, rootjQuery ) { ... // HANDLE: $(&quo ...

  3. List 接口以及实现类和相关类源码分析

    List 接口以及实现类和相关类源码分析 List接口分析 接口描述 用户可以对列表进行随机的读取(get),插入(add),删除(remove),修改(set),也可批量增加(addAll),删除( ...

  4. ZRender源码分析3:Painter(View层)-上

    回顾 上一篇说到:ZRender源码分析2:Storage(Model层),这次咱看来看看Painter-View层 总体理解 Painter这个类主要负责MVC中的V(View)层,负责将Stora ...

  5. 一个普通的 Zepto 源码分析(三) - event 模块

    一个普通的 Zepto 源码分析(三) - event 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块, ...

  6. Android源码分析—深入认识AsyncTask内部机制

    本文转载http://blog.csdn.net/singwhatiwanna/article/details/17596225该博主博文,谢谢该博主提供的好文章! 前言 什么是AsyncTask,相 ...

  7. [源码分析]AbstractStringBuilder

    [源码分析]AbstractStringBuilder Java中, AbstractStringBuilder是 StringBuilder 和 StringBuffer 的父类. 所以了解Stri ...

  8. django Rest Framework----APIView 执行流程 APIView 源码分析

    在django—CBV源码分析中,我们是分析的from django.views import View下的执行流程,这篇博客我们介绍django Rest Framework下的APIView的源码 ...

  9. 并发-ConcurrentHashMap源码分析

    ConcurrentHashMap 参考: http://www.cnblogs.com/chengxiao/p/6842045.html https://my.oschina.net/hosee/b ...

随机推荐

  1. C# 的AOP实现

    闲来无事,做了一个AOP示例,此示例只能捕获方法调用事件,无法动态阻止方法调用的执行.因为取消后构造返回值成了难题,返回null貌似会报错.如果不需要这个功能,其实还是很完美的. 缺点是没有以接口方式 ...

  2. nginx配置基于域名的虚拟主机

    其实基于域名和基于ip的虚拟主机配置是差不多的,在配置基于ip的虚拟主机上我们只需要修改几个地方就能变成基于域名的虚拟主机,一个是要修改域名,一个是host文件直接看代码 [root@localhos ...

  3. firefox一搜索就提示是否进入***网站和取消占地方的标题栏

    来看一下这个蛋疼的提示 每次都要手动关闭.后来在网上看到一个解决方法 解决方法: 地址栏输入about:config回车进入设置, 去掉警告那个勾 点击确定,进入配置页 搜索 取消最上面方人的fire ...

  4. HDU1370(中国剩余定理)

    昨天我细致一想,发现自己之前的分类(用OJ来划分,毫无意义啊.)太失败了,所以我又一次划分了一下大分类,在分到数论的时候,我就想起了中国剩余定理了.于是乎今天就刷了一题中国剩余定理的题目了.话说太久没 ...

  5. 论 大并发 下的 乐观锁定 Redis锁定 和 新时代事务

    在 <企业应用架构模式> 中 提到了 乐观锁定, 用 时间戳 来 判定 交易 是否有效, 避免 传统事务 的 表锁定 造成 的 瓶颈 . 在 现在的 大并发 的 大环境下, 传统事务 及其 ...

  6. Java集合整理

    0,基础概念 Collection:统计大小.插入或删除数据.清空.是否包含某条数据,等等.而Collection就是对这些常用操作进行提取,只是其很全面.很通用.size(),isEmpty(),c ...

  7. c#多线程与委托(转)

    一:线程在.net中提供了两种启动线程的方式,一种是不带参数的启动方式,另一种是带参数的启动的方式.不带参数的启动方式 如果启动参数时无需其它额外的信息,可以使用ThreadStart来实例化Thre ...

  8. 存储过程DT参数

    public static void TableValuedToDB(DataTable dt, string storedProcName, string TypeName) { using (Sq ...

  9. go bytes缓冲区使用介绍 -转自https://www.cnblogs.com/--xiaoyao--/p/5122138.html

    缓冲区原理简介: go字节缓冲区底层以字节切片做存储,切片存在长度len与容量cap, 缓冲区写从长度len的位置开始写,当len>cap时,会自动扩容.缓冲区读会从内置标记off位置开始读(o ...

  10. paramiko 实现ssh登录和sftp登录

    简单ssh登录 import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddP ...