设为首页 收藏本站
查看: 1162|回复: 0

[经验分享] RabbitMQ如何保证发送端消息的可靠投递-发生镜像队列发生故障转移时

[复制链接]

尚未签到

发表于 2017-12-9 16:35:23 | 显示全部楼层 |阅读模式
  上一篇最后提到了mandatory这个参数,对于设置mandatory参数个人感觉还是很重要的,尤其在RabbitMQ镜像队列发生故障转移时。
  模拟个测试环境如下:
  首先在集群队列中增加两个镜像队列的策略:
DSC0000.png

  对于ha-promote-on-shutdown这个参数,可以参考文档,其作用就是当集群中master出现故障时强制进行故障转移从而选出新的master节点,这里的master出现故障表示的是人为的故障比如通过命令行rabbitmqctl.bat start_app之类的关闭RabbitMQ实例或者说是关闭电脑之类的。因为这种强制切换master节点的情况通常发生在断电之类的非可控因素上,所以通过设置这个参数为always模拟非可控因素。
  当然设置这个参数会存在一定风险,文档里也说了,会发生消息不同步也就是会丢消息。
DSC0001.png

  然后创建四个队列和两个Echange,采用绑定Exchange的topic模式
DSC0002.png

DSC0003.png

  然后先贴一下测试代码在进行说明
  C#代码



      List<string> hosts = new List<string>();
hosts.Add("192.168.1.1");
hosts.Add("192.168.1.2");
int curHostIndex = 0;
string exchange = "always.exchange";
string touteKey = "yu.1";
byte[] msg = Encoding.UTF8.GetBytes("hello");
ConnectAgain:
ConnectionFactory factory = new ConnectionFactory();
factory.UserName = "admin";
factory.Password = "admin";
factory.VirtualHost = "/";
factory.HostName = hosts[curHostIndex];
IConnection conn = factory.CreateConnection();
IModel channel = conn.CreateModel();
IBasicProperties props = channel.CreateBasicProperties();
props.ContentType = "text/plain";
props.DeliveryMode = 2;
for (int i = 0; i < 5000000; i++)
{
try
{
channel.ConfirmSelect();
channel.BasicAcks += (sender, eventArgs) => { };
channel.BasicReturn += (sender, eventArgs) => Console.WriteLine("消息投递失败 " + eventArgs.ReplyText);
channel.BasicPublish(exchange, touteKey, true, props, msg);
bool success = channel.WaitForConfirms(new TimeSpan(0, 0, 0, 0, 1));
if (!success)
Console.WriteLine("表示消息投递失败 ");
}
catch (Exception ex)
{   //发生链接异常时换个IP进行连接
                    channel.Close();
conn.Close();
if (curHostIndex == 0)
curHostIndex = 1;
else
curHostIndex = 0;
goto ConnectAgain;
}
}
  Java代码:



public static void publish() throws Exception {
List<String> hosts = new ArrayList<String>();
hosts.add("192.168.1.1");
hosts.add("192.168.1.2");
int curHostIndex = 1;
String exchange = "common.exchange";
String routeKey = "yu.1";
byte[] msg = "hello".getBytes("UTF-8");
ConnectAgain:
while (true) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(hosts.get(curHostIndex));
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
// 创建一个新的连接
Connection connection = factory.newConnection();
// 创建一个频道
Channel channel = connection.createChannel();
channel.addConfirmListener(new ConfirmListener() {
public void handleAck(long l, boolean b) throws IOException {
System.out.println(l);
}
public void handleNack(long l, boolean b) throws IOException {
System.out.println(l);
}
});
channel.addReturnListener(new ReturnListener() {
public void handleReturn(int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
System.out.println("响应状态码-ReplyCode:" + i);
System.out.println("响应内容-ReplyText:" + s);
System.out.println("Exchange:" + s1);
System.out.println("RouteKey" + s2);
System.out.println("投递失败的消息:" + new String(bytes, "UTF-8"));
}
});
for (int i = 0; i < 5000000; i++) {
try {
channel.confirmSelect();
channel.basicPublish(exchange, routeKey, true, MessageProperties.PERSISTENT_TEXT_PLAIN, msg);
boolean sucess = channel.waitForConfirms(10);
System.out.println(sucess);
} catch (Exception ex) {
try {
connection.abort();
} catch (Exception e) {
e.printStackTrace();
}
if (curHostIndex == 0)
curHostIndex = 1;
else
curHostIndex = 0;
continue ConnectAgain;
}
}
}
}
  先测试下always.exchange也就是非人为因素导致的故障转移的情况,先开启客户端让客户端跑着,然后通过命令行停止master节点(也就是Node为WinServer12-1),停止时消息队列的消息状态为
