前段时间跟一个朋友聊起kafka,flint,spark这些是不是某种分布式运算框架。我自认为的分布式运算框架最基础条件是能够把多个集群节点当作一个完整的系统,然后程序好像是在同一台机器的内存里运行一样。当然,这种集成实现方式有赖于底层的一套消息系统。这套消息系统可以把消息随意在集群各节点之间自由传递。所以如果能够通过消息来驱动某段程序的运行,那么这段程序就有可能在集群中任何一个节点上运行了。好了,akka-cluster是通过对每个集群节点上的中介发送消息使之调动该节点上某段程序运行来实现分布式运算的。那么,kafka也可以实现消息在集群节点间的自由流通,是不是也是一个分布式运算框架呢?实际上,kafka设计强调的重点是消息的接收,或者叫消息消费机制。至于接收消息后怎么去应对,用什么方式处理,都是kafka用户自己的事了。与分布式运算框架像akka-cluster对比,kafka还缺了个在每个集群节点上的”运算调度中介“,所以kafka应该不算我所指的分布式运算框架,充其量是一种分布式的消息传递系统。实际上kafka是一种高吞吐量、高可用性、安全稳定、有良好口碑的分布式消息系统。

kafka的本质是一种commit-log,或者“事件记录系统”:上游产生的数据(即事件)会按发生时间顺序存入kafka,然后下游可以对任何时间段内事件按序进行读取,重演运算产生那段时间内的某种状态。这不就是妥妥的CQRS模式吗?当然kafka也可以使用在其它一些场景如:消息队列,数据存储等,不过这些都是commit-log的具体应用。

常常看到网上有朋友抱怨akka-cluster的一些处理方式太底层或太基础了。用户往往需要自己来增加一些方法来确保使用安全。我想作为一种消息驱动系统,如何保证akka消息的正确产生和安全使用应该是最基本的要求。而恰恰akka是没有提供对消息遗漏和重复消息的保障机制。我想这也是造成akka用户担心的主要原因。上面提到kafka是一种高吞吐量、高可用性、安全稳定的分布式消息系统,特别是它提供了对exactly-once,“保证一次”的消息使用支持。那么通过kafka实现一套CQRS模式的实时交易处理系统应该是可行的。这也是我使用kafka的主要目的。

上面提到,希望能充分利用kafka commit-log特性来开发一个基于CQRS的实时交易系统,比如支付系统、库存管理系统,从实践中了解kafka。kafka支持多种语言终端,怪异的是没有scala终端。kafka是用scala开发的,不提供scala终端实在是说不通啊。不过akka在alpakka社区提供了alpakka-kafka:这个东西是个基于akka-streams的kafka scala终端编程工具,稍微过了一下,感觉功能比较全面,那就是它了。不过在开始前先把kafka的原理和基本情况做个介绍:

从表面上看kafka就是一个简单的消息存储和传递工具。不过因为其特殊分布式的消息发布、存储、读取处理机制,使其成为一种高吞吐量、高可用性、安全稳定的分布式消息处理工具。从应用角度来讲,kafka应用包括三个方面,kafka本身,就叫kafka引擎吧,发布终端、订阅终端,即:kafka,writer,reader三部分,其中:所有复杂的功能实现是包嵌在kafka内部的,writer,reader应该整合到用户应用里。kafka的作业是围绕着消息的发布订阅/读写进行的。所谓消息即CQRS模式里的事件。那么kafka的工作原理直白点就是writer向kafka写事件,kafka把事件按发生时间顺序保存,reader再按顺序从kafka读取事件并进行处理以产生新的业务状态,如在某个库位的一个商品数量得到了更新。当然原理看似简单,但具体的实现才是真正复杂的地方。

