RabbitMQ+HAProxy构建高可用消息队列

用RabbitMQ的集群、镜像队列+HAProxy构建一个高可用的消息队列。

集群配置

主要参考官网:http://www.rabbitmq.com/clustering.html
现在有三台ubuntu服务器,ip分别是 192.168.56.111,192.168.56.112,192.168.56.113
如果没有这么多主机,本项目用到四个主机,可参考:http://blog.csdn.net/qq_34039315/article/details/77527082
服务器端安装配置rabbitmq,可参考:http://blog.csdn.net/qq_34039315/article/details/77587623

1、三台机器都需要在/etc/hosts里面输入相关的IP和机器名对应关系

192.168.56.111 ubuntu01
192.168.56.112 ubuntu02
192.168.56.113 ubuntu03

2、设置同一cookie

查看mq1的cookie

$ sudo cat /var/lib/rabbitmq/.erlang.cookie    #RFHZQHILNIHDGGKQYYNG

设置ubuntu02、ubuntu03的cookie为这个值,需要临时修改权限才能添加进去

$ sudo rabbitmqctl stop
$ sudo chmod 777 /var/lib/rabbitmq/.erlang.cookie
$ sudo echo 'RFHZQHILNIHDGGKQYYNG' >  /var/lib/rabbitmq/.erlang.cookie
$ sudo chmod 400 /var/lib/rabbitmq/.erlang.cookie
# 后台启动服务
$ sudo rabbitmq-server –detached&

3、ubuntu02、ubuntu03加入集群

$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl join_cluster rabbit@ubuntu01
$ sudo rabbitmqctl start_app

4、查看状态

可以看到3个节点都是采用disc模式的

$ sudo rabbitmqctl cluster_status
Cluster status of node rabbit@ubuntu03
[{nodes,[{disc,[rabbit@ubuntu01,rabbit@ubuntu02,rabbit@ubuntu03]}]},
 {running_nodes,[rabbit@ubuntu01,rabbit@ubuntu02,rabbit@ubuntu03]},
 {cluster_name,<<"rabbit@ubuntu02">>},
 {partitions,[]},
 {alarms,[{rabbit@ubuntu01,[]},{rabbit@ubuntu02,[]},{rabbit@ubuntu03,[]}]}]

5、修改ubuntu02节点为ram模式

$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl change_cluster_node_type ram
$ sudo rabbitmqctl start_app
$ sudo rabbitmqctl cluster_status
Cluster status of node rabbit@ubuntu02
[{nodes,[{disc,[rabbit@ubuntu03,rabbit@ubuntu01]},{ram,[rabbit@ubuntu02]}]},
 {running_nodes,[rabbit@ubuntu01,rabbit@ubuntu03,rabbit@ubuntu02]},
 {cluster_name,<<"rabbit@ubuntu02">>},
 {partitions,[]},
 {alarms,[{rabbit@ubuntu01,[]},{rabbit@ubuntu03,[]},{rabbit@ubuntu02,[]}]}]

可以看到成功修改了节点属性

6、ubuntu03退出集群(仅作尝试)

ubuntu03执行:

$ sudo rabbitmqctl stop_app
$ sudo rabbitmqctl reset
$ sudo rabbitmqctl start_app

ubuntu01执行:

$ sudo rabbitmqctl forget_cluster_node rabbit@ubuntu03

注意事项

cookie在所有节点上必须完全一样,同步时一定要注意。、

erlang是通过主机名来连接服务,必须保证各个主机名之间可以ping通。可以通过编辑/etc/hosts来手工添加主机名和IP对应关系。如果主机名ping不通,rabbitmq服务启动会失败。

如果queue是非持久化queue,则如果创建queue的那个节点失败,发送方和接收方可以创建同样的queue继续运作。但如果是持久化queue,则只能等创建queue的那个节点恢复后才能继续服务。

在集群元数据有变动的时候需要有disk node在线,但是在节点加入或退出的时候所有的disk node必须全部在线。如果没有正确退出disk node,集群会认为这个节点当掉了,在这个节点恢复之前不要加入其它节点。

