之前发过一篇帖子应用.Net+Consul维护RabbitMq的高可用性,然后最近老大问我当初我这么搞是抽的什么想法- -然后顺便贴了两行C#代码:

  1.          var factory = new ConnectionFactory()
  2. {
  3. UserName = "username",
  4. Password = "password",
  5. AutomaticRecoveryEnabled = true,
  6. TopologyRecoveryEnabled = true
  7. };
  8. Connection = factory.CreateConnection(new string[] { "ip1", "ip2", "ip3" });

AutomaticRecoveryEnabled的作用就是断线重连,假如当前的连接断开了,连接不会释放

TopologyRecoveryEnabled 重连后恢复当前的工作进程,比如channel、queue、发布的消息进度等。

看上去其实却是比较方便的,为了狡辩我当场列出了我设计的方案的主要优点:

1.rabbitmq网关设计的时候一方面是为了让客户端能够保证建立与master节点的连接,直连master性能较高。

2.通过配置队列名+VirthHost可以获取队列信息,Consul可以起到配置中心的作用,可配置性高一些(其实此处是狡辩。。)

3.RabbitMQ与Master队列建立的连接在发生故障时会第一时间查询网关获取新的Mater队列信息进行重连(当然代码内部也有重试机制,不会随便就更换连接的),相比于AutomaticRecoveryEnabled效率更高一些。

4.客户端SDK内部实现定时更新队列连接,发现Master节点更换时重新建立连接

看上去我设计的方案还是有点意义的(其实是真懒得改代码)

不过此处有个问题就是既然创建连接时的参数可以提供多个IP的集合,假如RabbitMQ提供的客户端SDK内部实现的更好,那我狡辩什么不也完戏了吗。。。于是假装下载了下客户端sdk代码扫了扫,此处以C#代码为例,代码还是比较简单的。github地址

直接定位ConnectionFactory类找CreateConnection()方法

  1. /// <summary>
  2. /// Create a connection using a list of hostnames using the configured port.
  3. /// By default each hostname is tried in a random order until a successful connection is
  4. /// found or the list is exhausted using the DefaultEndpointResolver.
  5. /// The selection behaviour can be overriden by configuring the EndpointResolverFactory.
  6. /// </summary>
  7. /// <param name="hostnames">
  8. /// List of hostnames to use for the initial
  9. /// connection and recovery.
  10. /// </param>
  11. /// <returns>Open connection</returns>
  12. /// <exception cref="BrokerUnreachableException">
  13. /// When no hostname was reachable.
  14. /// </exception>
  15. public IConnection CreateConnection(IList<string> hostnames)
  16. {
  17. return CreateConnection(hostnames, null);
  18. }
  19.  
  20. /// <summary>
  21. /// Create a connection using a list of hostnames using the configured port.
  22. /// By default each endpoint is tried in a random order until a successful connection is
  23. /// found or the list is exhausted.
  24. /// The selection behaviour can be overriden by configuring the EndpointResolverFactory.
  25. /// </summary>
  26. /// <param name="hostnames">
  27. /// List of hostnames to use for the initial
  28. /// connection and recovery.
  29. /// </param>
  30. /// <param name="clientProvidedName">
  31. /// Application-specific connection name, will be displayed in the management UI
  32. /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot
  33. /// be used as a connection identifier, e.g. in HTTP API requests.
  34. /// This value is supposed to be human-readable.
  35. /// </param>
  36. /// <returns>Open connection</returns>
  37. /// <exception cref="BrokerUnreachableException">
  38. /// When no hostname was reachable.
  39. /// </exception>
  40. public IConnection CreateConnection(IList<string> hostnames, String clientProvidedName)
  41. {
  42. var endpoints = hostnames.Select(h => new AmqpTcpEndpoint(h, this.Port, this.Ssl));
  43. return CreateConnection(new DefaultEndpointResolver(endpoints), clientProvidedName);
  44. }
  45.  
  46. /// <summary>
  47. /// Create a connection using a list of endpoints. By default each endpoint will be tried
  48. /// in a random order until a successful connection is found or the list is exhausted.
  49. /// The selection behaviour can be overriden by configuring the EndpointResolverFactory.
  50. /// </summary>
  51. /// <param name="endpoints">
  52. /// List of endpoints to use for the initial
  53. /// connection and recovery.
  54. /// </param>
  55. /// <returns>Open connection</returns>
  56. /// <exception cref="BrokerUnreachableException">
  57. /// When no hostname was reachable.
  58. /// </exception>
  59. public IConnection CreateConnection(IList<AmqpTcpEndpoint> endpoints)
  60. {
  61. return CreateConnection(new DefaultEndpointResolver(endpoints), null);
  62. }
  63.  
  64. /// <summary>
  65. /// Create a connection using an IEndpointResolver.
  66. /// </summary>
  67. /// <param name="endpointResolver">
  68. /// The endpointResolver that returns the endpoints to use for the connection attempt.
  69. /// </param>
  70. /// <param name="clientProvidedName">
  71. /// Application-specific connection name, will be displayed in the management UI
  72. /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot
  73. /// be used as a connection identifier, e.g. in HTTP API requests.
  74. /// This value is supposed to be human-readable.
  75. /// </param>
  76. /// <returns>Open connection</returns>
  77. /// <exception cref="BrokerUnreachableException">
  78. /// When no hostname was reachable.
  79. /// </exception>
  80. public IConnection CreateConnection(IEndpointResolver endpointResolver, String clientProvidedName)
  81. {
  82. IConnection conn;
  83. try
  84. {
  85. if (AutomaticRecoveryEnabled)
  86. {
  87. var autorecoveringConnection = new AutorecoveringConnection(this, clientProvidedName);
  88. autorecoveringConnection.Init(endpointResolver);
  89. conn = autorecoveringConnection;
  90. }
  91. else
  92. {
  93. IProtocol protocol = Protocols.DefaultProtocol;
  94. conn = protocol.CreateConnection(this, false, endpointResolver.SelectOne(this.CreateFrameHandler), clientProvidedName);
  95. }
  96. }
  97. catch (Exception e)
  98. {
  99. throw new BrokerUnreachableException(e);
  100. }
  101.  
  102. return conn;
  103. }

