在微服务中,一个逻辑上原子 *** 作可以经常跨越多个微服务。即使是单片系统也可能使用多个数据库或消息传递解决方案。使用多个独立的数据存储解决方案,如果其中一个分布式流程参与者出现故障,我们就会面临数据不一致的风险 - 例如在未下订单的情况下向客户收费或未通知客户订单成功。在本文中,我想分享一些我为使微服务之间的数据最终保持一致而学到的技术。
为什么实现这一目标如此具有挑战性?只要我们有多个存储数据的地方(不在单个数据库中),就不能自动解决一致性问题,工程师在设计系统时需要注意一致性。 目前,在我看来,业界还没有一个广为人知的解决方案,可以在多个不同的数据源中自动更新数据 - 我们可能不应该等待很快就能获得一个。
以自动且无障碍的方式解决该问题的一种尝试是实现两阶段提交(2PC)模式的XA协议。但在现代高规模应用中(特别是在云环境中),2PC似乎表现不佳。为了消除2PC的缺点,我们必须交易ACID for BASE并根据要求以不同方式覆盖一致性问题。
在多个微服务中处理一致性问题的最着名的方法是Saga模式。 您可以将Sagas视为多个事务的应用程序级分布式协调 。 根据用例和要求,您可以优化自己的Saga实施。 相反,XA协议试图涵盖所有场景。 Saga模式也不是新的。 它在过去已知并用于ESB和SOA体系结构中。 最后,它成功地转变为微服务世界。 跨越多个服务的每个原子业务 *** 作可能包含技术级别的多个事务。 Saga Pattern的关键思想是 能够回滚其中一个单独的交易 。 众所周知,开箱即用的已经提交的单个事务无法进行回滚。 但这是通过引入 补偿 *** 作来 实现的 - 通过引入“取消” *** 作。
除了 取消 之外,您还应该考虑使您的服务具有 幂等性 ,以便在出现故障时重试或重新启动某些 *** 作。 应监控故障,并应积极主动地应对故障。
如果在进程的中间 负责调用补偿 *** 作的系统崩溃或重新启动 ,该怎么办? 在这种情况下,用户可能会收到错误消息,并且应该触发补偿逻辑,或者 - 当处理异步用户请求时,应该恢复执行逻辑。
要查找崩溃的事务并恢复 *** 作或应用补偿,我们需要协调来自多个服务的数据。 对账
是在金融领域工作的工程师所熟悉的技术。你有没有想过银行如何确保你的资金转移不会丢失,或者两个不同的银行之间如何汇款?快速回答是对账。
回到微服务,使用相同的原则,我们可以在一些 动作触发器 上协调来自多个服务的数据。当检测到故障时,可以按计划或由监控系统触发 *** 作。最简单的方法是运行逐记录比较。可以通过 比较聚合值来 优化该过程。在这种情况下,其中一个系统将成为每条记录的真实来源。
想象一下多步骤交易。如何在对帐期间确定哪些事务可能已失败以及哪些步骤失败?一种解决方案是 检查每个事务的状态 。在某些情况下,此功能不可用(想象一下发送电子邮件或生成其他类型消息的无状态邮件服务)。在其他一些情况下,您可能希望立即了解事务状态,尤其是在具有许多步骤的复杂方案中。例如,预订航班,酒店和转机的多步订单。
在这些情况下,事件日志可以提供帮助。 记录是一种简单但功能强大的技术 。 许多分布式系统依赖于日志 。 “ 预写日志记录 ”是数据库在内部实现事务行为或维护副本之间一致性的方式。相同的技术可以应用于微服务设计。在进行实际数据更改之前,服务会写入有关其进行更改的意图的日志条目。实际上, 事件日志可以是协调服务所拥有的数据库中的表或集合 。
事件日志不仅可用于 恢复事务处理 ,还可用于为系统用户,客户或支持团队提供 可见性 。但是,在简单方案中,服务日志可能是冗余的, 状态端点或状态字段 就足够了。
到目前为止,您可能认为sagas只是编配(orchestration )方案的一部分。但是sagas也可以用于编排(choreography ),每个微服务只知道过程的一部分。 Sagas包括处理分布式事务的正流和负流的知识。在编排(choreography )中,每个分布式事务参与者都具有这种知识。
到目前为止描述的一致性解决方案并不容易。他们确实很复杂。但有一种更简单的方法: 一次修改一个数据源 。我们可以将这两个步骤分开,而不是改变服务的状态并在一个过程中发出事件。
在主要业务 *** 作中,我们修改自己的服务状态,而单独的进程可靠地捕获更改并生成事件。这种技术称为变更数据捕获(CDC)。实现此方法的一些技术是Kafka Connect或Debezium。
但是,有时候不需要特定的框架。一些数据库提供了一种友好的方式来拖尾其 *** 作日志,例如MongoDB Oplog。如果数据库中没有此类功能,则可以通过时间戳轮询更改,或使用上次处理的不可变记录ID查询更改。避免不一致的关键是使数据更改通知成为一个单独的过程。在这种情况下,数据库记录是 单一的事实来源 。只有在首先发生变化时才会捕获更改。
更改数据捕获的最大缺点是业务逻辑的分离。更改捕获过程很可能与更改逻辑本身分开存在于您的代码库中 - 这很不方便。最知名的变更数据捕获应用程序是与域无关的变更复制,例如与数据仓库共享数据。对于域事件,最好采用不同的机制,例如明确发送事件。
让我们来看看颠倒的单一事实来源。如果不是先写入数据库,而是先触发一个事件,然后与自己和其他服务共享。在这种情况下,事件成为事实的唯一来源。这将是一种事件源的形式,其中我们自己的服务状态有效地成为读取模型,并且每个事件都是写入模型。
“事件优先”方法面临的挑战也是CQRS本身的挑战。想象一下,在下订单之前,我们想要检查商品的可用性。如果两个实例同时收到同一项目的订单怎么办?两者都将同时检查读取模型中的库存并发出订单事件。如果没有某种覆盖方案,我们可能会遇到麻烦。
处理这些情况的常用方法是乐观并发:将读取模型版本放入事件中,如果读取模型已在消费者端更新,则在消费者端忽略它。另一种解决方案是使用悲观并发控制,例如在检查项目可用性时为项目创建锁定。
“事件优先”方法的另一个挑战是任何事件驱动架构的挑战 - 事件的顺序。多个并发消费者以错误的顺序处理事件可能会给我们带来另一种一致性问题,例如处理尚未创建的客户的订单。
诸如Kafka或AWS Kinesis之类的数据流解决方案可以保证将按顺序处理与单个实体相关的事件(例如,仅在创建用户之后为客户创建订单)。例如,在Kafka中,您可以按用户ID对主题进行分区,以便与单个用户相关的所有事件将由分配给该分区的单个使用者处理,从而允许按顺序处理它们。相反,在Message Brokers中,消息队列具有一个订单,但是多个并发消费者在给定顺序中进行消息处理(如果不是不可能的话)。在这种情况下,您可能会遇到并发问题。
实际上,在需要线性化的情况下或在具有许多数据约束的情况(例如唯一性检查)中,难以实现“事件优先”方法。但它在其他情况下确实很有用。但是,由于其异步性质,仍然需要解决并发和竞争条件的挑战。
有许多方法可以将系统拆分为多个服务。我们努力将单独的微服务与单独的域匹配。但域名有多细化?有时很难将域与子域或聚合根区分开来。没有简单的规则来定义您的微服务拆分。
虽然匹配帐户余额至关重要,但有许多用例,其中一致性不那么重要。想象一下,为分析或统计目的收集数据。即使我们从系统中随机丢失了10%的数据,也很可能不会影响分析的业务价值。
数据的原子更新需要两个不同系统之间达成共识,如果单个值为0或1则达成协议。当涉及到微服务时,它归结为两个参与者之间的一致性问题,并且所有实际解决方案都遵循一条经验法则:
在给定时刻,对于每个数据记录,您需要找到系统信任的数据源
事实的来源可能是事件,数据库或其中一项服务。实现微服务系统的一致性是开发人员的责任。我的方法如下:
早期需要延迟处理的业务场景,更多的是通过定时任务扫表,然后执行满足条件的记录,具有频率高、命中低、资源消耗大的缺点。随着消息中间件的普及,延迟消息可以很好的处理这种场景,本文主要介绍延迟消息的使用场景以及基于常见的消息中间件如何实现延迟队列,最后给出了一个在网易公开课使用延迟队列的实践。
1、有效期:限时活动、拼团。。。
2、超时处理:取消超时未支付订单、超时自动确认收货。。。
4、重试:网络异常重试、打车派单、依赖条件未满足重试。。。
5、定时任务:智能设备定时启动。。。
1、RabbitMQ
1)简介:基于AMQP协议,使用Erlang编写,实现了一个Broker框架
a、Broker:接收和分发消息的代理服务器
b、Virtual Host:虚拟主机之间相互隔离,可理解为一个虚拟主机对应一个消息服务
c、Exchange:交换机,消息发送到指定虚拟机的交换机上
d、Binding:交换机与队列绑定,并通过路由策略和routingKey将消息投递到一个或多个队列中
e、Queue:存放消息的队列,FIFO,可持久化
f、Channel:信道,消费者通过信道消费消息,一个TCP连接上可同时创建成百上千个信道,作为消息隔离
2)延迟队列实现:RabbitMQ的延迟队列基于消息的存活时间TTL(Time To Live)和死信交换机DLE(Dead Letter Exchanges)实现
a、TTL:RabbitMQ支持对队列和消息各自设置存活时间,取二者中较小的值,即队列无消费者连接或消息在队列中一直未被消费的过期时间
b、DLE:过期的消息通过绑定的死信交换机,路由到指定的死信队列,消费者实际上消费的是死信队列上的消息
3)缺点:
a、配置麻烦,额外增加一个死信交换机和一个死信队列的配置
b、脆弱性,配置错误或者生产者消费者连接的队列错误都有可能造成延迟失效
2、RocketMQ
1)简介:来源于阿里,目前为Apache顶级开源项目,使用Java编写,基于长轮询的拉取方式,支持事务消息,并解决了顺序消息和海量堆积的问题
a、Broker:存放Topic并根据读取Producer的提交日志,将逻辑上的一个Topic分多个Queue存储,每个Queue上存储消息在提交日志上的位置
b、Name Server:无状态的节点,维护Topic与Broker的对应关系以及Broker的主从关系
2)延迟队列实现:RocketMQ发送延时消息时先把消息按照延迟时间段发送到指定的队列中(rocketmq把每种延迟时间段的消息都存放到同一个队列中),然后通过一个定时器进行轮训这些队列,查看消息是否到期,如果到期就把这个消息发送到指定topic的队列中
3)缺点:延迟时间粒度受限制(1s/5s/10s/30s/1m/2m/3m/4m/5m/6m/7m/8m/9m/10m/20m/30m/1h/2h)
3、Kafka
1)简介:来源于Linkedin,目前为Apache顶级开源项目,使用Scala和Java编写,基于zookeeper协调的分布式、流处理的日志系统,升级版为Jafka
2)延迟队列实现:Kafka支持延时生产、延时拉取、延时删除等,其基于时间轮和JDK的DelayQueue实现
a、时间轮(TimingWheel):是一个存储定时任务的环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表
b、定时任务列表(TimerTaskList):是一个环形的双向链表,链表中的每一项表示的都是定时任务项
c、定时任务项(TimerTaskEntry):封装了真正的定时任务TimerTask
d、层级时间轮:当任务的到期时间超过了当前时间轮所表示的时间范围时,就会尝试添加到上层时间轮中,类似于钟表就是一个三级时间轮
e、JDK DelayQueue:存储TimerTaskList,并根据其expiration来推进时间轮的时间,每推进一次除执行相应任务列表外,层级时间轮也会进行相应调整
3)缺点:
a、延迟精度取决于时间格设置
b、延迟任务除由超时触发还可能被外部事件触发而执行
4、ActiveMQ
1)简介:基于JMS协议,Java编写的Apache顶级开源项目,支持点对点和发布订阅两种模式。
a、点对点(point-to-point):消息发送到指定的队列,每条消息只有一个消费者能够消费,基于拉模型
b、发布订阅(publish/subscribe):消息发送到主题Topic上,每条消息会被订阅该Topic的所有消费者各自消费,基于推模型
2)延迟队列实现:需要延迟的消息会先存储在JobStore中,通过异步线程任务JobScheduler将到达投递时间的消息投递到相应队列上
a、Broker Filter:Broker中定义了一系列BrokerFilter的子类构成拦截器链,按顺序对消息进行相应处理
b、ScheduleBroker:当消息中指定了延迟相关属性,并且jobId为空时,会生成调度任务存储到JobStore中,此时消息不会进入到队列
c、JobStore:基于BTree存储,key为任务执行的时间戳,value为该时间戳下需要执行的任务列表
d、JobScheduler:取JobStore中最小的key执行(调度时间最早的),执行时间<=当前时间,将该任务列表依次投递到所属的队列,对于需要重复投递和投递失败的会再次存入JobStore中。
注: 此处JobScheduler的执行时间间隔可动态变化,默认05s,有新任务时会立即执行(Object->notifyAll())并设置时间间隔为01s,没有新任务后,下次执行时间为最近任务的调度执行时间。
3)缺点:投递到队列失败,将消息重新存入JobStore,消息调度执行时间=系统当前时间+延迟时间,会导致消息被真实投递的时间可能为设置的延迟时间的整数倍
5、Redis
1)简介:基于Key-Value的NoSQL数据库,由于其极高的性能常被当作缓存来使用,其数据结构支持:字符串、哈希、列表、集合、有序集合
2)延迟队列实现:Redis的延迟队列基于有序集合,score为执行时间戳,value为任务实体或任务实体引用
3)缺点:
a、实现复杂,本身不支持
b、完全基于内存,延迟时间长浪费内存资源
6、消息队列对比
1、公开课延迟队列技术选型
1)业务场景:关闭超时未支付订单、限时优惠活动、拼团
2)性能要求:订单、活动、拼团 数据量可控,上述MQ均能满足要求
3)可靠性:使用ActiveMQ、RabbitMQ、RocketMQ作为延迟队列更普遍
4)可用性:ActiveMQ、RocketMQ自身支持延迟队列功能,且目前公开课业务中使用的中间件为ActiveMQ和Kafka
5)延迟时间灵活:活动的开始和结束时间比较灵活,而RocketMQ时间粒度较粗,Kafka会依赖时间格有精度缺失
结论: 最终选择ActiveMQ来作为延迟队列
2、业务场景:关闭未支付订单
1)关闭微信未支付订单
2)关闭IOS未支付订单
3、ActiveMQ使用方式
1)activemqxml中支持调度任务
2)发送消息时,设置message的延迟属性
其中:
a、延迟处理
AMQ_SCHEDULED_DELAY:设置多长时间后,投递给消费者(毫秒)
b、重复投递
AMQ_SCHEDULED_PERIOD:重复投递时间间隔(毫秒)
AMQ_SCHEDULED_REPEAT:重复投递次数
c、指定调度计划
AMQ_SCHEDULED_CRON:corn正则表达式
4、公开课使用中进行的优化
1)可靠性:针对实际投递时间可能翻倍的问题,结合ActiveMQ的重复投递,在消费者逻辑中做幂等处理来保证延迟时间的准确性
2)可追溯性:延迟消息及消费情况做数据库冗余存储
3)易用性:业务上定义好延迟枚举类型,直接使用JmsDelayTemplate发送,无需关心数据备份和参数等细节
1、无论是基于死信队列还是基于数据先存储后投递,本质上都是将延迟待发送的消息数据与正常订阅的队列分开存储,从而降低耦合度
2、无论是检查队头消息TTL还是调度存储的延迟数据,本质上都是通过定时任务来完成的,但是定时任务的触发策略以及延迟数据的存储方式决定了不同中间件之间的性能优劣
张浩,2018年加入网易传媒,高级Java开发工程师,目前在网易公开课主要做支付财务体系、版本迭代相关的工作。
作者 | Normcore Tech
译者 | 弯月,责编 | 屠敏
出品 | CSDN(ID:CSDNnews)
以下为译文:
可能有人没有听说过Kafka,这是一个非常复杂的分布式软件,可协调多台计算机之间的数据传输。更具体地说,该软件的功能是“展平”数据,然后快速地将数据从一个地方移动到另一个地方。一般来讲,如果你有很多数据需要快速处理并发送到其他地方,那么就可以考虑一下Kafka。Kafka还可以在一定期限内保留数据,比如设置数据保存2天、3天或7天,如果你的下游流程失败,那么你还可以利用存储在Kafka中的数据重新处理。
许多处理汇总数据的公司(比如Facebook和Twitter等社交网络数据,以及每晚需要处理大量星体运动的天文学家,或需要快速了解车辆周围环境数据的自动驾驶车辆公司等)都在使用Kafka,将任意地方生产的数据(即用户通过键盘输入的数据,通过望远镜读取的数据,通过车辆遥测读取的数据等)移动至下游流程进行处理和分析。
最近,WeWork更为名The We Company,他们在共享工作间领域取得了成功,其官网宣称公司的使命为:
“提升世界的意识。”其核心业务是从房地产出租公司那里租下办公室,然后转租给无法按照传统流程租赁办公室的个人和小公司。
为了“提升世界的意识”,该公司致力于为世界各地的个人和公司的团队打造独特却又不完全相同的办公空间。最近,该公司还开始涉足教育。
最近,因为上市,WeWork曝光了一些财务信息:
从好的方面来看,根据A xi os的数据,2018年WeWork的入住率为90%,且会员总数在不断增加。
有人常常将WeWork视为硅谷地区的公司过高估值的完美例子。作为一家房地产企业,WeWork烧钱的速度非常快,毫无疑问他们必须努力让公众市场投资者相信公司有长远的发展,同时和还要维护其作为 科技 公司的地位。
这家公司再三强调说它不是一家房地产公司(毕竟它在不断烧钱对吧?),那么一家消息中介技术公司究竟能提供什么?WeWork曾宣布,它使用Kafka来实现“内部部署的物联网需求”。这是什么意思?
“我们的产品是物理空间,”WeWork的首席开发负责人David Fano说,他在会议期间穿着一件印有“bldgs = data”字样的T恤。
每个办公室都有10个环境传感器——小巧的壁挂式绿色盒子,这些传感器可跟踪室内温度、湿度、空气质量、气压和环境光线水平。还有20个白色的壁挂式信标,呈三角形分布在公共空间(开放式办公区和会议室),用于测量WeWork成员的室内位置(数据是匿名的)。顶部四分之一的传感器通过计算机视觉观察成员的活动。
换句话说,WeWork会跟踪WeWork的多个物理事件并记录所有这些数据。但是他们真的有必要这样做吗?记录Keith Harring壁画周围开放区域的环境温度能给他们带来怎样的竞争优势?更重要的是,他们能否将这些信息用到重要的项目中?
对于公司而言,重要的是要了解办公室的“单位组合” ——私人办公室、会议空间和开放式办公桌——的比例,我们可以利用这些信息对下一个办公间作出调整。
我觉得这家新闻报道机构需要建立一种思考技术的心理模型。Ben Thompson为Stratechery提供了出色的服务,他建立了聚合理论(>
AWS在re:Invent 2018大会上首先发布了托管Apache Kafka消息队列服务(Amazon Managed Streaming for Apache Kafka,MSK)的消息,现在已经从预览成为正式服务。
Apache Kafka是一个分布式的消息队列系统,其使用发布以及订阅的架构,将产生的流数据的应用与利用流数据的角色分离。Apache Kafka让使用者可以捕捉如消息队列事件、交易、物联网等事件,或是应用与日志等流数据,还能实时进行分析,连续不间断地转换数据,并再将收到的数据经过处理后,分发到其他的数据湖和数据库中。
AWS提到,用户在生产环境中要配置Apache Kafka,需要克服一些障碍,特别是在后续的管理以及规模扩展工作上,而现在AWS正式推出的MSK服务,则由AWS负责管理任务,让用户可以简单地配置使用,而且由于近几个版本的Kafka,都需要与节点协调程序Zookeeper共同使用,因此MSK服务也只要简单地设定,就能让Kafka与ZooKeeper一同运行。
使用MSK服务,用户可以在几分钟内创建集群,并使用AWS身分管理与访问控制IAM管理集群 *** 作,也能通过ACM(AWS Certificate Manager)完全托管的TLS私密凭证颁发机构授权客户端,以TLS加密数据,并使用KMS(AWS Key Management Service)中的密钥加密其他数据。当服务器发生故障时,MSK还会替换故障机器,自动执行修补,用户可以从Amazon CloudWatch中,监控服务的状态指标。
AWS表示,MSK与Kafka 111和210版本完全兼容,因此用户可以在AWS直接执行原本的Kafka应用以及工具,而不需要修改任何的代码,用户能使用开源工具MirrorMaker,将数据从现有的Kafka集群直接迁移到MSK上。
MSK的计价方式是以Kafka Broker以及配置存储每小时计价,MSK的数据传输费用与原本的AWS数据传输相同,而集群所使用的Zookeeper节点,还有区域集群的Broker和Zookeeper节点互传数据是不额外收费的。现在用户已经可以在大部分的AWS区域使用到MSK服务,包括北美、亚洲与欧洲。
以上就是关于「微服务架构」跨多个微服务的数据架构模式全部的内容,包括:「微服务架构」跨多个微服务的数据架构模式、网易传媒技术团队:消息中间件实现延迟队列的应用与实践、“根本就不需要 Kafka 这样的大型分布式系统!”等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)