首先,writer和reader是以事件关联的,即:write发布某种类型的事件,而reader则是订阅相同类型的事件。 这里的事件也就是topic,或一项业务,如:图书类当前库存。为了提高数据吞吐量,每个topic又可以细分为多个partition。每个partition分担所属topic消息类型下的一些指定的细分类消息或者事件,如"图书库房101"。如果把这些partition再分布到一个集群的节点上,就可以实现高吞吐量的分布式读写,然后通过集群partition的复本同步又可以达到数据安全及系统高可用性的目的。这些集群节点就是所谓的broker了。发布消息内容由topic,key,value所组成。其中key值指定该消息应该写入那个partition,即通过对key进行hash计算得出partition id。hash算法可以保证相同的key值永远指定同一个partition。值得注意的是kafka保证每个partition上的事件肯定按照发生时间排序,所以要保证一种事件只能写入同一个partition。当然,一个partition可以承载多种事件。要注意的是创建topic和partition都是严格的管理工作admin,不是在某些程序中任意进行增减的。一般来讲,在创建一个新topic时就要确定它下面的partition数量了。这个partition数量要按照对数据吞吐量需求设定。但一般是集群节点的倍数,这样partition可以均匀分布在各broker上。

好了,该到reader这头了:reader作业从订阅某个topic开始。上面提过:一个topic下面可能有多个partition,但每个partition都会包含topic的其中几个子业务的全部事件,而且这些事件是严格按发生时间排序的。kafka有个reader group这么个概念:针对同一个topic,容许有一组多个reader对这个topic下的partition进行读取。但每个partition只容许组内一个reader读取。至于goup内reader是如何分配partition的完全由kafka内部解决。如果发现新partition或者组内reader有增减变化,kafka会自动进行再分配rebalance。所以总的来说订阅某个topic的一个组内reader应该负责那个partition是不确定的,加上随时可能发生动态再分配的情况,比如组内某个reader出问题倒了。换言之组内所有reader都必须具备处理整个topic所有类型业务的能力,如此才能解决组内reader-partition关系不确定的难题。kafka最重要的特点就是可以容许不同的应用通过不同的reader-group对同一个partition上的事件进行任意读取,本意应该是不同的应用可以利用同一个业务事件序列进行不同的业务处理。具体实现方式应该是每个组对某个partition上事件最后读取的位置分别进行了登记,offset-commit。这样,即使发生了重新分配rebalance组内任何一个reader对分配到的partition应从那个位置开始读还是确定的。这个offset-commit方式描述了几种事件读取模式:

1、at-most-once, 最多一次:如果刚读取事件,在进行业务处理之前就登记位置commit-offset,那么commit-offset后位置已经登记,即使业务处理失败也再也不可能二次读取了。

2、at-least-once,最少一次:读取事件、完成业务处理后才commit-offset。如果处理业务中系统故障,只能从上次登记的位置重新读取了,那么就会出现重复读取的情况。

3、exactly-once, 保证只一次:控制commit-offset的时间节点是取得at-most-once, at-least-once之间安全系数的一种方式。但exactly-once不容许有模糊地带。具体做法是把业务处理和commit-offset作为一个完整事物单元来处理(atomic-transaction)。两样操作同时成功或失败。

我觉着kafka的exactly-once能力最值得推介。因为在akka或者其它消息队列工具里不容易得到保证。而在一个消息驱动的实时交易系统里,保证事件重演能正确反映当时状态是关键。任何事件遗失或重复都会造成不可逆转的误差。那么下面的一系列讨论我就会尝试用alpakka-kafka来构建一个基于CQRS模式的实时交易系统,并和大家进行交流分享。

