探索cqrs和事件源

关于事件源和CQRS的讨论似乎通常集中在CQRS上下文中的整体系统架构或领域驱动设计的各种形式。 但是,尽管也有一些有趣的考虑,但读取模型经常被忽略。 在本文中,我们将介绍通过使用事件流来填充视图模型的示例实现。

总览

读取模型的想法非常简单。 您获取事件日志,使用适当的功能在最初为空的数据模型上应用(重放)所有事件,然后获得填充的模型。 代码如下所示:

List<Event> events = getEvents();
Model model = Model.empty();
for (Event event : events) {apply(model, event);
}

通过函数式编程,我们可以使此过程更短:

Model m = reduce(getEvents(),Model.empty(),(m, e) -> apply(m, e));

这就是本质。 请注意,这只是抽象的轮廓,实际的实现可能会有所不同,包括缓冲,批处理(或流式传输),持久性等。

申请活动

应用事件的实际Java代码可能类似于以下内容:

EventProcessingResult processEvents() {if (getState().isRunning()) {int batchSize = getEventsPerIteration();List<Event> events = eventStore.getEventsForAllStreams(getLastEventId(),batchSize);if (events.isEmpty()) {return NO_EVENTS_TO_PROCESS;} else {return processEvents(events);}} else {return NOT_RUNNING;}
}EventProcessingResult processEvents(List<Event> events) {try {for (Event event : events) {dispatchEvent(event);}return SUCCESS;} catch (RuntimeException e) {return FAILURE;}
}

总而言之,这确实非常简单明了。 在处理单个事件和整个批处理之前和之后,可以使用钩子来增强它。 这样的钩子可以用来:

  • 实施交易,
  • 插入监控,
  • 实施错误处理,
  • 根据速度计算批次大小,
  • 执行任意操作,例如设置某些内容或每批重新计算一次。

最后一个有趣的部分是dispatchEvent方法。 除了遍历类型层次结构,错误处理并使其全部可选之外,它还可以归结为:

void dispatchEvent(Event e) {Method handler = projector.getClass().findMethod("on", e.getClass());handler.invoke(projector, e);
}

换句话说,对于每种事件类型(例如OrderCreated ),我们在projector对象上寻找一个名为on的公共方法on该方法采用一个匹配类型的单个参数。

以上所有都是引擎的一部分,是支持许多视图模型的基础架构。 实施投影所需的所有操作实际上是为投影仪提供有趣的事件类型的处理程序。 所有其他事件将被忽略。

它可能看起来像这样:

public class OrderProjector {@Injectprivate OrderDao orders;public void on(OrderCreated e) {orders.save(new Order(e.getOrderNumber()));}public void on(OrderApproved e) {Order o = orders.find(e.getOrderNumber());o.setApproved(true);}
}

投影线

让我们讨论一下多线程。 共享的可变状态立即带来许多问题, 应尽可能避免 。 处理它的方法之一是首先没有并发性,例如通过限制对单个线程的写入。 在大多数情况下 ,单线程写入器与ACID事务相结合足以应付写入负载。 (读取/查询负载可能很重,并且使用许多线程–此处所有详细信息仅与写入有关。)

从查询事件存储到更新视图模型数据库,该线程负责将事件应用于读取的模型。 通常,它只是从商店加载一批事件并应用它们。 只要有更多事件要处理,它就会继续,并在捕获到事件后进入睡眠状态。 在一定时间后或事件存储通知新事件时,它将唤醒。

我们还可以控制该线程的生命周期。 例如,我们提供了一种以编程方式暂停和恢复每个投影线程的方法,甚至可以在管理GUI中公开。

推还是拉?

使用数据库支持的事件存储,可以很容易地重复查询新事件。 这是模型。 不幸的是,这也意味着您可能最终会轮询过多并产生不必要的负载,或者轮询频率太低,因此可能需要更长的时间才能将更改传播到视图模型。

这就是为什么除了轮询事件存储之外,最好引入通知,以在保存新事件后立即唤醒读取的模型。 这有效地成为了具有最小延迟和负载的推送模型。 我们发现JGroups是完成这项工作的非常好的工具-它支持多种协议,并且易于设置,与成熟的消息队列相比,所涉及的麻烦要少得多。

通知可能包含也可能不包含实际事件。

