#分布式系统架构之# 事件驱动模式以及与之匹配的长时间处理过程讨论

Wesley13
• 阅读 681

      在分布式系统下,可以很多种架构从事设计,或者分布式系统对技术架构本身没有做严格的限制。但是结合自己的实践以及基于《领域驱动设计》的推荐, 采用 【事件驱动模式】是比较好的一种分布式系统架构方式。该模式充分实现了 不同系统之间的代码解耦,所有的业务流转是通过事件广播进行驱动的。所有业务都是在针对 名为【事件总线】的组件在编程,也无需知道事件的生产者,每个业务只监听自己感兴趣的事件,然后根据事件触发不同的业务代码 以及 自己在处理完某种业务后,也对外广播发出事件,则标志着当前业务的完成。

      基于【事件驱动模式】是一种比较接近人类自然行为的一种模式,该模式给系统的解耦以及重构带来了先天优势。相对比与传统的业务模式,大量的业务流程都是基于代码或者是某种规则引擎加以实现,而 事件驱动模式 则是针对事件总线注册监听, 【事件总线】总是能够忠实的把消息投递给感兴趣的监听者,并且保证其对应业务逻辑的自然触发。一旦有业务变动,我们仅仅是在【事件总线】上增加 或者 移除对应的事件消费者,或者注册新的事件发布者,这种行为并没有影响到当前的业务代码变动。 用日常通俗讲法:更加接近于  小学老师讲课,老师常常会不自觉的吼一声:“小朋友们,记住了吗?”这时,这要是听到该咨询的小朋友 都会回应道:“听懂了”,在此过程中 ,声音作为事件、空气作为事件总线(忠实的传递事件到达监听者),如果这个时候,小学的校长在教师边走过,那么老师的咨询同样也被校长听到,但是校长对此不作任何反应,应为该事件对于校长而言,是不关心的事件,所以直接忽略。还是忙他自己的事情。

      引入事件驱动,让复杂的系统变得很容易,因为软件就是一直在模仿人类的神经传输,事件驱动是一种比较好的模仿行为。这个时候,大家会问,事件驱动,关键是事件的定义,以及在什么情况下发布事件呢?这个时候 就需要讨论的深入一点,事件是对一种业务行为完成的标识,只有当某一独立的原子业务执行完毕,就要对外发布事件。事件更多的需求术语为:当。。。。。。就该。。。。。。,类似的语境,恰好就是事件驱动完美的体现。  在方法的设计上,我们可以把方法拆分为 读方法 与 写方法,所谓的 读方法就是 该方法内部不对任何数据进行家修改,而是通过一个简单的叠加,组装,把数据有效的输出,而写方法恰好相反,写方法体现在对对象的修改上(并实现最终的持久化操作),所有的事件 是在 写方法中产生。这个以后作为单独的文章描述,为何如此设计呢?为了保证分布式下,数据的一致性等。

    一旦引入事件驱动,则事件发布者永远不知道到底有多少消费者在监听该事件,而且当消费者收到事件后,是否正确的处理完其对应的业务,还是中途发生异常呢?事件的驱动是 采用同步方式还是异步方式呢?这就是引入【事件驱动】后,先天不足的一面,所以我们需要在架构上考虑这种不足,通过其他的技术手段来消除这种不足,从而变得稳定可靠。 这样就引出了本文另外一个 所要讲述的话题:长时间过程处理过程的方案,该方案的提出,就是为了解决事件驱动模式下,事件生产者与消费者 在互相不知道对方存在的情况下,而有效的协调完成一个更加的业务呢?

    长时间过程处理(业务有专门的术语 称之为 Saga),通过个人的整理以及自己的理解,针对长时间过程处理 一般分为三种解决方案,通过技术手段使得【事件驱动模式】能够更好的为我们服务,三种方案分别如下:

   1、把处理过程设计成为一个组合任务,使用一个执行组件对任务进行跟踪,并且对每一个步骤和任务的完成情况进行存储(持久化),然后再根据执行的结果决定是否启动下一个业务环节。该方案更像是责任链模式的灵活应用,缺点是 一开始就需要把该事件 可能触发的子任务进行备案,并对其执行结果进行登记注册,显然,这种方式适合于业务场景比较固定的情况。

2、同第一种方案类似,同样把处理过程设计为一个聚合,聚合成为活动协作的中心,一个或者多个聚合的实例充当之心任务的组件并维护过程中产生的异常等

3、第三种是设计一种 无状态的事件跟踪器,该跟踪器每一个事件的消费者的事件获取与执行情况,并且把执行结果反馈给跟踪器,该跟踪器获取结果后对其进行存储,这样不断的累加事件在消费过程中不断增加的处理结果,这样可以跟踪到事件流转的全部状态,并且按照存储的事件结果,决定是否正常执行完其一个完整的业务,该方案适合于消费者动态的场合,每一次事件的发布,都可以保证到期监听者对事件的消费以及结果的持有,只有当所有事件的状态是正常的,才能确定这个业务是完整的。

     三种方案,第二种对代码的耦合度比较大,不建议使用,但是第二种方案是传统编程下最常采用的一种模式。  本人比较倾向于使用 第三种方案, 该方案如同人类神经网络一样,复杂而各司其职。

     如果大家要体验这种机制,单机模式 推荐采用 guava 自带的 eventbus作为模拟的事件总线

    如果是分布式模式 可考虑引入MQ 中间件作为消息总线。(事件更多的是应用为 P/S) 模式。

   另外需要提出的一点是:事件内容能的设计,出了基本类型 就是基本类型,所有的业务事件数据都应当为 某种字符串的展现,如XML、JSON等。。。。。。   另外,事件的发出是有顺序的,这一点也需要引起注意。。。。。

      2016年 开年过的有点 纲目不清,还是需要自己多反省,借用王守仁的 心学 “需要在事上下功夫,遇事多磨练自己的内心”。。。。。。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
NET Core Web API下事件驱动型架构CQRS架构中聚合与聚合根的实现
NETCoreWebAPI下事件驱动型架构在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅、通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件总线的实现。接下来对于事件驱动型架构的讨论,就需要结合一个实际的架构案例来进行分析。在领域驱动设计的讨论范畴,CQRS架构本身就是事件驱动的,因此,我打算首先介
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Stella981 Stella981
3年前
Noark入门之异步事件
引入异步事件主要是为了各模块的解耦,每当完成一个动作时,向系统发布一个事件,由关心的模块自己监听处理,可选择同步处理,异步处理,延迟处理。何时发布事件,当其他模块关心此动作时<br比如获得道具时,任务系统模块要判定完成进度,BI模块需要上报等等都可以监听此事件,已达模块解耦0x00事件源一个实现xyz.noark.core.event
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究