编译 | 朱先忠
策划 | Ethan
日常开发中,我们通常会在同一个体系架构中部署了多个不同角色的应用程序,而这些应用程序需要某种机制来通知彼此发生了哪些事件。这些事件可能是临时的(在运行时临时所做的更改),也可能是数据库事件(由于数据库中的更改)。如何应对这种复杂多变的分布式事件,一直是件相当棘手的问题。而这正是发布-订阅设计模式的用武之地。
众所周知,发布-订阅模式在众多设计模式中,可能是最常见、最有名的一个了。它定义了一种一对多的关系,让多个订阅者对象同时监听某一个主题对象,当这个主题对象的状态发生变化时就会通知所有订阅自己的订阅者对象,使得他们能够自动更新自己。
在“发布-订阅”的消息传递范式中,消息的发送者(发布者)并不知道预期的收件人(订阅者)。此外,发布者和订阅者应用程序之间并不直接交互,而是依赖于称为“主题”的公共媒体。因此,发布-订阅模式是一个松散耦合的消息传递cdxxd模型。
正是基于这些特性,发布-订阅模式(简称为“发布/订阅”)成为了构建企业级.NET应用程序不可或缺的工具。
分布式事件驱动架构探索
为了设计分布式事件驱动体系架构,开发人员以往倾向于采用以下方案之一。
1、RDBMS提供数据通知
如果数据存储仅限于关系数据库,数据库通知功能的确可以做到。除了允许向数据库服务器注册事件外,该功能还会在数据库结果集因更新、添加或删除而发生更改时,通知到应用程序侧。
但这里有一个问题,RDBMS本质上是不可扩展的,很容易成为应用程序的性能瓶颈。典型情况下,开发者不想让数据库承担不必要的额外任务。此外,数据库通知功能本身速度也较慢,而且不支持运行时数据共享。
这就不难理解,将数据库用作消息传递介质并不是最佳设计选择。
2、消息队列
另一种选择是在体系架构中引入单独的消息队列。虽然这些消息队列在帮助您在应用程序之间传输消息方面做得很好,但这些队列并不是以数据为中心的,即它们不监视数据库或任何其他源中的数据更改。此外,这些消息队列无法与应用层一起扩展。
3、自定义解决方案
剩下的最后一个选择是,打造适合自己需要的消息平台。定制解决方案这个想法乍一看很诱人,但是在所需的时间和资源方面都可能难以预估,因为构建和管理一个健壮且可扩展的消息传递平台是一项非常艰巨的任务。
问题症结在于:究竟哪种解决方案更易于合并、可扩展、高可用且非常可靠?
以分布式缓存作为消息传递平台
这里分享一种使用分布式缓存消息传递平台NCache。事实证明,这不仅是一个简单可行的解决办法,而且也是整合健壮消息传递平台的一种更现代化的方法。NCache是目前市场上提供的唯一真正的本地.NET/.NET内核分布式缓存。它是一种内存中的分布式缓存技术,速度极快,可扩展性极强。它使应用程序能够处理极端的事务负载,而不会使数据库成为瓶颈。此外,您还可以使用NCache以实时方式来处理数据/流。
NCache通常部署在N层体系架构的中间层。下图给出其所在架构层次的更直观的展示。
图1:N层体系架构中部署的NCache
NCache是一个缓存服务器集群,能够每秒为.NET应用以及Java应用提供数万个请求——通过将经常使用的数据保存在内存中,从而有效避免大量的数据库访问。
下面,深入分析一下NCache。首先,让我们看看NCache作为事件驱动体系架构的消息总线的原理。
1、NCache事件驱动消息
下图显示了NCache是如何作为.NET和Java应用程序的消息总线功能的。
图2:NCache作为消息传递平台
在上图架构中,NCache通过使用快速紧凑序列化来实现跨平台通信,该序列化可以转换.NET或Java对象转换为二进制文件,然后将它们传输到缓存集群。所以,这就给出了NCache如何支持.NET应用程序与Java应用程序进行交互的内在逻辑。有关这方面更多信息,请自行搜索查看可移植的数据类型相关内容。
NCache以事件的名称管理发布/订阅设计模式,并提供不同的方法将消息传播到其他侦听应用程序。让我们看看这两种消息类型,从而了解分布式缓存是如何传播它们的。
首先,考虑应用程序需要侦听的数据变化。因为NCache也是使用.NET键值存储技术,它提供在缓存中发生任何数据变化时更新应用程序的功能。因为所有这些都发生在内存中,所以不存在性能瓶颈问题。这些数据变化可以是:
- 缓存级项目变化,无论是更新还是删除
- 整个缓存级数据变化
- 连续查询,您可以在其中注册一个类似SQL的查询,以监视结果集是否在缓存中更改。如果确实如此,则会通知应用程序
- 如果由于任何新节点添加或删除或崩溃导致的集群发生更改。(对于管理方面来说)
NCache还允许数据库的依赖关系注册,包括SQL、Oracle和OleDb等。这有助于使数据库以及应用程序的缓存数据保持最新。有关支持的依赖关系类型的完整列表,请检查数据库依赖关系内容,在此不再赘述。这些依赖项是在不同的数据存储中注册的数据变化通知;但是,可以让NCache处理它。您还可以将数据库通知与NCache数据通知相结合,以便进一步丰富项目构建策略。
另一方面,如果您只想将简单消息传播到复杂的.NET或Java对象,那么你应该使用自定义消息传递功能。在这里,一个应用程序可以生成数据并触发事件,而感兴趣的侦听器几乎会在瞬间接收到事件。有关这方面的更多信息,读者可以自行查看自定义事件相关的文章。
2、NCache发布/订阅API
NCache提供了内存发布者/订阅者(发布/订阅)功能和专用的发布/订阅消息存储,以支持.NET Web应用程序中的实时信息共享,最终帮助应用程序更好地通信。
发布/订阅模式通过提供一个由感兴趣的用户发布和订阅消息的渠道,自然地将发布者和订阅者解耦。现在,当NCache充当消息传递总线时,发布/订阅模型受益于底层NCache分布式体系架构和许多方便的功能。
3、基本架构
先来了解一下NCache发布/订阅的基本组件及其工作原理。NCache发布/订阅消息的一般流程如下:首先,由发布者使用ITopic接口发布主题消息。接下来,订阅者可以创建对一个或多个主题的订阅,并接收相关消息。成功传递消息后,NCache将收到确认;否则,NCache将在消息过期之前继续重试(如果设置了过期信息)。未送达的消息将驻留在缓存中,直到过期消息被触发,请参考下图示意。
图3:NCache发布/订阅消息机制
4、订阅类型
NCache为发布/订阅消息提供了两种不同类型的订阅,即非持久订阅和持久订阅。此外,NCache支持独占和共享订阅策略。下文将讨论相关细节。
- 非持久订阅:默认情况下,在主题上创建的所有订阅都是非持久订阅。它仅在保持连接之前将预期消息传送给订阅者。如果订阅者的连接因任何原因而丢失,则不会在重新加入网络时收到旧消息。这种类型的订阅是独占的,这意味着一个订阅仅属于一个订阅者
- 持久订阅:它考虑了订阅者断开连接时的消息丢失。NCache在连接丢失时保留订阅者的订阅。因此,订阅者可以在重新连接时接收其所发布的感兴趣的消息
持久订阅提供了两种策略:
- 独占式:一次一个订阅仅属于一个活动的订阅方
- 共享式:一个订阅可以同时注册多个订阅者,并提供负载共享
NCahce发布/订阅应用案例
假设有一家电子商务商店,不同供应商定期向该商店提供新产品。同时,还提供产品的销售和优惠。对所提供产品感兴趣的门店经理和客户需要随时了解新产品、待售产品和折扣的最新情况。NCache发布/订阅功能可以在此场景中启用分布式通知系统。为此,可以首先创建NCache专用的发布/订阅消息存储。
接下来,让我们讨论一下在上述场景中通过使用NCache发布/订阅消息API实现分布式消息的逐步过程。
1、创建主题
作为第一步,需要创建一个主题,以便不同供应商可以发布新产品的更新。可以使用NCache中的ITopic接口创建一个具有唯一名称的新主题。下面的代码展示了发布方应用程序如何使用方法CreateTopic来创建一个名为newProducts的主题。
//前提条件:缓存已连接
//指定主题名称
string topicName = “newProducts”
//创建主题
ITopic topic = cache.MessagingService.CreateTopic(topicName);
- 1.
- 2.
- 3.
- 4.
- 5.
如果已经存在具有所提供名称的主题,则该主题的实例将作为ITopic返回。
NCache允许在创建主题时设置主题优先级。当某些事件需要以比其他事件更高的优先级进行沟通时,这很有用。例如,有关制成品的信息就是很紧急的;同样,由于折扣或销售导致的产品价格更新对于卖方/买方来说也是最重要的。在这种情况下,可以在创建主题时将主题优先级设置为高,并且可以毫不延迟地接收相关通知。
2、发布消息
创建主题后,发布方应用程序可以使用Publish方法将相关消息发布到该主题。为此,首先通过指定主题名称来获取主题的实例。NCache在发布消息时提供以下两种传递模式:
- All(默认):将消息传递给所有注册订阅者。当需要广播信息时,这很有用。
- Any:将消息传递给任何注册订阅者。
此外,为了有效地管理你的发布/订阅缓存的存储空间,你还可以设置消息的过期时间。
在以下代码中,发布者将在现有主题newProducts上广播有关新产品的消息。
//前提条件:缓存已连接
//主题“newProducts”已创建
string topicName = "newProducts"
//获取主题
ITopic productTopic = cache.MessagingService.GetTopic(topicName);
//创建要在消息中发送的对象
Product product = FetchProductFromDB(10248);
//创建相应于对象顺序的新消息
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
3、订阅主题消息
创建主题后,订阅者应用程序可以通过获得订阅来接收发布到该主题的消息。由于支持不同类型的订阅,因此对非持久订阅感兴趣的订阅者可以使用CreateSubscription方法;对于持久订阅,则可以使用CreateDurableSubscription方法。
下面的代码显示订阅者应用程序如何为主题newProducts创建订阅。MessageReceived注册回调以在收到通知时执行任何预期操作。例如,订阅者可以在收到销售通知时在MessageReceived回调中更新产品价格。
//前提条件:缓存已连接
//主题“newProducts”已创建
string topicName = "newProducts"
//获取主题
ITopic productTopic = cache.MessagingService.GetTopic(topicName);
//创建并注册主题newProducts的订阅者
//指定回调函数MessageReceived
ITopicSubscription orderSubscriber = orderTopic.CreateSubscription(MessageReceived);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
在上面的示例中,创建了一个非持久订阅。此外,当需要在重新连接时从订阅的主题接收旧消息时,订阅者可以创建持久订阅。
NCache还提供了一种基于模式的订阅方法,其中NCache支持多个通配符来订阅所提供模式下的单个/多个主题。
4、注册通知
NCache使发布者能够了解消息的状态和主题的可用性。发布者应用程序可以注册以下通知:
- MessageDeliveryFailure:如果消息因任何问题而无法传递,则通知发布者
- OnTopicDeleted:当消息被删除时通知发布者
以下是发布者注册这两种通知的简单方式:
//您有一个现有主题的实例productTopic
//注册消息传递失败
productTopic.MessageDeliveryFailure += OnFailureMessageReceived;
//注册主题删除通知
productTopic.OnTopicDeleted = TopicDeleted;
- 1.
- 2.
- 3.
- 4.
- 5.
通过遵循上述步骤,可以将基本的发布/订阅消息体系架构集成到任何一款ASP.NET或者.NET核心应用程序中。
总结
至此,总结一下NCache发布/订阅模式为克服现有解决方案中存在的局限性提供了哪些好处。
- 由于具有线性可扩展性,NCache发布/订阅可以通过添加缓存服务器和频繁地执行负载平衡来处理越来越多的订阅请求。该伸缩特征对用户透明,而且不会妨碍通信过程。因此,您可以使用NCache发布/订阅模式来轻松扩展通信性能。
- NCache发布/订阅有助于持久订阅、消息传递失效和传递失败通知,以避免消息丢失。此外,NCache的分布式和复制体系架构确保了NCache的高可用性,以适应动态环境中订阅者的连接。所有这些功能确保了可靠的通信。
- 由于NCache是内存中的分布式缓存,因此驻留在缓存中的消息存储本身就很快。此外,NCache允许对驻留在缓存中的数据项指定是否过期,以便智能地管理存储空间。
NCache的可扩展性、可靠性和存储效率,以及发布/订阅的松耦合和异步消息传递模式,使得NCache发布/订阅功能在未来的.NET/NET核心应用程序分布式消息传递开发中极具前景。
原文链接:https://dzone.com/articles/pubsub-design-pattern-in-net-distributed-cache
译者介绍
朱先忠,51CTO社区编辑,51CTO专家博客、讲师,潍坊一所高校计算机教师,自由编程界老兵一枚。早期专注各种微软技术(编著成ASP.NET AJX、Cocos 2d-X相关三本技术图书),近十多年投身于开源世界(熟悉流行全栈Web开发技术),了解基于OneNet/AliOS+Arduino/ESP32/树莓派等物联网开发技术与Scala+Hadoop+Spark+Flink等大数据开发技术。