Dawn's Blogs

分享技术 记录成长

0%

认证

认证就是识别用户的身份,通常而言有两种方式进行认证,一是 HTTP 认证(在请求资源时认证),二是Web 内容认证(在获取服务时认证)。

HTTP 认证

HTTP 认证通过 Authication Header 实现,服务器告知客户端应该采用哪种认证方式:

1
2
WWW-Authenticate: <认证方案> realm=<安全区域>
Proxy-Authenticate: <认证方案> realm=<安全区域>

客户端进行响应:

1
2
Authorization: <认证方案> <凭证内容>
Proxy-Authorization: <认证方案> <凭证内容>

认证方案如下:

  • Basic:让用户输入用户名和密码,经过 Base64 编码后作为凭证内容。
  • Digest:HTTP 摘要认证,Digest 认证把用户名和密码加盐(Nonce)后通过哈希后发送出去。
  • Bearer:基于 OAuth 2 规范来完成认证。

Web 认证

Web 认证就是通过 Web 表单让用户去填写用户名密码等信息。

这种认证方法没有固定的标准,没有一个统一的标准去规定用户名、密码、验证码是否需要加密,并且用何种方法进行加密。这不是缺点,反而是一大优点,因为足够的自主性,可以设计出符合需求的各种认证方式。

请注意,Web 认证是可以基于 HTTP 认证实现的,也就是说可以使用 HTTP Authentication Header 记录认证信息。

WebAuthn

2019 年 W3C 起草了第一份 Web 内容认证标准 WebAuthn,WebAuthn 彻底抛弃了传统的密码登录方式,改为直接采用生物识别(指纹、人脸、虹膜、声纹)或者实体密钥(以 USB、蓝牙、NFC 连接的物理密钥容器)来作为身份凭证。所以,这个规范不关注界面该是什么样子、要不要验证码、是否要前端校验这些问题。

WebAuthn包含注册和登录两大流程。

阅读全文 »

传输链路

数据通过 HTTP 协议进行通信,所以这里的传输链路指的是 HTTP 在传输链路上的优化。

连接数优化

HTTP over TCP,需要三次握手才能进行传输,并且 TCP 还有慢启动的特性,所以减少连接数可以优化传输时延。

HTTP 1.x

在 HTTP 1.0 版本就已经支持连接复用技术(在 HTTP 1.0 默认关闭,在 HTTP 1.1 默认开启),即与服务器维护一个或者多个持久连接。客户端维护一个 FIFO 队列,每一次得到完整的响应后发出下一个请求。这种方法的问题在于队头阻塞,如果第一个请求的响应非常慢那么会直接阻塞后续请求的发出。

后续又提出 HTTP 管道复用技术,令 HTTP 服务器也维护一个 FIFO 队列,客户端一次性把所有的请求都发送给服务器,由服务端来安排返回顺序。由于 HTTP 管道需要多方共同支持,协调起来相当复杂,推广得并不算成功。

HTTP 2.0

HTTP 2.0 中,帧(Frame)是传输的最小单位,每一个帧都附带一个 ID 用于标识这个帧属于哪个流,从而可以通过不同流重组出 HTTP 请求和响应,被称为 HTTP 2 多路复用技术。HTTP 与同一个服务器只维护一个连接

阅读全文 »

客户端缓存

客户端缓存依赖于 HTTP 缓存机制,通过 HTTP Header 控制。

强制缓存

HTTP 强制缓存规定了一个过期时间,在这个时间点之前客户端可以不发出请求,直接使用本地缓存。

根据约定,强制缓存在浏览器的地址输入、页面链接跳转、新开窗口、前进和后退中均可生效,但在用户主动刷新页面时应当自动失效

两类 HTTP Header 保证了强制缓存:

  • Expires:Expires 是 HTTP 1.0 提供的 Header,后面跟随一个截止时间用于定于缓存的有效时间。

