【中美技术专家分享实录】响应式编程的应用

图片描述
许多场景下为了更迅速的响应客户端的请求,将问题转化为实时反映业务状态的变化,能更好地提高用户体验以及支撑更大量的用户请求,因而催生了响应式编程,本期跨境茶话会仍旧邀请了中美两地的相关专家来谈谈响应式编程的应用。前端

全网首发·技术干货·大咖对话react

整理:魔窗丨 7728字丨15分钟阅读web

来源:本文整理自跨境茶话会线上分享,为魔窗(magic-window)原创首发,转载需得到受权算法

【主持丨大师兄】:这期为何要选响应式编程这样一个主题?由于前两期咱们作了一个性能优化方面的主题和微服务方面的主题。性能优化本质上会解决高并发状况下同步延时的一些问题,可是可能另外有一些业务和架构上的模式,采起一些消息驱动的方式能够解决后端即便不能实时处理相应,也能够低延迟的保证客户端体验的场景。这样的场景就是响应式编程的系统架构。首先介绍一下这期的嘉宾,首先是徐鹏程,他目前任职于谷歌,是负责广告技术方面的专家。数据库

图片描述

【嘉宾丨徐鹏程】:你们好,我是徐鹏程,如今在上海谷歌。我在谷歌上海作了大概七年的时间,前六年我都是在作广告优化的一些工做。最近这一年我开始作安卓的开发。在谷歌上海以前我还在谷歌北京本部也待过一段时间,中间也出来创过业。编程

图片描述

【主持丨大师兄】:接下来我介绍一下童奎松,他如今在Snapchat,负责Snapchat数据方面的工做。后端

【嘉宾丨童奎松】:我叫童奎松,2002年浙江大学毕业以后,我在国内写了差很少十年的代码,在2012年加入LinkedIn,主要负责和数据分析相关工具的开发。今年年初加入到Snapchat,作数据分析相关工具的开发。缓存

图片描述

【主持丨大师兄】:接下来我介绍一下徐焱飞,他如今是磐石科技的CEO,磐石科技也是一款投顾金融平台的产品,他自己在一些CQRS,Eveint Sourcing等相关技术方面很是有经验。性能优化

【嘉宾丨徐焱飞】:你们好,我是徐焱飞。我最先在Wipro作零售的一些跨国项目。后来加入爱立信上海研究院,一直作通讯方面后台的一些工做。以后加入普兰金融在作分布式后台的开发。今年如今开始出来创业,是一个投顾平台。网络

图片描述

【主持丨大师兄】:接下来咱们进入这期茶话会第一个环节,首先想请各位嘉宾谈一下关于他们在工做中关于响应式编程的实践。

【嘉宾丨徐鹏程】:我在谷歌内部假如如今从事的安卓开发,咱们在响应式编程上会有更多的实践,在以前的广告优化的地方,由于我长期是在作后台的工做,咱们反而在这方面作的实践相对少一点。可是安卓这边由于涉及到不少前端的交互,因此咱们在编程上的实践更多一些,由于应用的场景会更多一些。

【主持人丨大师兄】:能不能举一些大家在具体工做中用到的响应式编程的业务的场景和技术方面实践的经验呢?

【嘉宾丨徐鹏程】:好比我最近在作APP的时候,里面有一个常见的webview控件,他是一个事件驱动型的方式,好比你能够给它加载和定制化一些事件client,好比这个webviewclient上你能够作一些事件处理。可是,这个有一个比较很差的地方就是事件处理client是很难被重用的,由于两个不一样的webview上面可能有不一样的事件处理方式。可是它的业务逻辑可能80%是相似的,20%是不同的,可是你没有办法重用这80%的代码,除非你作一个级层的关系,那这样两个彻底不相关的webview可能就会存在一个依赖关系。咱们并不想要这样的耦合方式,因此咱们把wenview的事件彻底改形成了observer模式的这种方式。因此每一个事件咱们均可以把事件相对应的observer提出来,这样的话最终在不一样的webview的状况下咱们就能够把80%相似的observer组装在一块儿,经过这样的方式来解耦,使得这个代码可以获得重用,也比较易于管理。

【主持人丨大师兄】:谢谢鹏程,鹏程刚刚从一个客户端的角度帮咱们介绍了怎么样经过一些响应式编程,就是事件驱动的方式把真正的事件处理的逻辑给解耦。接下来奎松帮咱们介绍一下吧。

【嘉宾丨童奎松】:我最近在作一个项目,Snapchat对user ID其实管得很严的,当时在咱们全部处理的数据里面,全部的雇员均可以看到User ID的,因此咱们想把它转化成所有用一个叫自动生成的Ghost ID来替代全部的user ID,在咱们分析的数据库里面。而后这里咱们就要作一个系统,让全部其余第三方系统能经过User ID查到对应的Ghost ID,而后把User ID换掉。它其实就是一个User ID和Ghost ID的对应关系,已经在数据库里面有了。可是由于它的吞吐量很是大,基本上全部的由于是事件都会有user ID,天天的事件基本上在T的量级。因此,为了应付这么大量级的查询,咱们在前面作了一个cache。但这就是一个典型的高吞吐,它基本也没有什么CPU预算,基本是高吞吐、高IO的运用,因此咱们就想到用响应式编程来解决它。其实咱们在这里用响应式编程主要是解决的由于它大部分都是IO,因此咱们不想把IO等待来占用CPU的时间。这样的话,同时咱们就采用了全译步非阻塞的方式来响应请求,这样基本在单台机器能够达到100K以上的吞吐率。