kakafka - 为CQRS而生fka - 为CQRS而生的更多相关文章

  1. CQRS+ES项目解析-Diary.CQRS

    在<当我们在讨论CQRS时,我们在讨论些神马>中,我们讨论了当使用CQRS的过程中,需要关心的一些问题.其中与CQRS关联最为紧密的模式莫过于Event Sourcing了,CQRS与ES ...

  2. .net架构设计读书笔记--第三章 第10节 命令职责分离(CQRS)简介(Introducing CQRS)

    一.分离查询命令 Separating commands from queries     早期的面向DDD设计方法的难点是如何设计一个类,这个类要包含域的方方面面.通常来说,任务软件系统方法调用可以 ...

  3. Web API与OAuth:既生access token,何生refresh token

    在前一篇博文中,我们基于 ASP.NET Web API 与 OWIN OAuth 以 Resource Owner Password Credentials Grant 的授权方式( grant_t ...

  4. CQRS学习——最小单元的Cqrs(CommandEvent)[其一]

    [说明:博主采用边写边思考的方式完成这一系列的博客,所以代码以附件为准,文中代码仅为了说明.] 结构 在学习和实现CQRS的过程中,首要参考的项目是这个[http://www.cnblogs.com/ ...

  5. CQRS:When to use CQRS

    Now that we are at the end of our journey, we can suggest some of the criteria you should evaluate t ...

  6. 后端开发实践系列之四——简单可用的CQRS编码实践

    本文只讲了一件事情:软件模型中存在读模型和写模型之分,CQRS便为此而生. 20多年前,Bertrand Meyer在他的<Object-Oriented Software Constructi ...

  7. CQRS项目

    CQRS+ES项目解析-Diary.CQRS   在<当我们在讨论CQRS时,我们在讨论些神马>中,我们讨论了当使用CQRS的过程中,需要关心的一些问题.其中与CQRS关联最为紧密的模式莫 ...

  8. .NET 5 源代码生成器——MediatR——CQRS

    在这篇文章中,我们将探索如何使用.NET 5中的新source generator特性,使用MediatR库和CQRS模式自动为系统生成API. 中介者模式 中介模式是在应用程序中解耦模块的一种方式. ...

  9. DDD CQRS架构和传统架构的优缺点比较

    明天就是大年三十了,今天在家有空,想集中整理一下CQRS架构的特点以及相比传统架构的优缺点分析.先提前祝大家猴年新春快乐.万事如意.身体健康! 最近几年,在DDD的领域,我们经常会看到CQRS架构的概 ...

随机推荐

  1. request-promise post请求微信小程序云函数调用http

    微信小程序不支持http调用,但是可以通过服务器或者云函数实现,云函数相当于云服务器中的一段代码,可以使用http协议 首先要云函数安装request-promise,右键云函数,在终端中打开,输入n ...

  2. 给 Mac 添加右键菜单「使用 VSCode 打开」

    最终的实现效果是在文件 / 文件夹上右击时,会出现菜单项「用 VSCode 打开」,点击后会启动 Visual Studio Code 打开对应的文件 / 文件夹. 实现步骤 打开「自动操作.app」 ...

  3. 【总结】mysql调优

    一.事务 1.事务的特性 (1)原子性(Atomicity),可以理解为一个事务内的所有操作要么都执行,要么都不执行. (2)一致性(Consistency),可以理解为数据是满足完整性约束的,也就是 ...

  4. 在springmvc.xml中定义全局的异常处理

    在Controller类的内部方法上使用@ExceptionHandler,则此类的方法抛出未处理的异常时,回到此方法上处理. @ExceptionHandler可以指定异常的类型,会自动进行匹配 如 ...

  5. VSCode--HTML代码片段(基础版,react、vue、jquery)

    起因是最近在学习前端,看的网上的demo也是在react.vue.jquery之间穿插,为了方便一键生成html模板(懒)写demo,有了以下折腾. 本人使用的前端编辑工具是vscode(方便.懒), ...

  6. php生成gitbook路径

    public function file_list() { $path='文件路径'; $arr=scandir($path); $i=0; foreach ($arr as $k=>$v) { ...

  7. QQ彩贝热销时装

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  8. Activit的心路历程:获取当前节点的下一节点【可能存在多个】的nodeId

    上一任务节点 在我的开发任务中,突然给我提出了一个待办任务需要获取当前任务节点下一任务节点的表单信息,刚开始搞得我有点措手不及,后来仔细是靠后,灵感一下,直接操作流程的bpmn信息就可以获取到节点信息 ...

  9. Delphi ADO更新条件

    转https://blog.51cto.com/kinwar/1686710 代码将导致 ADO 在 WHERE 子句中包括的每个字段.如果您想确保所做的当前用户更新才会成功如果为表格中的行中的任何字 ...

  10. 你的旧版本 App 为何运行在 iPhone 12 上没有异常?

    背景 当我在 10月14日 iPhone 12 系列发布直播,看到 iPhone 12 系列的分辨率后,我注意到这些分辨率是全新的时,我立即在群里吐槽:又需要适配一波了.我只是以为宽高变化会导致字号变 ...