Expires 有设计缺陷:

  • 受限于客户端的本地时间。
  • 无法处理涉及客户端身份的私有资源。
  • 无法描述不缓存。
  • Cache-Control:Cache-Control 是 HTTP 1.1 种定义的 Header,如果 Cache-Control 和 Expires 同时存在,并且语义存在冲突,规定必须以 Cache-Control 为准。它有一系列参数:
    • max-age 和 s-maxage:max-age 后面跟随一个以秒为单位的数字,表明相对于请求时间多少秒以内缓存是有效的。s-maxage 中的 s 为 Share,即允许被 CDN、代理等持有的缓存有效时间。
    • public 和 private:指明是否涉及到用户身份的私有资源,如果是 public,则可以被代理、CDN 等缓存,如果是 private,则只能由用户的客户端进行私有缓存。
    • no-cache 和 no-store:no-cache 指明该资源不应该被缓存,如果一个页面中引用了两张相同的图片,则会请求两次。no-store 指不能保存在本地,但是用一个页面中引用了两张相同的图片,只会请求一次。
    • no-transform:禁止资源被任何形式地修改。某些 CDN、透明代理支持自动 GZip 压缩图片或文本,以提升网络性能,而 no-transform 就禁止了这样的行为。
    • min-fresh 和 only-if-cached:这两个参数是仅用于客户端的请求 Header。min-fresh 后续跟随一个以秒为单位的数字,用于建议服务器能返回一个不少于该时间的缓存资源。only-if-cached 表示客户端要求不必给它发送资源的具体内容,此时客户端就仅能使用事先缓存的资源来进行响应,若缓存不能命中,就直接返回 503/Service Unavailable 错误。

协商缓存

强制缓存有一个缓存过期时间,没有一个检测变化的机制,如果缓存还没有过期即使内容发生变化,客户端也不会向服务器请求而是使用本地缓存。

协商缓存在一致性上有更好的表现,但是性能上会差一些,因为需要一次检测变化的开销

强制缓存和协商缓存是完全并行工作的,当强制缓存存在时,直接从强制缓存中返回资源,无须进行变动检查;而当强制缓存超过时效,或者被禁止(no-cache)时,协商缓存依然可以工作。

协商缓存有两种变化检测机制,分别是根据资源的修改时间、以及根据资源唯一标识符进行检查。

  • Last-Modified 和 If-Modified-Since:服务器通过 Last-Modified 告诉客户端资源最后修改时间,客户端通过 If-Modified-Since 将这个时间回送给服务器。
  • Etag 和 If-None-Match:服务器通过 Etag 告诉客户端这个资源的唯一标识符,客户端通过 If-None-Match 将这个唯一标识符回送给服务器。

如果此时服务端发现资源没有变动(通过最后修改时间或者唯一标识符),就只要返回一个 304/Not Modified 的响应即可,无须附带消息体。如果发现有变动,则返回 200 并携带完整内容

Last-Modified 只能精确到秒级,如果一秒之内发生了多次变化,则不能准确标注修改时间。

HTTP Vary Header 在响应中,用于指定其他 HTTP 头部作为客户端缓存命中的依据,如下表示根据 MIME 类型和浏览器类型来缓存资源。

1
Vary: Accept, User-Agent
阅读全文 »

本地事务

以下都是基本原理,请不要与 MySQL 挂钩!MySQL 的基本思想肯定是这样,但肯定不是完全照搬。

原子性和持久性

按照事务提交时点为界,划分为 FORCE 和 STEAL 两类情况:

  • FORCE:指的是事务的实际写入发生在提交之后。当事务提交后,要求数据必须同时完成写入则称为 FORCE,如果不强制数据必须同时完成写入则称为 NO-FORCE。现实中绝大多数数据库采用的都是 NO-FORCE 策略,因为只要有了日志,变动数据随时可以持久化,从优化磁盘 I/O 性能考虑,没有必要强制数据写入立即进行。
  • STEAL:在事务提交前,允许数据提前写入则称为 STEAL不允许则称为 NO-STEAL。从优化磁盘 I/O 性能考虑,允许数据提前写入,有利于利用空闲 I/O 资源,也有利于节省数据库缓存区的内存。

在数据库中,因为写入磁盘这个操作不是原子操作,所以可能出现崩溃的情况。为了保证事务的原子性和持久性,通常通过日志(顺序追加,这是最高效的写入方式)的方式,分为两种方法:

  • Commit Logging:允许 NO-FORCE,但是不允许 STEAL。
  • Write-Ahead Logging:允许 NO-FORCE,也允许 STEAL。

通过日志实现事务的原子性和持久性是当今的主流方案,但并不是唯一的选择。除了日志外,还有一种称之为 Shadow Paging(影子分页)来实现,SqLite v3 就是这种机制。

Shadow Paging 在数据写入磁盘时,不会直接修改原来的数据,而是将原来的数据复制一份副本,保留原数据只修改副本。事务成功提交后,最后一步是去修改数据的引用指针,将引用从原数据改为新复制出来修改后的副本(这个修改指针被认为是原子操作)。但是 Shadow Paging 并发能力不强,所以应用不多。