在这以前,我在LinkedIn的时候,整个系统都是基于事件驱动的,它是基于Kafka架构上的事件驱动的响应式系统,这个对系统来讲是同样的,就是全部的编程全是异步。我先不说这个异步编程在整个应约上带来的高吞吐率、低延迟,其实延迟不会低,就是容错率有一个好处,另外带来一个好处,从咱们数据分析来讲,由于全部业务中的事件,因此你在作业务分析的时候只要基于事件来作,其实你能解决大部分数据分析的问题。这是我在这两个公司用响应式编程的一些经验。

【主持人丨大师兄】:刚刚谈到在LinkedIn上大家全部的数据分析都是用事件,用一些事件无非就是一些状态的改变,按照这样的模式去作,你能举个具体的例子吗?

【嘉宾丨童奎松】:我举个例子,好比最典型的PageView的事件,好比个人任何一个LinkedIn的用户查看个人profile页面,就有一个profile的PageView事件。可是这个Page view 的事件发到Kafka,它会返回个人相关信息,而后生成Profile页面。同时这个事件由于它是发的Kafka的,因此Kafka能够把它存到HDFS上,而后咱们作数据分析的时候只要从HDFS把这个PageView的事件拉出来,咱们就能算出当天的每一个页面的PageView是多少。

【主持人丨大师兄】:大家如何对这种事件作一些抽象的?由于可能大家定义了一个具体的状态,可是真正处理这个状态变化的不只仅一个业务模块,还会有一些其余业务上的模块会针对这个状态变化作处理。

【嘉宾丨童奎松】:咱们在总体设计的时候专门有一个committee来统一地审查这个事件,也就是一个团队你能够说我要这个事件那个事件,committee会去审核你这个事件是否是已经存在了,和之前的事件是能够重用的,若是不能够单独的话,那你的事件里面须要哪些属性。你要兼顾到业务须要和数据分析须要。由于事件进了Kafka以后,Kafka能够分发到处处,每一个系统只要监听一样的事件,它能作不一样的业务。

【主持人丨大师兄】:你还有什么要分享的吗?

【嘉宾丨童奎松】:这里有一个事情我要澄清的,从我本身的理解来讲,咱们这期的分享题目叫响应式编程,可是配图其实从个人角度来讲是响应式系统的图,其实它是整个系统。因此我会发一个连接:https://www.oreilly.com/ideas...,这个连接里面有详细的人家写的响应式编程和响应式系统的区别,在个人理解来讲,响应式编程基本上属于你在一个process里面怎么用异步来高效地使用你的线程,也就是让你的线程在你的任务之间切换会不多,你的CPU切换不多。响应式系统就涉及到你怎么去用事件驱动或者消息驱动来构造你的系统,你的系统须要很好的用户响应,还要容错,要有弹性,在高负载状况下也要有弹性。因此,从个人理解上响应式编程和响应式系统实际上是两个概念。

【主持人丨大师兄】:其实响应式编程是包含在响应式系统里面的?

【嘉宾丨童奎松】:对。

【主持人丨大师兄】:我这边也说一下,此次咱们整个的主题其实应该是响应式系统,而不是响应式编程,可能名字上会有有点误导你们。接下来徐焱飞你这边介绍一下吧。

【嘉宾丨徐焱飞】:好的。我很是赞成刚才奎松说的关于响应式编程和响应式系统的区别。就在本次交流前,刚和申竣线下沟通了一下,由于这个地方确实是要澄清一下。我以前的工做经历,有一个项目其实和奎松有点接近,也是用全异步式的用Kafka将全部的请求聚合起来,而后用storm对流数据进行处理,一方面是作一些大数据的分析,一方面是把这些数据进行落地。我介绍一下基于事件驱动的CQRS架构通常所适合的场景。好比咱们作传统的系统当中,拿电商举例,若是我如今发了一个订单出来,这个订单落地,若是咱们只关心订单的变化,好比订单的数量发生变化,那是一个很是简单的系统,常规的咱们用CURD的系统就能够解决。可是如今这个电子商务的时代,可能会遇到两个问题,第一个问题是高并发,且读大于写。若是咱们这个时候往数据库作简单的CURD的时候可能就承受不了。这个时候就得引入各类缓存、读写分离、分库分表之类的技术,CQRS其实算是从应用级开始的一种读写分离。第二个问题,是需求变化的太迅速以致于咱们难以推测系统在后面会如何演进。好比咱们如今最开始只是订单,那么后面可能对这个订单数据有一个延伸,好比我去减库存,或者从这个订单产生的后面有支付,红包等等,可能会延伸出更多的业务流程。在这种时候按照传统的CURD的系统,咱们函数式的往上去拼接各类代码,无心就显得效率比较低。除了采用微服务外,使用CQRS用相似事件流处理的方式去实现,能够避免业务系统间相互影响,相互隔离的同时,也能为将来的各类扩展作好准备。因此我如今最新的项目就是采用基于CQRS,加上EventSourcing,对整个的系统一个是知足读大于写的高并发,第二个是知足咱们将来对系统的快速调整和演进。

【主持人丨大师兄】:感谢各位嘉宾介绍了在工做当中的一些响应式系统的经验。接下来第二个环节咱们会讨论一些问题,这些问题都是在作响应式系统当中会出现的比较细节的业务或者是技术方面的具体的问题。为何会出现问题呢?可能你在作一些同步的方式当中是不会出现,作一些事件驱动,包括像高并发,异步处理就会衍生出这些问题。个人问题是在一个响应式系统中,可能你整个的端到端的业务场景中间会通过不少的事件处理的环节,若是某一个环节出了问题,我想问一下各位嘉宾,如何去作一些事务的回滚,不知道各位嘉宾在这方面有没有相关的一些经验。

