通过从实体事件流中按顺序处理实体事件,可以将信息物化成一个有状态的表。每个实体事件都会被更新插入键/值表中,这样对于一个给定的键,表中表示的就是最新读到的事件。相反,通过将每次更新发布到事件流,可以将表转化成一个实体事件流。这就是所谓的表流二元性(table-streamduality),它是事件驱动型微服务中创建状态的基础。如图 2-3 所示,AA和 CC 在其物化表中都有最新的值。
更新插入的意思是当表中不存在记录时就插入一个新行,否则更新这一行。
同样,可以将所有表记录的更新用于生成一个表示随时间变化的表状态的事件流。在下面的例子中,BB 被更新插入了两次,而 DD 只被更新插入了一次。图 2-4 中的输出流展示了表示这些操作的 3 次更新插入事件。
一个关系型数据库表是通过一系列插入、更新和删除命令来创建和填充的。
这些命令可以作为事件生成到不可变日志中,比如一个本地追加文件(如MySQL 中的二进制日志)或者一个外部事件流。通过回放日志中的所有内容,可以精确地重建表及其所有数据内容。
这种表流二元性用于在事件驱动型微服务之间传递状态。任何消费者客户端都可以读取键控事件的事件流并物化成自身的本地状态存储。这种简单而强大的模式让微服务可以单纯通过事件共享状态,而无须在生产者服务和消费者服务之间有任何直接的耦合。
键控事件的删除是通过生成一个“墓碑”(tombstone)来处理的。墓碑是一个值被设为 null 的键控事件。这是一种约定,它向消费者表明应该从物化数据存储中移除这个键对应的事件,因为上游生产者已经宣称它已被删除。
除非进行压缩,否则追加的不可变日志可能会无限增长。压缩由事件代理来执行,通过只保留指定键的最新事件以减小其内部日志的大小。相同键的旧事件会被删除,剩余的事件会被压缩到一个新的、更小的文件集中。事件流的偏移会保持不变,这样消费者就无须进行任何更改。下图说明了在事件代理中一个事件流的压缩逻辑,包括墓碑记录的完全删除。
压缩减少了磁盘的使用和到达当前状态所需的事件的数量,其代价是放弃了事件流所提供的事件历史信息。
在事件驱动架构中,为业务逻辑维护状态是一种非常常见的模式。几乎可以肯定地说,一个完整的业务模型不可能适应完全无状态的流式领域,因为过去的业务决策将影响你今天做出的决策。举个具体的例子,如果你从事零售业务,那么你需要知道你的库存水平以确定何时重新订购,进而避免向客户销售不存在的商品。你也希望跟踪应付账款和应收账款。也许你想每周向所有提供了电子邮箱地址的客户推送一次促销活动。所有这些系统都要求你能够将事件流物化为当前状态表示。
连载中,还没关注的小伙伴记得点个关注不迷路~