当前位置: 首页 > 编程日记 > 正文

微服务系列(五):事件驱动的数据管理

编者的话|本文来自 Nginx 官方博客,是「Chris Richardson 微服务」系列的第五篇文章。第一篇文章介绍了微服务架构模式,并且讨论了使用微服务的优缺点;第二和第三篇描述了微服务架构模块间通讯的不同方面;第四篇研究了服务发现中的问题。本篇研究微服务架构带来的分布式数据管理问题。

作者介绍:Chris Richardson,是世界著名的软件大师,经典技术著作《POJOS IN ACTION》一书的作者,也是 cloudfoundry.com 最初的创始人,Chris Richardson 与 Martin Fowler、Sam Newman、Adrian Cockcroft 等并称为世界十大软件架构师。

Chris Richardson 微服务系列全 7 篇:

1. 微服务架构的优势与不足

2. 使用 API 网关构建微服务

3. 微服务架构中的进程间通信

4. 服务发现的可行方案以及实践案例s

5. 微服务的事件驱动数据管理

6. 选择微服务部署策略

7. 将单体应用改造为微服务

Chris Richardson 所著所有文章已独家授权 DaoCloud 翻译并刊载。

本期内容:

一、微服务以及分布式数据管理中存在的问题

单体应用通常使用单个关系型数据库,由此带来的好处在于应用能够使用 ACID 事务,后者提供了重要的操作特性:

  • 原子化:原子粒度的更改
  • 一致性:数据库的状态始终保持一致
  • 隔离:并发执行的事务显示为串行执行
  • 持久:事务一旦提交就不会被撤销

如此,应用能够简单地开始事务、更改(插入、更新和删除)多行、以及提交事务。

使用关系型数据库的另一大好处是它支持 SQL。SQL 是一门丰富、可声明的和标准化的查询预约。用户能够轻松通过查询将多个表中的数据组合起来,然后 RDBMS 查询调度器决定执行查询的最优方法。用户不必关心底层细节,比如如何访问数据库。此外,由于所有的应用数据在一个数据库中,很容易查询。

然而,微服务架构中的数据访问变得复杂许多。每个微服务拥有的数据专门用于该微服务,仅通过其 API 访问。这种数据封装保证了微服务松散耦合,并且可以独立更新。但如果多个服务访问相同数据,架构更新会耗费时间、也需要所有服务的协调更新。

更糟糕的是,不同的微服务通常使用不同类型的数据库。现代应用存储和处理各种类型的数据,而关系型数据库并非总是好选择。对于一些使用场景,特定的 NoSQL 数据库能提供更方便的数据模型、更好的性能和可扩展性。譬如,服务使用 Elasticsearch 这样的文本搜索引擎来存储和查询文本;同样地,存储社交图谱数据的服务可能需要使用 Neo4j 这样的图谱数据库。因此,基于微服务的应用通常会混合使用 SQL 和 NoSQL 数据库,即多语言留存(polyglot persistence approach)。

分区的、多语言留存的架构对于数据存储有很多好处,包括服务的松耦合、更好的性能和可扩展性。然而,它也确实给分布式数据管理带来了挑战。

第一个挑战就是如何实现业务逻辑,保持多种服务的一致性。为了说明为何这是一个问题,我们以在线 B2B 商店为例。Customer Service(下文使用客户服务)维护与用户有关的信息,包括信用信息。Order Service(下文使用订单服务)管理订单,验证新订单没有超出用户的信用额度。在单体应用里,订单服务可以简单地使用 ACID 事务来核对提供的信用信息和创建订单。

相反,在微服务架构中,如下图所示,订单表和客户表为各自对应的服务私有。

订单服务无法直接访问客户表,只能通过客户服务提供的 API。订购服务可能使用分布式事务,也被称为两步提交(2PC)。然而,2PC 通常不是现代应用的可行选项。CAP 定理需要用户在可用性和 ACID 风格的一致性中二选一,通常可用性是更好的选择。此外,许多现代技术,譬如大多数 NoSQL 数据库并不支持 2PC。维护整个服务和数据库中的数据一致性是至关重要的,因此我们需要另一种解决方案。

第二个挑战就是如何实现检索多个服务数据的查询。假设应用需要显示一位客户和他的最近的订单。如果订单服务为检索客户订单提供了 API,那么可以使用应用端获取该数据。应用通过客户服务检索该客户,通过订单服务检索该顾客的订单。但是假如订单服务只支持通过订单主键查询订单(可能使用仅支持键值检索的 NoSQL 数据库),这种情况下,就没有合适的方法来检索所需数据。

二、事件驱动的架构