【嘉宾丨童奎松】:我先回答吧,当你作整个反应式系统的设计的时候,其实大部分状况下你应该结合DDD,领域设计驱动来作你的设计。你的数据其实在某种状况下是有边界的,就刚刚你说的,若是有一致性要求的话,事务应该是在某一个模块或者某一个服务的范围以内,它不会跨多个渠道,就是分布式事务,这是在反应式编程里面忌讳的东西,是不该该采用的。因此在单个叫application也好,或者叫服务也好,在单个服务里面你能够用你的事务来保证你的数据的一致性。由于无论是任何数据你总会有一个拥有者,好比说订单系统,全部和订单相关的信息都会属于这个订单系统。因此当你对订单作任何变动的时候,它的一致性是由订单系统处理的。当订单下单以后他要去预留库存的时候,这时候他会去发个消息给库存系统,而后由库存系统去预留这个库存。因此刚刚你说的,从个人理解来讲,它的跨系统之间的事务失败,这种状况是不存在的。

【主持人丨大师兄】:你的意思是说其实从一些业务设计的角度去讲,没有一个端到端的事务失败的概念?

【嘉宾丨童奎松】:当你作领域驱动设计来讲,每一个系统就是独立性的,是孤立的,没有端对端。好比端对端只是你整个订单系统或者单独库存系统,但没有说我一个订单下去到保留库存,到保留库存,到付款,到交货,这是整个端对端的失败。

【主持人丨徐鹏程】:我能请教一个问题吗?刚刚您说的订单系统里面,好比我在生成订单的过程当中须要减小库存,对于库存系统来讲那就是一个完整的事务,由于这个库存的事务是发生在订单操做的过程当中的,若是最终这个订单失败了,那么库存那边的事务是否是要回滚呢?

【嘉宾丨童奎松】:这看你怎么设计,个人设计方法是这样的,下订单的时候其实你不须要预留库存,为何呢?由于从某种角度上来讲,整个反应式系统里面最重要的一个概念是最终一致,而不是你要实时一致。也就是说即便我下定单的时候没有库存又会怎么样呢?最多我下了订单以后我再去发个消息给库存系统说你给我留一个库存,这时候库存系统若是没有库存的话他会返回一个好比说我没有库存,那订单系统读到这个没有库存的事件以后就会把本身状态改为backordered或者别的状态。也就是说下单的过程当中我是不须要和库存有这种分布式事务的交互的。

【嘉宾丨徐鹏程】:若是我这个订单除了库存以外还依赖其余的角色,好比依赖于付款,比方库存说我有,而后付款说最终失败了,那么你仍是须要再去让那个库存恢复过来是吗?中间这个状态怎么处理?

【嘉宾丨童奎松】:状态表示一致是最终的一致性,它是最终的一次性,不是我这样一个时刻下所有表现持续。好比你刚刚说的,我库存已经留了,我如今去付款,而后付款失败了,而后就会有一个付款失败的消息发出来。这个时候库存和订单都会监听这个付款失败的消息,库存监听到付款失败的消息,库存回滚,订单监听到付款失败的消息订单的状态变成付款失败。

【嘉宾丨徐焱飞】:这里我来讲几句吧。关于这个地方,首先须要明白一个问题,为何会有分布式事务这个问题。若是说咱们没有分布式,传统的好比下订单,到保留库存,到支付的流程,常规的理解确定是一个事务。后面一个环节必须等到前面一个环节成功,那才能进行到下一步。好比说若是下订单的时候这个商品原本就没有库存了,那这时不可能就支付它,买家不可能去买不存在的东西。可是由于压力的东西,咱们要到分布式上,一旦产生了分布式,状态就会落在不一样的节点上,这个时候问题就变得复杂了。

这就是咱们业界内一般来讲的CAP理论,所谓的CAP理论这里稍微普及一下,它是指分布式系统的一致性、分布性和容错性,这三方面是不可能知足三者的,最多只能知足其中二者。因此通常业界内在分布式事务上有两种作法,第一种是TPC,两段式提交,就是淘宝以前最经常使用的作法,好比这三个阶段每一个阶段都尝试着向数据库作一次写入,先各自尝试把当前资源给预留下来。当三个都OK了,固然这中间是有另一个第三方的协调者,好比像JBoss他们作JTA这样的事务实现的时候就会有一个第三方的监听,保证在三个阶段的提交都确定成功了,他再作第二次的提交,把三个对应的资源真正的commit掉,从而最终同步更新状态,这就是两阶段提交大体的过程。

还有一个作法就是刚刚奎松说的最终一次性,最终一次性在DDD领域设计当中,一般来说咱们是这样作的。整个大的流程这一个环节,咱们常规的理解是叫作一个事务。可是,由于咱们把它分红好比这三个服务的话可能会落在三个独立的领域、独立的服务当中去处理,就像如今微服务可能就变成了三个服务的节点,每一个服务节点各自的处理过程是一个事务,好比下一个订单,到数据库保存成功,那这个事务就算成功了。

那整个流程怎么保证呢?在领域当中咱们有一个概念叫Saga,Saga能够用来保证整个流程是按照设计好的流程走,好比下订单是成功的,可是去保存库存的时候可能失败了,这个时候就能够发出来一个库存失败的消息,其余的几个节点就能够监听到这个消息以作出不一样反应,例如订单服务就能够将订单状态改成下单失败。

