IT 老齐的架构 300 讲-4【通识】

2021年09月27日 21:55 · 阅读(1242) ·

来源

《IT 老齐的架构 300 讲》

课件地址:https://manongbiji.oss-cn-beijing.aliyuncs.com/ittailkshow/it300/download/ppt_all_in_one.zip

【009】为啥京东开发要禁用 IP 直连?

来源:https://www.bilibili.com/video/BV1rq4y1n7hs?spm_id_from=333.999.0.0

IP 直连有什么问题?

存在强耦合问题
线上建议用域名代替 ip 地址,因为在架构设计中需要考虑解耦问题。

场景:原代码连接数据库地址是 192.168.3.21,现在业务需要连接到另一台数据库 192.168.3.31,则可能存在代码修改,编译,部署,走流程等。
所以 ip 直连不可取。

  1. jdbc:mysql://202.99.183.21:3306/db
  2. http://192.168.31.205/webapp
  1. jdbc:mysql://rm-2zedsl7662jlm54g9zo.mysql.rds.aliyuncs.com:3306/db
  2. http://webhost/webapp

怎么解决 IP 直连的问题?

方法1:引入内部 DNS

引入内部 DNS,即建立一个域名解析服务器。

  1. jdbc:mysql://rm-2zedsl7662jlm54g9zo.mysql.rds.aliyuncs.com:3306/db
  2. http://webhost/webapp

直接访问数据库 ip 对应的域名,域名解析服务器根据配置解析该域名对应的 IP 返回。

优点:IP地址迁移变得灵活,后续直接修改域名解析服务器域名对应的 IP 地址即可。
缺点:1.没有故障发现和转移。2.一个域名绑定多个 ip,负载均衡只有轮询规则。

方法2:加入注册中心

Nacos / Eureka / Consul

弥补没有故障发现和转移。
多个数据库 IP,在注册中心进行配置,注册中心通过多种负载均衡,选取某个 IP 进行返回(跟内部 DNS 类似)

如果发现故障和故障转移。

数据库注册到注册中心,两者之间通过注册保持连续,注册相当于一个心跳包,服务器节点定时向注册中心发送信息,告知服务器正常。

若某节点异常,异常服务器会被注册中心移除。

优点:支持故障发现和转移。具有多种负载均衡策略。
缺点:架构复杂度增加。 (注册中心需要维护节点的状态,并且定时监听心跳包。一般会部署成集群,比内部 DNS 复杂,还需考虑高可用)

【019】阿里巴巴 Seata 分布式事务解决方案

来源:https://www.bilibili.com/video/BV1X3411q7bC?spm_id_from=333.999.0.0

分布式事务体系三个重要角色

● 事务管理器(TM):决定什么时候全局提交/回滚(司令官)
● 事务协调者(TC):负责通知命令的中间件 Seata-Server(传令官)
● 资源管理器(RM):做具体事儿的工具人(大头兵)

Seata AT 模式下如何实现数据自动提交、回滚

Seata 如何避免并发场景的脏读与脏写

Q: 4.怎么使用 Seata 框架,来保证事务的隔离性?

A:因 seata 一阶段本地事务已提交,为防止其他事务脏读脏写需要加强隔离。

1.脏读 select语句加 for update,代理方法增加 @GlobaILock + @Transactional@GlobalTransaction

2.脏写必须使用 @GlobalTransaction
注:如果你查询的业务的接口没有 GlobalTransactional 包裹,也就是这个方法上压根没有分布式事务的
需求,这时你可以在方法上标注 @GlobalLock + @Transactional 注解,并且在查询语句上加 for update

如果你查询的接口在事务链路上外层有 GlobalITransactional 注解,那么你查询的语句只要加 for update 就行。
设计这个注解的原因是在没有这个注解之前,需要查询分布式事务读已提交的数据,但业务本身不需要分布式事务。若使用 GlobalTransactional注解就会增加一些没用的额外的 rpc 开销比如 begin 返回 xid ,提交事务等。GlobalLock 简化了 rpc 过程,使其做到更高的性能。

【020】京东金融是如何保障接口幂等性的?

来源:https://www.bilibili.com/video/BV1J44y1k7Se?spm_id_from=333.999.0.0

幂等性是啥?
发一次接口调用与发多次相同的接口消息都能得到与预期相符的结果。