Commit Logging

Commit Logging 中只有一种日志,就是 redo 日志。对数据的操作都会记录在 redo 日志中,事务的提交就是在日志中写入代表提交成功的提交记录(Commit Record)。在数据库看到提交记录(Commit Record)后,才根据日志内容对数据进行真正的修改,修改完成后向日志中加入一条结束记录(End Record)表示事务已经成功完成持久化。

一旦写入了 Commit Record,即使在写入磁盘之前崩溃了,重启后也能根据日志恢复,保证了持久性。如果在日志没有 Commit Record 时就发生了崩溃,则整个任务就是失败的,因为没有真正写入磁盘,所以就好像发生了回滚一样,保证了原子性。

Commit Logging 的原理非常清晰,如 OceanBase 就是采用了这种方法。但是 Commit Logging 的一个缺陷就是:所有对数据的真实修改必须发生在提交之后,在提交之前即使磁盘 I/O 是空闲的也不允许写入数据,并且占用了大量的内存缓冲区。

因为 Commit Logging 只有 redo log,所以不允许 STEAL,否则无法回滚(回滚需要 undo log)。

Write-Ahead Logging

Write-Ahead Logging 是改进的方案,所谓 Write-Ahead(提前写入)指的就是在提交之前写入数据。Write-Ahead Logging 在 redo log 的基础上,引入了 undo log 用于回滚。当变动数据写入磁盘前,必须先记录 Undo Log,注明修改了哪个位置的数据、从什么值改成什么值等等。以便在事务回滚或者崩溃恢复时根据 undo log 对提前写入的数据变动进行擦除。

在崩溃恢复时,根据 redo log 对没有写入磁盘但是已经提交的数据进行持久化,根据 undo log 对没有提交但是已经写入磁盘的数据进行回滚。

redo log 保证事务的持久性,而 undo log 保证事务的原子性。

隔离性

实现隔离性最直观的方式,就是加锁。现代数据库均提供以下三种锁:

  • 写锁:对一行数据加写锁,写锁与其他锁都互斥。
  • 读锁:对一行数据加读锁,读锁与读锁不互斥,与写锁互斥。
  • 范围锁:对一个范围加锁。

注意,范围锁不等于对多行数据加锁,因为数据与数据之间有间隙(这就是为什么 MySQL 中有间隙锁)。

隔离性用隔离级别来体现,有以下几种隔离级别(这几种隔离级别都解决了脏读问题):

  • 可串行化(Serializable):可串行化是最高的隔离级别,也是并发程度最低的隔离级别。可串行化中相当于全局加锁,不存在幻读、不可重复读、脏读问题。
  • 可重复读(Repeatable Read):可重复读中,对于事务中涉及到的数据会加上读锁和写锁,但不会加范围锁。不加范围锁导致的问题,就是会出现幻读问题。
  • 读已提交(Read Commited):在读已提交中,写锁会持续到事务结束读锁在查询完成后会马上释放。读锁在查询结束后马上释放的带来的问题,就是会导致不可重复读
  • 读未提交(Read Uncommitted):读未提交中,写锁会持续到事务结束,但是完全不会加读锁。不加读锁带来的问题就是脏读,可能会读取到其他事务还未提交的数据。

在 MySQL 中的默认隔离级别未可重复读,但它在只读事务中可以完全避免幻读问题

MVCC

MVCC 多版本并发控制是一种读优化策略,MVCC 的基本思路是对数据库的任何修改都不会直接覆盖之前的数据,而是产生一个新版副本与老版本共存,以此达到读取时可以完全不加锁的目的。

  • 如果隔离级别是可重复读:读取小于等于当前事务 ID 的最大版本。
  • 如果隔离级别是读已提交:读取最新版本。

读未提交直接修改原始数据即可,不需要版本。

阅读全文 »

Kubernetes 中卷是 Pod 的组成部分,因此卷可以在 Pod 中定义。Kubernetes 卷不是单独的对象,也不能单独创建或者删除。

卷有很多类型可以选择,包括 emptyDir(临时卷,生命周期与 Pod 相同)、hostPath(指向节点文件系统上的文件或者目录)、持久卷等。

Pod 与底层存储技术解耦

