Dawn's Blogs

分享技术 记录成长

0%

索引

OLTP 数据库存储引擎的核心在于索引,所以介绍几种索引机制。

哈希索引

假设我们的数据存储只是一个追加写入的文件(日志),最简单的索引策略就是:在内存中维护一个 hash map,记录键和文件偏移量的映射。

img

文件分段与压缩

为了防止日志文件无限制的增长,可以采取文件分段压缩的策略。

  • 将日志分为特定大小的段,当日志增长到特定大小时就关闭,开始写入一个新的段文件。
  • 将段进行压缩,只保留每一个键最新的值。同样的,也可以把多个段的内容压缩合并到一起。

每一个段都有自己的 hash map,将键映射到文件偏移量中。在查询时,从后向前检索每一个段的 hash map 即可。

参考 Redis AOF 压缩的思路,在进行压缩时:

  • 首先记录位置,开启一个后台线程开始压缩位置之前的数据。
  • 正常写入 AOF 文件,后台线程将压缩之后的数据写入一个新的 AOF 文件中。
  • 暂停写入,将开始压缩后写入的数据追加到新文件中,用新的 AOF 文件替换旧的 AOF 文件。

崩溃恢复与并发控制

崩溃恢复

数据库可能在追加写入文件时崩溃,所以需要包含校验和用于检测和忽略日志的损坏部分。

在数据库重新启动时,内存中的 hash map 将丢失。原则上需要重新顺序读取段文件来重新构建 hash map,但是如果段文件非常大这是十分耗时的。所以可以通过在磁盘上保存 hash map 的快照,用于快速加载到内存中。

并发控制

由于写操作是以严格顺序的顺序附加到日志中的,所以常见的实现选择是只有一个写线程,但是可以有多个线程同时读取

局限性

哈希索引也有两个局限性:

  • 哈希表必须全部放入内存。原则上可以在磁盘上维护哈希表,但是这样会有大量的随机 I/O,性能表现很难优秀。
  • 范围查询效率不高。范围查询必须顺序查找每一个键。

LSM 树

SSTable

之前文件中的键值顺序为写入顺序,现在做出一个改变:键值对的顺序按照键进行排序,把这个格式称为排序字符串表(Sorted String Table,SSTable)。还要求每个键只在每个合并的段文件中出现一次(压缩过程已经保证)。

优势

SSTable 相比于哈希索引顺序写入的日志文件相比,有以下几个优势:

  • 合并段是简单且高效的,即使文件大于可用内存。类似于归并算法,每一次合并只取几个段中的最小键值(遇到相同的键时只需要最新的,其他旧段中的丢弃)。
  • 不需要保存所有键的索引,因为键在文件中的存储是有序的,所以部分索引就可以知道键值对所在的区间。
  • 索引区间内的数据,在保存到磁盘之前可以进行压缩,这样节省了磁盘空间。
阅读全文 »

常见数据模型

关系模型

关系模型是最常见,也是最著名的数据模型,SQL 就是基于关系模型的。

ORM 映射

关系型数据模型和编程语言对象之间是不匹配的,需要一个转换层,即对象关系映射(ORM)。

1
2
3
数据库中的一个表 <---> 一个类
表中的一条数据 <---> 类的一个对象
表中的一个列 <---> 类中的一个字段(field)

一对多的关系

在个人简历中,在工作经历一栏可能会有多个工作经历,并且可能有多段教育经历等一对多的关系。

这样的一对多关系,关系模型可以采用多种方式表示:

  • 在关系模型中,可以将工作经历、教育经历等放在单独的数据表中,并且使用外键引用。
  • 如果关系型数据库支持结构化的 Json(MySQL、PostgreSQL)或者 XML(Oracle、SQL Server、PostgreSQL)数据格式,可以将这些多值数据存储在一个属性内,并且支持查询和索引。
  • 将工作经历、教育经历编码为 Json 或者 XML 格式,存储在关系型数据库的文本列中,由应用程序解释其结构和内容。

文档模型

使用文档型数据库,就可以天然的解决关系模型中的一对多问题。并且文档型数据库在读取一对多关系的数据上,具有更好的局部性。如果在关系模型中读取一份简历,要么执行多个查询,要么进行多表联结查询。而在文档型数据库中,所有的信息都在一个地方,一次查询就已经足够了。

但是,文档型数据库通常不支持数据联结操作,只能在应用层通过多次查询来模拟联结。