DSC0004.png

  图片消息的总数虽然不准确(页面存在延迟) ,但截取的是master停止时刻的消息状态,也够用了,这时候发现slaver节点会切换为master节点(也就是Node为DESKTOP-078UA43),并继续接受消息,客户点也没有发生异常通知(因为订阅了BasicReturn事件并且开启了madartory,如果消息投递失败,我们可以得到通知,待会也会测试到)。
  然后我们让集群多跑会然后在消息有明显变化的时候在开启老的挂掉的当前为slaver的节点,当前队列消息的状态如下,master为DESKTOP-078UA43
DSC0005.png

  在开启slaver后我们在立即停止当前的master节点(也就是Node为WinServer12-1),这时候发现集群的master又切回到了DESKTOP-078UA43同时队列中的消息也跟着清除了。。也就是说在DESKTOP-078UA43之前挂掉到重启启动期间WinServer12-1接收到的消息全部丢掉了。。。由此我们可知,RabbitMQ镜像集群发生非可控因素造成的master故障为了保证可用性,会丢消息。
  而对于客户端而已,消息都是可靠投递的,所以监听事件并不会触发。
DSC0006.png

  当然也可以通过设置ha-sync-mode参数进行调整,默认情况下,新加入的节点不会同步已存在节点内的消息,设置为automatic后会进行同步。不过如果没同步完master挂掉的话消息还是会丢掉的
DSC0007.png

  然后测试下common.exchange会发生的情况,测试这个的时候就是体现mandatory作用的时刻了!
  还是先在集群正常的情况下选取个时间点关掉主节点,当前master为DESKTOP-078UA43
DSC0008.png

  然后WinServer12-1变为新的master,此刻发现正常接收消息,而且对客户端而言,消息也是正常投递的。然后打开被关闭的DESKTOP-078UA43节点,它会以slaver身份回归集群,开启前观察下当前队列状态
DSC0009.png

  然后开始操作!发现队列状态如下,NaN,难道说队列停止接受数据了么!!!(如果停止接受数据,客户端同步调用发送时会发送失败么?)而且无法将master进行切换了。
DSC00010.png

  这时候如果在启动WinServer12-1会发现,消息还是WinServer12-1关闭时刻的消息,WinServer12-1关闭期间DESKTOP-078UA43尽管在接受消息,但实际消息并没有被RabbitMQ可靠存储(比较master都没有了。。);
  观察下调试的代码,发现消息还是在正常向RabbitMQ投递。
DSC00011.png

  客户端为了保证向RabbitMQ投递消息的可靠,及开启了Conform模式,但此刻同步返回的RabbitMQ处理结果是消息处理完成。那岂不是NaN期间RabbitMQ把消息都吞了?而客户端还傻傻的以为发送成功了。。
  这时候就体现开启mandatory同时订阅 channel.BasicReturn += (sender, eventArgs) => Console.WriteLine("消息投递失败 " + eventArgs.ReplyText);事件的作用了。。因为这时候RabbitMQ会反馈给你消息实际上并没有投递成功的信息。
DSC00012.png

  这里包含了持久化失败的原因,同时包含发送消息的详细信息,方便客户端对消息进行在处理。
  其实说了这么多,最后想说的是对于消息的一致性,最好还是不要全部依赖于RabbitMQ,实现最终一致性并保证幂等性才是相对可靠的方案。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-422458-1-1.html 上篇帖子: 基础拾遗----RabbitMQ 下篇帖子: spring boot实战(第十二篇)整合RabbitMQ
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表