应用.Net+Consul维护RabbitMq的高可用性

  懒人学习的过程就是工做中老大让干啥让作啥就研究研究啥,国庆放假回来的周末老大经过钉钉给我布置了个任务, RabbitMQ高可用解决方案,我想说钉钉太坑了:html

这是国庆事后9号周日晚上下班给的任务,我周一看到的时候一看,下周五,那岂不是21号,时间是如此的充裕!那不还早呢么。。恰巧同窗要面试了9号晚上一块儿吃饭,而后问了我几个算法,而后被鄙视了。。他说我一个前端都比你作后台的算法牛逼,你请客吧-。-因而周一到周三光学算法了(程序员为了吹牛逼,哪有啥节操啊)直到周四老大说,明天任务到期了!研究咋样了!此时才恍然大悟,钉钉你个坑货,下周五是14号!!前端

RabbitMQ 高可用集群

简单配置

  对于RabbitMQ 高可用集群的说明,我以为这篇文章讲的挺详细的,就不说了。配置集群的方式看官网就能够了 ,为了采起所谓的Active/Active方案,因此只能选镜像模式了(3.x版本以上才支持.再抄一段解释过来(与普通集群相比,其实质和普通模式不一样之处在于,消息实体会主动在镜像节点间同步,而不是在 consumer 取数据时临时拉取。该模式带来的反作用也很明显,除了下降系统性能外,若是镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通信大大消耗掉。因此在对可靠性要求较高的场合中适用),在搭建好RabbitMq集群之后,node

镜像模式能够经过命令行为队列添加同步策略,好比python

为全部队列应用镜像模式的策略git

rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'程序员

或者指定指定队列名的:面试

rabbitmqctl set_policy yu-ha "^yu" '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'算法

或者直接在管理页面添加后端

点击Admin菜单-->右侧的Policies选项-->左侧最下下边的Add / update a policyapi

 

 

name就是队列名,Pattern就是匹配的规则,好比写个^yu就是以yu开头的队列,ha-mode=啥就是会同步什么队列,好比=all的话就是同步全部匹配的队列。

而后新建队列的时候就能够指定那台机器上跑主队列了。

所谓的坑

       仍是抄一下这篇文章的内容,经常使用的手段就是经过HAProxy+KeepAlive保证RabbitMq的集群高可用:

建立 queue 的过程:

  1. LB 将 client request 分发到 node 2,client 建立队列 “NewQueue”,而后开始向其中放入 message。
  2. 最终,后端服务会对 node 2 上的 “NewQueue” 建立一个快照,并在一段时间内将其拷贝到node 1 和 3 上。这时候,node2 上的队列是 master Queue,node 1 和 3 上的队列是 slave queue。

假如如今 node2 宕机了:

  • node 2 再也不响应心跳,它会被认为已经被从集群中移出了
  • node 2 上的 master queue 再也不可用
  • RabbitMQ 将 node 1 或者 3 上的 salve instance 升级为 master instance

假设 master queue 还在 node 2 上,客户端经过 LB 访问该队列:

  1. 客户端链接到集群,要访问 “NewQueue” 队列
  2. LB 根据配置的轮询算法将请求分发到一个节点上
  3. 假设客户端请求被转到 node 3 上
  4. RabbitMQ 发现 “NewQueue” master node 是 node 2
  5. RabbitMQ 将消息转到 node 2 上
  6. 最终客户端成功链接到 node 2 上的 master 队列

可见,这种配置下,2/3 的客户端请求须要重定向,这会形成大几率的访问延迟,可是终究访问仍是会成功的。要优化的话,总共有两种方式:

  • 直接连到 master queue 所在的节点,这样就不须要重定向了。可是对这种方式,须要提早计算,而后告诉客户端哪一个节点上有 master queue。
  • 尽量地在全部节点间平均分布队列,减小重定向几率

 为了不这种n-1/n的这种重定向,知道Master queue所在的节点很重要啊,接下来就不抄了。

思路

     大体的意思就是这张图:

 

  1.将RabbitMq注册到Consul中(步骤1),经过Consul对RabbitMq进行健康监测,同时Consul提供配置中心的服务,能够存储一些RabbitMq的配置信息,好比队列帐号,密码,队列主机名,所在Ip等,举个例子:

  将RabbitMq服务注册到Consul:  

{
   "services": [{
     "id":"rabbit@rabbitmq1",
     "name":"RabbitMqServer",
     "tags":["rabbitMq"],
     "address": "192.168.1.101",
     "port": 15672,
     "checks": [
       {
         "Http": "http://192.168.1.101:15672/",
         "interval": "10s"
       }
     ]
   },
   {
     "id":"rabbit@rabbitmq2",
     "name":"RabbitMqServer",
     "tags":["rabbitMq"],
     "address": "192.168.1.102",
     "port": 15672,
     "checks": [
       {
         "Http": "http://192.168.1.102:15672/",
         "interval": "10s"
       }
     ]
   }
   ]
 } 
View Code

  将RabbitMq的队列信息存入到Consul中:

  2.业务服务须要配置QueueName+VirthHost,经过步骤2从RabbitMq网关进行队列信息的获取,而后才能经过步骤5与队列进行推拉操做,这里能够获取可用队列对应的Master队列所在的节点信息,避免n-1/n这种接受推送转发的问题。

  3.RabbitMq网关接受到业务服务的请求后,经过Consul获取集群中任意一个健康的RabbitMq队列的信息(Consul提供的健康监测功能),而后根据该队列得到与RabbitMq通讯的WebApi,RabbitMq的Http Api文档提供了获取队列详情的接口,好比获取队列对用的Master信息是可用经过接口:http://127.0.0.1:15672/api/queues/%2F/yu_queue,%2F对应的是VirthHost,是/的转码,yu_queue是队列名,这俩参数经过业务服务请求Api时提供,经过Api返回的Json字符串中包含了该队列Master的节点的对用信息,其中node属性对应的就是rabbitmq的节点名,如:rabbit@rabbitmq1,而后能够经过以前在consul中配置的RabbitMq信息来查找该节点对用的队列信息返回给业务服务。

  这里会有个坑须要注意:

  访问RabbitMq的Http Api是须要身份验证的,这个Basic验证的Token获取查了查文档没找到- -

  

  后来惊奇发现。。 Convert.ToBase64String(Encoding.ASCII.GetBytes(userName + ":" + password))); 哎。。不想多说了。。。

  还有个Api参数中带/的问题,.net 4.5以上的版本用HttpClient没啥问题,4.5如下版本或者用HttpWebRequest的时候须要对Uri作下处理

