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

探索cqrs和事件源

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

总览

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

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中公开。

projection_admin

推还是拉?

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

这就是为什么除了轮询事件存储之外,最好引入通知,以在保存新事件后立即唤醒读取的模型。 这有效地成为了具有最小延迟和负载的推送模型。 我们发现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和事件源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337092.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 11正式发布

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删C# 11 现已发布。公告称&#xff0c;“随着每个版本的发布&#xff0c;社区的参与度越来越高&#xff0c;贡献了从建议、见解和错误报告到整个功…

分享一个通用的嵌入式驱动层

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删C 语言面向对象编程的最佳实践一、前言以STM32为例&#xff0c;打开网络上下载的例程或者是购买开发板自带的例程&#xff0c;都会发现应用层中会…

cks32和stm32_cks子,间谍,局部Mo子和短管

cks32和stm32本文是我们名为“ 用Mockito测试 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入了解Mockito的魔力。 您将了解有关“模拟”&#xff0c;“间谍”和“部分模拟”的信息&#xff0c;以及它们相应的存根行为。 您还将看到使用测试双打和对象匹配器进行验证…

CSON+CJSON,解析json数据更优雅?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删前言json是目前最为流行的文本数据传输格式&#xff0c;特别是在网络通信上广泛应用&#xff0c;随着物联网的兴起&#xff0c;在嵌入式设备上&a…

让C语言源码可知自身函数的实际地址与大小

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删事情的起因大概是这样……在很久很久以前&#xff0c;我最早用的是MASM&#xff08;Win32ASM&#xff09;写程序&#xff0c;从平台兼容性、开发…

java设计模式迭代器模式_迭代器设计模式示例

java设计模式迭代器模式本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查…

平衡二叉树 C语言代码实现

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删1.什么是平衡二叉树平衡二叉树&#xff0c;我们也称【二叉平衡搜索树/AVL】,树中任何节点的两个子树的高度最大差别为1&#xff0c;巴拉巴拉。。…

UDP/TCP 包的大小限制知多少

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删一、MTU 简述 - 分包后数据包最大长度1、定义Maximum Transmission Unit&#xff08;最大可传输单元&#xff09; 的缩写&#xff0c;它的单位是…

java ee的小程序_扩展Java EE应用程序的基础

java ee的小程序老实说&#xff0c;“可扩展性”是一个详尽的主题&#xff0c;并且通常没有被很好地理解。 通常&#xff0c;它被认为与高可用性相同。 我已经看到新手程序员和“经验丰富”的建筑师都建议将“ 群集 ”作为可伸缩性和HA的解决方案。 它实际上没有任何问题&#…

28 张图,一次性说清楚 TCP,速度

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删做IT相关的工作&#xff0c;肯定都离不开网络&#xff0c;网络中最重要的协议是TCP。无论是实际工作还是笔试面试&#xff0c;你看哪里能少得了T…

晨风机器人怎么买奴隶_潮牌复刻和正品该怎么抉择???带你了解了解

今天带你们聊一聊潮牌复刻和正品&#xff0c;简单介绍一下我自己&#xff0c;在复刻圈子五年&#xff0c;我的原则从始至终就是质量放在第一位&#xff0c;之所以能走这么久也是这个原因。回归正题&#xff0c;接着往下看。无论是正品还是复刻&#xff0c;其实还要根据自己的能…

C语言字符串函数strcat | strcpy | strlen | strcmp的用法及原型

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删strcat(str1,str2) 意为将字符串str2连接到字符串str1之后strcat用法如下#include <stdio.h> #include <string.h>int main () {cha…

C语言字符数组与字符串的使用详解

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删正文1、字符数组的定义与初始化字符数组的初始化&#xff0c;最容易理解的方式就是逐个字符赋给数组中各元素。char str[10]{ I, ,a,m, ,‘h,a,p…

5 个牛逼的算法设计,你知道几个?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删1、分治法概念&#xff1a;将一个难以直接解决的大问题&#xff0c;分割成一些规模较小的相同问题&#xff0c;以便各个击破&#xff0c;分而治之…

javafx窗体程序_JavaFX实际应用程序:AISO HRC-Matic

javafx窗体程序“ Real-World JavaFX Apps”系列中的第三个应用程序是一种重型数据输入应用程序&#xff0c;由称为HRC-Matic的关系数据库支持。 它由AISO在日内瓦开发。 AISO是一家专门开发基于JavaFX的业务应用程序的公司。 他们还在研究我在本系列的第一个博客&#xff08; …

几十个Shell分析日志文件脚本!

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删收集&#xff0c;整理一些服务器日志分析命令&#xff0c;可以用来分析自己网站服务器日志&#xff0c; 看看网站的访问量。看看有没有黑阔搞破坏…

flex布局水平垂直 垂直_垂直和水平装饰

flex布局水平垂直 垂直装饰器模式是在不更改其接口的情况下向对象添加功能的最佳方法之一。 我经常使用可组合装饰器&#xff0c;并且总是会问自己在功能列表必须可配置时如何正确设计它们。 我不确定我的答案是否正确&#xff0c;但是这里有一些值得深思的地方。 The Apartme…

一文读懂 | Linux 中的各种栈:进程栈 线程栈 内核栈 中断栈

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删栈是什么&#xff1f;栈有什么作用&#xff1f;首先&#xff0c;栈 (stack) 是一种串列形式的数据结构。这种数据结构的特点是后入先出 (LIFO, L…

【C语言】彻底搞懂内存屏障与volatile

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删最有价值的写在最前面内存屏障与 volatile 是高并发编程中比较常用的两个技术&#xff0c;无锁队列的时候就会用到这两项技术。然而这两项技术涉…

熟悉又陌生的arm 编译器详解(armcc/armclang)

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删arm编译器学习首先来了解一下编译器&#xff0c;其通常分为三个部分&#xff1a;前端优化器后端。前端&#xff1a;词法、语法和语义分析&#x…