说到这里不得再也不啰嗦几句,关于领域设计当中咱们通常说到聚合状态的更新,其实已经牵扯到了Actor模型了。在领域设计当中,咱们把具备相关联关系的一些数据单元用Aggregate聚合来表示,若是说要找一个类比便于理解的话,就相似于咱们OO编程中的一个大对象。好比订单,常规咱们去更新订单的时候,在传统的系统当中咱们能够前面发起一个函数调用去更新这个订单的状态。可是在领域当中,好比Actor模型中,它实际上是经过一个对外的事件接收器,外面的人不可以直接更改聚合内部的状态,必须是经过事件,你发一个事件告诉我你要干什么,而后至于怎么干是整个聚合根内部本身来作的一件事,这就是一个Actor模型。

因此刚才提到了在整个过程中的异常处理体系,这当中的整个异常处理体系其实分两部分的,一部分是聚合根内部的体系,这个时候它的异常咱们能够用常规的手段来解决,好比若是发生了异常也好,或者写入失败也好,这个时候咱们能够发出一些事件发给外部,交给外部一些对这个事件有兴趣的订阅者。

还有一种是咱们整个事务的,整个体系的,像刚刚那个流程从建立订单到保留库存,到支付,整个环节实际上是靠聚合以外的,好比Saga的这种方式来进行保证的。

【主持人丨大师兄】:我这边总结一下,徐焱飞和奎松说的,我若是在领域和领域之间其实没有所谓事务的概念,全部的东西都是基于状态变化的处理,整个领域只是负责接收外部领域的消息,作一个相应的处理。

【嘉宾丨徐鹏程】:对的。其实我这边还有一种状况就是我以前作系统的时候咱们有一个索引系统,就是咱们对存储的数据作了一个索引,那个东西也是事件触发的,可是那个东西咱们认为是一个比较弱的辅助系统,因此咱们不强行保证它的最终一致性。若是更新失败了那也就随他去了,由于对于咱们整个系统来说不是必需要求。因此这种状况下咱们一般的作法,若是事件处理的时候失败了那就不去管它。可是咱们另外还作了一个offline的re-indexer,也就是天天它会从新把全部的数据从新索引一遍,保证它在必定时间以后数据仍是正确的。

【嘉宾丨童奎松】:对,这个就涉及到当你的领域系统里面处理一个消息的时候若是失败了怎么处理,失败了其实有各类方式,一种就像你说的我直接把消息丢掉,由于我失败就失败了,我不在乎这个。好比说去写log,我若是log写不进去,我失败了就把它丢掉。另一种就是我重试,我会按期重试,或者各类算法按期重试,或者我有多台机器的话我换一台重试。第三种就是刚才你提到的,我可能不重试,我也不把它丢掉,我把它放到一个错误消息的队列里面,定时好比天天或者每小时,定时对错误队列里的消息再重试。

【主持人丨大师兄】:刚刚的问题是关于事务的,我看你们把异常处理的问题也讨论了,和事务是相关联的。因此接下来的问题是响应式系统是创建在事件驱动之上的,那这个问题就是当事件愈来愈多, 公司的系统愈来愈大的时候,因为公司的组织架构,可能我研发一个工程师或者一个小工程的团队只是负责两三个事件的处理,就是两三个领域,两三个事件的处理。可是可能做为我总体的系统架构或者是大的产品经理,极可能面对的是整个事件处理的流程有成百上千个状态。其实,我做为更大的一个架构层面或者产品层面的角度,可能有架构师须要了解全部的成千上百的事件处理的状态过程究竟是怎么样的。我在设计的时候,或者编写代码的时候,整个在存在成千上百个事件的过程当中,我怎么样了解到整个事件处理的全貌,使我当中要作一些修改的时候不至于发生任何的问题。这个我想请各位嘉宾发表一下本身的意见,怎么样管理这么多的事件?

【嘉宾丨徐鹏程】:首先,你刚才提到了,若是你是一个复杂系统的话,不是说必定,可是百分之八九十必定推荐你要用领域驱动设计。当你作领域驱动设计的时候你会把整个大的系统分红不一样的领域,好比分红十个领域或者八个领域,简单一点来讲就是咱们刚才说的网上订单你会分红订单、库存、付款三个领域。你的业务是有流程的,好比下订单你是先下订单再去保留库存,再付款,再发货。

可是咱们刚刚也提到另一个事,你的领域之间的交互只能经过消息来处理。你把这个结合起来,你的流程就是你的消息,这样的话当你把你的领域画个图,而后它们之间的链接就是消息,这个消息是决定你的流程的。好比我做为订单的流程第一步是下单,在订单系统里面发生,而后订单会发一个事件说下单成功,这个时候库存会监听到下单成功这个消息,而后再作保留库存的动做,而后保留库存成功的时候会发一个消息说库存保留成功,而后再是付款系统。而后付款会有成功和失败两个不一样消息。若是成功了以后会进入发货系统,回到库存以后发货。

因此,刚才你说怎么去肯定我有哪些消息,我哪些消息是必要的,哪些消息是没必要要的,哪些消息是重复的。这其实就由设计的时候你的业务流程决定,由于消息就是你的业务流程当中的一步,你没有这个消息,你业务流程走不通。当你的业务流程不须要步骤的话你也应该不存在有单独的消息。这是个人理解。

【主持人丨大师兄】:我这边延伸到了一个问题,刚刚讲到可能从业务上来说我是须要作这样一个设计,可能就是画一个比较大的流程图,或者是用文档的方式把他们画下来。可是有一个问题是在于,在你真正作一个开发的时候,可能分好几个阶段,你在开发的时候需不须要我这边全局地去维护一张好比XML描述文件也好,这样一个存在我全部的一个消息处理,全部一个工做流的文件,让我整个工程师可以去到单一的文件上去找相应的流程步骤,第一个是开发过程中。第二个是我整个系统上来之后有没有一些比较好的监控的手段,可以让我清晰地看到如今每个事件处理,每个领域每个事件处理的状态会有没有什么异常,我是否是须要进行一些人工的干预?