看下面的例子

  1. PUT https://xxxxx.com/employee/salary
  2. {"id" : "1,"incr_salary":500}

伪代码

  1. //查询1号员工数据
  2. Employee employee = employeeService.selectById(1);
  3. //更新工资
  4. employee.setSalary(employee.getSalary() + incrSalary);
  5. //执行更新语句
  6. employeeService.update(employee)

有什么问题?
对喽,每重发一次请求 1 号工资就会 +500,幂等性就被破坏了。

怎么解决?
传统办法是代码增加前置判断

  1. if(!员工已调薪){进行调薪}

有什么不好?
需要前置判断的地方太多了,一不留神就漏了
这种技术问题不应该成为干扰程序员写业务代码的因素。

我们需要一种无侵入的幂等解决方案
构建幂等表是我们的通用解决方案
让兄弟们专心的写 CRUD 就好啦 :)

  1. @GetMapping(“/abc/bcd”)
  2. //利用AOP After通知,更新Redis状态
  3. @Idempotent
  4. public Object 接口方法() {
  5. //处理方法
  6. }

优点:后台服务无代码侵入,无需修改业务逻辑
缺点:前台应用要针对幂等进行改造
架构复杂度增加,需要额外部署 Nginx、Redis

【024】前后端分离架构下 JWT 认证该怎么设计?

来源:https://www.bilibili.com/video/BV1C44y1k7VC/?spm_id_from=333.788.recommend_more_video.8

1.什么是 JWT?

Json Web Token(JWT)
JWT 是一个经过加密的,包含用户信息的且具有时效性的固定格式字符串

2.JWT 的认证架构设计?

PS:JWT续签的问题明天讲,表急

JWT 的创建与校验
Java 提供了 JJWT
其他语言也提供了对应的组件

方案一:网关统一校验

方案一:JWT 校验无感知,验签过程无侵入,执行效率低,适用于低并发企业级应用

方案二:应用认证方案

方案二:控制更加灵活,有一定代码侵入,代码可以灵活控制,适用于追求性能互联网应用

【025】无状态的 JWT 令牌如何实现续签功能?

来源:https://www.bilibili.com/video/BV1ov411N7iw/?spm_id_from=333.788.recommend_more_video.-1

为什么 JWT 要续签?

那 JWT 不设置过期时间行不行?
不行,会留下”太空垃圾”,后患无穷
JWT 不建议设置长时有效期
续签 JWT 必须有退出机制

1.不允许改变 Token 令牌实现续签

为什么加入 Redis 后 JWT 中过期时间可以去掉?
● 因为过期时间的被放到后端 Redis 存储,可以灵活控制
● 同时在生成 MD5 时加入环境特征,尽量避免人为盗取
● 但这也意味着 JWT 是有状态的,但也是我思考后唯一不改变前端 JWT 的续签方案了

2.允许改变 JWT 实现续签

高频问题:
问:为什么必须要两个 refresh_token?为什么不直接设置 token 一个小时过期,判断还有 10 分钟过期的时候,生成新的 token 进行替换?
答:这两个 token 的职责不一样:
access_token 用于业务系统交互,是最核心的数据。
refresh_token 只用于向认证中心获取新的 access_token 与 refresh_token。
refresh_token 的出现本质解决了在用户超过 30 分钟后,access_token 已经失效,此时 access_token 被送给认证中心是无法解析的,而 refresh_token 因为生存时间更长,且主体内容与 access_token 一致,因此被送达认证中心后可以被正确解析,进而重新生成新的 access_token 与 refresh_token。

客户端需要大量针对 JWT 续签的改造工作

仍然会遇到的问题?
● 临界时间 59分59秒,用户提交数据,到 60 分正好 refresh_token 过期,也会导致执行失败。
● 这属于小概率事件,在过往的 30 分时间里面任何一次请求都会重新生成 jwt,剩余时间重新计算

3.续约时的重发 JWT 问题解决

存在重复生成 JWT 的问题

解决办法:
● 认证中心设计一个计时 Map 数据结构
● 只记录过去n秒内的原始 jwt 刷新所生成新 jwt 数据
● 几秒内如果发现同样的 jwt 在再次请求刷新,就返回相同的新 jwt 数据。

【057】这可能是最简单粗暴的 Raft 选举算法讲解了吧!

来源:https://www.bilibili.com/video/BV13Q4y167Hy?spm_id_from=333.999.0.0

Raft 选举的用途