多对一和多对多的关系

在表示多对一多对多的关系时,关系数据库和文档数据库的思想并没有根本的不同:相关项都由唯一的标识符引用

  • 关系模型中,这个唯一标识符为外键
  • 文档模型中,被称为文档引用

如果存在多对多关系,那么文档模型就不太适用了。

  • 可以通过反范式化来减少联结,但是需要在应用层做出额外的工作去保证数据的一致性
  • 也可以在应用层执行多次查询来模拟联结,但是这通常比数据库内的专用代码慢,使得文档型数据库应用程序代码复、性能差
阅读全文 »

高并发读

缓存

在高并发读的场景下,首先想到的就是加缓存

  • 本地缓存或者 Redis/Memcached 集中式缓存。在缓存时,注意缓存的一致性、以及缓存雪崩缓存击穿等问题。
  • MySQL 的 Master/Slave。如果查询是多张表的联合查询结果,而不是 kv 对(当然也可以用 kv 来缓存,但缺点就是任何一张数据库表变化了,缓存都需要更新),可以采用 MySQL 的 Master/Slave 集群,通过 Slave 来分担读压力
  • CDN。对于静态文件,如图片、HTML、CSS、JS 等文件,可以采用 CDN 内容分发网络,CDN 的本质就是在传输链路上做就近缓存

并发读

将串行操作改为并行操作是常用的优化策略。

  • 异步 RPC。当查询的内容没有依赖关系时,可以采用异步 RPC 来减少查询时间。
  • 冗余请求。如果在集群中,每一个节点都可能以很小的概率出现调用延迟,可以对同一种请求发出多个冗余请求,取出最快的响应作为查询结果。

重写轻读

在查询数据时需要聚合数据的场景下,可以采用重写轻读的方式进行优化。重写轻读的思路,就是将聚合数据的操作,提前到写入数据时完成,查询的是已经聚合过的数据

推送 拉取

比如某个用户需要获取他关注的人发的微博,可以分为两种获取方式:

  • 推送:在用户发微博时,写入自己的数据库就成功返回。然后利用后台程序,把这条微博主动推送给所有粉丝的收件箱(可以采用 Redis 实现,收件箱可以是一个 list,记录被推送的微博)中。
  • 拉取:如果用户粉丝数量很大,那么推送给全部的粉丝这一操作本身就是十分耗时的。所以在查询时还可以主动的从数据库中拉取数据

在实际应用中,可以采用推拉结合的方式。

对于粉丝数量少的用户,可以采用推送的方式。对于粉丝数量多的用户,只推送给在线粉丝离线粉丝采用主动拉取的方式。

对于读取的一方,有的是推送给他的、有的是主动拉取的,返回查询结果时,需要把二者再聚合起来。

总结 - CQRS

上述的方法本质上都是读写分离,也就是 CQRS(Command Query Responsibility Separation)。CQRS 的特点如下:

  • 读写两侧可以采用不同的数据结构:在写入的一侧通常采用 MySQL 这类关系型数据库,可以采用分库分表缓解读压力。而在读的这一侧则根据业务需求选择合适的数据库。
  • 读和写的串联:可以在读写之间加入消息队列,来保证读写的最终一致性

image-20230629151531310

阅读全文 »

下面主要介绍 MySQL 中的一些实现,包括 Redo Log、Undo Log。

Redo Log

一个事务如果要修改一张表中的多条记录,记录都分布在不同的 Page 中。如果事务每一次都直接写磁盘,相当于随机 I/O,性能非常差。

所以先写日志,再把内存中的数据异步的刷到磁盘中。日志是顺序追加的,所以写日志是顺序 I/O,所以性能相比于随机 I/O 有很大的提升。

异步刷盘策略

在 MySQL InnoDB 中,不光数据写入到磁盘是异步的,而且从内存写入到 Redo Log 也是异步的。MySQL innodb_flush_log_at_trx_commit 参数可以控制 Redo Log 的刷盘策略,分别是:

  • 0:每秒钟刷一次盘(默认)。
  • 1:每次提交事务都会将内存中的数据刷到 Redo Log 中。
  • 2:不刷盘,根据 innodb_flush_log_at_timeout 决定刷盘频率。

1 是最安全的,却也是性能最低的。InnoDB 选择 0 作为默认值,就是安全性和性能之间做出一个平衡。

物理结构

