大概从2013年开始自己接触RabbitMQ,到现在已经七年多了。在这七年里,对RabbitMQ有过一些深入的体会,也有无数的血泪。并且根据我多年的经验,我会形成一些提示或者规范,来和大家分享一下RabbitMQ的使用心得,让大家在以后使用RabbitMQ的时候,不至于走我走过的弯路。我想把我对RabbitMQ的体验和感悟分为三篇文章:开发前的规范;开发过程中的注意事项;以及MQ本身的优化。这一次,让我们从前期开发规范开始。我一直很奇怪为什么大家都有使用开发语言的开发规范和使用数据库的数据库规范,但是使用MQ的规范却很少。缺少使用MQ的规范。这是个常见的问题吗?难道只是我身边的例子?无论采用哪种答案,为了避免开发中出现更少的问题,在使用RabbitMQ时达到事半功倍的效果,需要提前规范一些配置和项目。1、在一个RabbitMQ应用中创建多个vhost,对应不同的开发项目。我们在使用数据库的时候,会针对不同的项目在一个数据库应用中创建多个不同的数据库,而不是在不同的服务器上使用。每个项目安装一个数据库应用程序。在RabbitMQ的虚拟主机中,这个概念是类似的。虚拟主机本质上是RabbitMQ服务器的迷你版,具有自己的队列、绑定、开关和访问控制。当在RabbitMQ中创建用户时,该用户通常被分配到至少一个虚拟主机,并且只能访问分配的虚拟主机内的队列、交换和绑定,虚拟主机之间是绝对隔离的。因此,不同的vhosts对应不同的项目,互不影响,而且这些vhosts实际上是在一台宿主机和一个RabbitMQ应用上。然而,目前的情况是,大多数使用RabbitMQ的技术团队往往使用默认的vhost:“/”。如果有一个额外的项目,他们将创建一个RabbitMQ进程。这样做是对开发资源的浪费。建议一个项目对应一个vhost。2、不要直接使用RabbitMQ自带的客户端。很多公司直接使用RabbitMQ自带的java版客户端。但是由于RabbitMQ本身固有的复杂性和多样性,有很多技术细节需要单独处理。比如网络连接的处理,比如异常处理,比如消息失败处理等等。这些,如果手边没有成熟的框架,很可能在一些细节上没有处理好,导致出现很多问题,这些都是不必要的成本。因此,要么使用现有的RabbitMQ客户端框架(如Spring的RabbitMQ框架),要么封装一套底层RabbitMQ客户端框架,而不是单独使用RabbitMQ客户端3.无论如何,消费者必须回馈ACK响应ACK机制是在之后consumer从RabbitMQ接收到一条消息并完成处理,反馈给RabbitMQ,RabbitMQ收到反馈后从队列中删除消息。由于ACK机制本身必须回复RabbitMQ,所以消息会被丢弃。关于什么时候给ACK,我们在做开发的时候,一定要在开发项目之前,提前做好规划和设计。我们在使用RabbitMQ的时候,通常不会希望在收到消息后立即返回一个ACK,也不会设置autoACK机制,即消费者收到消息后会自动返回一个ACK响应。一般来说,我们会根据不同的业务逻辑,在不同的位置手动返回ACK。这时候可能会出现问题:在接收消息时,有时处理业务逻辑报错,处理完业务逻辑后往往会忽略ACK,导致消息一直卡在队列中……如果数量越来越多,后续处理起来就很麻烦。4.考虑建立死信交换。为什么很多人不建立死信交换?这是我很困惑的地方。出去用RabbitMQ和各个项目组沟通,发现很少有人设置死信交换。这是有问题的。我们要知道,消息传递有时会出现错误,并非总是应用收到错误,就会出现很多非应用的问题。例如:消费端出现问题,发送的消息被拒绝。而且我们还设置了requeue=false;消息可能因为ACK超时没有收到,或者消费者的消费速度跟不上消息超时而被删除;消息数量超过队列最大长度限制,被丢弃;消息总大小超过队列总消息大小限制被丢弃。对于这些问题,设置死信交换是一种解决方案。当消息出现我上面列出的情况时,就会发送到我们设置的deadletterexchange。然后我们可以单独处理这些特殊情况的消息,这样可以使我们的项目更健壮,更容易追踪问题。5、尽量使用DirectExchangeRabbitMQ的Exchange是一个消息交换机,它指定消息根据规则路由到哪个队列。这个家伙有四种类型:Direct:处理routingkey,需要绑定一个queue到一个exchange,要求消息精确匹配特定的routingkey。这是一场完整的比赛。如果一个queue绑定到exchange并且要求routingkey为“green”,只有routingkey为“green”的消息才会被转发,routingkey为“red”的消息不会被转发,只有routingkey为“green”》将被转发。主题:将路由键与模式匹配。此时队列需要绑定一个模式。符号“#”匹配一个或多个单词,符号“*”只能匹配一个单词。Fanout:不处理路由键。您只需将队列绑定到交换器。发送到此类交换器的消息将广播到绑定到该交换器的所有队列。headers:不处理routingkey,而是根据发送的消息内容中的headers属性进行匹配。绑定Queue和Exchange时指定一组键值对;当消息发送到RabbitMQ时,将检索消息的标头以匹配绑定到Exchange时指定的键值对;如果完全匹配,消息将被路由到队列中,否则不会被路由到队列中。在这四种类型中,Direct类型的Exchange传递消息是最快的。对于其他交易所,MQ必须花时间计算交付位置。因此,如果可以使用Direct类型,建议使用Direct。以上是使用RabbitMQ前需要考虑的规范。有了这些规范,很多程序员就可以写出相对稳定健壮的程序,而不会出现各种莫名其妙的问题。强烈建议您在团队使用之前获得规范。否则,如果很多程序员很随意的使用RabbitMQ,很容易导致各种蛀虫问题。在下一篇文章中,我会写一些开发过程中需要注意的事情。虽然是开发中需要注意的东西,但其实是我在开发一个成熟的RabbitMQ客户端框架时遇到的各种问题的总结。如果有读者碰巧在一个有类似经历的开发团队,那么你会发现你可能会遇到我提到的所有问题。本文转载自微信公众号“四猪外”,可通过以下二维码关注。转载本文请联系思源外公众号。
