如何保证幂等
考虑如下问题:在一个电商系统中,如果用户下单后,在模块之间及逆行远程调用的过程中,出现了问题,给用户响应了请求超时,那么会影响到用户购买,导致损失订单。
但是,如果对请求超时进行重试,那么会导致用户重复下单,用户明明点击了一次,却显示购买了两次,导致一些问题。
这里就涉及到了一个问题,接口的幂等性,即不管执行几次,它的影响都和执行一次一样。
如何保证Exactly-once不能保证 Exactly-once 的原因主要有两个,一个是网络出现丢包或者分区等故障,另一个是远端服务发生了故障。
一般来说,在分布式系统中,实现消息的 Exactly-once 传递,主要有三种方式:一种是至少一次消息传递加消息幂等性,一种是分布式快照加状态回滚,一种是整体重做。
一次传递加幂等该思路比较简单,就是当处理失败后,并不给用户返回请求超时,而是进行重试,保证至少请求一次成功,然后每次请求带上一个全局的唯一id,当失败重试的时候,该id并不会发生改变,成功后,将本次请求保存在数据库中,如果第二次发现该id后,并不会执行,直接返回。
分布式快照加回滚分布式快照加状态回滚指的是,在整个分布式系统运行的过程 ...
分布式事务
分布式事务的实现,一般有三种方案:
基于 XA 协议的二阶段提交协议方法;
三阶段提交协议方法;
基于消息的最终一致性方法
基于 XA 协议的二阶段提交方法XA是一个分布式事务协议,规定了事务管理器和资源管理器接口,可以分为两部分:事务管理器和本地资源管理器。
大致原理是:事务管理器作为协调者,负责各个本地资源的提交和回滚,而资源管理器就是分布式事务的参与者,通常由数据库实现。
基于 XA 协议的二阶段提交方法中,二阶段提交协议(The two-phase commit protocol,2PC),用于保证分布式系统中事务提交时的数据一致性,是 XA 在全局事务中用于协调多个资源的机制。
两阶段提交投票为第一阶段,协调者会向事务的参与者发起执行操作的请求,允许他们发起提交,参与者收到请求后,会执行请求中的事务,记录日志信息但不提交,待参与者执行成功后,向协调者发送yes表示同意操作,若不成功,则发送no,表示终止。
当所有的参与者都返回了yes或者no信息后,才会进入提交阶段。
在该阶段,如果所有的参与者都发送的是yes,那么协调者将通知所有参与者提交事务,参与者收到通知后,才 ...
分布式下如何访问共享资源
互斥资源指的是那种一次只可以一个系统访问的资源。
集中式算法该算法需要借助于一个第三方软件,或者说一个数据中心,每当有系统需要访问互斥资源时,需要先该数据中心发送请求,看是否其他的系统请求该资源。然后数据中心给系统返回是否可以访问该资源,如果同意,则系统访问互斥资源。
存在的问题:数据中心容易成为瓶颈,所有的系统都要请求数据中心,如果数据中心出现阻塞或者不可用,则有可能导致整个系统不可用。
民主协商:分布式算法在该算法下,如果要访问互斥资源,则采用互相通信的形式,比如说现在有A,B,C,D四个系统,其中A想要访问某资源,它会向B,C,D三个系统发送请求,询问是否有人需要使用该资源。如果没有,则A访问该资源。假如D此时也想要访问该资源,则A,B,C会收到请求,而B,C即收到了A的请求,也受到了D的请求,由于A先到,所以会优先允许A访问资源。
而不同系统的请求会被放入一个队列当中,依次允许访问共享资源。
缺点:如果系统特别多,则访问一次资源所要发送的广播信息会消耗大量的资源,导致一定的浪费。而且部分节点可能不可用,导致一直处于等待,该问题的解决办法是忽略下线了的节点。
轮值 CEO:令牌环 ...
数据分区再平衡
分区再平衡采用取余的坏处如果采用取模的话,新添加节点或者删除节点,都有可能导致分区中的数据进行移动,最简单的例子,之前有10个节点,那么分区1000计算方法为1000%10 = 0,就在第0个分区,如果此时添加一个新的节点,变为了1000%11 = 10,此时就要将之前存在于下标为0的节点数据重新分配到下标为10的节点,导致性能损失。
固定分区数量改策略是指提前规定好分区有多少个,比如说提前固定分区有1000个,然后当前有10个节点,那么每个节点就存在100个分区。这样做的好处就在于,如果存在新添加的节点,那么只需要将之前节点中存在的分区划分给新节点即可,如下图所示:
这样可以避免采用取余的坏处,并不需要频繁的移动每个分区的数据。
动态分区如果之前的分区不是很合理,导致了一个分区中存在大量的数据,而其他分区几乎是空的,就可以利用到动态分区。
他的设计方式如下:提前设置一个阈值,当某一个分区达到该值之后,就会将该分区拆分为两个分区,如果某些分区数据特别少,那么就会将相邻的两个分区进行合并。该过程类似于B树的分裂或者合并。
它的一个优点是分区数量可以自适应数据的总量 ...
如何实现高性能延时消息
延时消息使用场景需要某些事件在特定的时间点上触发时,就需要用到延时消息。
如何实现
延时消息可以通过定时扫描来实现,但是资源浪费太多。
如果使用消息队列,可以理解为生产者生产一条消息,但是并不会被消费者看到,当到达固定的时间点后,消费者才能够看到并且消费消息。所以从技术上来看,消息队列实现延时消息主要包含数据存储、如何让消息可见、定时机制、主动推送四个部分。
以下主要介绍消息可见和定时机制。
如何让消息可见让消息从不可见变为可见的思路:先将数据写入到临时存储,然后根据一定机制在数据到期后让消费端可以看到该消息。
临时存储大多有以下三种选择:
单独设计的数据结构
独立的 Topic
本地的某个存储引擎(如 RocksDB、Mnesia 等)
延时到期后,消费者如何得知该消息可以消费,有以下两种实现:
定时检测写入
消费时判断是否可见
定时检测写入:指的是先将消息写入某个地方,同时有独立的线程去判断数据是否到期,如果到期则将数据写入真正的存储当中。
消费时判断数据是否可见:是指每次消费时判断是否有到期的延时消息,如果有则从第三方存储中拉取,供消费者消费。
实际上,大多采用 ...
消息队列分布式限流方案
限流分类1、单机限流
限制每一台broker的流量,如下图所示:
2、全局限流
限制所有broker的流量和,这种方案往往需要一个第三方的平台来统计目前所有Broker的总流量,如下图所示:
对什么限流主要对流量、连接数、请求数三类资源进行限流。
流量限制指对生产、消费的流量限制。
连接数限制指对客户端连接到服务端的 TCP 连接数量进行限制。因为 TCP 连接的建立和关闭需要消耗 CPU、内存等资源,限制是为了保护服务端不会因为连接数太多,耗尽资源,导致服务不可用,主要从一下三个方面:
服务端单机可承载的最大连接数限制。
客户端单个 IP 可建立的连接数。
单个集群可建立的总链接数。
请求数限制指对单个接口的访问频次进行限制,来保护集群自身的可用性。
No title
Mybatis的一些复杂写法123456789101112# 这里拿到的是key<foreach collection="columns.keys" item="key" separator="," open="(" close=")"> #{key}</foreach># 这里拿到的也是value<foreach collection="columns.keys" item="key" separator="," open="(" close=")"> #{columns[${key}]}</foreach># 这里拿到的是value<foreach collection="columns.values" item="key" ...
Java开发分布式系统的编码技巧
PageCache 调优和 Direct IO应用程序读取文件,会经过应用缓存、PageCache、DISK(硬盘)三层。
Linux内核读取到文件数据后,会把它缓存一段时间,这个文件缓存就是PageCache。它会进行适当的预读,比如用户当前只需要读取1kb的文件,但是它的算法觉得读取16k或者更多更合适,那么它就会读取16kb,加载到PageCache中,下次读取先去PageCache中查找。
但是以下三种情况没法使用PageCache:
使用 FIleChannel 读写时,底层可能走 Direct IO,不走页缓存。
在内存有限或者不够用的时候,频繁换页,导致缓存命中率低。
大量随机读的场景,导致页缓存的数据无法命中。
一种解决思路是:通过使用Direct IO 来模拟实现PageCahce的效果。原先PageCache的底层实现,是由操作系统实现的,比如说数据加载,缓存命中,换页,刷盘等,我们无法控制。我们可以通过自定义 Cache + Direct IO 来实现自己可控的操作。
FileChannel 和 mmapJava 原生的 IO 主要可以分为普通 IO、 ...
RabbitMQ的设计
下图是RabbitMQ的系统架构:
RabbitMQ 由 Producer、Broker、Consumer 三个大模块组成。
生产者将数据发送到 Broker,Broker 接收到数据后,将数据存储到对应的 Queue 里面,消费者从不同的 Queue 消费数据。它有 Exchange、Bind、Route 这几个独有的概念。
Exchange 称为交换器,它是一个逻辑上的概念,用来做分发,本身不存储数据。流程上生产者先将消息发送到 Exchange,而不是发送到数据的实际存储单元 Queue 里面。然后 Exchange 会根据一定的规则将数据分发到实际的 Queue 里面存储。
这个分发过程就是 Route(路由),设置路由规则的过程就是 Bind(绑定)。即 Exchange 会接收客户端发送过来的 route_key,然后根据不同的路由规则,将数据发送到不同的 Queue 里面。
协议和网络模块在网络通信协议层面,RabbitMQ 数据流是基于四层 TCP 协议通信的,跑在 TCP 上的应用层协议是 AMQP。
RabbitMQ 的网络层有 Connectoion 和 Cha ...
消费客户端的SDK(下)
这里介绍上篇剩下的三部分,也就是消费分组(订阅)、消费确认、消费失败处理。
消费分组消费分组是用来组织消费者、分区、消费进度关系的逻辑概念。
在没有消费分组直接消费 Topic 的场景下,如果希望不重复消费 Topic 中的数据,那么就需要有一个标识来标识当前的消费情况,比如记录进度。这个唯一标识就是消费分组。
消费分组主要有管理消费者和分区的对应关系、保存消费者的消费进度、实现消息可重复被消费三类功能。
因为 Topic 不存储真实数据,分区才存储消息数据,所以就需要解决消费者和分区的分配关系,即哪个分区被哪个消费者消费,这个分配的过程就叫做消费重平衡(Rebalance)。
由上图可以看出,当新增一个消费分组时,为了使得消费平衡,就需要重新分配消费关系。
协调者如果要对消费者和分区进行分配,肯定需要有一个模块拥有消费分组、所有的消费者、分区信息三部分信息,这个模块我们一般命名为协调者。协调者主要的工作就是执行消费重平衡,并记录消费分组的消费进度。
分区分配的操作可以在协调者内部或者消费者上完成。这两种,一种是协调者获得所有的信息,然后进行分配,分配完同步给其他的消费者。一种 ...