【框架学习与探究之消息队列--EasyNetQ(2)】

声明

本文欢迎转载系博主原创,本文原始链接地址:http://www.cnblogs.com/DjlNet/p/7654902.html


前言

此文章,是承接上篇:【框架学习与探究之消息队列--EasyNetQ(1)】未完待续篇,依然对消息队列及其应用的一个补充说明和一些博主的见解。这里说一点废话:就博主自身所见而言的话还有后面了解,现在招聘说明当中部分虽然没有明确提出关于消息队列方面的需求,但是面试官看见几年工作经验的你肯定会多多少少问问关于对此的认识和见解,所以当你以一种颇为了解与实战的姿态说出自己的看法的时候,相信肯定会对这场面试的你加分不少的呐,从而也说明了现今开发当中消息队列的重要性,说到底,它RabbitMQ/EasyNetQ如果能给你带来加分的筹码以及薪资的提升,学习它何乐而不为呐!!!或许这就是面向工资编程吧!!


继续EasyNetQ文档划重点与测试

这里再次强调在基于系统之间的消息交互开发方面,尤其需要注意的就是消息的Publish/Consumer的两种操作的强制落地的思考与解决方案,博主在上文做出一定的见解和实际操作方案。

4、关于交换机的Type以及命名、消息队列的命名

这里如果使用了EasyNetQ框架的情况下(如其名字一样为了简单使用使用了约束),框架会产生如下约束:1、发布订阅的消息必须是.NET Class类型,且类似与DTO一样Public访问修饰符和默认的构造函数没有方法 2、根据消息的类型来进行路由选择。这里首先要说明的是,交换机的名字默认情况就是:因为上面的约定,所以例如当我们的消息定义为public class TextMessage{}的时候,那么默认情况所产生交换机的名字就是:Messages.TextMessage:Messages类型的全名称:程序集的名字并且Exchange类型指定为了Topic,这里需要说明一下就是在几种交换机类型当中,Topic是最灵活可以变相的通过它实现其他类型的工作方式,所以我们的EasyNetQ直接指定该工作类型为了简单化的使用原则;接下来在消费者去订阅该消息的时候默认情况产生队列的名字就是:Messages.TextMessage:Messages_{YourSubscriptionId} 这里YourSubscriptionId可以为empty那么产生的队列名字就会与交换机同名了。

那么框架这里提供这个YourSubscriptionId玩意儿给使用者是干吗使用的呐?

这里博主的理解就是有这个功能点的实现,多半是有这方面的需求,例如当我们需要一个交换机绑定多个队列,但是使用的消息格式是同一个.NET类型,这里就可使用自定义的SubscriptionId来实现多队列多消费者模式,例如日志分发消息系统的需求,也可以当这个标记符来当做类似与模块或者系统的标记达到一看见队列的名字就知道消费者的创建者,等等一些具有意义的标记,切记注意最大字符个数225,还有一点就是如何实现工作队列模式?这里只需要重复订阅同一个消息类型且SubscriptionId一致即可实现多个消费者对同一个队列的共同消费模式,例如当出现既有的消费者已经满足不了处理效率支持的情况下,但是又不想改动原先系统的情况,就可以实现无痛式的增加一个消费者来共同消费达到负载均衡的效果,以及诸如此类的场景。注意:当订阅者消息类型一致的时候,SubscriptionId不同的话就会形成消息复制分发的效果
当然拉,如果想实现更改默认的框架约束自定义交换机和队列名字,参考如下(博主实验证明可行):

[Queue("TestMessagesQueue", ExchangeName = "MyTestExchange")]
public class TestMessage
{
   public string Text { get; set; }
}

但是这里一般情况博主认为,就如同代码就是最好的注释一样,让消息类型作为消息队列的名字也是一种不错的解决方案,有一种自圆其说的感觉,从而也印证了框架作者如出一辙的想法就是采用消息类型路由的做法


5、主题路由与消息过滤

这里主要就是交换机的类型了Topic使用了,这里具体的解释与使用参考第一篇文中给出的地址,这里博主需要说明的是框架中注意点当多个消费者指定了相同的消息类型且指定相同的SubscriptionId,那么这些消费者就算是指定了自己的WithTopic也不会达到消息过滤的效果,因为Topic的RouteKey是作用于交换机与队列的不是消息本身,那么既然消费者们都已经是消费同一个队列了那肯定就不会产生消息的过滤了

那么在此框架中提供topic有什么其他用途么,我该怎么使用呐