但是!!在 Kubernetes 上部署应用的开发人员不需要知道底层使用的是哪种存储技术,同理他们也不需要了解应该使用哪些类型的物理服务器来运行 Pod。所以,需要令具体的类型的存储卷与开发人员解耦,当开发入员需要一定数量的持久化存储来进行应用时,可以向 Kubernetes 请求然后 Kubernetes 再进行分配。

因此,Kubernetes 引入了两种新的资源,持久卷(Persistent Volume,PV)持久卷声明(Persistent Volume Claim)。开发人员无需向 Pod 中添加特定技术的卷,而是由集群管理员设置底层存储,创建持久卷并注册。开发人员创建持久卷声明,指定需要的容量和访问模式,Kubernetes 会自动匹配到持久卷并绑定到 PVC 中

可以声明访问模式、回收策略等。

动态声明持久卷

通过 PV 和 PVC 的配合,开发人员已经无需关心底层的存储是怎样实现的,但是还需要管理员来配置和创建持久卷。

Kubernetes 可以通过 StorageClass 来动态的执行此过程,管理员只需要定义 StorageClass 对象,系统在每次通过持久卷声明请求时创建一个新的持久卷。用户也可以再 PVC 中引用 StorageClass,并且可以指定大小和访问模式,StorageClass 对象会根据 PVC 中的要求动态的创建持久化卷并与相应的 Pod 绑定。

配置

Kubernetes 中的应用程序需要读取配置信息,ConfigMap 用于读取一般配置,而 Secret 用于读取密码等敏感信息。

ConfigMap

ConfigMap 实际上就是键值对的内容可以是字面量,也可以是完整的配置文件。应用程序无法直接读取 ConfigMap,甚至不知道其是否存在,内容通过环境变量或者传递给容器。

如果 Pod 引用了不存在的 ConifgMap,则会无法启动。

ConfigMap 相比于直接在镜像中打入环境变量更加灵活,因为 ConfigMap 可以随时更新配置信息。

Secret

Secret 也是键值对,内容通过环境变量或者传递给容器。

Secret 只会存储在节点的内存中,永远不会写入物理内存。

服务

Kubernetes 中服务是一种为一组功能相同的 Pod 提供单一不变的入口点的资源。当服务存在时,入口地址不会改变。 客户端通过 服务的入口地址建立连接,这些连接会被路由到提供该服务的任意一个 pod 上。 通过这种方式, 客户端不需要知道每个单独的提供服务的 pod 的地址, 这样这些 pod 就可以在集群中随时被创建或移除。

创建服务

服务使用标签来选择哪些 Pod 属于服务,哪些标签不属于服务。spec. ports.port 声明服务对外暴露的端口,而 spec.ports.targetPort 声明的是将消息转发给 Pod 的端口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
selector:
app: kubia

如果希望特定客户端产生的所有请求每次都指向同一个 Pod,则可以设置服务的 spec.sessionAffinity 属性为 ClientIP。sessionAffinity 属性仅支持两个值,none(默认)和 ClientIP。

DNS 服务发现

在集群内部的客户端如何知道访问服务的地址,这就需要服务发现了。在 Kubernetes 中提供了 DNS 服务器,Pod 可以使用内部的 DNS 服务器作为服务发现。客户端可以通过全限定域名(FQDN)来访问。

在创建一个服务时,Kubernetes 会创建一个相应的 DNS 条目,条目的内容为 <服务名称>.<名字空间名称>.svc.cluster.local,其中 svc.cluster.local 是在所有集群本地服务名称中使用的可配置集群域后缀。

如果客户端 Pod 和目标 Pod 在同一个命名空间下,则可以省略 svc.cluster.local 后缀,甚至是命名空间名字。

Endpoint

介绍

服务 和 Pod 不是直接相连的,而是通过 Endpoint 资源进行连接。Endpoint 就是一个记录 IP 和 端口的列表,用于指示服务的转发地址。

尽管在 服务中定义了 Pod 选择器,但在重定向传入连接时不会直接使用它。选择器用于构建 Pod 的 IP 端口列表,然后存储在 Endpoint 资源中。当客户端连接服务时,会从 Endpoint 资源中选择一个地址,将客户端的请求重定向到这个地址上。

手动配置 Endpoint

当服务和 Endpoint 解耦后,可以分别手动配置它们。如果创建了不包含 Pod 选择器的服务,那么 Kubernetes 就不会创建 Endpoint 资源。所以在创建服务时不指定 Pod 选择器,然后手动的创建 Endpoint 资源,实现服务的任意重定向