Raft 算法是分布式系统开发首选的共识算法。
主要在分布式集群架构下进行领导者(主节点)的确认。
比如现在流行的组件 Etcd、Consul、Nacos、RocketMQ、Redis Sentinel 底层都是采用 Raft 算法来确 认集群中的主节点,再通过主节点向其他节点下发指令。

如果掌握了这个算法,就可以较容易地处理绝大部分场景的容错和一致性需求。比如分布式配置系统、分布式 NoSQL 存储等等,轻松突破系统的单机限制。

Raft 算法是通过一切以领导者为准的方式,实现一系列值的共识和各节点日志的一致。

Raft 角色

角色 描述
跟随者(Follower) 普通群众,默默接收和来自领导者的消息,当领导者心跳信息超时的时候,就主动站出来,推荐自己当候选人。
候选人(Candidate) 候选人将向其他节点请求投票 RPC 消息,通知其他节点来投票,如果赢得了大多数投票选票,就晋升当领导者。
领导者(Leader) 霸道总裁,一切以我为准。
处理写请求、管理日志复制和不断地发送心跳信息,通知其他节点“我是领导者,我还活着,你们不要”发起新的选举,不用找新领导来替代我

如下图所示,分别用三种图代表跟随者、候选人和领导者。

Raft 选举过程

1.初始状态

初始状态下,集群中所有节点都是跟随者的状态。

如下图所示,有三个节点(Node) a、b、c,任期(Term)都为 0。

Raft 算法实现了随机超时时间的特性,每个节点等待领导者节点心跳信息的超时时间间隔是随机的。

比如 A 节点等待超时的时间间隔 150 ms,B 节点 200 ms,C 节点 300 ms。
那么 a 先超时,最先因为没有等到领导者的心跳信息,发生超时。
如下图所示,三个节点的超时计时器开始运行。

2.发起投票

当 A 节点的超时时间到了后,A 节点成为候选者,并增加自己的任期编号,Term 值从 0 更新为 1,并给自己投了一票。
● Node A:Term = 1, Vote Count = 1。
● Node B:Term = 0。
● Node C:Term = 0。

3.成为领导者的简化过程

我们来看下候选者如何成为领导者的。

1.节点 A 成为候选者后,向其他节点发送请求投票 RPC 信息,请它们选举自己为领导者。
2.节点 B 和 节点 C 接收到节点 A 发送的请求投票信息后,在编号为 1 的这届任期内,还没有进行过投票,就把选票投给节点 A,并增加自己的任期编号。
3.节点 A 收到 3 次投票,得到了大多数节点(n/2+1)的投票,从候选者成为本届任期内的新的领导者。 4.节点 A 作为领导者,固定的时间间隔给节点 B 和节点 C 发送心跳信息,告诉节点 B 和 C,我是领导者,组织其他跟随者发起新的选举。
5.节点 B 和节点 C 发送响应信息给节点 A,告诉节点 A 我是正常的。

4.领导者的任期

英文单词是 term,领导者是有任期的。

● 自动增加:跟随者在等待领导者心跳信息超时后,推荐自己为候选人,会增加自己的任期号,如上图所示,节点 A 任期为 0,推举自己为候选人时,任期编号增加为 1。

● 更新为较大值:当节点发现自己的任期编号比其他节点小时,会更新到较大的编号值。比如节点 A 的任期为 1,请求投票,投票消息中包含了节点 A 的任期编号, 且编号为 1,节点 B 收到消息后,会将自己的任期编号更新为 1。

● 恢复为跟随者:如果一个候选人或者领导者,发现自己的任期编号比其他节点小,那么它会立即恢复成跟随者状态。这种场景出现在分区错误恢复后,任期为 3 的 领导者受到任期编号为 4 的心跳消息,那么前者将立即恢复成跟随者状态。

● 拒绝消息:如果一个节点接收到较小的任期编号值的请求,那么它会直接拒绝这个请求,比如任期编号为 6 的节点 A,收到任期编号为 5 的节点 B 的请求投票 RPC 消息,那么节点 A 会拒绝这个消息。

● 一个任期内,领导者一直都会领导者,直到自身出现问题(如宕机),或者网络问题(延迟),其他节点发起一轮新的选举。

5.防止多个节点同时发起投票

为了防止多个节点同时发起投票,会给每个节点分配一个随机的选举超时时间。这个时间内,节点不能成为候选者,只能等到超时。比如上述例子,节点 A 先超时,先成为了候选者。这种巧妙的设计,在大多数情况下只有一个服务器节点先发起选举,而不是同时发起选举,减少了因选票瓜分导致选举失败的情况