在后一种(和更简单的)设计中,它们仅传播已保存新事件的信息及其顺序ID(以便所有预测都可以估算出其背后的数量)。 唤醒后,执行程序可以从查询事件存储开始沿其正常路径继续。

为什么? 因为处理来自单一来源的事件比较容易,但是更重要的是,由于数据库支持的事件存储可轻松保证排序,并且消息丢失或重复不存在任何问题。 鉴于我们正在按主键顺序读取单个表,而且大多数情况下数据仍在RAM高速缓存中,因此查询数据库的速度非常快。 瓶颈在投影线程中,正在更新其读取模型数据库。

但是,将事件数据放入通知中没有任何障碍(可能是出于大小或网络流量方面的考虑)。 这可能会减少事件存储上的负载并节省一些数据库往返时间。 投影仪将需要维护一个缓冲区,并在需要时退回查询事件存储。 或者系统可以使用更可靠的消息队列。

重新开始投影

除了暂停/恢复之外,上面的屏幕快照还显示了另一项操作:重新启动。 从外观上看是无害的,这是一个非常不错且功能强大的功能。

由于视图模型是完全从事件日志派生的,因此可以随时丢弃它并从头开始创建(或从某个初始状态/足够老的快照开始)。 在事件日志中,数据是安全的,这是事实的最终来源。

当有关视图的任何内容发生更改时,此功能很有用:添加了字段或表,修复了错误,计算出的内容有所不同。 当发生这种情况时,通常从一开始就更容易(或需要),而不是例如实施大量SQL迁移脚本。

甚至可以进行完全自动化,以便在系统启动并检测到DB模式与相应的Java模型不匹配时,它可以自动重新创建模式并重新处理事件日志。 就像使用Hibernate create-drop策略运行一样,不同之处在于它不会丢失数据。

性能

该解决方案在性能方面可能看起来非常有限。

单线程作家可能引起人们的注意。 实际上,单个线程通常足够快,可以轻松地跟上负载。 并发不仅更难以实现和维护,而且还引入了竞争。 读取(查询)可以是多线程的,并且易于扩展。

通过拥有多个读取模型,例如从管理和“交易”数据中分离分析,我们也获得了很多收益。 每个模型都是单线程的(用于编写),但是多个模型并行使用事件。 最后,可以将解决方案修改为使用分片或某种fork-join处理。

另一个有趣的观点是从头开始重新启动投影

一个好的解决方案是类似于kappa体系结构 :

  • 保持过时的预测正常运行并回答所有查询。
  • 开始一个新的投影,例如到另一个数据库。 只要让它处理事件,就不要指向任何流量。
  • 当新的预测赶上时,请重定向流量并关闭旧的预测。

在很小的情况下,尤其是对于开发而言,甚至可以在同一情况下在线重新启动。 它取决于以下问题的答案:重新处理所有事件需要多长时间? 这个投影陈旧30分钟是否可以接受? 我们可以在没有人使用该系统的夜晚或周末进行部署吗? 我们需要重播所有历史吗?

这里要考虑的另一个因素是持久性。 如果瓶颈太大,无法进一步优化,请考虑使用内存视图模型。

加起来

本质上,这就是实现使用事件存储区的读取模型所需的全部工作。 有了线性事件存储并在单个线程中处理所有内容,它变得非常简单。 如此之多,最后实际上只是一个循环,实现了开头所示的减少。

在以后的文章中,我将更深入地探讨实施预测的实际问题。

翻译自: https://www.javacodegeeks.com/2015/09/writing-an-event-sourced-cqrs-read-model.html

探索cqrs和事件源