对于许多应用,解决方案就是事件驱动的架构。在这一架构里,当有显著事件发生时,譬如更新业务实体,某个微服务会发布事件,其它微服务则订阅这些事件。当某一微服务接收到事件就可以更新自己的业务实体,实现更多事件被发布。

用户能够使用事件来实现跨多个服务的业务逻辑。事务由一系列步骤组成,每一步都有一个微服务更新业务实体,然后发布触发下一步的事件。下面的系列图展示了如何使用事件驱动的方法在创建订单时检查可用信用。微服务通过消息代理来交换事件。

1. 订单服务创建状态为 NEW 的订单,并发布“订单已创建”事件。

2. 客户服务获取“订单已创建”事件,为此订单保留信用,发布“信用保留”事件。

3. 订单服务获取“信用保留”事件,把订单状态修改为 OPEN。

更为复杂的场景可能涉及更多的步骤,比如在核对客户信用的同时预留库存。

基于(a)每个服务自动更新数据库和发布事件,以及(b)消息代理确保事件传递至少一次,用户能够跨多个服务完成业务逻辑。注意它们并非 ACID 业务。这种模式提供弱确定性,比如最终一致性。这种事务模型也被称作 BASE 模型。

用户也可以使用事件来维护不同微服务拥有的预连接数据的物化视图。维护此视图的服务订阅相关事件,并更新视图。例如,维护客户订单视图的客户订单视图更新服务会订阅由客户服务和订单服务发布的事件。

当客户订单查看更新服务收到客户或者订单事件,就会更新客户订单查看的数据存储。用户能够使用类似 MongoDB 的文档数据库查看用户订单,并为每位客户存储一个文档。用户订单预览查询服务通过客户订单预览数据存储,处理来自客户和最近订单的请求。

事件驱动的架构有优点也有缺点。它使得事务跨多个服务并提供最终一致性,也可以让应用维护物化视图。缺点之一在于,它的编程模型要比使用 ACID 事务的更加复杂。为了从应用级别的失效中恢复,还需要完成补偿性事务,例如,如果信用检查不成功则必须取消订单。此外,由于临时事务造成的改变显而易见,因而应用必须处理不一致的数据。此外,如果应用从物化视图中读取的数据没有更新时,也会遇到不一致的问题。此架构的另一缺点就是用户必须检测并忽略重复事件。

三、实现原子化

事件驱动的架构还存在以原子粒度更新数据库并发布事件的问题。例如,订单服务必须在订单表中插入一行,然后发布“订单已创建”事件。这两个操作需要原子化实现。如果服务在更新数据库之后、发布事件之前崩溃,系统变得不一致。确保原子化的标准做法是使用包含数据库和消息代理的分布式事务。然而,基于以上描述的 CAP 理论,这并非我们所想。

使用本地事务发布事件

实现原子化的方法是使用多步骤进程来发布事件,该进程只包含本地事务。诀窍就是在存储业务实体状态的数据库中,有一个事件表来充当消息队列。应用启动一个(本地)数据库事务,更新业务实体的状态,在事件表中插入一个事件,并提交该事务。独立的应用线程或进程查询事件表,将事件发不到消息代理,然后使用本地事务标注事件并发布。下图展示了这一设计。

订单服务在订单表中插入一行,然后在事件表中插入“订单已创建”的事件。时间发布线程或进程在事件表中查询未发布的事件并发布,然后更新事件表,将该事件标记为已发布。

这种方法优缺点兼具。优点之一是保证每个更新都有对应的事件发布,并且无需依赖 2PC。此外,应用发布业务级别的事件,消除了推断事件的需要。这种方法也有缺点。由于开发者必须牢记发布事件,因此有很大可能出错。此外这一方法对于某些使用 NoSQL 数据库的应用是个挑战,因为 NoSQL 本身交易和查询能力有限。

通过此方法,应用使用本地事务来更新状态和发布事件,排除了对 2PC 的需要。接下来,我们了解使用应用更新状态实现原子化的方法。

挖掘数据库事务日志

无需 2PC 实现原子化的另一种方式是由线程或者进程通过挖掘数据库事务或提交日志来发布事件。应用更新数据库,数据库的事务日志记录这些变更。事务日志挖掘线程或进程读取这些日志,并把事件发布到消息代理。如下图所示:

这一方法的范例是开源的 LinkedIn Databus 项目。Databus 挖掘 Oracle 事务日志并发布与之对应的事件。LinkedIn 使用 Databus 维持各种来源的数据存储与记录系统一致。