代码比较直观,第二个方法的时候把传入的字符串集合转换成了AmqpTcpEndpoint集合,AmqpTcpEndpoint里包含队列绑定信息,包含默认ip,端口,SSL配置信息,RabbitMQ的Amqp协议信息,初始化Socket连接的协议类型(一个AddressFamily枚举),当然都是可配置的(除了ip貌似都喜欢使用默认的)。

然后就倒主要部分了。

  1. if (AutomaticRecoveryEnabled)
  2. {
  3. var autorecoveringConnection = new AutorecoveringConnection(this, clientProvidedName);
  4. autorecoveringConnection.Init(endpointResolver);
  5. conn = autorecoveringConnection;
  6. }
  7. else
  8. {
  9. IProtocol protocol = Protocols.DefaultProtocol;
  10. conn = protocol.CreateConnection(this, false, endpointResolver.SelectOne(this.CreateFrameHandler), clientProvidedName);
  11. }

由于主要想看看传入多个连接是不是可以智能的选择master连接,所以此处直接看else里的代码就行了。。。(其实一样。。)

protocol.CreateConnection方法调用时传入的第三个参数endpointResolver.SelectOne(this.CreateFrameHandler)作用其实就是选择连接然后扔到CreateFrameHandler委托里作为参数进行Socket初始化的。看一下SelectOne这个扩展方法即可。。看了两行终于可以继续狡辩了。。

  1. public static T SelectOne<T>(this IEndpointResolver resolver, Func<AmqpTcpEndpoint, T> selector)
  2. {
  3. var t = default(T);
  4. Exception exception = null;
  5. foreach(var ep in resolver.All())
  6. {
  7. try
  8. {
  9. t = selector(ep);
  10. if(t.Equals(default(T)) == false)
  11. {
  12. return t;
  13. }
  14. }
  15. catch (Exception e)
  16. {
  17. exception = e;
  18. }
  19. }
  20.  
  21. if(Object.Equals(t, default(T)) && exception != null)
  22. {
  23. throw exception;
  24. }
  25.  
  26. return t;
  27. }