RabbitMQ只要求集群中至少有一个磁盘节点,其他节点都可以是内存节点。当节点加入或者离开集群时,它们必须要将变更通知到至少一个磁盘节点。如果只有一个磁盘节点,磁盘节点奔溃后,集群可以继续路由消息(即保持运行),但是直到该节点恢复之前,无法更改任何东西。通常在集群中设置两个磁盘节点。

RabbitMQ镜像配置

在上述的集群中,如果某个节点挂掉,整个集群还是可以正常工作的,但是挂掉的那个节点的消息就清空了。这种情况在生产环境中是不可接受的,所以需要用到镜像功能,也就是主从配置。

在集群中的任意一台主机上执行:

$ sudo rabbitmqctl set_policy -p /test ha-allqueue "^" '{"ha-mode":"all"}'

这行命令在vhost名称为/test创建了一个策略,策略名称为ha-allqueue,策略模式为 all 即复制到所有节点,包含新增节点,策略正则表达式为 “^” 表示所有匹配所有队列名称。

配置镜像队列后,其中1台节点失败,队列内容是不会丢失,如果整个集群重启,会恢复在disc中的持久化消息。

HAProxy负载均衡

上述操作已经建立起一个高可用的消息队列了,那么客户端怎么连接呢?

1、客户端可以连接集群中的任意一个节点,如果一个节点故障,客户端自行重新连接到其他的可用节点;(不推荐,对客户端不透明)
2、通过动态DNS,较短的ttl
3、HAProxy负载均衡

当然我们推荐第三种方式了:

在一台新的主机上:192.168.56.114

1、安装HAProxy(本次安装了1.6.3版本):

$ sudo apt-get update
$ sudo apt-get install haproxy
$ haproxy -v       #查看版本

2、修改配置文件

配置文件路径:/etc/haproxy/haproxy.cfg
修改为如下:

##############全局配置########################
global
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        chroot /var/lib/haproxy                #改变当前工作目录
        stats socket /run/haproxy/admin.sock mode 660 level admin   #创建监控所用的套接字目录
        stats timeout 30s       #超时时间
        user haproxy            #默认用户
        group haproxy           #默认用户组
        daemon                  #创建一个守护进程

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Default ciphers to use on SSL-enabled listening sockets.
        # For more information, see ciphers(1SSL). This list is from:
        #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3
#################默认配置#####################
defaults
        log     global
        mode    http            #默认的模式   tcp|http|health  可选,tcp是4层,http是7层,health至返回OK
        option  httplog         #http日志模式
        option  dontlognull     # 启用该项,日志中将不会记录空连接。所谓空连接就是在上游的负载均衡器
                                # 或者监控系统为了探测该 服务是否存活可用时,需要定期的连接或者获取某
                                # 一固定的组件或页面,或者探测扫描端口是否在监听或开放等动作被称为空连接;
                                # 官方文档中标注,如果该服务上游没有其他的负载均衡器的话,建议不要使用
                                # 该参数,因为互联网上的恶意扫描或其他动作就不会被记录下来
        timeout connect 5000    #连接超时时间
        timeout client  50000   #客户端连接超时时间
        timeout server  50000   #服务端连接超时时间
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

####################################################################
listen http_front
        bind 0.0.0.0:1080           #监听端口  
        mode http
        stats refresh 30s           #统计页面自动刷新时间  
        stats uri /haproxy?stats    #统计页面url  
        stats realm Haproxy Manager #统计页面密码框上提示文本  
        stats auth admin:admin      #统计页面用户名和密码设置  
        #stats hide-version         #隐藏统计页面上HAProxy的版本信息

###############我把RabbitMQ的管理界面也放在HAProxy后面了#################
listen rabbitmq_admin
    bind 0.0.0.0:8004
    server node1 192.168.56.111:15672
    server node2 192.168.56.112:15672
    server node3 192.168.56.113:15672