public static void ForceCanonicalPathAndQuery(Uri uri)
        {
            string paq = uri.PathAndQuery; // need to access PathAndQuery 
            FieldInfo flagsFieldInfo = typeof(Uri).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
            ulong flags = (ulong)flagsFieldInfo.GetValue(uri);
            flags &= ~((ulong)0x30); // Flags.PathNotCanonical|Flags.QueryNotCanonical 
            flagsFieldInfo.SetValue(uri, flags);
        } 
View Code

附加说明:

  其实思路很简单,不过明明能够经过封装一个SDK(何况原本就得封装- -)就完成的事情为何还要牵扯出Consul和多余的一个RabbitMq网关呢,何况有了RabbitMq网关岂不是说还要单点问题了?!

  针对单点问题。。我以为部署几份无状态的网关仍是没啥压力的吧。。SKD封装的时候本身轮训去吧!

  对于为何要用到Consul,一方面是为了健康监测,健康监测可让Consul经过Consul Http Api直接获取可用的RabbitMq的Http Api信息,还有就是配置中心,业务服务经过配置队列名,VirthHost,和获取队列服务的RabbitMq网关便可,RabbirMq网关负责经过配置中心获取队列信息,配置中心的数据是动态的,更新起来也比较方便。

  对于为啥不经过SDK直连RabbitMq的Api而是经过网关做为一个中间代理,在经过Consul获取队列信息时能够作个定时缓存,并且像队列的用户名密码的这种信息经过业务服务配置的话维护不方便,业务服务经过SDK直接经过Consul获取的话,依赖关系也会变得略微错综复杂。经过RabbitMq的Api也能够动态获取队列的集群节点信息,权限信息等,在业务服务SDK里按期更新也何尝不可,但SDK终究变复杂了,你干那个多不累么。。

  为何参数要VirthHost+QueueName,不一样部门的人总归不是同一我的。。

正在纠结的问题:

  针对这种思路,对于RabbitMq中的Routing模式Topic模式会有问题,单routekey对应单队列时能够经过队列获取Exhange下该routekey有效发送到某台服务器上的队列上,假如该routekey绑定的队列分布在多台服务器上,而且这些队列的主从分布在多台服务器上时,我经过获取到的“Master队列”只能针对某一队列时真Master,对于其余队列若是Master不在该ip上仍是会存在转发的问题。这个问题在拉数据时没啥问题(拉数据我是须要队列名的!),推数据时只用到Exchange+RouteKey,谁还管你的队列是主是从呢?

 

 

参考连接:

http://www.cnblogs.com/sammyliu/p/4730517.html

https://insidethecpu.com/2014/11/17/load-balancing-a-rabbitmq-cluster/

相关文章
相关标签/搜索