6.触发新的一轮选举

如果领导者节点出现故障,则会触发新的一轮选举。如下图所示,领导者节点 A 发生故 障,节点 B 和 节点 C 就会重新选举 Leader。

1.节点 A 发生故障,节点 B 和节点 C 没有收到领导者节点 A 的心跳信息,等待超时。
2.节点 C (175ms) 先发生超时,节点 C 成为候选人。
3.节点 C 向节点 A 和 节点 B 发起请求投票信息。
4.节点 C 响应投票,将票投给了 C,而节点 A 因为发生故障了,无法响应 C 的投票请求。
5.节点 C 收到两票(大多数票数),成为领导者。
6.节点 C 向节点 A 和 B 发送心跳信息,节点 B 响应心跳信息,节点 A 不响应心跳信息。
7.节点 A 恢复后,收到节点 C 的高任期消息,自身将为跟随者,接收节点 C 的消息。

总结:Raft 算法的几个关键机制

Raft 算法通过以下几个关键机制,保证了一个任期只有一位领导,极大减少了选举失败的情况。

● 任期机制
● 领导者心跳信息
● 随机选举超时时间
● 先来先服务的投票原则
● 大多数选票原则

【071】哎,如果我这么讲 Paxos 选举过程你还听不懂,那我就停更吧!

来源:https://www.bilibili.com/video/BV1xR4y1E772?spm_id_from=333.999.0.0

【058】利用 Zookeeper 解决分布式系统商品库存超卖问题

来源:https://www.bilibili.com/video/BV1Lf4y1c7TP?spm_id_from=333.999.0.0

单线程扣减库存逻辑

并发环境扣减库存会出现问题

解决方案:
1.传统的 synchronized 是无效的,它只针对一个 JVM 进程内多个线程起到同步作用,对跨进程无效。
2.利用数据库 select ... for update 语句对库存进行锁定,依赖数据库自身特性,遇到跨库 (分库分表)处理起来比较麻烦。
3.利用 Zookeeper、Redis 实现分布式锁特性,通过分布式锁调度进程处理,数据程序级别控制,处理更为灵活。

“锁”带来的问题

无论是数据库排它锁,还是 ZK、Redis 的分布式锁都属于“悲观锁”的范畴,虽然以阻塞的方式保证数据的一致性,但并发量也会直线下降,这是要付出的代价。适用分布式锁有以下几个场景:

● 数据价值大,必须要保证一致性的。例如:金融业务系统间的转账汇款等。
● 并发量低但重要的业务系统。比如:各种大宗商品的分布式交易

总结下:重要的但对并发要求不高的系统可以使用分布式锁,对于并发量高、数据价值小、对一致性要求没那么高的系统可以进行最终一致性(BASE)处理,保证并发的前提下通过重试、程序矫正、人工补录的方式进行处理。

什么是 Zookeeper?

Zookeeper(业界简称 zk)是一种提供配置管理、分布式协同以及命名的中心化服务,这 些提供的功能都是分布式系统中非常底层且必不可少的基本功能,但是如果自己实现这些功能而且要达到高吞吐、低延迟同时还要保持一致性和可用性,实际上非常困难。因此 zookeeper 提供了这些功能,开发者在 zookeeper 之上构建自己的各种分布式系统。

Zookeeper 分布式锁的实现原理

Zookeeper 的数据存储结构就像一棵树,这棵树由节点组成,这种节点叫做 Znode。

Znode 分为四种类型

类型 描述
持久节点 (PERSISTENT) 默认的节点类型。
创建节点的客户端与 zookeeper 断开连接后,该节点依旧存在 。
持久节点顺序节点(PERSISTENT_SEQUENTIAL) 所谓顺序节点,就是在创建节点时,Zookeeper 根据创建的时间顺序给该节点名称进行编号
临时节点(EPHEMERAL) 和持久节点相反,当创建节点的客户端与 zookeeper 断开连接后,临时节点会被删除
临时顺序节点(EPHEMERAL_SEQUENTIAL) 顾名思义,临时顺序节点结合和临时节点和顺序节点的特点:
在创建节点时,Zookeeper 根据创建的时间顺序给该节点名称进行编号;
当创建节点的客户端与 zookeeper 断开连接后,临时节点会被删除。

为什么是临时顺序节点?

利用临时顺序节点可以避免“惊群效应”,当某一个客户端释放锁以后,其他客户端不会一 窝蜂的涌入争抢锁资源,而是按时间顺序一个一个来获取锁进行处理。

