设为首页 加入收藏

TOP

消息队列一:为什么需要消息队列(MQ)?(三)
2019-09-17 19:06:13 】 浏览:197
Tags:消息 队列 为什么 需要
eItem(string aName); }

Value为字符串类型,它利用了HashTable存储Key和Value的键值对。Item则为IMessageItem类型,在IMessageItemSequence的实现类中,同样利用了HashTable存储Key和Item的键值对。

IMessageItem支持消息体的嵌套。它包含了两部分:SubValue和SubItem。实现的方式和IMessageItemSequence相似。通过定义这样的嵌套结构,使得消息的扩展成为可能。一般的消息结构如下所示:

       IMessage——Name
                     ——ID
                     ——Body(IMessageItemSequence)
                            ——Value
                            ——Item(IMessageItem)
                                   ——SubValue
                                   ——SubItem(IMessageItem)
                                          ——……

各个消息对象之间的关系如图6所示:

图6 消息对象之间的关系

在实现服务进程通信之前,我们必须定义好各个服务或各个业务的消息格式。通过消息体的方法在服务的一端设置消息的值,然后发送,并在服务的另一端获得这些值。例如发送消息端定义如下的消息体:

IMessageFactory factory = new MessageFactory();
IMessage message = factory.CreateMessage();
message.SetMessageName("service1");

IMessageItemSequence body = message.CreateMessageBody();
body.SetSubValue("subname1","subvalue1");
body.SetSubValue("subname2","subvalue2");

IMessageItem item1 = body.CreateMessageItem(”item1”);
item1.SetSubValue("subsubname11","subsubvalue11");
item1.SetSubValue("subsubname12","subsubvalue12");

//Send Request Message
MyServiceClient service = new MyServiceClient("Client");
service.SendRequest(message);

我们在客户端引入了一个ServiceLocator对象,它通过MessageQueueListener对消息队列进行侦听,一旦接收到消息,就获取该消息中的name去定位它所对应的服务,然后调用服务的Execute(aMessage)方法,执行相关的业务。

ServiceLocator承担的定位职责其实是对存储在ServiceContainer容器中的服务进行查询。ServiceContainer容器可以读取配置文件,在启动服务的时候初始化所有的分布式服务(注意,这些服务都是无状态的),并对这些服务进行管理。它封装了服务的基本信息,诸如服务所在的位置,服务的部署方式等,从而避免服务的调用者直接依赖于服务的细节,既减轻了调用者的负担,还能够较好地实现服务的扩展与迁移。

在这个系统中,我们主要引入了Messaging模式,通过定义的IMessage接口,使得我们更好地对服务进行抽象,并以一种扁平的格式存储数据信息,从而解除服务之间的耦合。只要各个服务就共用的消息格式达成一致,请求者就可以不依赖于接收者的具体接口。通过引入的Message对象,我们就可以建立一种在行业中通用的消息模型与分布式服务模型。事实上,基于这样的一个框架与平台,在对制造行业的业务进行开发时,开发人员最主要的活动是与领域专家就各种业务的消息格式进行讨论,这样一种面向领域的消息语言,很好地扫清了技术人员与业务人员的沟通障碍;同时在各个子系统之间,我们也只需要维护服务间相互传递的消息接口表。每个服务的实现都是完全隔离的,有效地做到了对业务知识与基础设施的合理封装与隔离。

对于消息的格式和内容,我们考虑引入了Message Translator模式,负责对前面定义的消息结构进行翻译和解析。为了进一步减轻开发人员的负担,我们还可以基于该平台搭建一个消息-对象-关系的映射框架,引入实体引擎(Entity Engine)将消息转换为领域实体,使得服务的开发者能够以完全面向对象的思想开发各个服务组件,并通过调用持久层实现消息数据的持久化。同时,利用消息总线(此时的消息总线可以看做是各个服务组件的连接器)连接不同的服务,并允许异步地传递消息,对消息进行编码。这样一个基于消息的分布式架构如图7所示:

图7 基于Message Bus的CIMS分布式架构

场景二:消息中间件的架构决策

在一个医疗卫生系统中,我们面临了客户对系统性能/可用性的非功能需求。在我们最初启动该项目时,客户就表达了对性能与可用性的特别关注。客户希望最终用户在进行复杂的替换删除操作时,能够具有很好的用户体验,简言之,就是希望能够快速地得到操作的响应。问题在于这样的替换删除操作需要处理比较复杂的业务逻辑,同时牵涉到的关联数据量非常大,整个操作若需完成,最坏情况下可能需要几分钟的时间。我们可以通过引入缓存、索引、分页等多种方式对数据库操作进行性能调优,但整个操作的耗时始终无法达到客户的要求。由于该系统是在一个遗留系统的基础上开发,如果要引入Map-Reduce来处理这些操作,以满足质量需求,则对架构的影响太大,且不能很好地重用之前系统的某些组件。显然,付出的成本与收益并不成正比。

通过对需求进行分析,我们注意到最终客户并不需要实时获得结果,只要能够保证最终结果的一致性和完整性即可。关键在于就用户体验而言,他们不希望经历漫长的等待,然后再通知他们操作究竟是成功还是失败。这是一个典型需要通过后台任务进行异步处理的场景。

在企业应用系统中,我们常常会遭遇这样的场景。我们曾经在一个金融系统中尝试通过自己编写任务的方式来控制后台线程的并发访问,并完成对任务的调度。事实证明,这样的设计并非行之有效。对于这种典型的异步处理来说,基于消息传递的架构模式才是解决这一问题的最佳办法。

因为消息中间件的逐步成熟,对于这一问题的架构设计,已经由原来对设计实现的关注转为如何进行产品选型和技术决策。例如,在.NET平台下,架构师需要重点考虑的是应该选择哪种消息中间件来处理此等问题?这就需要我们必须结合具体的业务场景,来识别这种异步处理方式的风险,然后再根据这些风险去比较各种技术,以求寻找到最适合的方案。

通过分析业务场景以及客户性质,我们发现该业务场景具有如下特征:

  • 在一些特定情形下,可能会集中发生批量的替换删除操作,使得操作的并发量达到高峰;例如FDA要求召回一些违规药品时,就需要删除药品库中该药品的信息;
  • 操作结果不要求实时性,但需要保证操作的可靠性,不能因为异常失败而
首页 上一页 1 2 3 4 5 6 下一页 尾页 3/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Serverless无服务应用架构纵横谈 下一篇Python 环境搭建

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目