####################################################################
listen rabbitmq_cluster
    bind 0.0.0.0:5672
    option tcplog
    mode tcp
    timeout client  3h
    timeout server  3h
    option          clitcpka
    balance roundrobin      #负载均衡算法(#banlance roundrobin 轮询,balance source 保存session值,支持static-rr,leastconn,first,uri等参数)
    #balance url_param userid
    #balance url_param session_id check_post 64
    #balance hdr(User-Agent)
    #balance hdr(host)
    #balance hdr(Host) use_domain_only
    #balance rdp-cookie
    #balance leastconn
    #balance source //ip
    server   node1 192.168.56.111:5672 check inter 5s rise 2 fall 3   #check inter 2000 是检测心跳频率,rise 2是2次正确认为服务器可用,fall 3是3次失败认为服务器不可用
    server   node2 192.168.56.112:5672 check inter 5s rise 2 fall 3
    server   node3 192.168.56.113:5672 check inter 5s rise 2 fall 3

3、重新启动HAProxy

$ sudo service haproxy restart

4、验证

1、打开url:http://192.168.56.114:1080/haproxy?stats 帐号密码:admin/admin
然后看到相关信息即为成功
RabbitMQ+HAProxy构建高可用消息队列_第1张图片
2、打开:http://192.168.56.114:8004 同样也可以看到rabbitmq的web控制台,因为配置文件也绑定了这个
3、发送rabbitmq消息:
此时,向192.168.56.114:5672即可成功,我用的rabbitmq+spring测试:
具体可参考:http://blog.csdn.net/qq_34039315/article/details/77484292
成功发送消息.png

集群知识

如何选择RabbitMQ的消息保存方式?

RabbitMQ对于queue中的message的保存方式有两种方式:disc和ram。如果采用disc,则需要对exchange/queue/delivery mode都要设置成durable模式。Disc方式的好处是当RabbitMQ失效了,message仍然可以在重启之后恢复。而使用ram方式,RabbitMQ处理message的效率要高很多,ram和disc两种方式的效率比大概是3:1。所以如果在有其它HA手段保障的情况下,选用ram方式是可以提高消息队列的工作效率的。

当消息发送的速率超过了RabbitMQ的处理能力时该怎么办?

RabbitMQ会自动减慢这个连接的速率,让client端以为网络带宽变小了,发送消息的速率会受限,从而达到流控的目的。 使用”rabbitmqctl list_connections”查看连接,如果状态为“flow”,则说明这个连接处于flow-control 状态。

RabbitMQ集群结构

RabbitMQ基于Erlang编写,天然支持clustering。集群是保证可靠性的一种方式,同时可以通过水平扩展以达到增加消息吞吐能力的目的.
RabbitMQ+HAProxy构建高可用消息队列_第2张图片
上图中是三个节点的RabbitMQ集群,Exchange A的metadata信息在所有节点上是一致的,queue的完整信息则只在它创建的那个节点上。每个RabbitMQ节点通常以“rabbit@”表示,所以hostname在运行RabbitMQ的节点中很重要。注意:如果更改了hostname,需要重置RabbitMQ内部的数据库,否则服务无法工作。

数据流动

RabbitMQ维护着四种类型的metadata: queue/exchange/binding/vhost,在集群中这些信息被同步到每个节点,因此当用户访问任何一个节点时,通过rabbitmqctl查询到的queue/user/exchange等信息都是相同的。

通常我们将这些信息保存到磁盘上,也就是查询RabbitMQ状态时的“disc”方式,以便集群重启时可以根据保存的metadata信息重建exchange等。

对于exchange来讲,它的所有信息就是一个exchange名字加上一个查询表。查询表中记录了所有的queue binding。当message被发送到exchange时,client连接的channel对routing key进行比对,根据binding进行正确的转发。

对于Queue来讲,虽然它的metadata在每个节点上都有,但只有在它被创建的那个RabbitMQ 节点上才具有完整的信息:比如state/contents等,这个node被称为此queue的owner node。其他节点只知道这个queue的metadata信息和一个指向queue的owner node的指针。

如果一个client访问RabbitMQ的节点上没有需要的queue的完整信息,RabbitMQ将根据这个指针将请求转发到owner node。所以一个客户端最好一直只和一个节点连接,这样效率高一点。

Mnesia是RabbitMQ中的数据库,它是内嵌在Erlang中的no-SQL数据库。Exchange/Queue/Binding等的metadata信息都保存在mnesia的数据库文件中。关于RabbitMQ的集群信息也保存在这里。Rabbitmqctl的reset操作实际上就是清空了mnesia数据库所在目录的内容。

你可能感兴趣的:(java)