临时顺序节点的实现原理

● 每一个客户端在尝试获取锁时都自动由ZK创建该顺序节点的“子节点”,按 0001、0002 这样的编号标识访问顺序
● 从第二个客户端开始,ZK 不但创建 0002 子节点,还会监听前一个 0001 节点。当客户端 1 处理完毕或者其他原因释放 0001 节点后,ZK 会通过 Watch 监听机制通知客户端 2 进行后续处理,以此保证处理的有序性,避免“惊群效应”产生。

【059】库存超卖代码实战,基于 Apache Curator 实现 Zookeeper 分布式锁

来源:https://www.bilibili.com/video/BV1Hh411J76n?spm_id_from=333.999.0.0

安装部署 Zookeeper

CentOS 手动安装部署

  1. # Zookeeper依赖JVM运行,先安装JDK
  2. yum y install java1.8.0openjdk
  3. cd /usr/local
  4. # 下载Zookeeper Tar
  5. wget http://dlcdn.apache.org/zookeeper/zookeeper‐3.7.0/apache‐zookeeper‐ 3.7.0‐bin.tar.gz
  6. tar zxvf apachezookeeper3.7.0bin.tar.gz
  7. cd ./apachezookeeper3.7.0bin/
  8. cd ./conf/
  9. # 按实例文件复制重命名到 zoo.cfg
  10. cp zoo_sample.cfg zoo.cfg 11 cd ../bin
  11. # 启动Zookeeper
  12. ./zkServer.sh start
  13. # 防火墙放行 2181 是Zookeeper服务端口 8080是Web命令端口(可选)
  14. firewallcmd ‐‐zone=public ‐‐addport=2181/tcp ‐‐permanent
  15. firewallcmd ‐‐zone=public ‐‐addport=8080/tcp ‐‐permanent
  16. # 重载防火墙
  17. firewallcmd ‐‐reload 19

访问下面的 URL 看到状态说明安装成功

http://192.168.31.103:8080/commands/stat

Docker 安装 Zookeeper

  1. docker run --privileged=true -d --name zookeeper -p 2181:2181 -p 8080:8080 -d zookeeper:latest
  2. firewall-cmd --zone=public --add-port=2181/tcp --permanent
  3. firewall-cmd --zone=public --add-port=8080/tcp --permanent
  4. firewall-cmd --reload

IDEA Zookeeper 插件安装

插件面板安装 Zoolytic

安装成功,访问 view - tools windows - zoolytic

右边栏出现 zoolytic,点 “+” 输入 192.168.31.103:2181 ,完成安装过程。

效果

开发应用程序

