一、 channel 信道:
概念:信道是生产消费者与rabbit通信的渠道,生产者publish或是消费者subscribe一个队列都是通过信道来通信的。信道是建立在TCP连接上的虚拟连接,什么意思呢?就是说rabbitmq在一条TCP上建立成百上千个信道来达到多个线程处理,这个TCP被多个线程共享,每个线程对应一个信道,信道在rabbit都有唯一的ID,保证了信道私有性,对应上唯一的线程使用。
疑问:为什么不建立多个TCP连接呢?原因是rabbit保证性能,系统为每个线程开辟一个TCP是非常消耗性能,每秒成百上千的建立销毁TCP会严重消耗系统。所以rabbitmq选择建立多个信道(建立在tcp的虚拟连接)连接到rabbit上。
类似概念:TCP是电缆,信道就是里面的光纤,每个光纤都是独立的,互不影响。
二、exchange 交换机和绑定routing key
exchange的作用就是类似路由器,routing key就是路由键,服务器会根据路由键将消息从交换器路由到队列上去。
exchange有多个种类:direct,fanout,topic,header(非路由键匹配,功能和direct类似,很少用)。前三种类似集合对应关系那样,(direct)1:1,(fanout)1:N,(topic)N:1
direct: 1:1类似完全匹配
fanout:1:N 可以把一个消息并行发布到多个队列上去,简单的说就是,当多个队列绑定到fanout的交换器,那么交换器一次性拷贝多个消息分别发送到绑定的队列上,每个队列有这个消息的副本。
ps:这个可以在业务上实现并行处理多个任务,比如,用户上传图片功能,当消息到达交换器上,它可以同时路由到积分增加队列和其它队列上,达到并行处理的目的,并且易扩展,以后有什么并行任务的时候,直接绑定到fanout交换器不需求改动之前的代码。
topic N:1,多个交换器可以路由消息到同一个队列。根据模糊匹配,比如一个队列的routing key 为*.test ,那么凡是到达交换器的消息中的routing key 后缀.test都被路由到这个队列上。
三、结合信道、交换器和路由键到队列
总结几点重要知识:1.信道才是rabbit通信本质,生产者和消费者都是通过信道完成消息生产消费的。2.交换器本质是一张路由查询表(名称和队列id,类似于hash表),这是一个虚拟出来的东西,并不存在真实的交换器。
消息的生命周期:生产者生产消息A交由信道,信道通过消息(消息由载体和标签)的标签(路由键)放到交换器发送到队列上(其实就是查询匹配,一旦匹配到了规则,信道就直接和队列产生连接,然后将消息发送过去)
1.rabbitmq是Erlang语言开发的AMQP高级消息队列协议,开源实现
2.rabbitmq的应用场景
高并发消息处理
异步解耦
削峰填谷
如果想让mq按照消息顺序执行,就一个交换机绑定一个队列,如果绑定多个队列则无法顺序;Rocketmq支持顺序执行(阿里用Java写的)
使用时生产环境不要使用默认的账号密码要自己创建一个并分配权限
默认是先将数据放到内存中,一段时间之后会将数据放到硬盘中,以文件形式存储
.netcore 中引入依赖RabbitMQ.Client 包
![](https://img-blog.csdnimg.cn/img_convert/a5ce1ee104ff66aa8429af09959d1e65.png)
当没有绑定交换机时,使用的时rabbitmq中默认的交换机路由默认路由key喝队列名称完全一致
![](https://img-blog.csdnimg.cn/img_convert/3d44e0cfd56bc36bc3ac72165756b56b.png)
为什么消费端要再一次创建队列,因为如果没有创建只启动消费端就会有异常,为了规避异常所以要在定义一次
![](https://img-blog.csdnimg.cn/img_convert/f0616965027f136125c4a22c18a6123b.png)
工作队列
一个队列多个监听客户端工作模式是一个轮循的工作模式
使用的都是默认交换机(注意)
![](https://img-blog.csdnimg.cn/img_convert/9c7927e6a0a3169e8677e7c7b7f07802.png)
![](https://img-blog.csdnimg.cn/img_convert/31fa0691e45cfaa7e9effb4228bc9f69.png)
//设置prefetchCount: 1来告知RabbitMQ,在未收到消费端的消息确认时,不再分发消息,也就确保了当消费端处于忙碌状态时,不再分配任务。
![](https://img-blog.csdnimg.cn/img_convert/41959994b42ef1e101530eca34ceb776.png)
交换机队列
对于操作每一个队列都是通过交换机进行数据交互
![](https://img-blog.csdnimg.cn/img_convert/f1c2dccd6bfcce159002ad566d4f4dd0.png)
Fanout扇形模式(订阅) 在程序中只写交换机不写路由key(为空)只要和交换机绑定的队列都将收到生产者p发送的消息(类似于广播)
![](https://img-blog.csdnimg.cn/img_convert/567a23bf5c1db0e1718664b106332da2.png)
Direct模式 完全匹配模式 发送的消息需要和routekey匹配,,根据key来发送到指定的队列
![](https://img-blog.csdnimg.cn/img_convert/fa1049a8a4e5f40bf460a83f25307712.png)
![](https://img-blog.csdnimg.cn/img_convert/8d031bb1789b50a1b59b0f1aae2ba35b.png)
在消费者有签收模式 分为
手动 保证正确的消费,不会丢失消息
和
自动 容易丢失消息
签收意味着消息从队列中删除
还可以批量签收
![](https://img-blog.csdnimg.cn/img_convert/92eeb8e6c33072e9e53f432b6a955cd3.png)
将false改为ture则为批量签收
![](https://img-blog.csdnimg.cn/img_convert/3e25a38b2f049c1578b85c74cb118cbe.png)
![](https://img-blog.csdnimg.cn/img_convert/4fbbbf0c025b55fa3c43099f91fe7dca.png)
topic 工作模式 匹配通配符注意
![](https://img-blog.csdnimg.cn/img_convert/51f28ca8fff2f5c9e0b145243dcdff1d.png)
mq 高可用集成
如何选型(对于技术层面的理解)
Nginx(反向代理(集群)负载均衡)
HAProxy(选这个性能好,可以提供监控面板,和负载均衡的算法,失败恢复)不停机保证热切换主备热切功能
![](https://img-blog.csdnimg.cn/img_convert/318af3e188a54bac62d10ad53e9a6587.png)
技术选型之后要话架构图
![](https://img-blog.csdnimg.cn/img_convert/1c8d877001e2d8fe36325fa1454fce3b.png)
构建Rabbitmq集群(一般情况下使用硬装)
创建三节点rabbitmq容器
如何连接rabbitmq 集群两种方式大同小异
![](https://img-blog.csdnimg.cn/img_convert/118caf8a4aea3f0d126f4f254317e946.png)
![](https://img-blog.csdnimg.cn/img_convert/1813d79fa0c7c0813cdd62a0a4969a58.png)
rabbitmq的消息持久化
![](https://img-blog.csdnimg.cn/img_convert/fb28b040206ae7fcc378c9868907f3f5.png)
创建为网络
docker networkcreate rabbitmanet
创建容器
sudo docker run-d --name=rabbitmq1 -p 5672:5672 -p 15672:15672 -e RABBITMQ_NODENAME=rabbitmq1-e RABBITMQ_ERLANG_COOKIE='YZSDHWMFSMKEMBDHSGGZ' -h rabbitmq1--net=rabbitmanet rabbitmq:management
sudo docker run-d --name=rabbitmq2 -p 5673:5672 -p 15673:15672 -e RABBITMQ_NODENAME=rabbitmq2-e RABBITMQ_ERLANG_COOKIE='YZSDHWMFSMKEMBDHSGGZ' -h rabbitmq2--net=rabbitmanet rabbitmq:management
sudo docker run-d --name=rabbitmq3 -p 5674:5672 -p 15674:15672 -e RABBITMQ_NODENAME=rabbitmq3-e RABBITMQ_ERLANG_COOKIE='YZSDHWMFSMKEMBDHSGGZ' -h rabbitmq3--net=rabbitmanet rabbitmq:management
sudo docker run-d --name=rabbitmq4 -p 5675:5672 -p 15675:15672 -e RABBITMQ_NODENAME=rabbitmq3--erlang-cookie='YZSDHWMFSMKEMBDHSGGZ' -h rabbitmq4 --net=rabbitmanetrabbitmq:management
分别进入rabbitmq2 和rabbitmq3容器(docker exec -it 容器id /bin/bash),执行以下
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctljoin_cluster --ram rabbitmq1@rabbitmq1
rabbitmqctlstart_app
设置镜像队列
#随便进入一个容器
docker exec -itrabbitmq01 bash
#设置策略匹配所有名称是amp开头的队列都存储在2个节点上的命令如下
rabbitmqctlset_policy -p / ha "^amp*"'{"ha-mode":"exactly","ha-params":2}'
#或者
#设置策略匹配所有名称的队列都进行高可用配置
rabbitmqctlset_policy -p / ha "^"'{"ha-mode":"all","ha-sync-mode":"automatic"}'
#查询策略
rabbitmqctllist_policies -p / #查看vhost下的所有的策略(policies )
删除镜像队列
rabbitmqctl clear_policy
部署HAProxy
在编辑文件之前需要查看rabbitmq得容器IP
docker inspect “容器名称”
HAProxy 配置文件
编辑haproxy.cfg配置文件如下:
vim haproxy.cfg
global
daemon
maxconn 256
defaults
mode http
timeout connect5000ms
timeout client5000ms
timeout server5000ms
listenrabbitmq_cluster #监听5677端口转发到rabbitmq服务
bind 0.0.0.0:5677
option tcplog
mode tcp
balance leastconn
serverrabbit1 rabbitmq1IP:5672 checkinter 2s rise 2 fall 3
serverrabbit2 rabbitmq2IP:5672 checkinter 2s rise 2 fall 3
serverrabbit3 rabbitmq3IP:5672 checkinter 2s rise 2 fall 3
listenhttp_front #haproxy的客户页面
bind 0.0.0.0:80
stats uri/haproxy?stats
listenrabbitmq_admin #监听8001端口转发到rabbitmq的客户端
bind 0.0.0.0:8001
serverrabbit1 rabbitmq1IP:15672 checkinter 2s rise 2 fall 3
serverrabbit2 rabbitmq2IP:15672 checkinter 2s rise 2 fall 3
serverrabbit2 rabbitmq3IP:15672 checkinter 2s rise 2 fall 3
在/home/haproxy下新建两个文件Dockerfile和haproxy.cfg 文件如下:
![](https://img-blog.csdnimg.cn/img_convert/b117f7934b49ae6c79b728a118355044.png)
编写Dockerfile
FROMhaproxy:1.7
COPY haproxy.cfg/usr/local/etc/haproxy/haproxy.cfg
构建镜像
在当前文件夹下执行
docker build -t rabbitmq-haproxy .
在haproxy.cfg同目录下执行:
![](https://img-blog.csdnimg.cn/img_convert/bfffcbba4bf80182d2001ec5daf5d9d6.png)
创建HAProxy镜像
docker run -d --name rabbitmq-haproxy1 --privileged=true -p 8090:80 -p 5677:5677 -p 8001:8001--net=rabbitmanet rabbitmq-haproxy:latest
docker run -d --name rabbitmq-haproxy2 --privileged=true -p 8091:80 -p 5678:5677 -p 8002:8001 --net=rabbitmanet rabbitmq-haproxy:latest
keepalived搭建
keepalived的实现是要在docker服务内创建,最后宿主机中也要创建实现docker内外的连接;
docker服务内创建,我们需要进入到该服务并安装keepalived
进入docker-haproxy服务
sudo docker exec -it rabbitmq-haproxy1 /bin/bash
更新update,安装keepalived
apt-get update
apt-get installkeepalived
安装vim 安装ifconfig命令安装ping
apt-getinstall vim
apt-get installnet-tools
apt-get installiputils-ping
新建并写入一个keepalived的配置文件
vim/etc/keepalived/keepalived.conf
在编辑文件之前需要查看haproxy得容器IP
dockerinspect rabbitmq-haproxy1
配置文件信息:
#取名为K1,可自定义
vrrp_instance VI_1 {
#定义节点属性
主服务器 state MASTER、
从服务器 state BACKUP
#定义虚拟网卡
interface eth0
#定义组vriid
virtual_router_id 100
#定义权重
priority 100
#定义心跳检测时间1秒
advert_int 1
#定义组用户密码
authentication {
auth_type PASS
auth_pass 123456
}
#定义docker内ip地址,必须要在和haproxy同一个网段
virtual_ipaddress {
172.20.0.100
}
}
启动
service keepalivedstart
到这一步理论上就完事了,只需要在新建一个haproxy+keepalived组成集群即可,在这里我遇到一个坑是:
启动keepalived后宿主机无法ping通用keepalived,报错:
[root@centos7 ~]# ping 172.20.0.100
PING 172.20.0.100(172.20.0.100) 56(84) bytes of data.
From 172.20.0.100icmp_seq=1 Destination Host Unreachable
From 172.20.0.100icmp_seq=2 Destination Host Unreachable
解决方案:
大多数都是我把配置文件没写对,重写配置文件,重启服务;
这里检查能否ping通,需要看服务内你的配置文件写入的ip有没有出现在docker的网卡上,具体方法是:
进入到docker服务内,不是在宿主机上哦,查看配置文件
root@8351443065ea:/etc/keepalived#cat keepalived.conf
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 100
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 123456
}
virtual_ipaddress {
172.20.0.100
}
}
如果配置文件信息都是正确的,通过ip a命令会显示如当前docker服务有的网卡
root@8351443065ea:/etc/keepalived#ip a
1: lo:<LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN groupdefault qlen 1000
link/loopback00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8scope host lo
valid_lft foreverpreferred_lft forever
inet6 ::1/128 scopehost
valid_lft foreverpreferred_lft forever
73: eth0@if74:<BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP groupdefault
link/ether02:42:ac:14:00:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.20.0.7/24scope global eth0
valid_lft foreverpreferred_lft forever
inet6fe80::42:acff:fe14:7/64 scope link
valid_lft foreverpreferred_lft forever
很明显没有我配置文件中的172.20.0.100ip地址,代表配置文件未生效,大多数配置文件错误
停掉keepalived服务重新修改编辑后重启
root@8351443065ea:/etc/keepalived#service keepalived stop
[ ok ] Stoppingkeepalived: keepalived.
重启服务
root@8351443065ea:/etc/keepalived#service keepalived start
[ ok ] Startingkeepalived: keepalived.
再次查看docker服务的ip
root@8351443065ea:/etc/keepalived#ip a
1: lo:<LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN groupdefault qlen 1000
link/loopback00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8scope host lo
valid_lft foreverpreferred_lft forever
inet6 ::1/128 scopehost
valid_lft foreverpreferred_lft forever
73: eth0@if74:<BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP groupdefault
link/ether02:42:ac:14:00:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.20.0.7/24scope global eth0
valid_lft foreverpreferred_lft forever
这里出现了我的配置文件ip地址
inet 172.20.0.100/32scope global eth0
valid_lft foreverpreferred_lft forever
inet6fe80::42:acff:fe14:7/64 scope link
valid_lft foreverpreferred_lft forever
在服务内直接ping这个ip,发现能ping通,切换宿主机,也能ping通,解决问题
root@8351443065ea:/etc/keepalived#ping 172.20.0.100
PING 172.20.0.100(172.20.0.100) 56(84) bytes of data.
64 bytes from172.20.0.100: icmp_seq=1 ttl=64 time=0.042 ms
64 bytes from172.20.0.100: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from172.20.0.100: icmp_seq=3 ttl=64 time=0.060 ms
最后,再新建一个haproxy再添加slave的keepalive,加入到master中,即完成负载均衡集群的搭建。
新建第二个haproxy容器的配置文件和第一个容器是一样的,需要注意的地方是,在docker run新建容器的时候,由于4001,4002端口和ip都被占用了,所以在run的时候要指定未使用的新端口和ip,比如4003,4004端口等等;
第二个keepalived的配置文件和第一个配置文件完全一样,如果想要分配不同的权重比列,只需要更改权重参数就可以了。
最后,本地宿主机安装keepalived进行外网路由:
宿主机操作:
安装keepalived
yum install -ykeepalived
安装完后配置文件本省有自带的在,/etc/keepalived文件夹中,通过ftp替换掉这个配置文件
宿主机KP配置文件信息:
vrrp_instance VI_1 {
state MASTER
#这里是宿主机的网卡,可以通过ip a查看当前自己电脑上用的网卡名是哪个
interface ens33
virtual_router_id100
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
#这里是指定的一个宿主机上的虚拟ip,一定要和宿主机网卡在同一个网段,我的宿主机网卡ip是192.168.1.85,所以指定虚拟ip是给的90
192.168.1.90
}
}
#接受监听数据来源的端口,网页入口使用
virtual_server192.168.1.90 8888 {
delay_loop 3
lb_algo rr
lb_kind NAT
persistence_timeout50
protocol TCP
#把接受到的数据转发给docker服务的网段及端口,由于是发给docker服务,所以和docker服务数据要一致
real_server172.20.0.100 8888 {
weight 1
}
}
#接受数据库数据端口,宿主机数据库端口是3306,所以这里也要和宿主机数据接受端口一致
virtual_server192.168.1.90 3306 {
delay_loop 3
lb_algo rr
lb_kind NAT
persistence_timeout50
protocol TCP
#同理转发数据库给服务的端口和ip要求和docker服务中的数据一致
real_server172.20.0.100 3306 {
weight 1
}
}
配置完后,通过命令service keepalived start启动宿主机的KP,通过ip a查看网卡中是否出现定义的90 IP地址,如果没有出现大多数是配置文件不正确。
通过ping 90ip地址,发现能够ping通
切换到windows上,进入cmd ping宿主机90ip地址发现能ping通,通过浏览器输入