因为磁盘是按块设备,所以 MySQL 日志也是按照块(Log Block)去读写的。每一个 Log Block 都是固定大小,即 512 字节。

而且因为日志可以看作是无限增长的文件,所以在具体实现时,Redo Log 是固定大小的(可以看作是一个循环队列,后面的块会覆盖最前面的块)。

Physiological Logging

在 Log Block 中的日志采用何种方式去存储呢?这也是一个问题。

MySQL 中采用的是物理和逻辑相结合的方式。以 Page 为单位记录日志,每一个日志里面再采取逻辑记法(记录 Page 中的哪一行被修改了),这种叫做 Physiological Logging

纯逻辑和物理记法的缺点

  • 纯逻辑记法:就是直接将 SQL 语句记录在日志中。而需要修改的记录可能被分散在多个 Page 中,如果涉及到多个 Page 的修改,修改到一半系统宕机。那么恢复的时候很难知道哪个 Page 写成功了,哪个写失败了
  • 纯物理记法:就是记录每一个 Page 在哪一个字节被修改了什么。因为 InnoDB 中采用 B+ 树存储数据,如果增加一条记录就需要修改前后指针,会产生多条物理日志
阅读全文 »

HTTP

HTTP 协议主要有三个版本,分别是 1996 年发布的 HTTP 1.0、1999 年发布的 HTTP 1.1 以及 2015 年发布的 HTTP 2。

HTTP 1.0

在 HTTP 1.0 中,默认发出一个请求就建立一个 TCP 连接。但是可以使用 Keep-Alive 和 Content-Length 来实现持久连接

  • Keep-Alive:用于声明这个字段是持久连接,多个 HTTP 请求都在同一个 TCP 连接上。
  • Content-Length:之前服务器响应完成就会关闭连接,而在持久连接中客户端不知道服务器的数据是否发送完成,所以需要 Content-Length 字段来告知数据长度

但是 Content-Length 字段有一个缺点,如果服务器使用了压缩,那么在生成 Header 时并不知道压缩之后的长度,所以无法生成 Content-Length。

所以在 HTTP 1.0 中,压缩和持久连接是冲突的。

HTTP 1.1

分块传输编码 chunk

在 HTTP 1.1 中,Keep-Alive 变成了默认选项,也可以利用 Content-Length 字段,让客户端判断服务器响应是否结束。但是因为 Content-Length 的缺点,所以引入了 Chunk 机制(分块传输编码)

在响应 Header 中添加 Transfer-Encoding: chunked,Body 中可以采用分块传输。分块包含十六进制的长度值和对应长度的数据内容,长度值独占一行,数据从下一行开始。最后以一个长度值为 0 的分块来表示资源结束

pipeline

尽管 HTTP 1.0 中的已经引入了长连接模式,但是 HTTP 1.0 中在收到上一次请求之后才能发出下一次请求。

在 HTTP 1.1 中改进了这个问题,引入了 Pipeline 机制。在没有收到响应之前,就可以发出之后的请求。

image-20230619204542901

但是不管是 HTTP 1.0 还是 Pipeline,都有个问题就是队头阻塞。因为服务器的响应顺序必须是请求发出的顺序,所以如果前面的响应处理流程缓慢,则会延缓后面请求响应的处理。

由于队头阻塞的问题,Pipeline 的推广并不是很成功。

阅读全文 »

I/O

分类

缓冲 I/O 与直接 I/O

不管是文件 I/O 还是网络 I/O,最基本的分为两种类型:缓冲 I/O 和直接 I/O:

  • 缓冲 I/O:读写操作都进行三次数据拷贝操作,以读操作为例,数据从磁盘进入内核缓冲区(Page Cache,操作系统会把数据以 Page 为单位存储在操作系统内存空间中),从内核缓冲区进入用户缓冲区避免频繁的切换内核态,所以设立用户缓冲区,每次都读满缓冲区),最后从用户缓冲区进入应用程序内存

image-20230619111345104

  • 直接 I/O:直接 I/O 的读写操作会进行两次数据拷贝,与缓冲 I/O 不同的是,直接 I/O 中内核缓冲区直接与应用程序内存打交道

image-20230619111358424

内存映射文件与零拷贝

在读写操作时的拷贝消耗了 CPU 和内存资源,为了进一步减少拷贝,又引入了内存映射文件和零拷贝技术。

  • 内存映射文件:内存映射文件中,拷贝次数为一次。在应用程序中的地址指向系统内核缓冲区逻辑地址,所以在读操作时只需要将数据从磁盘读入内核缓冲区,应用程序通过内存映射就可以使用这些数据了。