Apache Curator(https://curator.apache.org/)是一个比较完善的 ZooKeeper 客户端框架,通过封装的一套高级 API 简化了 ZooKeeper 的操作。通过查看官方文档,可以发现 Curator 主要解决 了三类问题:

● 封装 ZooKeeper client 与 ZooKeeper server之 间的连接处理
● 提供了一套 Fluent 风格的操作A PI
● 提供 ZooKeeper 各种应用场景(recipe, 比如:分布式锁服务、集群领导选举、共享计数器、缓存机制、分布式队列等)的抽象封装

代码下载:zlock.zip

1.新建 SpringBoot 工程,引入依赖

curator-recipes:封装了一些高级特性,如:Cache 事件监听、选举、分布式锁、分布式计数器、分布式 Barrier 等

  1. <dependency>
  2. <groupId>org.apache.curator</groupId>
  3. <artifactId>curator-recipes</artifactId>
  4. <version>5.2.0</version>
  5. </dependency>

2.开发 Service,引入 ZK 分布式锁

  1. @Service
  2. public class WarehouseService {
  3. public static int shoe = 10;
  4. public int outOfWarehouseWithLock() throws Exception {
  5. //设置ZK客户端重试策略, 每隔5秒重试一次,最多重试10次
  6. RetryPolicy policy = new ExponentialBackoffRetry(5000, 10);
  7. //创建ZK客户端,连接到Zookeeper服务器
  8. CuratorFramework client = CuratorFrameworkFactory.builder().connectString("192.168.31.103:2181").retryPolicy(policy).build();
  9. //创建与Zookeeper服务器的连接
  10. client.start();
  11. //声明锁对象,本质就是ZK顺序临时节点
  12. final InterProcessMutex mutex = new InterProcessMutex(client, "/locks/wh-shoe");
  13. try {
  14. //请求锁,创建锁
  15. mutex.acquire();
  16. //处理业务逻辑
  17. if (WarehouseService.shoe > 0) {
  18. Thread.sleep(1000);
  19. //扣减库存
  20. return --WarehouseService.shoe;
  21. } else {
  22. //库存不足,
  23. throw new RuntimeException("运动鞋库存不足");
  24. }
  25. } finally {
  26. //释放锁
  27. mutex.release();
  28. }
  29. }
  30. public int outOfWarehouse() throws Exception {
  31. //处理业务逻辑
  32. if (WarehouseService.shoe > 0) {
  33. Thread.sleep(1000);
  34. //扣减库存
  35. return --WarehouseService.shoe;
  36. } else {
  37. throw new RuntimeException("运动鞋库存不足");
  38. }
  39. }
  40. }

3.开发控制器,对外暴露服务

  1. @RestController
  2. public class OrderController {
  3. @Resource
  4. private WarehouseService warehouseService;
  5. @GetMapping("/create_order")
  6. public String createOrder(String name){
  7. try {
  8. //创建订单
  9. int i = warehouseService.outOfWarehouseWithLock();
  10. System.out.println("[" + Thread.currentThread().getName() + "]商品出库成功,剩余库存:" + i);
  11. return "{\"code\":\"0\"}";
  12. } catch (Exception e) {
  13. //e.printStackTrace();
  14. System.out.println("[" + Thread.currentThread().getName() + "]商品出库失败,异常信息:" + e.getMessage());
  15. return "{\"code\":\"500\"}";
  16. }
  17. }
  18. @GetMapping("/reset")
  19. public String reset(){
  20. WarehouseService.shoe = 10;
  21. return "success";
  22. }
  23. }

4.执行结果

运行程序

使用 Apache JMeter 开启 10 个线程进行测试,结果如下

ZooKeeper 结果

【063】大型电商整点秒杀业务场景下,商品库存如何预防超卖现象产生

来源:https://www.bilibili.com/video/BV1Qv411u7K7?spm_id_from=333.999.0.0

分析库存超卖的产生

正常情况

产生超卖的情况

秒杀场景的分析

秒杀商品库存总量固定
先到先得,瞬间并发极大,但写库量有限

高并发下预防超卖的解决方案

1.利用预减库存方式杜绝超卖
2.利用 Nginx+Lua 在网关层面将无效请求阻挡
3.利用 MQ 消息队列的限流特性保证 MySQL 不会被瞬间击垮
4.APP 需要额外设计轮询机制查询订单状态

TIPS:
订单创建后,用户取消订单或未支付怎么办?
为订单设置过期时间,订单支付有效期过期或用户取消订单
LUA 执行 incr “stk-1001” 自增即可

【061】大白话聊聊 BASE 最终一致性

来源:https://www.bilibili.com/video/BV1N3411k7ba?spm_id_from=333.999.0.0

相关内容:【010】什么是 CAP 定理

什么是 BASE 最终一致性

什么是 CAP 定理
分布式架构的基本理论。
指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。

CP 表现为订单创建后一直等待库存减少后才返回结果

XA 方案符合 CP 的设计
适用于数据一致性要求高、低并发的场景
表现为短信没发成功,用户一直处于阻塞等待

AP 表现为订单创建后无需等待短信是否发送
直接将订单创建成功结果返回
AP 可以保证用户体验

那短信数据就不管了?
BASE(最终一致性)设计
就排上用场了

Basically Available(基本可用)
Soft state(软状态)
Eventually consistent(最终一致性)

基本可用就是快速实现
用户的基本价值与诉求
“创建订单”后立即返回就是基本可用的体现

软状态代表了在业务操作
没有最终完成前的中间状态
在订单创建后,短信记录未发送成功前就属于软状态

最终一致性代表通过技术手段
过一段时间后让数据保持完整的状态

保障 BASE 常见措施有哪些

重试

数据校对程序

人工介入

【065】十分钟上手阿里巴巴分布式流控神器 Alibaba Sentinel

来源:https://www.bilibili.com/video/BV1Gh41187FN?spm_id_from=333.999.0.0

开发环境

名称 版本
操作系统 Windows 10 X64
JDK 1.8(jdk-8u151-windows-x64)
IntelliJ IDEA ULTIMATE 2021.2.1
Maven 3.6.0
alibaba Sentinel 1.8.2

为什么需要做流量控制

很多情况会遇到高并发流量进入系统,如果全部进入到对应系统,可能会把系统给冲垮,为了保证系统正常运行,所以需要做流量控制。

Alibaba Sentinel 部署与接入

什么是 Alibaba Sentinel

在 Spring Cloud Aliblaba 生态中有一个重要的流控组件 Sentinel, Sentinel 以流量为切入点,从流
量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Github: https://github.com/alibaba/Sentinel
中文官网: https://sentinelguard.io/zh-cn/

部署 Alibaba Sentinel

下载地址:https://github.com/alibaba/Sentinel/releases

下载最新版的 sentinel-dashboard-1.8.2.jar,执行下面的命令

  1. java -jar -Dserver.port=9100 sentinel-dashboard-1.8.2.jar

启动成功,浏览器访问 http://localhost:9100/

用户名:sentinel
密码:sentinel

进入管理页面

接入 Alibaba Sentinel

IDEA 创建一个新项目,服务器 URL 使用:http://start.aliyun.com

这里选择两个依赖项

  1. Spring Web
  2. Spring Cloud Alibaba Sentinel

创建了项目

pom.xml 中已经引用了 Sentinel

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  4. </dependency>

修改 application.properties

  1. # Sentinel 控制台地址
  2. spring.cloud.sentinel.transport.dashboard=localhost:9100

新建一个 Controller SentinelSampleController

内容如下

  1. package com.example.test_flow.controller;
  2. import org.springframework.web.bind.annotation.GetMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. @RestController
  5. public class SentinelSampleController {
  6. @GetMapping("/test_flow_rule")
  7. public String testFlowRule(){
  8. return "SUCCESS";
  9. }
  10. }

运行程序,访问 http://localhost:8080/test_flow_rule

假设这个接口是一个重要的支付接口,每秒钟只支持一个请求进来,现在要对它进行限流,怎么配置?

回到 Sentinel 控制台 http://localhost:9100

点击刷新,可以看到接口已经被接入了

找到簇点链路-/test_flow_rule-流控

回到对应接口,1 秒内连续刷新

有时候出现 SUCCESS,有时候出现 Blocked by Sentinel (flow limiting),可以看到限流成功

Sentinel 拦截执行原理

Alibaba Sentinel 流控的实现原理
Sentinel Core 为服务限流、熔断提供了核心拦截器 SentinelWebInterceptor
这个拦截器默认对所有请求 /** 进行拦截,然后开始请求的链式处理流程
在对于每一个处理请求的节点被称为 Slot (槽),
通过多个槽的连接形成处理链,在请求的流转过程中,如果有任何一个 Slot 验证未通过,都会产生BlockException,请求处理链便会中断,并返回 “Blocked by sentinel” 异常信息。

那这些 Slot 都有什么作用呢?我们需要了解一下,默认 Slot 有 7 个,前 3 个 Slot 为前置处理,用于收集、统计、分析必要的数据;后 4 个为规则校验 Slot,从 Dashboard 推送的新规则保存在 “规则池”中,然后对应 Slot 进行读取并校验当前请求是否允许放行,允许放行则送入下一个 Slot 直到最终被 RestController 进行业务处理,不允许放行则直接抛出 BlockException 返回响应。

以下是每一个 Slot 的具体职责:

Slot 名称 具体职责
NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,
以树状结构存储起来,用于根据调用路径来限流降级。
ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,
例如该资源的 RT(运行时间), QPS, thread count (线程总数)等等,
这些信息将用作为多维度限流,降级的依据。
StatistcSlot 则用于记录,统计不同维度的 runtime 信息。
SystemSlot 则通过系统的状态,例如 CPU、内存的情况,来控制总的入口流量。
AuthoritySlot 则根据黑白名单,来做黑白名单控制。
FlowSlot 则用于根据预设的限流规则,以及前面 slot 统计的状态,来进行限流。
DegradeSlot 则通过统计信息,以及预设的规则,来做熔断降级。

到这里我们理解了 Sentinel 通信与降级背后的执行过程,下面咱们学习如何有效配置 Sentinel 的限流策略。

【066】分布式雪崩效应是怎么回事?如何进行熔断保护

来源:https://www.bilibili.com/video/BV18L411g7rM?spm_id_from=333.999.0.0

为什么分布式架构必须预防雪崩

服务熔断到底是怎样的机制

分布式架构下需要一种机制来保证当下游系统处理超过一段时间后,无论处理成功、失败,当前的调用必须立即中断,来保证线程不会产生长时间积压,减少雪崩出现的可能。

分布式架构设计中借鉴了股市的“熔断”机制,为分布式应用提供了系统调用的“保险丝”。

2016 年的第一个交易日是中国交易史上的一个里程碑,从这天开始,a 股交易实施了一股指融合机制。 按照相关规定,当沪深 300 指数达到 5% 熔断阈值的时候,三家交易所暂停交易 15 分钟,如果在尾盘阶段或全天任何时间触发 7% 的时候,它们将暂停交易,直到收市。

服务熔断

微服务的熔断是指在某个服务接口在执行过程中频繁出现故障的情况,我们便认为这种状态是“不可接受”的,立即对当前接口实施熔断。在规定的时间内,所有送达该接口的请求都将直接抛出 BlockException,在熔断期过后新的请求进入看接口是否恢复正常,恢复正常则继续运行,仍出现故障则再次熔断一段时间, 以此往复直到服务接口恢复。

Alibaba Sentinel 支持哪些熔断模式

回到 Sentinel 控制台 http://localhost:9100

找到簇点链路-/test_flow_rule-熔断

统计时长 1 秒内,发生最少 2 个请求,50% 的几率响应时间大于 1 毫秒,触发熔断机制
熔断时间 2 秒

慢调用比例

触发熔断机制:当前秒,有超过50% (比例阈值)的请求超过 1ms (最大RT) ,如:3 次里面有2次(75%)
超过 1ms,触发熔断,之后 5 秒钟(熔断时长)所有请求被 BLOCKED,5 秒后半开状态检查
●异常比例是指 1 秒内按接口调用产生异常的比例(异常调用数/总数量)触发熔断。

异常比例

开启条件: 1 秒钟内,有 1 个(最小请求数) list 请求,便开启熔断检查
触发熔断:当前秒,有超过 50% (比例阈值)的请求产生异常触发熔断,之后 5 秒钟(熔断时长)所
有请求被 BLOCKED, 5 秒后半开状态检查
●异常数是指在 1 分钟内异常的数量超过阈值则触发熔断。

异常数

开启条件:当前秒有 1 个 list 接口请求,便开启熔断检查
触发熔断: 1 分钟内 list 接口调用异常数大于 100 次触发熔断,之后 80 秒所有请求被 BLOCKED,80 秒后半开状态检查。

【072】科普向,全文检索执行原理,解释分词与倒排索引的作用

来源:https://www.bilibili.com/video/BV13T4y197Wv?spm_id_from=333.999.0.0

全文检索引擎就是对非结构化文本进行解析、搜索的技术。
非结构化文本的处理关键在于分词倒排索引

分词是指将一段文本中有用的词汇提取出来
原文:nice to meet you
英文按空格分词: nice | to | meet | you

中文分词“bo大jing深”一直是分词领域的难点
比如:中华人民共和国
按语义分词为:中华 | 华人 | 人民 | 共和 | 共和国 …
而下面的就不应被分词:民共 | 华人民

常见的中文分词算法:

中文分词算法 描述
Ngram穷举 n=2 中华|华人|人民|民共|共和|和国
语法分析 + 字典 按中文动名词分析推测外加分词字典维护
爬虫 + 大数据 + AI分析 根据语义分析(NLP)、词频、上下文推测筛选

产生分词后便会形成正向索引
文本与分词的对应关系

原文 分词
id1=中华人民共和国,简称中国 中华、人民、华人、共和、中国
id:2=清朝华人在外国 清朝、华人、外国…
id3=海外华人思念中国的家乡 海外、华人、思念、中国、家乡…

倒排索引是反向将分词与文本的对应
用户搜索“清朝”,根据倒排索引会快速找到id=2的原文

分词 原文
中华 id=[1]
人民 id=[1]
华人 id=[1,2,3]
共和 id=[1]
中国 id=[1,3]
清朝 id=[2]
.

多分词复杂情况要通过算分 Score 决定结果与排序前后
用户搜索“清朝华人买办”,包含多个分词
全文检索引擎要根据相似度算法(TF-IDF 和 BM25)进行算分
按分数从高到低进行排序

分词 原文
中华 id=[1]
人民 id=[1]
华人 id=[1,2,3]
共和 id=[1]
中国 id=[1,3]
清朝 id=[2]
海外 id=[3]
.
结果
id:2=清朝华人在外国 0.77
id3=海外华人思念中国的家乡 0.44
id1=中华人民共和国,简称中国 0.28