另一个范例则是 AWS DynamoDB 采用的流机制,AWS DynamoDB 是一个可管理的 NoSQL 数据库。每个 DynamoDB 流包括 DynamoDB 表在过去 24 小时之内的时序变化,包括创建、更新和删除操作。应用能够读取这些变更,将其作为事件发布。

事务日志挖掘具有多个优点。首先,它能保证无需使用 2PC 就能针对每个更新发布事件。其次,通过将日志发布于应用的业务逻辑分离,事务日志挖掘能够简化应用。事务日志挖掘也有缺点,主要缺点就是事务日志的格式与每个数据库对应,甚至随着数据库版本而变化。此外,很难从底层事务日志更新记录中逆向工程这些业务事件。

通过让应用更新数据库,事务日志挖掘消除了对 2PC 的需要。接下来我们会讨论另一种方法——消除更新,只依赖事件。

使用事件源

通过采用一种截然不同的、以事件为中心的方法来留存业务实体,事件源无需 2PC 实现了原子化。不同于存储实体的当前状态,应用存储状态改变的事件序列。应用通过重播事件来重构实体的当前状态。每当业务实体的状态改变,新事件就被附加到事件列表。鉴于保存事件是一个单一的操作,本质上也是原子化的。

要了解事件源如何运行,可以以订单实体为例。在传统的方法中,每个订单映射为订单表的一行,例如一个 ORDERLINEITEM 表。使用事件源的时候,订单服务以状态更改事件的方式存储订单,包括已创建、已批准、已发货、已取消等。每个事件都包含足够的数据去重建订单状态。

事件长期保存在事件数据库,使用 API 添加和检索实体的事件。事件存储类似上文提及的消息代理,通过 API 让服务订阅事件,将所有事件传达到所有感兴趣的订阅者。事件存储是事件驱动的微服务架构的支柱。

事件源有不少优点。它解决了实施事件驱动的微服务架构时的一个关键问题,能够只要状态改变就可靠地发布事件。另外,它也解决了微服务架构中的数据一致性问题。由于储存事件而不是域对象,它也避免了对象关系抗阻不匹配的问题(object‑relational impedance mismatch problem)。事件源提供了 100% 可靠的业务实体变化的审计日志,使得获取任何时间点的实体状态成为可能。事件源的另一大优势在于业务逻辑由松耦合的、事件交换的业务实体构成,便于从单体应用向微服务架构迁移。

事件源也有缺点。由于采用了不同或不熟悉的编程风格,会有学习曲线。事件存储只直接支持通过主键查询业务实体,用户还需要使用 Command Query Responsibility Segregation (CQRS) 来完成查询。因此,应用必须处理最终一致的数据。

四、总结

在微服务架构中,每个微服务都有其私有数据存储,不同的微服务可能使用不同的 SQL 和 NoSQL 数据库。这些数据库架构带来便利的同时,也给分布式数据管理带来挑战。第一个挑战就是如何实现业务事务,保持多个服务的一致性。第二个挑战就是如何从多个服务中检索数据,实现查询。

对于许多应用,解决方案就是使用事件驱动的架构。事件驱动的架构带来的挑战是如何原子化地更新状态和发布事件。有几个方法可以做到这一点,包括把数据库用作消息队列、事务日志挖掘和事件源。

下期:选择微服务部署策略 ,敬请期待!

英文原文:https://www.nginx.com/blog/event-driven-data-management-microservices/#rd?sukey=3903d1d3b699c208399dd6922d8d126482bd8560185d42bce5e1cbadd95d73b2ec7920d426d50fb40c313e8d269066a4

本文转自:http://blog.daocloud.io/microservices-5/

转载于:https://www.cnblogs.com/jpwahaha/p/10601342.html

相关文章:

js基础知识温习:Javascript中如何模拟私有方法

本文涉及的主题虽然很基础,在很多人眼里属于小伎俩,但在JavaScript基础知识中属于一个综合性的话题。这里会涉及到对象属性的封装、原型、构造函数、闭包以及立即执行表达式等知识。 公有方法 公有方法就是能被外部访问并调用的方法。 // 在对象中 var R…

格式化测试数据,组装用于插入表中的sql语句

最近闲的蛋疼,每每在写测试例子的时候,万恶的测试数据需要手工书写insert语句的向表中插入,很费事,于是就像写个脚本来实现自动生成插入语句 测试数据: 100 北京 20120203123 100 天津20120203123 101 湖南20120203nul…

linux内核功能有,好消息!LINUX内核2.6.18终于支持实时功能了

LINUX内核2.6.18终于支持实时功能了,这是个好消息。当LINUX实时功能加强后,它在嵌入式系统的应用就会更广泛了。在这一版发布前,为了使嵌入式系统的LINUX具有实时特性,需要给标准和LINUX系统加上具有实时功能的补丁。实际上,就算是…

