<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Architecture on NoneBack</title><link>https://noneback.github.io/zh/tags/architecture/</link><description>Recent content in Architecture on NoneBack created by</description><generator>Hugo -- gohugo.io</generator><language>zh</language><copyright>@NoneBack All rights reserved</copyright><lastBuildDate>Thu, 20 May 2021 23:55:11 +0800</lastBuildDate><atom:link href="https://noneback.github.io/zh/tags/architecture/index.xml" rel="self" type="application/rss+xml"/><item><title>分布式事务</title><link>https://noneback.github.io/zh/blog/zh/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/</link><pubDate>Thu, 20 May 2021 23:55:11 +0800</pubDate><guid>https://noneback.github.io/zh/blog/zh/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/</guid><description>&lt;h1 id="事务与分布式事务"&gt;事务与分布式事务&lt;/h1&gt;
&lt;h2 id="事务"&gt;事务&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;事务&lt;/strong&gt;是数据库执行过程中的一个逻辑单位，由一个有限的数据库操作序列构成。数据库需要确保事务操作的&lt;strong&gt;原子性&lt;/strong&gt;：当事务成功时，意味着事务的所有操作全部都执行完成；但事务失败时，数据库将所有执行过的SQL操作回滚。&lt;/p&gt;
&lt;p&gt;数据库单机事务主要拥有四个特性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原子性，事务作为一个整体被执行，包含在其中的对数据库的操作要么全部被执行，要么都不执行&lt;/li&gt;
&lt;li&gt;一致性，事务应确保数据库的状态从一个一致状态转变为另一个一致状态，一致状态的含义是数据库中的数据应满足完整性约束&lt;/li&gt;
&lt;li&gt;隔离性，多个事务并发执行时，一个事务的执行不应影响其他事务的执行&lt;/li&gt;
&lt;li&gt;持久性，已被提交的事务对数据库的修改应该永久保存在数据库中&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="分布式事务"&gt;分布式事务&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;分布式事务&lt;/strong&gt;是指事务的&lt;strong&gt;参与者&lt;/strong&gt;、&lt;strong&gt;支持事务的服务器&lt;/strong&gt;、&lt;strong&gt;资源服务器&lt;/strong&gt;以及&lt;strong&gt;事务管理器&lt;/strong&gt;分别位于&lt;strong&gt;不同&lt;/strong&gt;的分布式系统的不同节点之上。&lt;/p&gt;
&lt;p&gt;随着微服务架构的普及，大型业务域往往包含很多服务，一个业务流程需要由多个服务共同参与完成。在特定的业务场景中，需要保障多个服务之间的数据一致性。&lt;/p&gt;
&lt;p&gt;例如在大型电商系统中，下单接口通常会扣减库存、减去优惠、生成订单 id, 而订单服务与库存、优惠、订单 id 都是不同的服务，下单接口的成功与否，不仅取决于本地的 db 操作，而且依赖第三方系统的结果，这时候分布式事务就保证这些操作要么全部成功，要么全部失败。&lt;/p&gt;
&lt;p&gt;所以本质上来说，&lt;strong&gt;分布式事务就是为了保证不同数据库的数据一致性。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="使用场景"&gt;使用场景&lt;/h1&gt;
&lt;p&gt;在电商系统中，典型的使用场景有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;下单扣减库存&lt;/p&gt;
&lt;p&gt;下单时，需要的操作有生成订单记录，扣减商品库存等操作。两者同上是独立的微服务，所以严格来说，需要分布式事务保证下单操作的原子性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第三方支付&lt;/p&gt;
&lt;p&gt;微服务架构下，支付与订单都是独立的服务。订单的支付状态依赖于财经服务的通知。财经服务又依赖于第三方支付服务的通知。&lt;/p&gt;
&lt;p&gt;一个经典的场景如图：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://xiaomi-info.github.io/2020/01/02/distributed-transaction/notify-message.png" alt="https://xiaomi-info.github.io/2020/01/02/distributed-transaction/notify-message.png" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从图中可以看出有两次调用，第三方支付调用支付服务，以及支付服务调用订单服务，这两步调用都可能出现&lt;strong&gt;调用超时&lt;/strong&gt;的情况，此处如果没有分布式事务的保证，就会出现用户订单实际支付情况与最终用户看到的订单支付情况&lt;strong&gt;不一致&lt;/strong&gt;的情况。&lt;/p&gt;
&lt;h1 id="实现方案"&gt;实现方案&lt;/h1&gt;
&lt;h2 id="两阶段提交"&gt;两阶段提交&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://i.loli.net/2021/05/19/MfWzxseBFKaAnhk.png" alt="https://i.loli.net/2021/05/19/MfWzxseBFKaAnhk.png" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;一次事务的提交主要分为两个阶段：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;准备阶段：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TM开始事务，记录事务开始的日志，并向参与事务的RM询问是否能够执行提交操作，并等待RM响应&lt;/li&gt;
&lt;li&gt;RM执行本地事务，记录Redo/Undo日志，向TM返回执行结果，但不提交事务&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;提交/回滚阶段&lt;/p&gt;
&lt;p&gt;（ 1 ）如果所有参与的RM执行成功，进入&lt;strong&gt;提交阶段&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TM记录事务提交日志，并向所有RM发送提交事务指令&lt;/li&gt;
&lt;li&gt;RM收到指令后，提交本地事务，向TM返回执行结果&lt;/li&gt;
&lt;li&gt;TM记录事务结束&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（ 2 ）如果准备或提交任一RM执行失败或者超时&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TM记录回滚记录，并向所有RM发送回滚指令&lt;/li&gt;
&lt;li&gt;RM回滚本地事务，向TM返回结果&lt;/li&gt;
&lt;li&gt;TM记录事务结束&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="特性"&gt;特性&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;- 原子性：支持
- 一致性：强一致
- 隔离性：支持
- 持久性：支持
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="弊端"&gt;弊端&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;- 同步阻塞：当参与事务者存在占用公共资源的情况，其中一个占用了资源，其他事务参与者就只能阻塞等待资源释放，处于阻塞状态。
- 单点故障：一旦事务管理器出现故障，整个系统不可用
- 数据不一致：如果事务管理器只发送了部分 commit 消息，此时网络发生异常，那么只有部分参与者接收到 commit 消息，也就是说只有部分参与者提交了事务，使得系统数据不一致。
- 不确定性：当事务管理器发送 commit 之后，并且此时只有一个参与者收到了 commit，那么当该参与者与事务管理器同时宕机之后，重新选举的事务管理器无法确定该条消息是否提交成功。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="本地消息表"&gt;本地消息表&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;事务发起方维护一张**本地消息表**，业务表与本地消息表的操作处于同一个本地事务内，通过异步的**定时任务**扫描消息表并投递到下游。
广义的本地消息表方案中，下游通知方式并不局限于消息投递，也可以通过RPC调用等方式通知。
![https://i.loli.net/2021/05/19/tmNeiALsdof24PW.png](https://i.loli.net/2021/05/19/tmNeiALsdof24PW.png)
1. 事务发起者执行本地事务，同时操作业务表和本地消息表
2. 定时任务定时扫描待发送的本地消息(本地消息表中)，将其发送到消息队列
- 发送成功，则将本地消息标记为已发送
- 发送失败，则重试直至成功
3. 消息队列投递消息至下游
4. 下游事务参与者收到消息后，执行本地事务
- 本地事务执行失败，不返回ACK，消息队列重复投递消息
- 本地事务执行成功，则向消息队列返回ACK，全局事务结束
- 消息或ACK丢失，消息队列重复投递消息
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="异常场景"&gt;异常场景&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;- 消息发送丢失，通过定时任务重复发送解决
- 投递到下游的消息丢失，通过重复投递机制解决，需保障下游操作幂等
- 下游回复的ACK丢失，通过重复投递机制解决，需保障下游操作幂等
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="优点与问题"&gt;优点与问题&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;优点：
- 系统吞吐量高，通过消息中间件解耦，下游事务异步化
- 业务侵入度适中，需要实现本地消息表和定时任务
问题：
- 事务支持不完备，不接受下游分支事务的回滚，只能重试
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="特性-1"&gt;特性&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;- 原子性：支持
- 一致性：最终一致
- 隔离性：不支持（分支事务提交之后对其它事务可见）
- 持久性：支持
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="尽最大努力通知"&gt;尽最大努力通知&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;最大努力通知是最简单的一种柔性事务，适用于一些最终一致性时间敏感度低的业务，且被动方处理结果 不影响主动方的处理结果。
这个方案的大致意思就是：
1. 系统 A 本地事务执行完之后，发送个消息到 MQ；
2. 这里会有个专门消费 MQ 的服务，这个服务会消费 MQ 并调用系统 B 的接口；
3. 要是系统 B 执行成功就 ok 了；要是系统 B 执行失败了，那么最大努力通知服务就定时尝试重新调用系统 B, 反复 N 次，最后还是不行就放弃。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="优点与问题-1"&gt;优点与问题&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;优点：
- 实现简单
问题：
- 无补偿机制，不保证送达
- 幂等要求，需要提供接口保证一致性与原子性，系统无法保证
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="特性-2"&gt;特性&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;- 原子性：不支持(需要额外接口保证)
- 一致性：不支持(需要额外接口保证)
- 隔离性：不支持（分支事务提交之后对其它事务可见）
- 持久性：支持
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="经典场景"&gt;经典场景&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;支付回调：
支付服务收到第三方服务支付成功通知后，先更新自己库中订单支付状态，然后同步通知订单服务支付成功。如果此次同步通知失败，会通过异步脚步不断重试地调用订单服务的接口。
![https://xiaomi-info.github.io/2020/01/02/distributed-transaction/try-best-notify.jpg](https://xiaomi-info.github.io/2020/01/02/distributed-transaction/try-best-notify.jpg)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="参考"&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://xiaomi-info.github.io/2020/01/02/distributed-transaction/"&gt;分布式事务，这一篇就够了&lt;/a&gt;&lt;/p&gt;</description></item><item><title>服务发现基本原理</title><link>https://noneback.github.io/zh/blog/zh/%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/</link><pubDate>Wed, 12 May 2021 22:37:30 +0800</pubDate><guid>https://noneback.github.io/zh/blog/zh/%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/</guid><description>&lt;p&gt;服务发现是什么？基本的实现思路？&lt;/p&gt;
&lt;h2 id="总览"&gt;总览&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://pic2.zhimg.com/80/v2-c5e1d05128694eaffc043d1acf1cab41_1440w.jpg" alt="https://pic2.zhimg.com/80/v2-c5e1d05128694eaffc043d1acf1cab41_1440w.jpg" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="服务发现的角色组成"&gt;服务发现的角色组成&lt;/h2&gt;
&lt;p&gt;服务发现由三种角色组成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;服务提供者：服务的实际提供者，提供对应的微服务&lt;/li&gt;
&lt;li&gt;服务消费者：使用服务提供者提供的微服务，一般是其他上游微服务&lt;/li&gt;
&lt;li&gt;服务中介：用于联系服务的提供者与消费者的一个服务，一般为KV存储，以服务名作为Key，以服务提供者IP作为Value，在&lt;strong&gt;有服务提供者状态变化的时候需要及时的更新与通知&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基本的流程是：首先创建一个服务提供者(微服务)，服务提供者将自身的服务地址(一般为IP:PORT)和服务名称注册到服务中介；服务消费者在调用其他微服务时，去服务中介查找服务地址，进行调用。&lt;/p&gt;
&lt;h2 id="服务中介"&gt;服务中介&lt;/h2&gt;
&lt;p&gt;服务中介是服务发现的核心，除了支持服务注册与查找等基本功能，它还需要解决几个核心的问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;服务提供者Crash后，无法主动通知中介，怎么处理？如何保证服务列表的有效性。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;客户端心跳：服务提供者每隔一定时间主动发送“心跳”的方式来向服务端表明自己的服务状态正常，或者维护一个长连接&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;只说明了链路的正常，不代表服务的正常&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;服务端主动探测：服务中介主动调用服务节点的某个接口进行健康检查&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;服务注册中心主动调用服务的某个接口无法做到通用性。在很多场景下服务注册中心到服务发布者的网络是不通的，服务端无法主动发起健康检查，那么往往需要在宿主机器上部署一个 agent 来代替服务端的接口探测&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;服务列表更改之后，如何及时通知服务消费者&lt;/p&gt;
&lt;p&gt;有一下集中机制处理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;消费者轮询中介 + 服务列表版本号&lt;/li&gt;
&lt;li&gt;消费中介推送：TCP长连接推送 or Http Polling&lt;/li&gt;
&lt;li&gt;推拉结合：消费者主动拉取，中介主动推送&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;服务发现分布式，避免单点问题&lt;/p&gt;
&lt;p&gt;采用分布式KV存储，如etcd等&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="常见服务发现工具"&gt;&lt;strong&gt;常见服务发现工具&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ECTD&lt;/li&gt;
&lt;li&gt;ZooKeeper&lt;/li&gt;
&lt;li&gt;Consul&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="参考"&gt;&lt;strong&gt;参考&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://zhuanlan.zhihu.com/p/34332329"&gt;知乎:服务发现基本原理&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.infoq.cn/article/lknumimtzy08qxqckqma"&gt;InfoQ:聊一聊微服务架构中的服务发现系统&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>