【嘉宾丨童奎松】:第一个是否是要一个中心,一个地方放全部的事件,这个我我的是不建议的。由于每一个模块是独立的,每一个模块负责处理本身的事件,它发哪一个事件,好比库存、订单,订单成功以后库存系统会关心,这些事是由每一个人本身决定的,而不是我由一个中心的地方来放全部的事件。怎么肯定我有哪些事件呢?你要有一个流程图,每一个领域之间的交互关系是怎么样的,在你作领域设计的时候,作系统设计的时候你的这个文档是须要的。

第二个就是刚刚说的监控,由于全部的事件其实你会有一个地方去存储的。好比说Kafka,这时候你事件处理的成功与失败彻底能够经过监控kafka来肯定。若是你的流程设计得好的时候,当某个事件处理失败的时候你会把它放到一个错误队列里面,或者你会有必定的标准说这是执行失败了或者执行成功了,你能够监控kafka里面队列的状态,或者监控你的metric系统来决定。好比我一个事件成功了,我会往个人metric系统里面发一个+1,成功+1或者失败+1,或者尝试+1,这取决于整个系统怎么设计。

【主持人丨大师兄】:我这边理解了,其余两位嘉宾有什么想法?

【嘉宾丨徐鹏程】:首先我以为你刚刚说的咱们可能有成千上万个不一样的事务,不一样的状态,可是一般来说是能够分模块的,实际上未必可能真的有那么多状态。由于比方我有一千个状态,可是可能这一千个状态分别属于十个不一样的领域,可能有80%是内部的状态,并不必定暴露到外面去,因此你可能最终画的那个图我以为最终不该该是我这个状态互相的转换关系,而是我这十个领域之间有哪些依赖关系,可能80%是在某个领域以内的。这是第一点。

第二点,其实在我本身看来,由于以前我负责那个项目咱们也是能够认为是一个领域,咱们其实并非太关心调用者或者使用者之间的状态,由于这也是他们的事情,咱们只关心第一咱们内部的状态,第二咱们暴露出去服务的状态。全部的东西咱们都会有log,咱们一般是会知道谁在使用咱们某一个接口,最终比方咱们须要在技术上改进这个接口,或者须要改接口的参数的时候咱们一般会看谁用了这些东西,它是否会跟咱们新的接口有问题,我会通知咱们的调用者你的这个东西可能须要进行怎么样的修改才能符合咱们调用的正确性,可能从何时开始咱们会改变调用的接口。只有在这个状况下,咱们才会对咱们上下游的状态有更多的沟通,不然咱们只要保证对外的接口不变就能够了,对内的状态和事件咱们不认为咱们的上下游接口是应该知道这些东西的。

【嘉宾丨童奎松】:这里我补充一点,刚刚谈到的是调用接口,其实这仍是原来的http或者rest调用,或者RPC调用这种同步方式,或者说它不是事件驱动的。真正当你事件驱动、消息驱动的时候,其实你消息的内容和消息的类型已经决定了它是要作什么事情。当你作升级的时候其实你要作到一个上下兼容,大部分状况下只是你的消息里面的内容只增不减或者不改,其实你就能作到向下兼容。

【嘉宾丨徐鹏程】:对,可是有具体状况,好比咱们碰到的状况这个消息你不会再支持了,你发过来也没有用,咱们没有这样的功能了。

【嘉宾丨童奎松】:这个是这样的,你接不接消息取决于你,可是发不发消息取决于咱们,我在反应系统里面发消息那一方面为主的,我把个人消息发出去,你收不收,你作什么事情我根本管不了,这可能调用方式不同。

【嘉宾丨徐鹏程】:实际状况下你一般发一个消息过来,你多是但愿我处理完以后生成一个新的消息出去,可是像这种状况下咱们以前会反馈回去,可是如今由于某些缘由咱们再也不有这样的反馈了,其实会影响到你的一些东西。

【嘉宾丨童奎松】:这个就看你怎么设计,从个人角度来讲我发消息的时候我根本不关心谁来处理不处理。我关心的是好比下订单那一块,我订单生成以后我有一个订单成功的消息,我根本不在意处理不处理这个消息,我只等下一个库存预留成功这个消息,我只要等这个消息,我无论你听不听。因此,我决定须要当我作订单系统的时候,我须要关心你付款成功不成功这个消息,你库存成不成功这个消息,我不关心个人订单生成成功这个消息发出去以后到底有多少人去处理,或者说你今天处理,明天不处理。

【嘉宾丨徐鹏程】:可是一般状况是这样的,好比你是订单系统,我是库存系统,你发一个消息给我说要求我预留这个库存,可是好比我从如今开始当机了,我既不发出去一个预留成功的,也不发出去一个预留失败的,那在你那边的流程不就work了吗?

【嘉宾丨童奎松】:首先个人订单成功以后我不是发一个告诉库存你要预留这个事件,我发的是订单成功事件。库存那方是监听我订单成功事件来决定我保留库存成功不成功。就是订单系统自己不知道你库存是要作保留库存仍是直接去发货,订单系统不在意。库存系统拿到订单建立成功这个消息以后他本身来决定我是要保留库存仍是直接发货。这个理解我以为反应式编程要反过来的。

【主持人丨大师兄】:奎松的观点是说,我全部发的消息都是基于状态的变化,不是我告诉你作什么事情,是我告诉你我这边状态发生了哪些变化?

