背景介绍
需求:定时消息. 支持分布式,宕机后消息可恢复,消息的最终一致性.
目标:
1. 支持业务方定时消息的场景,例如”定一个明天早上8点钟的闹钟”
2. 消除重复的 ScheduledExecutorService 代码
3. 高可用.
调研:开源的kafuka和rabbitmq无延迟队列,但可以通过自身特性实现.
阿里的mq 免费版支持秒级别,收费版支持毫秒级.
方案一:定时扫mysql,取出到期的任务,通知业务方.(http相对tcp延迟高)
方案二:通过Rabbitmq的 DeadQueue 及消息的TTL特性实现,消息会经历两次publish, 第一次投放到delay-queue中,第二次是消息过期时通过routingkey 投放到实际期望投放的queue中.(kafuka的持久化需要手工处理,且数据最终一致性不满足业务场景)
环境部署文档
1.打包好定制的Docker镜像.
2.分别运行Rabbitmq节点.
2.1 第一版方案节点间通过docker的network相互发现, 但部署到三台不同的机器上还需要服务注册发现.
2.2 节点间相互发现直接使用FQDN(Fully Qualified Domain Name完全合格域名/全程域名缩写,访问时将由DNS进行解析得到IP)
docker run -d -h {机器hostname} --name rabbit -p "4369:4369" -p "5672:5672" -p "15672:15672" -p "25672:25672" -p "35197:35197" -e "RABBITMQ_USE_LONGNAME=true" -e "RABBITMQ_LOGS=/var/log/rabbitmq/rabbit.log" -v /data/users/ltfu/rabbitmq/data:/var/lib/rabbitmq -v /data/users/ltfu/rabbitmq/logs:/var/log/rabbitmq {镜像id}
2.3 这里有三点很重要
2.3.1 指定的-h 参数必须同机器 hostname .
2.3.2 RABBITMQ_USE_LONGNAME = true 代表通过FQDN发现集群节点.
2.3.3 指定挂载的目录.
3.节点加入集群
3.1 三台机器都启动后,将三个节点 /data 目录下的 .erlang.cookie 改成相同的值.
3.2 三台机器分别 在hosts文件中加入节点的 ip 及 host .
加入后分别重启正在运行rabbitmq的docker容器。
3.3 在两个Rabbitmq Slave节点上执行(不退出docker容器):
docker exec rabbit rabbitmqctl stop_app
docker exec rabbit rabbitmqctl join_cluster rabbit@{Master Hostname}
docker exec rabbit rabbitmqctl start_app
3.4退出集群
docker exec rabbit bash -c "rabbitmqctl stop_app && rabbitmqctl reset && rabbitmqctl start_app"
4.选择队列需要的镜像模式
若队列名称以ha开头, 则集群中的所有节点为该队列配置镜像
rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}'
除了amq开头的交换器绑定队列,设置所有队列为镜像队列.
docker exec rabbitmq1 rabbitmqctl set_policy HA '^(?!amq\.).*' '{"ha-mode": "all"}'
以 two 开头的队列,镜像到集群中的任意两个节点,自动同步:
rabbitmqctl set_policy ha-two "^two\."'{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
5.为集群提供一个入口,通过Haproxy做负载均衡
5.1 安装Haproxy
sudo add-apt-repository ppa:vbernat/haproxy-1.6
sudo apt-get update
sudo apt-get install haproxy
haproxy -v #验证安装成功
5.2 修改配置文件
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy # 改变当前工作目录
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy # 默认用户
group haproxy # 默认用户组
daemon # 创建1个进程进入deamon模式运行
# 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 # 默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
option httplog # 采用http日志格式
option dontlognull # 启用该项,日志中将不会记录空连接。所谓空连接就是在上游的负载均衡器或者监控系统为了探测该 服务是否存活可用时,需要定期的连接或者获取某一固定的组件或页面,或者探测扫描端口是否在监听或开放等动作被称为空连接;官方文档中标注,如果该服务上游没有其他的负载均衡器的话,建议不要使用该参数,因为互联网上的恶意扫描或其他动作就不会被记录下来
timeout connect 5000 # 连接超时时间
timeout client 50000 # 客户端连接超时时间
timeout server 50000 # 服务器端连接超时时间
option httpclose # 每次请求完毕后主动关闭http通道
option httplog
option redispatch # serverId对应的服务器挂掉后,强制定向到其他健康的服务器
maxconn 60000 # 最大连接数
retries 3 # 默认3次连接失败认为服务不可用
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 #监听端口
stats refresh 30s #统计页面自动刷新时间
stats uri /haproxy?stats #统计页面url
stats realm MobvoiHaproxy Manager #统计页面密码框上提示文本
stats auth user:password #统计页面用户名和密码设置
#stats hide-version #隐藏统计页面上HAProxy的版本信息
listen rabbitmq_admin
bind 0.0.0.0:8004
server 机器0host 0ip:15672
server 机器1host 1ip:15672
server 机器2host 2ip:15672
listen rabbitmq_cluster
bind 0.0.0.0:5673
option tcplog
mode tcp
timeout client 3h
timeout server 3h
option clitcpka
balance roundrobin
server 机器0host 0ip:5672 check inter 5s rise 2 fall 3
server 机器1host 1ip:5672 check inter 5s rise 2 fall 3
server 机器2host 2ip:5672 check inter 5s rise 2 fall 3
5.3 重启Haproxy
sudo service haproxy restart
5.4 通过
{Haproxy机器域名}:1080/haproxy?stats 查看Haproxy状态
{Haproxy机器域名}:8004 查看Rabbitmq集群状态
{Haproxy机器域名}:5673 TCP连接Rabbitmq集群