探索cqrs和事件源_编写基于事件的CQRS读取模型相关推荐

  1. 编写基于事件的CQRS读取模型

    关于事件源和CQRS的讨论似乎通常集中在CQRS上下文中的整体系统架构或领域驱动设计的各种形式. 但是,尽管也有一些有趣的考虑,但读取模型经常被忽略. 在本文中,我们将展示一个通过使用事件流填充视图模 ...

  2. 探索cqrs和事件源_实践中的事件源和CQRS

    探索cqrs和事件源 任何尝试实施完全符合ACID的系统的人都知道,您需要做很多事情. 您需要确保可以自由创建,修改和删除数据库实体而不会出错,并且在大多数情况下,解决方案将以性能为代价. 可以用来解 ...

  3. 探索cqrs和事件源_假人的CQRS和事件源

    探索cqrs和事件源 CQRS(命令和查询责任隔离)和事件源不是什么新概念. 除了NoSql,Functional Programming和Microservices之外,这些复兴概念由于能够应对现代 ...

  4. python编程求长方体体积_编写基于对象的程序求3个长方体的体积

    /* * 程序的版权和版本声明部分 * Copyright (c)2012, 烟台大学计算机学院学生 * All rightsreserved. * 文件名称: volume.cpp * 作者:孙锐 ...

  5. 用java编写打印时间_编写一个java程序,读取系统时间,然后将时间用中文输出...

    展开全部 import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.util.Calendar; ...

  6. 事件触发控制_基于事件触发机制的直流微电网多混合储能系统分层协调控制方法...

    点击下面标题,了解通知详情第九届电工技术前沿问题学术论坛征文通知 华北电力大学电气与电子工程学院的研究人员郭伟.赵洪山,在2020年第5期<电工技术学报>上撰文,以含有多个混合储能系统(H ...

  7. 实践中的事件源和CQRS

    任何尝试实施完全符合ACID的系统的人都知道,您需要做很多事情. 您需要确保可以自由创建,修改和删除数据库实体而不会出错,在大多数情况下,解决方案将以性能为代价. 可以用来解决此问题的一种方法是根据一 ...

  8. 快速轻巧的CQRS和事件源解决方案

    目录 介绍 何苦呢? 优先级 1.可读性 2.性能 3.可调试性 4.最小的依赖 5. 单独的命令和事件 6.多租户 7. Sagas/流程经理 8. 调度 9. 聚合到期 10. Async/Awa ...

  9. 网络安全模型_基于TCM的网络安全访问模型

    摘要:分析Google公司的BeyondCorp安全访问模型,基于TCM标准的可信计算平台,借鉴 BeyondCorp企业安全方法,结合TNC可信网络接入.用户PKC证书验证和基于属,性证书的访问 控 ...

最新文章

  1. LRU算法 -- 链表 完整实现
  2. 将Java程序变成可执行文件的一个简单方法
  3. 业余无线电通信_登山与业余无线电的完美结合,便携式电台参加VHF比赛心得体会...
  4. python tcl 控件_在Tkinter.Tcl()中使用Python函数
  5. 142-练习8和9 for循环的嵌套调用和随机数的生成
  6. Java使用HtmlUnit抓取js渲染页面
  7. easyUI不同版本的combotree控件clear方法的区别
  8. python字符串常用函数-Python字符串常用函数详解
  9. 手机能识别sim卡但是没信号_一篇文章扫盲手机SIM卡相关知识
  10. [4.6]-AutoSAR零基础学习-CAN通信协议
  11. 联想硬盘保护安装linux,【原创参赛】联想硬盘保护系统 (详细说明)
  12. 微处理器 微型计算机 单片机之间有何区别,微处理器,微计算机,微处理机,CPU,单片机,它们之间有何区别...
  13. 【Scratch】青少年蓝桥杯_每日一题_3.01_画莲花
  14. 尚硅谷Javase项目一(家庭收支记账软件)
  15. 饥荒控制台输入没用_饥荒控制台怎么用 控制台的使用方法以及代码说明解析...
  16. IntelliJ IDEA 的 Code Coverage 测试
  17. 工作中遇到的常见问题
  18. 基于进程的资源监控系列(三)--nethogs
  19. python+opencv图像处理之七:直方图均衡化
  20. 基于thinkphp校园二手交易网站#毕业设计

热门文章

  1. P3369-[模板]普通平衡树【有旋Treap】
  2. nssl1254-A(林下风气)【树形dp】
  3. nssl1196-摘果子【树形依赖背包,dp】
  4. 【2018.4.7】模拟赛之六-ssl2387 树【图论,树】
  5. 【2018.3.31】模拟赛之二-ssl2407 负进制【贪心】
  6. ssl1063-统计数字【哈希表】
  7. 【kruskal】【倍增】严格次小生成树(P4180)
  8. 【整体二分】区间第k小(金牌导航 整体二分-1)
  9. 2018年10月17日普级B组【模拟赛】
  10. CF650E Clockwork Bomb(树上构造类问题、并查集)