这个foreach还是“比较好的”,能够建立一个Socket连接就愉快的返回了!,不过这个resolver.All()里是不是还有玄机呢!看了一眼。。终于放心了。。

  1. public class DefaultEndpointResolver : IEndpointResolver
  2. {
  3. private List<AmqpTcpEndpoint> endpoints;
  4. private Random rnd = new Random();
  5.  
  6. public DefaultEndpointResolver (IEnumerable<AmqpTcpEndpoint> tcpEndpoints)
  7. {
  8. this.endpoints = tcpEndpoints.ToList();
  9. }
  10.  
  11. public IEnumerable<AmqpTcpEndpoint> All()
  12. {
  13. return endpoints.OrderBy(item => rnd.Next());
  14. }

就是随机排序一下。。不过这么看自己实现下IEndpointResolver接口改个选择master队列的策略也是不错的。

RabbitMQ镜像队列初始化连接时的“优化”的更多相关文章

  1. rabbitmq——镜像队列

    转自:http://my.oschina.net/hncscwc/blog/186350?p=1 1. 镜像队列的设置 镜像队列的配置通过添加policy完成,policy添加的命令为: rabbit ...

  2. RabbitMQ镜像队列集群搭建、与SpringBoot整合

    镜像模式 集群模式非常经典的就是Mirror镜像模式,保证100%数据不丢失,在实际工作中也是用的最多的,并且实现集群比较的简单. Mirror镜像队列,目的是为了保证 RabbitMQ 数据的高可靠 ...

  3. springboot~rabbitmq的队列初始化和绑定

    配置文件,在rabbit中自动建立exchange,queue和绑定它们的关系 代码里初始化exchange 代码里初始化queue 代码里绑定exchange,queue和routekey 配置文件 ...

  4. RabbitMQ如何保证发送端消息的可靠投递-发生镜像队列发生故障转移时

    上一篇最后提到了mandatory这个参数,对于设置mandatory参数个人感觉还是很重要的,尤其在RabbitMQ镜像队列发生故障转移时. 模拟个测试环境如下: 首先在集群队列中增加两个镜像队列的 ...

  5. Oracle 表三种连接方式(sql优化)

    在查看sql执行计划时,我们会发现表的连接方式有多种,本文对表的连接方式进行介绍以便更好看懂执行计划和理解sql执行原理. 一.连接方式: 嵌套循环(Nested Loops (NL)) (散列)哈希 ...

  6. 18-基于CentOS7搭建RabbitMQ3.10.7集群镜像队列+HaProxy+Keepalived高可用架构

    集群架构 虚拟机规划 IP hostname 节点说明 端口 控制台地址 192.168.247.150 rabbitmq.master rabbitmq master 5672 http://192 ...

  7. RabbitMQ镜像模式双节点部署时故障转移过程中队列中消息的状态

    场景 现有节点Node1和Node2,建立Exchange:yu.exchange,创建队列yu1.queue镜像队列master位于Node1,yu2.queue镜像队列位于Node2,使用topi ...

  8. RabbitMQ 高可用之镜像队列

    如果RabbitMQ集群只有一个broker节点,那么该节点的失效将导致整个服务临时性的不可用,并且可能会导致message的丢失(尤其是在非持久化message存储于非持久化queue中的时候).可 ...

  9. RabbitMQ高可用镜像队列

    ## RabbitMQ高可用镜像队列 在分布式系统中,通常使用多个术语来标识主要副本和辅助副本.本指南通常使用"主"来引用队列的主要副本,而对于辅助副本则使用"镜像&qu ...

随机推荐

  1. [转]Shared——回调函数是什么

    本文内容转自知乎 作者:no.body 链接:https://www.zhihu.com/question/19801131/answer/27459821 回调函数(callback)是什么? 什么 ...

  2. java停止一个线程

    Thread类中有start(), stop()方法,不过stop方法已经被废弃掉. 平时其实也有用过,共享一个变量,相当于标志,不断检查标志,判断是否退出线程 如果有阻塞,需要使用Thread的in ...

  3. SQL server查找指定表的所有索引

    WITH tmp AS ( SELECT indexname = a.name , tablename = c.name , indexcolumns = d.name , a.indid FROM ...

  4. FineReport和泛微OA(Ecology)的单点登录集成方案

    最近出现了很多关于帆软报表和泛微OA的集成问题,均出现在“单点登录”上.直接也有相关的文章介绍一些FineReport和泛微集成的背景.价值等,以及FineReport和OA的深度集成的方案,但是并没 ...

  5. IEC62304-2006解读

    IEC62304强调医疗软件在明确和满足其预期用途的前提下,不能引发不可接受的风险 62304提供一个医疗软件开发的框架,并指出框架下每个过程的要求,62304将过程分解为若干活动,活动分解为若干任务 ...

  6. GOOGLE高级搜索技巧

    前记:  我是完整的看完了.内容有点乱啊,自己没有时间整理,先放在自己的印象笔记里了....   二,GOOGLE特色 GOOGLE支持多达132种语言,包括简体中文和繁体中文: GOOGLE网站只提 ...

  7. ActiveMQ5.8.0安装及启动

    一.下载 官网地址:http://activemq.apache.org/download.html Windows版本:apache-activemq-5.8.0-bin.zip Linux版本:a ...

  8. 【 PostgreSQL】十条实用数据库SQL优化建议

    基于PostgreSQL,总结几条常用的查询操作的优化建议,部分也适用于Oracle等数据库. 1.选择合适的分布键 分布键选择不当会导致重分布.数据分布不均等,而数据分布不均会使SQL集中在一个se ...

  9. .NET的那些事儿(9)——C# 2.0 中用iTextSharp制作PDF(基础篇) .

    该文主要介绍如何借助iTextSharp在C# 2.0中制作PDF文件,本文的架构大致按照iTextSharp的操作文档进行翻译,如果需要查看原文,请点击一下链接:http://itextsharp. ...

  10. Android开发(7)数据库和Content Provider

    问题聚焦: 思想:应用程序数据的共享 对数据库的访问仅限于创建它的应用程序,但是事情不是绝对的 Content Provider提供了一个标准的接口,可供其他应用程序访问和使用其他程序的数据 下面我们 ...