Newbe.Claptrap-一套以“事件溯源”和“Actor模式”作为基本理论的服务端开发框架...

本文是关于 Newbe.Claptrap 项目主体内容的介绍,读者可以通过这篇文章,大体了解项目内容。

轮子源于需求

随着互联网应用的蓬勃发展,相关的技术理论和实现手段也在被不断创造出来。诸如 “云原生架构”、“微服务架构”、“DevOps” 等一系列关键词越来越多的出现在工程师的视野之中。总结来看,这些新理论和新技术的出现,都是为了解决互联网应用中出现的一些技术痛点:

更高的容量扩展性要求。在商业成功的基础前提下,互联网应用的用户数量、系统压力和硬件设备数量等方面都会随着时间的推移出现明显的增长。这就对应用本身的容量可扩展性提出了要求。这种容量可扩展性通常被描述为 “应用需要支持水平扩展”。

更高的系统稳定性要求。应用程序能够不间断运行,确保商业活动的持续进展,这是任何与这个应用系统相关的人员都希望见到的。但是要做到这点,通常来说是十分困难的。而现今的互联网应用在面对诸多同类竞争者的情况下,如果在这方面做得不够健全,那么很可能会失去一部分用户的青睐。

更高的功能扩展性要求。“拥抱变化”,当人们提到 “敏捷项目管理” 相关的内容时,都会涉及到的一个词语。这个词语充分体现了当今的互联网应用若要成功,在功能性上做到出彩做到成功是多么的重要。也从一个侧面体现了当前互联网环境下产品需求的多变。而作为系统工程师,在应用建立之初就应该考虑这点。

更高的开发易用度要求。这里所属的开发易用度是指,在应用系统自身在进行开发时的难易程度。要做到越易于开发,在应用自身的代码结构,可测试性,可部署性上都需要作出相应的努力。

更高的性能要求。这里提到的性能要求,是特指在系统容量增加时的性能要求。避免系统的单点性能问题,让应用系统具备可水平扩展的特性。通常来说,在性能出现问题时,若可以通过增加物理设备来解决问题,通常来说是最为简单的办法。而在不同的系统容量之下,系统性能的优化方案通常是不同的。因此结合应用场景进行技术方案的选型一直都是系统工程师所需要考虑的问题。

本项目,就是基于以上这些系统功能特性要求所总结出来的一套开发框架。这其中包含了相关的理论基石、开发类库和技术规约。

世界上本也不存在 “银弹”。一套框架解决不了所有问题。—— 不愿意透露姓名的月落

从需求出发

在讲解分布式系统时,常常会用到 “账号转账” 这个简单的业务场景来配合描述。这里阐述一下这个业务场景。

假设我们需要建设一个具备账号体系的业务系统。每个账号都有余额。现在需要执行一次转账操作,将账号 A 的余额中的 300 划转给账号 B。另外,基于上节的基本要求,我们在实现这个场景时,需要考虑以下这些内容:

  • 需要应对系统容量的激增。应用初期可能只有 1000 个初始用户。由于应用推广效果良好以及机器人账号的涌入,用户数量实现了在一个月内实现了三个数量级的攀升,也就是增长到了百万级别。

  • 需要考虑系统的稳定性和可恢复性。尽可能减少系统整体的平均故障时间,即使出现系统故障也应该是尽可能易于恢复的。也就是,要避免出现单点故障。

  • 需要考虑业务的可扩展性。后续可能需要增加一些业务逻辑:按照账户等级限制日转账额、转账成功后进行短信通知、转账支持一定额度的免密转账、特定的账号实现 “T+1” 到账。

  • 需要考虑代码的可测试性。系统的业务代码和系统代码能够良好的分离,能够通过单元测试的手段初步验证业务代码和系统代码的正确性和性能。

轮子的理论

本节将介绍一些和本框架紧密结合的理论内容,便于读者在后续的过程中理解本框架的工作过程。

Actor 模式

Actor 模式是一种并发编程模型。通过这种编程模型的应用可以很好的解决一些系统的并发问题。这里所提到的并发问题是指计算机对同一数据进行逻辑处理时,可能由于存在多个同时发起的请求可能导致数据出现不正确的问题。这个问题在进行多线程编程时一定会遇到的问题。举个简单的例子,假如在不加同步锁的情况下,使用 100 个线程并发对内存中的一个 int 变量执行 ++ 操作。那么最终这个变量的结果往往小于 100。此处 Actor 模式是如何避免此问题的。