【嘉宾丨童奎松】:对的,我告诉你我作出了哪些变化,或者说我告诉你,这个其实就像他们会计记帐同样,会计记帐你告诉个人时候你就来报销,你告诉我今天吃饭一百块钱,我无论你吃了什么,无论你怎么吃,我只记录一点,吃饭一百块。

【主持人丨大师兄】:焱飞对这个问题有什么见解吗?

【嘉宾丨徐焱飞】:我很是赞成奎松刚刚说的,由于在领域驱动里面其实每一个聚合根本身是经过事件驱动的,一般它不会去管其余领域的状况,只管本身。说白了,有什么样的事件进来,而后聚合根内部逻辑根据这个事件进行处理,而后对外发出相应的事件,就是事件传递驱动的过程。可是从系统设计的角度来讲,可是像鹏程刚刚讲的,有的时候会遇到宕机,这实际上是属于运维级别的东西,咱们要对这个信息了解。一般的作法除了作自启外,刚刚奎松也讲了,一个是记log,一个是metics,若是用记log的方式咱们通常会在每个事务的发起点,产生一个全局的ID,这个ID会带在每个消息中。好比,上面的例子,从建立订单开始的时候就能够产生一个事务级的ID,那这个ID在订单建立成功后随着建立成功的事件发出去,那库存系统在收到这个事件以后依然会把着这个ID带着往下游或者其余的地方发出去。这个时候不管是日志系统也好,仍是metrics也好,咱们去查找这个事件的时候能够很容易地经过这个惟一的ID,把它上下游全部的事件串起来,这样后台的管理员能够看的到,好比像刚刚说的支付系统宕机了,那这个时候能够很明显的看到,同一ID的事件流会存在大量的更新库存以后,后面就没有支付了。像在SpringCloud全家桶里面有一个SpringMetrics的集成或者SpringBootAdmin里面就有相似的图形界面,能够很清晰地在里面看到每个流程走到每个节点的状态,我以为这个状况下能够快速地发现问题。

【嘉宾丨童奎松】:这个彻底赞成,原来在Linkin系统的时候也是,在你的业务流程的最开始的步骤会生成一个UUID,这个UUID会带到你整个业务流程的每一步。

【主持人丨大师兄】:各位是谈了一些监测方面的时候可能要有一个像奎松说的UUID,这个UUID负责端到端的业务链过程的监控,这是上线之后的监控过程。可是我顺便引入下一个问题,由于刚刚鹏程说的,实际上你即便保证你每个领域,每个事件处理的模块,在本身的范围内都测试经过的状况下,可是做为我整个的测试环节,针对我整个测试工程来说我也不能保证你有这么大的事件处理业务是彻底正确的。因此整个开发管理以后你仍是要通过端到端的测试过程的,可是这样的测试过程确定和传统的方式有很大的区别的,我想问一下各位,在最后整个系统开发完以后你的上线测试期,我想让各位分享一下在测试过程当中有什么经验,有什么和传统的方式不一样的地方是须要注意或者是须要花时间的。由于即便你各个模块测试充分了,可是从工程上来说我仍是要把整个连在一块儿的集成作测试的。

【嘉宾丨徐焱飞】:我以为这个是不冲突的,这个东西已经跟咱们说的领域系统也好,或者是事件驱动的系统也好,关系不大。包括咱们如今最流行的微服务,也要处理这样的问题。首先把每一个节点系统本身的测试作掉,由于这个很是简单,其实只要花时间进去就行了,能够用穷举的方式把全部的事件传进去,看看它的output是否是咱们想要的。把这个作掉,而后整个大环节的集成测试其实就跟传统的测试没有什么区别,只要在源头发出一个请求,好比像CQRS当中先发出一个command,而后去query出一个结果就行了。若是这个结果不是咱们想要的,那就说明这个环节过程中是出了问题,而且对一些异常的状况咱们也要去作相应的测试和处理。

【主持人丨大师兄】:在用传统的方式我这边一个负责测试的,出了一个问题,我立刻知道去找哪一个负责人,像这种状况的话个人测试人员找谁,他其实是不知道的。

【嘉宾丨童奎松】:其实你是知道的,咱们刚刚说了,你的业务流程有个UUID,你用这个UUID就能够查到你事件的列表,其实你就知道你应该在哪一步事件没有发出去,或者哪一步事件出错了,你就应该知道找谁。

【主持人丨大师兄】:因此是整个的监测系统必须是事先就上掉的,而不是我过后再去补,若是为了系统的健壮性的话我必须是要在整个的系统上线以前把整套的监测系统也作一下部署。

【嘉宾丨童奎松】:对,这个监测系统和各类错误处理实际上是你程序的一部分。反应式编程四个东西,一个是说你的用户响应,你的反应无论任何状况下你的反应时间都要相对在必定范围以内的。第二,你高负载的时候,照样能够撑得住,你不能出错。第三,当你的系统错的时候,你也能容错的,无论是硬件错仍是你的程序自己有错,仍是各类网络错,你都是要处理一下。这三个是它的要求,由于反应式系统这个要求就是这样的。因此,为了达到这三个要求,你刚才说的监测,刚才说的自动恢复机制,好比你部署了三个实例,有个实例当掉了,你的系统要负责把重启新的实例而后补回三个实例。就是你的整个库存是三个实例,这些东西都是一个自动或者自愈的过程,不能说我今天三个实例当掉一个我经过人去手工启,这个在如今的高负载的要求下你来不及的。或者刚刚说的,在akka里面的actor模式,全部的actor会有supervisor,supervisor要负责把不工做的,好比说异常,或者状态有错,或者硬件失败,或者网络失败这种失败的actor所有给杀掉,而后再建立新的actor来替换掉它。