这里我们依然采用举例子(当然可能例子有些牵强为了迎合我们的topic功能点)的方式来说明,假设我们现在有性能监控系统(A)、日志处理报警系统(B),这里我们其他应用系统底订单系统、库存系统、财务系统等产生的性能安全错误相关消息日志都会统统就发送到A,然后由A分析和挑选出需要转发到B的消息以LogInfoMessage的方式统一推送到MQServer当中,这里使用了同一个消息类型就意味着同一个交换机,这里Publish的时候根据不同携带了不同的RouteKey,例如订单系统->Order.Create、Order.Cancel等等,库存系统->Inventory.Adjust、Inventory.Delivery等等,诸如此类的路由键,然后假设我们的B系统有LogInfoMessage的消费者们,它们可能并不需要关心所有的日志消息,例如OrderLogInfoMessageConsumer只关心订单相关的,配置为 WithTopic("Order.#"),类似InventoryLogInfoMessageConsumer只关心库存相关的日志,配置为了 WithTopic("Inventory.#"),且消费者需要指定唯一的标识,这样一来将会形成一个交换机绑定了多个多个队列以及各自的消费者们,具体参考如下:

上图是关于RabbitMQ交换机的bindings配置图也说说明了问题,这样一来就可以实现同一个消息类型也可以实现消息的按需求过滤式消费了,参考配置对应代码:
例子发布者代码如图
:

例子消费者代码如下

bus.Subscribe<LogInfoMessage>(string.Empty, x => Console.WriteLine(x.Body + " " + x.Creator));
bus.Subscribe<LogInfoMessage>("Order", x => Console.WriteLine(x.Body + " " + x.Creator), x => x.WithTopic("Order.#"));
bus.Subscribe<LogInfoMessage>("Inventory", x => Console.WriteLine(x.Body + " " + x.Creator), x => x.WithTopic("Inventory.#"));
bus.Subscribe<LogInfoMessage>("Finance", x => Console.WriteLine(x.Body + " " + x.Creator),x => x.WithTopic("Finance.#"));
bus.Subscribe<LogInfoMessage>("Order_Inventory", x => Console.WriteLine(x.Body + " " + x.Creator),x => x.WithTopic("Order.#").WithTopic("Inventory.#"));

以上例子便展示topic的应用场景和使用规则,这与上面讲到的SubscriptionId有一定关系,它产生了不同的队列才得以实现不同的队列用不同的routekey来绑定交换机才足以实现消息可以根据自定规则投递到对应的队列当中去


6、自动订阅者划重点

这里EasyNetQ,可以使用它来轻松扫描实现IConsume 或IConsumeAsync 的接口的类的特定程序集,然后自动将这些消费者订阅到RabbitMQ中去。 IConsume 的实现将使用总线Subscribe方法,而IConsumeAsync 的实现将使用总线SubscribeAsync方法。
1、一个消费者可以实现多个泛型消费接口或者异步的泛型消费接口,就可以实现承担消费多种类型的消息的功能
2、[ForTopic("Topic.Foo")]使用该标签在 实现类的方法上面就可以实现与普通订阅使用withtopic同样的效果
3、[AutoSubscriberConsumer(SubscriptionId = "MyExplicitId")] 为消费者对应的队列名称后缀
4、通过控制 AutoSubscriber 的构造函数的 GenerateSubscriptionId 就可以实现全局自动订阅者的配置项 、ConfigureSubscriptionConfiguration 实现全局消费者的配置
5、[SubscriptionConfiguration(CancelOnHaFailover = true, PrefetchCount = 10)] 控制消费者配置,作用于实现类的方法上面
6、替换 AutoSubscriber 的 MessageDispatcher 可以实现我们自定义消费者生产逻辑


总结

**通过第二篇文章我们再一次了解了如何通过框架去了解RabbitMq,以及使用和注意事项,当然这个离我们正真使用到项目集成到系统当中去还有一定的距离,不过相信阅读了这两篇文章的园友,应该还是有所收获,这里博主不光光是只是对框架做了一定的分许和解读,主要还是对rabbitmq本身一些注意点和特性机制做了一定实验研究,也从消息落地方面做了一定设想,这样在改善现有系统和以后新系统的搭建的时候能够做到心中有数的话也是起到了一定的作用了。这里抛出两个问题,也是博主后续将会去了解,1、就目前而言项目中发生了,消息的无故重发,因为隔离了系统的耦合性,导致消息内容一致却被重复处理的问题?提示:做到消息内容处理的幂等验证,不能依赖发送方的可靠性 2、关于如何搭建集群的rabbitmq服务与使用easynetq访问?集群搭建上文有参考链接,这里给出easynetq关于cluster support参考链接:https://github.com/EasyNetQ/EasyNetQ/wiki/Cluster-Support **

如果上述内容对您来说有一定作用的话,您的点赞和评论都是对博主最大的支持的呐,O(∩_∩)O嗯!

posted @ 2017-10-14 18:08  DJLNET  阅读(1446)  评论(2编辑  收藏  举报