首先,为了便于理解,读者在此处可以将 Actor 认为是一个对象。在面向对象的语言(Java、C# 等)当中,可以认为 Actor 就是通过 new 关键词创建出来的对象。不过这个对象有一些特别的特性:

拥有属于自身的状态。对象都可以拥有自身的属性,这是面向对象语言基本都具备的功能。在 Actor 模式中,这些属性都被统称为 Actor的状态(State)。Actor 的状态由 Actor 自身进行维护。

这就强调了两点:

第一、Actor 的状态只能由自身进行改变,若要从外部改变 Actor 的状态,只能通过调用 Actor 才能改变。

第二、Actor 的状态只在 Actor 内部进行维护,不与当前 Actor 之外的任何对象共享。这里说的不共享也是强调其不能通过外部某个属性的改变而导致 Actor 内部状态的变化。这点主要是为了区别于一些具备 “对象引用” 语言特性的编程语言而言的。例如:在 C# 的 class 的 public 属性,假如是引用类型,那么在外部获得这个 class 之后是可以改变 class 中的属性的。但是这在 Actor 模式当中是不被允许的。

不过从 Actor 内部读取数据到外部,这仍然是允许的。

单线程。Actor 通常同一时间只能接受一个调用。这里所述的线程不完全是指计算机中的线程,是为了凸显 “Actor 同一时间只能处理一个请求的特性” 而使用的词语。假如当前 Actor 正在接受一个调用,那么剩余的调用都会阻塞,直到调用结束,下一个请求才允许被进入。这其实类似于一个同步锁的机制。通过这种机制就避免了对 Actor 内部状态进行修改时,存在并发问题的可能。具体一点说明:如果使用 100 个线程对一个 Actor 进行并发调用,让 Actor 对状态中的一个 int 变量进行 ++ 操作。最终这个状态的数值一定是 100。

不过单线程也不是绝对的,在不存在并发问题的请求情况下,允许并发处理。例如读取 Actor 中的状态,这通常不会有并发问题,那么此时就允许进行并发操作。

读到 Actor 单线程特性时,通常读者会考虑到这是否会导致 Actor 本身处理过慢而产生性能问题呢?关于这点,希望读者继续持有这个问题往后阅读,寻找答案。

事件溯源模式

事件溯源模式是一种软件设计思路。这种设计思路通常与传统的采用增删查改(CRUD)为主的系统设计思路相区别。CRUD 应用通常存在一些局限性:

  1. 通常来说 CRUD 应用会采用直接操作数据存储的做法。这样的实现方式可能会由于对数据库优化不足而导致性能瓶颈,并且这种做法会较难实现应用伸缩。

  2. 在特定的领域通常存在一些数据需要注意对并发问题进行处理,以防止数据更新的错误。这通常需要引入 “锁”、“事务” 等相关的技术来避免此类问题。但这样又有可能引发性能上的损失。

  3. 除非增加额外的审计手段,否则通常来说数据的变更历史是不可追踪的。因为数据存储中通常保存的是数据最终的状态。

与 CRUD 做法对比,事件溯源则从设计上避免了上述描述的局限性。接下来围绕上文中提到的 “转账” 业务场景简述事件溯源的基础工作方式。

采用 CRUD 的方法实现 “转账”。

采用事件溯源的方式实现 “转账”。

图片过大,原图链接:https://www.newbe.pro/images/20190227-001.gif

如上图所示,通过事件溯源模式将转账业务涉及的余额变动采用事件的方式进行存储。同样也实现了业务本身,而这样却带来了一些好处:

  • 通过事件,可以还原出账号任何阶段的余额,这就一定程度实现了对账号余额的跟踪。

  • 由于两个账号的事件是独立处理的。因此,两个账号的处理速度不会相互影响。例如,账号 B 的转入可能由于需要额外的处理,稍有延迟,但账号 A 仍然可以的转出。

  • 可以通过订阅事件来做一些业务的异步处理。例如:更新数据库中的统计数据,发送短信通知等其他的一些异步操作。

当然引入事件溯源模式之后也就引入了事件溯源相关的一些技术问题。例如:事件所消耗的存储可能较为巨大;不得不应用最终一致性;事件具备不可变性,重构时可能较为困难等。相关的这些问题在一些文章中会有较为细致的说明。读者可以阅读后续的延伸阅读内容,进而进行了解与评估。

业务复杂度是不会因为系统设计变化而减少的,它只是从一个地方转移到了另外的地方。—— 总说自己菜的月落

让轮子转起来

基于读者已经大体理解了上节理论的基础上,本节将结合上述描述的 “转账” 业务场景,介绍本框架的工作原理。首先读者需要了解一下本框架的两个名词。

Claptrap

Claptrap 是本框架定义的一种特殊 Actor。除了上文中提到 Actor 两种特性之外,Claptrap 还被定义为具有以下特性:

状态由事件进行控制。Actor 的状态在 Actor 内部进行维护。Claptrap 同样也是如此,不过改变 Claptrap 的状态除了限定在 Actor 内修改之外,还限定其只能通过事件进行改变。这就将事件溯源模式与 Actor 模式进行了结合。通过事件溯源模式保证了 Actor 状态的正确性和可追溯性。这些改变 Claptrap 状态的事件是由 Claptrap 自身产生的。事件产生的原因可以是外部的调用也可以是 Claptrap 内部的类触发器机制产生的。

Minion

Minion 是本框架定义的一种特殊 Actor。是在 Claptrap 基础上做出的调整。其具备以下特性:

从对应的 Claptrap 读取事件。与 Claptrap 相同,Minion 的状态也由事件进行控制。不同的是,Minion 就像其字面意思一样,总是从对应的 Claptrap 处获取事件,从而改变自身的状态。因此,其可以异步的处理 Claptrap 产生事件之后的后续操作。

业务实现

接下来有了前面的基础介绍,现在介绍一下本框架如何实现上文中的 “转账” 场景。首先可以通过下图来了解一下主要的流程:

图片过大,原图链接:https://www.newbe.pro/images/20190228-003.gif

如上图所示,整个流程便是本框架实现业务场景的大体过程。另外,还有一些需要指出的是:

  • 图中 Client 与 Claptrap 的调用等待只有第一阶段的时候存在,也就是说,这使得 Client 可以更快的得到响应,不必等待整个流程结束。

  • Claptrap A 在处理完自身请求,并将事件发送给 Minion A 之后就可以重新接受请求,这样提高了 Claptrap A 的吞吐量。

  • Minion 不仅仅只能处理 Claptrap 之间的调用代理。在 Minion 当中还可以根据业务需求进行:发送短信,更新数据库统计数据等其他操作。

  • Minion 也可以具备自己的状态,将部分数据维持在自身的状态中以便外部可以从自身进行查询,而不需要从对应的 Claptrap 中进行查询。例如:统计该账号最近 24 小时的转账变动,以便快速查询。

业务容量

前文提到本框架需要建设的是一个可以水平扩展的系统架构,只有如此才能应对业务容量的持续增长。在这点上,本框架现阶段采用的是微软开源的 Orleans 实现应用程序和物理设备的放缩。当然,涉及数据存储部分时势必也涉及到数据库集群等一系列问题。这些属于技术应用的细节,而非框架理论设计的内容。因此,此处只表明本框架可以基于以上的开源架构进行容量放缩。应用过程中的实际问题,读者可以在后续的项目内容中寻求解答。

延伸阅读

以下这些内容都对本框架产生了深远的影响。读者可以通过阅读以下这些内容,增加对本框架的理解。

  • 基于 Actor 框架 Orleans 构建的分布式、事件溯源、事件驱动、最终一致性的高性能框架 ——Ray

  • Event Sourcing Pattern

  • Event Sourcing Pattern 中文译文

  • Orleans - Distributed Virtual Actor Model

  • Service Fabric

  • ENode 1.0 - Saga 的思想与实现

最后但是最重要!

最近作者正在构建以反应式Actor模式事件溯源为理论基础的一套服务端开发框架。希望为开发者提供能够便于开发出 “分布式”、“可水平扩展”、“可测试性高” 的应用系统 ——Newbe.Claptrap

本篇文章是该框架的一篇技术选文,属于技术构成的一部分。如果读者对该内容感兴趣,欢迎转发、评论、收藏文章以及项目。您的支持是促进项目成功的关键。

联系方式:

  • Github Issue

  • Gitee Issue

  • 公开邮箱 newbe-claptrap@googlegroups.com (发送到该邮箱的内容将被公开)

  • Gitter

  • QQ 群 610394020

GitHub 项目地址:https://github.com/newbe36524/Newbe.Claptrap

Gitee 项目地址:https://gitee.com/yks/Newbe.Claptrap

您当前查看的是先行发布于 www.newbe.pro 上的博客文章,实际开发文档随版本而迭代。若要查看最新的开发文档,需要移步 claptrap.newbe.pro。

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

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

相关文章

NCF框架揭秘直播来了!红包、抽奖、还有神秘嘉宾…(内含彩蛋)

盛派周三技术分享会直播开讲又来啦~为了更好地赋能开发者,盛派已将系统框架 SCF(SenparcCoreFramework)全部开源,收到了社区非常多的关注,现在 SCF 已正式更名为NCF(NeuCharFramework)&#xff…

leetcode45. 跳跃游戏 II

一:论语 己所欲 也要勿施于人 &#xff0c;每个人的经历和阅历都是不同的 你凭啥说你认为的很开心的事情 去要求别人呢 二:题目 三:上码 class Solution { public:int jump(vector<int>& nums) {/**思路:1.这里的难点就在于 我们需要判断下一步的的最远跳跃距离…

leetcode1005. K 次取反后最大化的数组和

一:论语 这个用在自己身上感觉值得反省&#xff0c;很多道理我都能明白 也能讲给别人听 但是很多时候 自己往往做的不好 而且还很容易 自我感动 最近真的很讨厌自己这样 不要自我感动 要正向积累 多去做 多去做 这只是个开始 然后慢慢的长进 再者就是坚持 二:题目 三:上码 …

.NET Core全Linux开发体验分享

“ 2016年.NET Core首个正式版本问世&#xff0c;如今已发布到了.NET Core3.1&#xff0c;再有2个月.NET5也将如约而至&#xff0c;跨平台开发已经快5年&#xff0c;然而很多人却还只是在Windows上用Visual Studio SQL Server去做.NET Core跨平台开发&#xff0c;欠缺对Linux的…

使用Microsoft Word2016无法正常对Latex文本转换的踩坑和解决方法

相信很多人都遇到像我一样的问题。word2016中&#xff0c;有latex的按钮&#xff0c;按ALT就可以开始写公式&#xff0c;复制粘贴latex公式之后&#xff0c;怎么就转换不了呢&#xff1f;就是如图这样的&#xff0c;左上角转换按钮为灰色。 上网找呀找&#xff0c;找了很多资料…

leetcode134. 加油站

一:论语 二&#xff1a;题目 三&#xff1a;上码(暴力解法超时 但方法二还是可以的) // class Solution { // public: // int canCompleteCircuit(vector<int>& gas, vector<int>& cost) { // /** // 思路:1.暴力解法,我们遍历所…

ffmpeg 硬件解码零拷贝unity 播放

ffmpeg硬件解码问题 ffmpeg 在硬件解码&#xff0c;一般来说&#xff0c;我们解码使用cuda方式&#xff0c;当然&#xff0c;最好的方式是不要确定一定是cuda&#xff0c;客户的显卡不一定有cuda&#xff0c;windows 下&#xff0c;和linux 下要做一些适配工作&#xff0c;最麻…

Git入门教程(一)

今天开始学习Git&#xff0c;所以就把每天学的东西写下来&#xff0c;不然真的太复杂&#xff0c;容易忘记呀。 这里推荐一个网站Webscripting2 — Serverside Webscripting — xx.git&#xff0c;英文一般般的都可以上去看看&#xff0c;我觉得非常直观。 首先Git&#xff0c;…

leetcode135. 分发糖果

一:论语 二:题目 三&#xff1a;上码 class Solution { public:int candy(vector<int>& ratings) {/**思路:1.这里我们分两次遍历(从左向右 从右向左)2.当我们从左向右遍历的时候 如果右边的评分比左边孩子分数高 那么右边孩子的糖果数上就在左边孩子糖果数上加一3.当…

Java国家/地区使用限制条款引发争议

喜欢就关注我们吧&#xff01;今天 JDK/Java 15 发布&#xff08;看今天推送的头条&#xff09;&#xff0c;在 RI 包里有开发者发现其标注了一则国际使用限制条款&#xff1a;由于某些国家/地区的知识产权保护和执法有限&#xff0c;因此 JDK 源代码只能分发到授权的国家/地区…

反射的基本知识(详解)

一:反射的引出 1:问题 比如我们给出一个student类 其方法show(),我们将其写入配置文件中&#xff1b;现在我们来一个新的需求说是要改变重写一个show()方法 show()2,那么如何在不修改源码的情况下进行修改&#xff0c;这时我们通过反射就可以完成 2:过程 student类&#xf…

.NET 5.0 RC1 发布,离正式版发布仅剩两个版本,与 netty 相比更具竞争力

原文&#xff1a;http://dwz.win/Qf8作者&#xff1a;Richard翻译&#xff1a;精致码农-王亮说明&#xff1a;1. 本译文并不是完全逐句翻译的&#xff0c;存在部分语句我实在不知道如何翻译或组织就根据个人理解用自己的话表述了。2. 本文有不少超链接&#xff0c;由于微信公众…

一个例子带你搞懂python作用域中的global、nonlocal和local

在编程中&#xff0c;只要接触过函数的&#xff0c;我相信都理解什么是全局变量和局部变量&#xff0c;概念比较简单&#xff0c;这里就不做解释了。在python中&#xff0c;用global语句就能将变量定义为全局变量&#xff0c;但是最近又发现有个nonlocal&#xff0c;一时搞不太…

初识ABP vNext(10):ABP设置管理

点击上方蓝字"小黑在哪里"关注我吧定义设置使用设置前言上一篇介绍了ABP模块化开发的基本步骤&#xff0c;完成了一个简单的文件上传功能。通常的模块都有一些自己的配置信息&#xff0c;比如上篇讲到的FileOptions类&#xff0c;其中配置了文件的上传目录&#xff0…

类加载机制(整个过程详解)

一:背景 类加载机制是在我们的真个java的运行阶段中的其中一个阶段。 二:什么是快乐星球(类加载机制) 我们编写的 Java 文件都是以.java 为后缀的文件&#xff0c;编译器会将我们编写的.java 的文件编译成.class 文件&#xff0c;简单来说类加载机制就是jvm从文件系统将一系…

leetcode860. 柠檬水找零

一:论语 这里的小不忍指的是一方面我们受到挫折而控制不住自己而大发脾气&#xff0c;还有一方面指的是我们的过于优柔寡断&#xff0c;对于自己或者他人的过失&#xff0c;不忍心加以责罚。 二:题目 三:上码 class Solution { public:bool lemonadeChange(vector<int&g…

跟我一起学.NetCore之Asp.NetCore启动流程浅析

前言一个Asp.NetCore项目&#xff0c;知道大概的启动流程是有必要的&#xff0c;比如后续遇见配置信息覆盖等相关问题时也大概知道是什么原因&#xff0c;了解原因之后&#xff0c;再去搜索引擎找答案&#xff0c;否则目标不明确&#xff0c;茫茫人海怎么会一下找到自己想要的&…

leetcode406. 根据身高重建队列

一&#xff1a;你不一定逆风翻盘&#xff0c;但请一定向阳而生 二&#xff1a;题目 三&#xff1a;上码 class Solution { public:/**解析题意:这个给出的people的数组中,我们需要根据其元素people[i] [hi,ki];然后根据其ki来表示大于hi身高的人的个数&#xff0c;来进行排序…

.NET Core 下使用 RabbitMQ

介绍RabbitMQ是一个开源的,基于AMQP(Advanced Message Queuing Protocol)协议的完整,可复用的企业级消息队列(Message Queue 一种应用程序与应用程序之间的一种通信方法)系统,RabbitMQ可以实现点对点,发布订阅等消息处理模式官网&#xff1a;https://www.rabbitmq.com/dotnet.h…

leetcode452. 用最少数量的箭引爆气球

一:论语 少些自我感动&#xff0c;多谢反思&#xff0c;时刻警惕自己是否在假装很努力&#xff0c;自己懂不懂 自己会不会 自己想要什么 只有自己 最清楚 二&#xff1a;题目 三:上码 class Solution { public:/**思路:1.这道题类似无重叠空间,我们先按每个气球的右边界升序…