Endpoints 资源与服务需要具有相同的名称,这样才能使二者进行绑定。

阅读全文 »

单体系统

需要摆正这样一个观念,单体系统并不是反派角色

单体系统适用于小型系统,有自己的优缺点,并不是一无是处。

优点:

  • 小型系统而言,单体系统不仅易于开发、易于测试、易于部署,且由于系统中各个功能、模块、方法的调用过程都是进程内调用,不会发生进程间通信。

缺点:主要是缺乏隔离性,全部的代码全部在一个进程内运行,这样带来的坏处有两个。

  • 无法隔离错误,如果某个模块出现了错误,遇到了如内存泄漏等 Bug,那么整个程序都会受到影响。
  • 无法做到模块级别的单独停止、升级,因为不可能停止一部分的进程、重启一部分的经常。

单体系统架构潜在的要求整个系统都是可靠的,但是我们在软件架构的演进中应该有这样的观念:从尽量追求不出错,到正视出错是必然

架构如凤凰一样不死,反复涅槃重生,这就是软件架构所追求的目标。

SOA 时代

SOA(Service Oriented Architecture)面向服务的架构,就是将一个大型的单机系统进行拆分,拆分成独立的子系统。看起来 SOA 与微服务的概念差不多,实际上有很大的差异。

微服务架构 vs SOA

SOA(Service Oriented Architecture)指的是面向服务的架构,SOA 也是进行了服务划分,它与微服务架构看似相同,但是深入研究就会发现二者的巨大差异。

  • 服务间的通信:SOA 常吃采用重量级的技术,如 ESB(Enterprise Service Bus)、SOAP 和 WebService。而微服务架构倾向于使用轻量级的技术,如 REST 或者 gRPC 这类轻量级的协议。
  • 数据管理:SOA 一般有一个全局的数据库。而微服务与之相反,每一个服务都有一个自己的私有数据模型。
  • 服务规模:SOA 应用通常包含若干个大型的服务。而微服务架构则常常由属实个甚至上百个更小的服务组成。

微服务时代

微服务是一种通过多个小型服务组合来构建单个应用的架构风格,这些服务围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言不同的数据存储技术,运行在不同的进程之中。服务采取轻量级的通信机制自动化的部署机制实现通信与运维。

微服务的九个核心特征如下:

  • 围绕业务能力构建。因为团队之间的沟通成本是巨大的,一个微服务应该是一个可以自治的单体,所以一种业务就应该被限定在一种服务内部。这就是为什么微服务中的服务划分非常重要,而微服务的划分通常与 DDD 一致。即使刚开始的架构并不是按照业务能力划分的,随着时间的发展,最终的架构也会向着这种形式演化。
  • 分散治理。一个微服务应该是一个可以自治的单体,而不会依赖于其他服务。也就是说,服务之间可以是异构的、可以是跨语言的
  • 通过服务来实现独立自治的组件。通过服务,而不是通过类库(Library)来构建组件。因为服务是进程外组件,通过远程调用来提供功能,尽管进程外调用比进程内调用复杂且效率低,但是这是微服务的必然代价。
  • 产品化思维。服务看作是一种产品,开发人员负责整个生命周期(开发、测试、运维、部署等等)。
  • 数据去中心化。每一种服务独立的维护自己的数据库,而不是一个中心化的数据库。中心化的数据库的好处就是数据的一致性可以保证,缺点则是可能会让多个服务使用同一个数据表(服务无法独立的修改数据表 schema),使得服务之间丧失独立性。微服务去中心化的存储选择了维护独立性,而数据之间的一致性可以通过分布式事务保证。
  • 强终端弱管道。管道指进程间通信机制,通信机制采用最基本的即可,特殊的能力需求由服务(终端)完成。
  • 容错性设计。微服务设计中,必须有容错性设计,在必要的时候进行隔离。所以熔断器在实际生产环境中是必不可少的组件,如果没有这种容错性设计,则系统很容易就因为一两个服务的崩溃所带来的雪崩效应淹没。
  • 演进式设计。演进式设计允许服务被升级、淘汰。如果系统中出现了无法升级、淘汰的服务,这是一种设计上的脆弱表现。
  • 基础设施自动化。微服务下运维的对象比起单体架构要有数量级的增长,基础设施自动化减少了构建、发布、运维工作的复杂性。

与 SOA 这种强约束和规定的架构风格不同,微服务是一种自由的风格。