【主持人丨大师兄】:我这边再说最后一个问题,刚刚的问题都是和监测,测试响应式系统相关。最后一个问题是跟消息自己相关的,在某些场景下可能个人上游系统同时可能会发来许多消息,不止一个的消息,可能你在下游系统当中接收消息的时候会出现这种状况,其中我有一个消息要等另外一个消息处理完我才能去作。其实有一个消息处理的前后顺序的保障,我不知道各位在这个方面有没有相似的一些实践呢?若是是我要去保证一个消息处理的顺序的状况下我应该怎么样去保证?

【嘉宾丨童奎松】:首先我以为这是一个伪命题,由于你说的消息有先后的,同一个系统发出去之后我会发出去一个消息,这个消息必定会有系统来处理,而后发出另一个消息,另一个消息再触发下一步,他是一步步来进行的。没有说我同时收到两个消息,第二个消息效果等第一个消息处理完以后才能够处理。更多状况下是我先收到第一个消息,而后我再处理,发出第二个消息,第二个消息再处理发出第三个消息。

【嘉宾丨徐鹏程】:存不存在我这个系统发出去两个消息,可是我接收到这两个消息的下游消息以后再作下一步,其实至关于一个定型的状态。可是我又但愿他能够作到,这两个都作完以后才能作到下一步。

【嘉宾丨童奎松】:这个是能够的,这个其实就是说你在你的系统自己你的状态,好比咱们把刚刚的例子从新变一下。当个人订单成功以后我把客户付款,预留库存这两个是同时发生的,这时候库存系统和付款系统,两个系统会同时监听订单成功这个消息,分别作他的工做,分别发出保留成功和付款成功两个事件,而后在你的订单系统里面你会分别监听这两个事件,你不须要关心这两个事件谁先来谁后来,你只要关心当这两个事件都来了以后我再进行下一步的处理。

【嘉宾丨徐鹏程】:其实就是说我在接收这两个消息的时候我去检查一下另外的有没有成功?

【嘉宾丨童奎松】:你不须要检查你的另外消息成不成功,由于你不须要关心另外消息成不成功,你只关心你的系统的状态。由于当我接触到个人库存保留成功的状态的时候我可能会在个人订单更改一个状态的时候订单成功了,在我付款成功以后我可能会生成一个付款成功的交易号,而后填在订单里面,无论谁先来后来,只要我后来的检查到个人订单里面有这个状态了,我就能够往下一步走,而不是我检查到那个消息来没来。

【主持人丨大师兄】:可是以前由于在作最后一步处理的时候其实仍是等消息,而不是去看订单的状态,由于订单的状态变化自己应该是会发一个消息吧。

【嘉宾丨童奎松】:没有,订单状态本身不发消息。

【主持人丨大师兄】:我今天必定要看两个状态的变化吗?

【嘉宾丨徐鹏程】:它至关因而在收到消息的时候触发那个状态检查吗?

【嘉宾丨童奎松】:对,它收到消息的时候你能够说是中间状态,这个取决于你的系统怎么写,你的程序怎么写。你收到两个消息以后你分别会在你的订单mark两个状态,你后收到的那个你只要检查前面的是否是收到,或者你的逻辑,你的订单,你都不须要担忧个人哪一个消息先到,个人逻辑是说,当个人订单知足了这个这个以后我走下一步,我无论你这个这个是事件改的,仍是手工改的,仍是经过rest改的,我不在意。

【主持人丨大师兄】:其实遇到一些相似的可能须要有一些dependency的状况,那我整个把它设计也是归结为一个可能某个领域的状态的变化,可能就是有一些相似于可能我已经付款了,可是库存没拿货这样一个相似的中间状态你是须要考虑进去的,帮助你作下一步?

【嘉宾丨童奎松】:对的,只要你的业务设计的时候有存在这个可能,你的订单整个模块的设计就要考虑到这个状况。考虑到我付了款,可是没有库存,这个时候怎么办?是backorder仍是退款?

【嘉宾丨徐焱飞】:我补充一下,在作分布式消息同步的状况下可能会有记录消息的顺序问题,作一些分析的时候,由于好比咱们去查某一个特定的时间节点它的先后发生了什么顺序的时候,这个时候若是消息带有时间顺序那对咱们分析这个问题是有帮助的。这种状况下咱们通常会在消息本身的属性里面可能会增长一个好比相似于happenBefore或者happenAfter的关联关系,把这种关联关系放在消息自己里面去。好比一个消息A到一个聚合根的处理过程中产生另一个消息B,那就能够在消息B当中可能会加一个属性,就是happenBefore,就是A这个消息是在这个B消息前发生过的。当咱们把全部的消息落地,落到持久层里面的时候,咱们最终回溯找问题的时候,或者把整个回溯去获取最新状态的时候,那这个前后顺序是有帮助的。这是基于EventSourcing的方式。

【嘉宾丨童奎松】:对,每一个消息必定会有它的timestamp,每一个消息必定有它何时发生的。第二个,加上咱们刚才说过的UUID,其实你就能够拎出一条线来。

【主持人丨大师兄】:对,可是UUID应该不会用到一个具体的业务场景中吧,应该也只是你外部的一个监测,从Devops的角度去加这个UUID。

【嘉宾丨童奎松】:由于咱们刚刚说的,你的UUID是在你整个的业务流程都存在的。因此你整个业务完成以后你拿到你的事件的列表你就知道这个业务是怎么走的。你经过你的UUID能找出你全部的事件你就知道你的业务流程怎么走的,他究竟是走到了订单建立失败仍是订单成功,到底走到了付款成功仍是付款失败。