image-20230619112432185

  • 零拷贝:零拷贝应用于避免数据从一个内核缓冲区拷贝到另一个内核缓冲区的场景(比如读取一个文件,然后将文件内容通过 socket 发送出去)。具体的做法是,直接使得一个内核缓冲区映射到另一个内核缓冲区中,两个内核缓冲区共用同一片内存空间。实际上这是有两次拷贝的,零拷贝指的是内存内部不需要拷贝操作。

image-20230619113753783

Golang 中内存映射文件使用 syscall.mmap,零拷贝使用 syscall.sendfile。

阅读全文 »

可观测性包含三个方面:日志、追踪、度量:

  • 日志:日志用于记录离散的事件,这些事件就是程序的行为。
  • 追踪:追踪就是为了得到完整的远程调用链路,主要目的是排查故障、观察调用链是否符合预期。
  • 度量:度量是指对系统中某一类信息的统计聚合,如内存占用率、磁盘占用率等等。

img

日志

日志不仅仅是输出并记录日志这么简单,在微服务架构中的海量日志处理也是十分重要的。日志处理过程包括如下步骤:

img

输出

日志应该无有遗漏地记录信息,格式统一,内容恰当(不应该过多占用 I/O,不应该过少缺少必要信息)。

收集和缓冲

将日志文件统一收集起来集中存储、索引,由此便催生了专门的日志收集器。

最初日志收集器是由 Logstash 负责的,但是 Logstash 需要跑在单独的 JAVA 虚拟机上,默认堆栈就达到了 1GB。作为每一个节点上都要部署的收集器,这就过于沉重了。

后来 Elastic.co 公司利用 Golang 编写了轻量高效的日志收集器 Filebeat。不过 Beats 家族不仅于此,处理 Filebeat 外,有用于收集 Linux 审计数据的 Auditbeat、用于收集 Windows 事件日志的 Winlogbeat、用于心跳检测的 Heartbeat 等等。

因为集群中有很多节点都在日志收集,产生的日志数据数以万计。在日志收集过后的流程中,需要保证日志不丢失,在做到在后续处理能力出现瓶颈时做到削峰填谷。所以在这之后架设一个 Kafka 或者 Redis 作为缓冲层,以应对突发流量。

聚合加工

将日志集中收集之后,存入 Elasticsearch 之前,一般还要对它们进行加工转换和聚合处理。这是因为日志是非结构化数据,一行日志中通常会包含多项信息,如果不做处理,那在 Elasticsearch 就只能以全文检索的原始方式去使用日志,既不利于统计对比,也不利于条件过滤。

Logstash 的基本职能是把日志行中的非结构化数据,通过 Grok 表达式语法转换为结构化数据,进行结构化的同时,还可能会根据需要,调用其他插件来完成时间处理(统一时间格式)、类型转换(如字符串、数值的转换)、查询归类(譬如将 IP 地址根据地理信息库按省市归类)等额外处理工作,然后以 JSON 格式输出

存储和查询

Elasticsearch 是一种文档数据库,它用于存储日志。对于加工过后已经结构化的日志,Elasticsearch 便可针对不同的数据项来建立索引,进行条件查询、统计、聚合等操作的了。

Elasticsearch 只提供了 API 层面的查询能力,它通常搭配同样出自 Elastic.co 公司的 Kibana 一起使用,Kibana 作为 GUI 图形界面

追踪

追踪用于知晓服务调用链路,所以又称之为链路追踪。

追踪和跨度

对于链路追踪,有追踪跨度两个概念。Trace 就是一次调用的完整过程(纵向的),而 Span 是在一个服务内部的处理过程(横向的,包括处理时间、处理结果)。想要进行链路追踪,对于每一次服务调用都需要记录 Trace 和 Span,通过 Trace 和 Span 生成服务调用拓扑图。根据拓扑图中 Span 记录的时间信息和响应结果(正常或异常返回)就可以定位到缓慢或者出错的服务;将 Trace 与历史记录进行对比统计,就可以从系统整体层面分析服务性能,定位性能优化的目标。

img

OpenTelemetry + Jaeger

OpenTelemetry 只涉及到遥感数据(链路追踪信息、日志、指标)的采集和传输,为遥感数据的采集和传输提供一套统一的标准。