Asp.Net 构架(HttpModule 介绍)

Http 请求处理流程 和 Http Handler 介绍 这两篇文章里,我们首先了解了Http请求在服务器端的处理流程,随后我们知道Http请求最终会由实现了IHttpHandler接口的类进行处理(应该记得Page类实现了IHttpHandler)。从 Http 请求处理流程 一文的最后的一幅图中…

.net下WinDbg使用说明

加载调试文件 .loadby sos mscorwks #.Net 3.5版本及以下 .loadby sos clr #.Net 4.0 WinDbg的基本命令 !threads #显示所有线程 !dumpheap #显示托管堆的信息 !dumpheap -stat #统计堆栈内存 !clrstack #显示调用栈 !dumpobj #显示一个对象的内容 !dumparray #显示数组…

【系列】EOS开发3 EOS提供的程序和工具

上一篇文章使用了nodeos命令来启动eos服务,这一篇文章,就来介绍一下eos提供的相关程序和工具。 nodeos EOSIO的核心守护进程,它可通过插件配置来启动一个节点。 cleos 这是一个命令行工具,它跟nodeos开放的REST API接口进行交互。…

Linux qgis 编译,QGIS简介与源代码编译

QGIS(http://qgis.org)是一个集成了很多个开源的空间数据处理库的桌面GIS软件,支持Python脚本。QGIS基于QT和SIP进行开发,支持Windows\Linux\MacOS等操作系统,可以访问MySQL/PostGRE等数据库的空间数据,可以连接到OpenstreetMap拉…

一步步写自己SqlHelper类库(五):Command对象

1.Command对象基础 当我们使用Connection对象里面的方法打开数据库后,要查询自己所需的数据或对数据库的内容进行增删改时,Command对象就派上用场了! MSDN定义:建立与数据源的连接后,利用Command对象来执行命令并从数据…

MVC-前台调用后台action 传递upload file 参数问题

在后台获取upload file 数量的时候发现count一直为0,经检查发现了问题 ,代码如下: 前台: var data $("#DetailForm").serialize(); $.ajax({ url: Url.Action("SaveRequest", "RegistrationRequest&quo…

Storybook 5.0正式发布:有史以来变化最大的版本\n

Storybook 5.0(SB5)于近日正式发布,这是目前为止最大的一个版本。新版本的内容包括: 全新的开发者体验;带有主题的组件库;改进的前端插件架构;全新的Storybook网站。 Storybook的进展和未来 Sto…

linux6直连存储挂载磁盘,CentOS7mount网络磁盘的详细步骤

在配置前一定先关闭防火墙systemctl disable firewalldsystemctl stop firewalldsystemctl mask firewalld一.修改iscsi客户端的用户名vi /etc/iscsi/initiatorname.iscsiInitiatorName iqn.************;【注:eg:iqn.2017-09.com.centos:cl…

如何将Java源代码文件的编码从GBK转为UTF-8?

有时候看到有意思的demo,在头痛导入项目的编码和workspace的编码不一样的时候 我试着将 笔记本打开一个类一个类的复制, demo的类比较少的时候 可以忍受,demo的类多的时候 除了靠之外 别无办法 今天再找仿ios样式demo的时候 实在受不了乱码&a…

电子书格式怎么在线转换为PDF格式

电子书格式怎么在线转换为PDF格式,现在很多朋友喜欢用PDF格式的电子书,因为其有纸质书的排版和格局,但实际生活中出现的最多的是TXT等格式的电子书,那如何将其进行转换成PDF格式的呢,下面就由小编来告诉大家转换的解决…

设计模式笔记(18)---迭代器模式(行为型)

Gof定义 提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露该对象的内部表示。 动机 在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访…

linux卸载mpfr,Red Hat Linux在安装gcc时遇到的问题汇总

找到了一个比较老的RedHatlinux,想要装上apachetomcat。由于是在虚拟机中运行,我想先装一个vmware-tools提高性能。但是安装的过程中我发现竟然找不到gcc。于是我就去网上(] http://www.linuxidc.com/Linux/2009-01/18242.htm 与 http://www.linuxidc.co…

【数据结构】某些难理解点

1 typedef char ListData;2 //表示以后可以用ListData来代替char类型3 4 typedef struct node{ //此处node,只在结构体中出现和使用5 ListData data;6 struct node *link;7 }ListNode; //表示将整个结构体命名为ListNode,也就是说…

利用jQuery实现用户名片小动画

我爱撸码,撸码使我感到快乐!大家好,我是Counter。下面给大家介绍利用jQuery实现的小动画,非常的简便,如果有原生js操作的话,那么就不止这么多行了。至于CSS,个人觉得,这边CSS布局也蛮…

Linux二进制导出配置文件,Go打包二进制文件的实现

背景众所周知,go语言可打包成目标平台二进制文件是其一大优势,如此go项目在服务器不需要配置go环境和依赖就可跑起来。操作需求:打包部署到centos7笔者打包环境:mac os方法:进入main.go所在目录,输入如下命…

crud springmvc

实体类:Student.java Java代码 package demo.entity; public class Student { private int id; private String name; private String sex; private String address; public int getId() { r…

从 Flutter 的视频渲染到 App 落地经验

3月23日周六,由 RTC 开发者社区主办的 “RTC Dev Meetup 北京站”如约举行,超过100位求知若渴的开发者参加了活动。来自 LeanCloud、声网 Agora、阿里、美团点评的资深工程师,与他们共同分享了 Flutter 开发中的实践经验。 我们在这里回顾一下…

screen命令

原文链接 screen其实就是linux虚拟终端,你可以用它创建很多虚拟终端,每个终端可以创建1~10个窗口,每个窗口相当于一个linux shell窗口,可以执行的操作和正常shell完全一样。 【终端操作】 (需要在物理shell中操作&a…

linux 565显示格式,RGB565转BMP格式 C语言程序

#include#include#include#include"rgb2bmp.h"int RGB2BMP(char *,int ,int ,FILE *);int main(int argc,char *argv[]){double num_tmp 0;FILE *p;/*************** input data ***********filename :RGB数据文件名称nWidth :所生成文件的水平像素n…

Spring Boot @ConfigurationProperties使用指导

1.简介 Spring Boot的一个非常有用的功能是外部化配置,并且可以轻松访问属性文件中定义的属性。 我们现在将详细地探索ConfigurationProperties注释。 2.设置 本文使用相当标准的设置。我们首先在我们的pom.xml中添加spring-boot-starter-parent作为父项&#xff1a…

UVA 10494 - If We Were a Child Again(高精度除法和取余)

题目链接:http://uva.onlinejudge.org/index.php?optioncom_onlinejudge&Itemid8&pageshow_problem&problem1435 这个题做的好没状态。上几天刷的高精度都白刷了啊。。。首先代码除法和取余以前都做过,还要查看以前的代码,模板还…

python显示当前时间

import time time.strftime("%Y%m%d %X", time.localtime()) #当前时区 time.strftime("%Y%m%d %X", time.gmtime(time.time()))#0时区 下面是format字符串的解释: strftime(format[, tuple]) -> string 将指定的struct_time(默认为当前时间…

linux系统 插优盘安装xvidcap,linux下的视频录制软件xvidcap

1.xvidcap简介在linux如果我们想要进行视频录制,那么xvidcap是一个不错的选择。Xvidcap 是一个可将屏幕上的操作过程录制下来并保存为视频的小工具。对于需要制作产品演示和教学的朋友来说,这个屏幕录像机十分实用。Xvidcap 支持生成 avi、mpeg、asf、fl…

ASP.NET Cookie

最经在使用Cookie的过程中遇到了一些疑问,查阅参考MSDN,记录Cookie相关知识点 什么是Cookie Cookie是一小段文本信息,伴随着用于的请求和页面在Web服务器和浏览器之间传递,并将它存储在用户硬盘上的某个文件夹中。Cookie包含每次用户访问站点…

1111 评论

201406114205 陈嘉慧 http://www.cnblogs.com/hui1005039632/ 201406114219 林宇粲 http://www.cnblogs.com/zlcan/ 201406114220 蔡舜 http://www.cnblogs.com/caishun/ 201406114215 林志杰 http://www.cnblogs.com/15linzhijie/ 201406114252 王俊杰 http://www.cnblogs.c…

React 16.8.6 发布,构建用户界面的 JavaScript 库

React 16.8.6 已发布,该版本更新如下: React DOM 修复 useReducer() 中的问题(acdlite in #15124)修复 Safari DevTools 中的 iframe 警告(renanvalentin in #15099)若 contextType 设置为 Context.Consume…

linux禁止路由器,FCC 新规可能禁止在 WiFi 路由器安装 OpenWRT

FCC(美国联邦通讯委员会)的新规则可能会禁止在 WiFi 路由器安装 OpenWRT。OpenWrt 类似于 Buildroot 的路由器固件,为嵌入式设备所研发的 Linux 发行版。目前 OpenWrt 已支持多个平台(如 ARM、mips、x86 等),且提供了许多开源应用程序!许多便…