【主持人丨大师兄】:对,这个UUID的目的仍是在于背后监测你的系统的状态,可是自己应该是不会去取这样一个关系链吧。

【嘉宾丨童奎松】:会,这个UUID很是重要。

【嘉宾丨徐鹏程】:一般咱们是用来作事件回溯的时候用。

【嘉宾丨童奎松】:包括我举个例子,好比你新版本上线你想测试一下,你怎么办?你一种是想作压力测试,一种是说你生成不少的数据来作压力测试。第二,你就把老的事件拿过来,UUID排序去作测试。其实就是事件另一个好处,你能够重放,对于任何的场景你能够重放。

【主持人丨大师兄】:对,因此就像焱飞刚刚说的,可能就是用来回溯的一个场景,由于回溯可能大部分是作一个问题的查询或者帮你作测试。可是我在作真正的一个线上的业务逻辑里面应该不会再去拿这关系链去作这样一个查询吧。

【嘉宾丨童奎松】:对,你真正写你的程序里面是不会用UUID来决定作任何identity的,你确定是拿订单ID或者业务ID来作的。可是这个UUID主要就是说的,一个是你监控,还有一个是你数据分析。好比你过后分析的时候你会把事件联起来。

【主持人丨大师兄】:咱们今天全部的问题就到这里了,接下来我想看一下各位听众有什么问题吗?

【嘉宾丨童奎松】:我看到一个问题是状态机的问题,刚刚咱们说到每一个系统,每一个领域都接收到消息来负责它本身的处理。可是当一些复杂的流程,好比有一些判断或者复杂的流程的时候每一个系统本身自己已经处理不了的时候你可能会有一个状态机统一处理,就是我往你哪一个系统去发,负责收到哪一个系统事件,而后告诉哪一个系统要作什么事情。可是这个状态机固然有不一样的作法,一种是说你单独一个系统去作状态机来负责整个其实就像你把每一个领域去耦合,第二种作法就是我某一个子系统,我一个子领域做为状态机,也就是刚刚说到的订单系统。可能订单系统里面有一个状态机来决定我全部的事情,针对这张订单,包括你的预留库存、付款都是针对这张订单的,因此我就会把状态机放在订单系统里面。不知道我这个问题解释得清楚吗?

【主持人丨大师兄】:因此状态机制我自己仍是一个,我是否是能够理解为它其实不是一个正常的业务流程,而是一个处理平常运营状况的场景,就是发生了某些状况下我能够不从业务的角度,而是从中间去干预,直接往一个中间系统去发一个消息,去处理一些可能平常的例外状况。

【嘉宾丨童奎松】:状态机其实就是业务系统,状态机有一个好处是你能够很灵活地修改你的业务流程,多是我经过必定的配置,不须要再修改个人代码,只是要修改我必定的配置我就能够从新调整个人业务流程。好比刚刚说的,开始可能我系统设计的时候,我没有库存我就不能收款,或者我不能发货。可能我这时候业务说了,像苹果来讲,我能够预留三到四周之后发货,或者我先backorder,这个时候你就须要改流程。一种作法是你改代码,第二种是我功能都在,我只须要把个人状态机从新配置一下就能够了。

【主持人丨大师兄】:刚刚群里有人在说仍是提出状态机的问题他在问状态机每一个状态是否都是独立的?仍是说一个全局的?

【嘉宾丨童奎松】:每一个状态是独立的,相互不干扰的。可是从个人角度来讲,状态机还有另一个用处,解决冲突。好比你的系统是有状态的,你要把状态保存到某个地方,可是你又想分布式,好比你的数据库,你的分布式,这时候你要有两台数据库,以防我某台数据库当掉了,我能用另外的数据库来顶替,这有不一样的模式,一种是主从模式,就是你是彻底靠主服务。第二种是我主主模式,两个都是主,你的系统能够用任何一个数据库来写数据,这时候他们之间可能就会产生冲突。

可是冲突有几种作法,一种是我冲突以后我提醒数据库按照必定的规则,好比后来的确定是先把前面的覆盖,这是一种规则。第二种规则,你经过设计状态机作一种叫conflict free,就不会产生冲突的一个状态机。当你两个系统,你的一个订单在两个数据库里面有不一样的状态的时候它会合并进入到另一个状态。举例说,当一个订单在一个地方付款成功,另一个地方是取消的话,那你可能进入第三个状态,好比用户取消,这个时候你再根据这个状态去作相应的业务处理。

【主持人丨大师兄】:因此,你实际上是在说我整个的消息处理的工做的流程中能够设置多套策略,而后根据不一样的状况去运营一些不一样的策略。

【嘉宾丨童奎松】:对,这是状态机的一个用法。另一个用法我刚刚说了,当你要解决你分布式里面的冲突的时候这也是一个作法。

【主持人丨大师兄】:好的,这期就到这里,谢谢三位嘉宾。

“跨境茶话会”是由移动增加技术服务商“魔窗”联合国内外众多技术专家发起的在线技术交流活动,目前已邀请嘉宾来自Google、ebay、Snap、Uber、VISA、Pinterest、BranchMeteics、Splunk、小红书、华为等国际知名IT企业在职高级工程师,面向全部互联网从业技术人员分享交流先进理念和实战案例,同时为中美技术朋友提供跨境交友和深度学习的平台。

“跨境茶话会”结合前沿热点技术话题,精心策划每个月一个线上技术主题交流,每期邀请来自国内外互联网界的三位实战嘉宾分享一线技术最佳实践,并针对核心技术问题对话答疑,为技术从业者拓宽思路、提高技术实力、驱动业务增加。

下期免费报名:https://www.wenjuan.net/s/3iu...

相关文章
相关标签/搜索