ENode框架单台机器在处理Command时的设计思路

简介:

设计目标

  1. 尽量快的处理命令和事件,保证吞吐量;
  2. 处理完一个命令后不需要等待命令产生的事件持久化完成就能处理下一个命令,从而保证领域内的业务逻辑处理不依赖于持久化IO,实现真正的in-memory;
  3. 保证命令、事件处理的顺序性,先来的先处理,先产生的先处理;
  4. 保证一个聚合根的事件只有一个线程在持久化,并按事件产生的顺序持久化;
  5. 持久化事件时如果遇到并发冲突时(聚合根ID+事件版本号出现重复)的处理代价要轻;
  6. 要能利用多核的优势;

总体设计思路

  1. 先将命令根据聚合根ID路由到CommandMailBox里;
  2. 单线程处理CommandMailBox中的命令,由于聚合根在in-memory本地内存,所以处理非常快;
  3. 处理成功后更新聚合根的in-memory内存;
  4. 内存更新后将聚合根产生的事件同样原理路由到EventMailBox里;
  5. 单线程批量处理EventMailBox中的事件;由于是批量,所以持久化的吞吐量也可以保证;
  6. 处理完成一批事件后,把这一批事件对应的命令从CommandMailBox中移除;

详细设计思路

  1. 设计N个存放命令的CommandMailBox,命令首先按聚合根ID的hashcode取摸路由到对应的CommandMailBox;
  2. 每个CommandMailBox都有一个maxOffset, consumeOffset,以及一个CommandProcessor(单线程)在不停的处理;maxOffset表示最后一个命令的位置;consumeOffset表示当前正在处理的命令的位置;
  3. CommandProcessor的处理逻辑;
    • 创建、修改聚合根;
    • 更新聚合根的in-memory缓存;
    • 将聚合根产生的事件按聚合根ID的hashcode取摸路由到对应的EventMailBox;EventMailBox的个数也是程序启动时配置;
  4. 每个EventMailBox都有一个maxOffset, consumeOffset,以及一个EventProcessor(单线程)在不停的处理;maxOffset表示最后一个事件的位置;consumeOffset表示当前正在处理的事件的位置;
  5. EventProcessor的处理逻辑:
    • 按次序批量获取一批要处理的事件;
    • 批量持久化事件到EventStore,采用SqlBulkCopy;
    • 如果持久化一切顺利,则publish这一批事件(publish如果遇到网络IO异常,则重试,直到成功为止),然后继续持久化下一批,并同时将当前这一批事件对应的命令从CommandMailBox中删除;.如果持久化遇到并发冲突(事件的aggregateRootId+Version重复),则对当前这一批事件一个个按顺序持久化。如果当前事件持久化成功,则同样publish该事件,当然遇到IO异常时也要不断重试,直到成功为止;成功后通知CommandMailBox移除当前事件对应的命令;如果当前事件持久化出现并发冲突,就做如下处理:
    1. 先通知当前事件对应聚合根暂停处理后续的命令;
    2. 用Event Sourcing技术将in-memory中的聚合根的状态还原到最新状态,确保下次执行command时基于的聚合根的状态是最新的;
    3. 把这一批里该聚合根的所有事件移除,把EventMailBox中的该聚合根的所有事件移除;
    4. 将CommandMailBox的处理位置重置为当前事件对应的命令的offset;从而可以确保产生并发冲突的事件对应的命令以及后续的命令能再重新被处理一遍;
    5. 通知当前事件对应聚合根继续处理后续的命令(从哪个位置开始处理,在上面第4步已经重置过了);
    6. 这一批的所有事件都一个个处理完之后,按同样的逻辑继续处理下一批事件;

其他说明

  1. 上面的设计基于一个前提,就是一个聚合根几乎不会同时在两台服务器上同时存在并处理命令,否则就会出现并发冲突,而并发冲突的处理的代价还是比较复杂的,应该尽量避免;这点可以通过EQueue保证;
  2. 当聚合根处理了命令,尝试更新in-memory内存时,可能有一种情况会失败。就是如果这个命令是创建聚合根的,而有可能并发的时候这个聚合根可能在内存中已经有了,则创建完聚合根添加到内存时,应该能检测出来并记录错误日志,然后该命令产生的事件也不必放入EventMailBox,然后认为该命令处理成功即可。
  3. 上面的设计中没有谈到当遇到命令重复执行时的设计思路,大家可以自己想想:)