Jaeger 是分布式链路追踪工具,用于管理和监控链路追踪数据。

链路追踪可以涉及两个步骤:

  • 采集链路请求数据。
  • 存储、管理和可视化收集的数据以采取快速行动。

OpenTelemetry 解决了第一步,而 Jaeger 旨在解决后者。使用 OpenTelemetry,可以生成链路信息、日志、指标,而 Jaeger 只负责链路追踪分析。

度量

度量虽然没有一个统一的协议标准,但是 Prometheus 已经成为了实践标准。下面以 Prometheus 为例进行说明。

特点

Prometheus 作为一种云原生监控系统,有以下特点:

易于管理

  • Prometheus 核心部分只有一个单独的二进制文件,不存在任何的第三方依赖(数据库、缓存等),因此不会存在级联故障
  • Prometheus 基于 Pull 模型的架构方式,可以在任何地方搭建监控系统。
  • 对于一些复杂的情况,还可以使用 Prometheus 服务发现功能动态管理监控目标。

监控服务的内部运行状态

  • Pometheus 鼓励用户监控服务的内部状态,基于 Prometheus 丰富的 Client 库,用户可以轻松的在应用程序中添加对 Prometheus 的支持,从而让用户可以获取服务和应用内部真正的运行状态。

强大的数据模型

  • 所有采集的监控数据均以指标(metric)的形式保存在内置的时间序列数据库当中。所有的样本除了基本的指标名称以外,还包含一组用于描述该样本特征的标签。每一条时间序列由指标名称(Metrics Name)以及一组标签(Labels)唯一标识。如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
// 时间戳
"timestamp": 1599117392,
// 指标名称
"metric": "total_website_visitors",
// 标签组
"tags": {
"host": "xxx.cn",
"job": "prometheus"
},
// 指标值
"value": 10011
}

强大的查询语言 PromQL

  • Prometheus 内置了一个强大的数据查询语言 PromQL。 通过 PromQL 可以实现对监控数据的查询、聚合。同时 PromQL 也被应用于数据可视化(如 Grafana)以及告警当中。

可扩展

  • Prometheus 对于联邦集群的支持,可以让多个 Prometheus 实例产生一个逻辑集群,当单实例 Prometheus Server 处理的任务量过大时,通过使用功能分区 + 联邦集群可以对其进行扩展。

架构

Prometheus 整体上可以分为三个模块:

  • 采集层
  • 存储计算层
  • 应用层

img

采集层

采集层主要用于采集被监控的数据,层分为两类,一类是生命周期较短的作业,还有一类是生命周期较长的作业(这种对应于监控特定的机器)。

  • 短作业:直接通过 API,在退出时将指标推送给 Pushgateway(推送网关,短作业将指标推送给推送网关,之后 Prometheus server 中的 Retrieval 统一从推送网关中拉取短作业的监控数据)。Prometheus 基于 Pull 架构的同时还能够有限度地兼容 Push 式采集,是因为它有 Push Gateway 的存在。
  • 长作业:Retrieval 组件直接从 Job 或者 Exporter(Exporter 为专门为一些应用开发的数据获取组件,如 mysqld_exporter、node_exporter 等)拉取数据。Exporter 以 HTTP 协议返回符合 Prometheus 格式要求的文本数据给 Prometheus 服务器。

存储计算层

存储计算层包含两个主要组件,Prometheus server 和 Service discovery。

  • Prometheus server:
    • Retrieval:取数据组件,它会主动从 Pushgateway 或者 Exporter 中拉取指标数据。
    • TSDB:时序数据库,提供数据核心存储与查询
    • HTTP server:对外提供 HTTP 服务。
  • Service discovery:可以动态发现要监控的目标。

应用层

应用层主要分为两种,AlertManager 和数据可视化。

  • AlertManager:专门用于处理告警的组件。
  • 数据可视化:用于数据可视化或者向外提供 API 用于提供监控数据,数据可视化可以采用 Prometheus Web UI、Grafana。

服务容错

微服务架构中必须有服务容错的设计,否则会造成故障服务带来的雪崩效应。下面介绍几种服务容错设计模式。

断路器模式

断路器就是熟悉的熔断器,通过熔断器对象接管服务调用者的远程调用,一个熔断器一对一的对应一个远程服务对象。

状态