微服务中存在服务发现、进程间调用、负载均衡、事务处理等等问题,但是不会约束具体用什么方式去实现,微服务倡导以实践标准代替规范标准。

阅读全文 »

安全性

在安全性中,主要讨论用户身份认证的问题。目前有两种方法实现身份认证,Session 和 JWT。

Session 的缺陷

在单体应用程序中,可以使用 Cookie+Session 来进行身份认证,Session 信息是由服务器维护的。但是在微服务架构中,由于会水平扩展所以由服务本地维护 Session 信息是不现实的,有两种解决方法:

  • 将 Session 信息放在数据库中,这样 Session 信息在多个实例中就是共享的。
  • 使用特定的负载均衡算法,如对客户端地址进行哈希,使得同一个客户端发出的请求到达同一个实例。

JWT

如果由服务器端来维护用户身份信息会带来种种设计上的问题,那么可以由客户端自己维护用户身份信息。JWT 就是这样一种思想,JWT 包含一个 Json 对象,其中有用户信息以及其他元信息(如到期时间)。JWT 包含的 Json 对象是被加密过的(token),并且签名可以保证第三方无法伪造。

因为 JWT 是客户端自包含的,所以请求不论被路由到哪一个服务实例上,服务实例都可以根据 JWT 去验证用户信息。

JWT 的缺点在于无法验证被恶意第三方偷取的 JWT token,只要恶意第三方获取到了 token 就等于获取了这个用户的登录信息。一种缓解的方法是使用较短的到期时间,但是随之而来的问题就是客户端需要频繁的请求 JWT 认证重新获取 token。

可配置性

在微服务架构中,最常使用的是外部化配置,即从服务外部而不是源代码中获取配置信息。

在源代码中直接定义配置文件有两个缺点

  • 密码等敏感信息是直接存在于源代码中的,这可能会造成一些安全性问题。
  • 不够灵活,如果需要修改配置信息就必须修改源代码文件。

外部化配置主要有两种方式实现,推送模型和拉取模型。

阅读全文 »

客户端直接访问服务会导致很多问题:

  • 多次客户端请求导致用户体验不佳:因为客户端到服务之间是互联网(带宽低,时延高),而服务之间是局域网(带宽高,时延低)。多次客户端直接到服务的请求均通过互联网传输,传输时延高。
  • 客户端与服务耦合度高:因为客户端与服务是直接请求的,所以做出的任何更改都会影响到彼此。
  • 服务可能选用对客户端不友好的进程间通信机制:一般而言,客户端使用的通信协议通常是 HTTP 或者 WebSockets 等协议,而服务选用的通信协议可能是 gRPC,RPC 通信对客户端是不友好的,甚至无法穿透防火墙。

所以可以采用 API Gateway 或者后端前置的方式。

API Gateway

把 API Gateway 作为外部客户端请求的唯一入口点,它为客户端提供 API,也可以进行身份认证充当 API 组合器传输协议转换等职责。

可以把 API Gateway 看作是面向设计中的外观模式,它封装了内部架构,并为客户端提供API。

作用

下面说明 API Gateway 的典型作用。

请求路由

将客户端的请求路由到相应的服务中,来实现一些 API 操作,这与反向代理的功能类似。

API 组合

API Gateway 也可以作为 API 组合器,它通过对内通过对多个服务的查询、并且将查询结果聚合,来实现对外提供粗粒度的 API。

image-20230525100940886

协议转换

API Gateway 可以实现协议转换,它可以对外提供 HTTP RESTful API,内部使用 gRPC 进行通信。

实现边缘功能

边缘功能包括:身份认证、访问授权、速率限制、缓存、请求日志等。

阅读全文 »

在微服务中,查询通常是复杂的,因为所需要的数据通常分散在多个服务之中。微服务的查询通常分为两种不同的模式:

  • API 组合模式:最简单的方法,应该首先考虑和使用。让一个统一的服务或者应用网关调用多个服务,然后将查询到的结果进行组合并返回。
  • 命令查询职责隔离(CQRS,Command-Query Responsibility Segregation)模式:比 API 组合模式更加复杂,也更强大。维护一个或者多个数据视图,目的仅仅是查询。

API 组合模式

API 组合模式就是访问多个服务进行查询,将多个服务的查询结果组合起来并返回给用户。这种模式有两种参与者:

  • API组合器:用于查询多个服务的数据,并负责将结果组合起来。
  • 数据提供方服务:拥有部分数据的服务提供者。

image-20230524205406597

阅读全文 »