目录
相关文章
|
5月前
|
Arthas 监控 Java
Arthas mc(Memory Compiler/内存编译器 )
Arthas mc(Memory Compiler/内存编译器 )
131 6
|
7月前
|
人工智能 边缘计算 分布式计算
《分布式软总线:AI动态推理架构的智能“建造师”》
分布式软总线是一种具备自组织特性的关键技术,可灵活构建适应人工智能动态推理需求的分布式计算架构。它通过自主设备发现、灵活组网、动态资源调度及自我修复机制,实现高效协同计算。在智能交通、智慧医疗和工业智能制造等领域,分布式软总线优化了实时数据处理与任务分配,推动了AI技术与行业应用的深度融合,为社会发展带来变革性影响。其核心优势在于去中心化设计,能快速响应动态需求并保障系统稳定性,助力复杂推理任务高效完成。
228 2
|
11月前
|
机器学习/深度学习 Python
机器学习中评估模型性能的重要工具——混淆矩阵和ROC曲线。混淆矩阵通过真正例、假正例等指标展示模型预测情况
本文介绍了机器学习中评估模型性能的重要工具——混淆矩阵和ROC曲线。混淆矩阵通过真正例、假正例等指标展示模型预测情况,而ROC曲线则通过假正率和真正率评估二分类模型性能。文章还提供了Python中的具体实现示例,展示了如何计算和使用这两种工具来评估模型。
457 8
|
11月前
|
Go vr&ar 图形学
重塑体验:AR/VR技术在游戏与娱乐行业的创新应用
【10月更文挑战第29天】本文探讨了AR/VR技术如何改变游戏与娱乐行业,介绍了AR和VR的基本概念及其在游戏和娱乐中的应用实例,包括《精灵宝可梦GO》的AR开发和VR视频播放器的实现代码,并展望了未来的发展趋势。
767 2
|
12月前
|
人工智能 自然语言处理 数据挖掘
Claude 3.5:一场AI技术的惊艳飞跃 | AIGC
在这个科技日新月异的时代,人工智能(AI)的进步令人惊叹。博主体验了Claude 3.5 Sonnet的最新功能,对其卓越的性能、强大的内容创作与理解能力、创新的Artifacts功能、视觉理解与文本转录能力、革命性的“computeruse”功能、广泛的应用场景与兼容性以及成本效益和易用性深感震撼。这篇介绍将带你一窥其技术前沿的魅力。【10月更文挑战第12天】
469 1
|
存储 缓存 机器人
哈弗架构和冯诺伊曼架构
在计算机体系结构中,有两种主要的模型:冯诺伊曼架构(Von Neumann Architecture)和哈弗架构(Harvard Architecture)。冯诺伊曼架构是传统的计算机设计模型,采用统一的存储器空间存储程序指令和数据。哈弗架构则采用分离的存储器空间,分别存储程序指令和数据,以提高系统性能。这两种架构各有优缺点,并在不同的应用场景中得到广泛应用。
387 1
|
JavaScript 前端开发 Java
v-if和v-show的区别?使用场景?v-if状态改变调用钩子函数的示例
这篇文章详细阐述了Vue中`v-if`和`v-show`指令的共同点、区别、使用场景以及它们在组件和普通元素上附属时的不同表现,并通过示例展示了状态改变时对钩子函数调用的影响。
v-if和v-show的区别?使用场景?v-if状态改变调用钩子函数的示例
|
8月前
|
前端开发 数据挖掘 关系型数据库
视频号轻创优选达人系统开发
视频号轻创优选系统开发是一个结合了短视频平台(如微信视频号)和电商功能的综合性项目
基于AutoJs实现的薅羊毛专业版第四次大更新
基于AutoJs实现的薅羊毛专业版第四次大更新
160 0