熔断器实际上可以看作是一个有限状态机,有三种状态 CLOSE、OPEN、HALF-OPEN

  • 当熔断器监控到服务返回的故障超过了阈值时,就会变为 OPEN,此后一段时间内不会对该服务(实例)发出任何实际请求,而是直接返回错误
  • 一段时间之后,熔断器会变为 HALF-OPEN 状态,此时下一个远程调用请求会尝试发出,熔断器根据结果变更状态,如果成功则变为 CLOSE,如果失败则继续为 OPEN。

image-20230610145226779

阈值

什么情况下 CLOSE 会变为 OPEN,最简单的方法就是如果出现任何一个错误,就变更状态。但是在用户的角度看来,这是十分糟糕的体验,因为此时调用的服务可能只是偶然的错误并不是真正的故障,在用户看来系统表现十分不稳定。

一个可行的方案是从两个方面考虑,只有同时满足时才从 CLOSE 变为 OPEN:

  • 一段时间的请求数量达到阈值:如果请求本身很少,不用熔断器介入。
  • 一段时间内的请求故障率达到阈值:如果错误很少,也不用熔断器介入。
阅读全文 »

服务发现

服务发现既要高可用,又要高可靠。因为服务注册中心的地位是特殊的,不依赖其他服务,但被所有其他服务共同依赖(类似于配置中心),是系统中最基础的服务。服务注册中心一旦崩溃,整个系统都不再可用,因此,必须尽最大努力保证服务发现的可用性。

实际用于生产的分布式系统,服务注册中心都是以集群的方式进行部署的。

采用集群部署注册中心时,就会遇到 CAP 矛盾。所以注册中心分为两类,满足 AP 的注册中心满足 CP 的注册中心

  • 满足 AP 的注册中心,保证了高可用,但是注册中心内部可能会发生不一致的情况。代表如 Eureka、Redis、Nacos 等
  • 满足 CP 的注册中心,保证了一致性,但是必须经过集群之间的同步之后,才算是注册完成。代表如 Consul、Zookeeper、Etcd、Nacos 等

实现注册中心

实现注册中心,主要通过以下三种方式实现:

  • 在分布式 K/V 存储框架上自己开发的服务发现:这一类的代表为 Etcd、Zookeeper 等,需要在 CRUD 和 Watch 等 API 的基础上,自己去实现服务的注册和发现功能,还需要考虑健康检查。
  • 以基础设施来实现服务发现:代表就是 Kubernetes 的服务注册发现功能,如 Kubernetes 中采用 CoreDNS 进行服务发现。这种方案是 CP 还是 AP,取决于后端采用何种存储。
  • 专门用于服务发现的框架和工具:代表是 Eureka、Consul 和 Nacos,其中 Nacos 支持 AP 或者 CP 二选一。这类服务发现提供了专门的 API,使用这类服务发现必须有语言的支持。

网关路由

在为微服务中,路由网关又称之为 API 网关或者服务网关。作为微服务架构的入口,具有路由的功能,还可以具有认证、监控、缓存等辅助功能。

阅读全文 »

授权

授权的过程就是解决这样一个问题:谁拥有什么权限去操作哪些资源。

访问控制模型

可以通过访问控制模型来解决授权问题,有常见的三种访问控制模型:DAC(自主访问控制)、MAC(强制访问控制)、RBAC(基于角色的访问控制)。

DAC

自主访问控制就是资源的所有者,规定谁有权限访问它们

Linux 文件的权限就是 DAC 的一种实现方式。

MAC

主体和客体都有一个固定属性,系统用该安全属性来决定一个主体是否可以访问某个客体。

保护敏感信息一般用 MAC,需要用户提供灵活的保护,更多的考虑共享信息时,使用 DAC。

RBAC

将权限从用户身上剥离,将权限分配在角色上,再给用户分配角色,这是最常用的访问控制模型。

RBAC 有以下特点:

  • 通过角色实现用户和权限的解耦,具有很高的灵活性
  • 同时还天然的满足最小分配原则。在 RBAC 模型中,角色拥有许可的数量是根据完成该角色工作职责所需的最小权限来赋予的。
  • 不同的角色之间可以有继承性,也可以有互斥性(角色的互斥约束可限制同一用户只能分配到一组互斥角色集合中至多一个角色)。

OAuth2

OAuth2 是面向于解决第三方应用的认证授权协议,以 token 代替密码访问资源服务器。整个授权的流程如下图所示:

image-20230609155251566

阅读全文 »