未来架构-从服务化到云原生(读书笔记)
2022-08-19 21:51:39 17 举报
AI智能生成
未来架构-从服务化到云原生(读书笔记
作者其他创作
大纲/内容
第2章 远程通讯
通信方式
OSI模型和TCP/IP模型
图
通信协议
传输层协议
传输层的作用是使源端和目的端的计算以对等的方式进行会话,实现端到端的传输
TCP(Transmission Control Protocol)传输控制协议
特点
面向连接的协议
提供可靠的双向字节流
确保连接的可靠性
连接创建过程较为昂贵
可以保证数据的正确性、顺序性和不可重复性
三次握手
图
Java 使用 Socket 开发 TCP 的过程
服务端程序绑定一个未占用的端口用于监听客户端程序的连接请求
客户端程序向服务端发起连接请求,请求过程中附带自身的主机IP地址和通信端口号
服务端接受客户端的连接请求
客户端与服务端通过 Socket 进行 I/O 通信
建立通信管道后,可以考虑使用多线程机制增加服务端的吞吐量
完成通信,客户端断开与服务端的连接
UDP(User Datagram Protocol)用户数据报文协议
特点
无连接的、面向数据报文的协议
每个数据报文都是一个独立的信息
不可靠传输
没有复杂的建立连接的过程,它关注的仅是数据报文本身
可能产生网络丢包,并且无法保证传输顺序
性能方面更占优势
更加适用于允许部分数据被丢弃的场景,如系统调用追踪日志,视频会议流等
应用层协议
HTTP
HTTP/2
多路复用机制允许通过单一的连接同时发起多个请求和响应消息,这极大地提升了网络传输的性能
支持长连接
HTTP/2 通过数据流(stream)的方式支持连接的多路复用
HTTP/2 将每次的请求和响应以帧(frame)为单位进行了更细粒度的划分,所有的帧都在数据流上进行传输
HTTP/2 还提供服务器推送和请求头压缩等功能
图
长连接与短连接
误区
TCP 和 HTTP 的区别并不在于使用长连接还是短连接
HTTP 只能使用短连接
I/O模型
回避 I/O 瓶颈从而提升性能
I/O 模型可以分为阻塞与非阻塞、同步与异步
阻塞与非阻塞
阻塞 I/O
在用户进程发起 I/O 操作后,需要等待操作完成才能继续运行
非阻塞 I/O
在用户进程发起 I/O 操作后,无须等待操作完成即可继续进行其他操作
同步与异步
同步 I/O
在系统内核准备好处理数据后,还需要等待内核将数据复制到用户进程, 才能进行处理
异步 I/O
用户进程无须关心实际 I/O 的操作过程,只需在 I/O 完成后由内核接收通知I/O,操作全部由内核进程来执行
总结
同步 I/O 和异步 I/O 针对的是内核,而阻塞 I/O 与非阻塞 I/O 针对的则是调用它的函数
判断是同步 I/O 还是异步 I/O,主要关注内核数据复制到用户空间时是否需要等待
select、poll、epoll 都属于同步 I/O
I/O 涉及两个系统对象,一个是调用它的用户进程,另一个是系统内核(kernel)
用户进程调用 read 方法向内核发起读请求并等待就绪
内核将要读取的数据复制到文件描述符所指向的内核缓存区
内核将数据从内核缓存区复制到用户进程空间
Java中的I/O
Java 对于 I/O 的封装分为 BIO、NIO 和 AIO
BIO 对应的是阻塞同步 I/O
每一个连接都分配了一个线程
图
NIO 和 AIO 对应的都是非阻塞同步 I/O
NIO
NIO 的一切处理都是通过事件驱动的
NIO核心概念
Buffer
NIO 中所有数据的读/写操作均通过缓冲区进行
capacity
缓冲区可容纳的最大数据量
limit
是缓冲区当前数据量的边界
position
是下一个将要被读或写的元素索引位置
Channel
是一个双向的数据读/写通道
Selector
通过不断轮询注册在其上的 Channel 来选择并分发已处理就绪的事件
Selector 是整个 NIO 的核心,理解 Selector 机制是理解整个 NIO 的关键所在
Selector 是所有 Channel 的管理者
Selector 可以被认为是 NIO 中的管家
由于处理 I/O 的线程数大幅减少,因此 CPU 用于处理线程切换和竞争的时间也相应减少,即 NIO 中的 CPU 利用率比 BIO 中的 CPU 利用率大幅提高
最常见的 Selector 监听事件
OP_CONNECT
客户端连接服务端事件
OP_ACCEPT
服务端接收客户端连接事件
OP_READ
读事件
OP_WRITE
写事件
图
NIO 的 Reactor 模式
I/O多路复用机制采用事件分离器将I/O事件从事件源中分离,并分发至相应的读写事件处理器,开发者只需要注册待处理的事件及其回调方法即可
NIO 采用 Reactor 模式来实现 I/O 操作
Reactor 模式是 I/O 多路复用技术的一种常见模式, 主要用于同步 I/O
事件分离器的角色由 Selector 担任
它负责查询 I/O 是否就绪,并在 I/O 就 绪后调用预先注册的相关处理器进行处理
Reactor 模式的流程
Selector阻塞并等待读事件发生
Selector被读事件唤醒,发送读就绪事件至预先注册的事件处理器
应用程序读取数据
应用程序处理相关业务逻辑
采取 I/O 多路复用可以在同一时间处理多客户端 的接入请求,该项技术能够将多个 I/O 阻塞复用至同一个 Selector 阻塞,让应用具有通过单线程同时处理多客户端请求的能力
AIO
AIO 采用 Proactor 模式实现 I/O 操作
Proactor 模式是 I/O 多路复用技术的另一种常见模式,它主要用于异步 I/O 处理
使用事件分离器分离读/写与任务派发
Reactor 模式的回调方法是在读/写操作执行之前被调用的,由应用开发者负责处理读/写事件,而 Proactor 模式的回调方法则是在读/写操作完毕后被调用的,应用开发者无须关心与读/ 写相关的事情
Proactor 模式的流程
事件分离器阻塞并等待读事件发生
事件分离器被读事件唤醒,并发送读事件至操作系统进行异步 I/O 处理
事件分离器将数据准备完毕的消息发送至预先注册的事件处理器
应用程序处理相关业务逻辑
AIO 有两种使用方式
较为简单的将来式
稍为复杂的回调式
Netty
网络通信 应用开发的首选框架
通过异步非阻塞和事件驱动来实现的一个高性能、高可靠、高可定制的通信框架
内置了各种协议和序列化的支持
Netty 在 4.x 版本中将 AIO 移除
图
Netty由核心(Core)、传输服务(Transport Servies)以及协议支持(Protocol Support)这几个模块组成
核心模块提供了性能极高的零拷贝能力,还提供了统一的通信 API 和可高度扩展的事件驱动模型
输服务模块支持 了 TCP 和 UDP 等 Socket 通信,以及 HTTP 和同一 JVM 内的通信通道
协议支持模块则对常见 的序列化协议进行支持,如 Protobuf、gzip 等
Java 目前并不支持异步 I/O
第三方的 I/O 处理框架,如 Netty、Mina 等
序列化
分布式系统中的序列化主要用于应用程序进行网络通信时的数据交互
它所关注的重点是性能和对异构语言的支持能力
性能主要有三个衡量指标
对象序列化后的字节占位大小
对象序列化之后是一串字节数组,其中包含对象的属性值以及元数据信息
序列化与反序列化的性能
这主要取决于它生成和解析字节数组的方法
序列化工具自身的性能
主要取决于序列化工具创建自身对象的消耗
序列化格式
文本序列化
使用字符串文本的明文序列化方式
JSON
XML
Java JSON 序列化框架
流式解析 JSON 序列化框架 Jackson
在解析数据量比较大的 JSON 文本时比较有优势
轻量级 JSON 序列化框架 Gson
在处理数据量较大的 JSON 文本时,其性能略逊于 Jackson
FastJson
二进制序列化
文本格式由于未经压缩,其内容所占据的空间较大并且解析较慢
二进制Java序列化
高性能序列化框架 Kryo
在 Java 同构语 言的序列化框架中,Kryo 是一个理想的选择
对象序列化不会保存静态变量,因为它们是类的属性
二进制异构语言序列化
Protobuf
Google 开源的跨平台、跨语言的轻便高效的序列化协议
图
远程调用(RPC)
核心概念
抽象为五个概念模型
User:应用的客户端,RPC的发起者
它的职责是通过本地调用User-stub发起对远端的调用,并负责接收 User-stub 的返回值。是本地调用,还是远程调用,这对于 User 来说是完全透明的
User-stub:客户端的存根对象
存根对象的作用是通过使用本地模拟对象来屏蔽需要通过远程调用才可以获取的对象
第一件事情是将需要远程调用的接口、方法以及参数通过事先约定好的协议进行序列化
第二件事情是通过本地的 RPCRuntime 对象将序列化的数据传输到服务端的 RPCRuntime 对象中
第三件事是将服务端的返回值反序列化为 User 可以直接使用的对象
RPCRuntime:远程调用的运行时对象
它同时存在于客户端和服务端,负责网络间信息的发送与接收
Server-stub:服务端的存根对象
它负责将服务端的RPCRuntime对象接收到的数据进行反序列化并调用服务端的本地方法,以及将服务端本地方法的返回值序列化后交给服务端的 RPCRuntime
Server:应用的服务端
用于处理相关业务逻辑
客户端和服务端需要互相熟悉相同的业务方法接口。服务端需要将远程接口导出给客户端
图
对于一个完整的 RPC 调用来说,核心是通信、序列化和透明化调用
Java 远程方法调用
远程方法调用(Remote Method Invocation,RMI)
RMI 的运行机制与 RPC 中介绍的流程极为相似,只是在其基础上增加了 RMI Registry 这一概念
核心机制
图
开发流程
开发服务接口
实现服务业务逻辑
发布服务
客户端使 用服务
局限性
性能低
灵活性差
缺乏对异构语言的支持
综上所述,RMI 已经不适合作为现代应用系统的技术选型了
异构语言RPC框架gRPC
RESTful API 未必是互联网高并发场景下的合理选择
它使用 HTTP/2 进行网络通信,并将 Protobuf 作为其序列化工具
在 gRPC 里,客户端应用可以像调用本地对象一样调用处于另一台机器上的服务端应用方法
gRPC 解决了在不同语言及环境间通信时的复杂性和性能低下问题
图
图
第4章 服务治理
服务发现
服务发现概述
服务发现是指使用一个注册中心来记录分布式系统中的全部服务的信息,以便让其他服务能够快速找到这些已注册的服务
服务发现模块需要具有服务注册、服务查找、服务健康检查和服务变更通知等关键功能
应用无须以硬编码或配置文件的方式提供网络地址和端口号,而可以通过服务名称来查找和使用服务
服务的消费方无须了解整个架构的部署拓扑结构就可以找到该服务实例
提供一个高可用且网络位置稳定的服务注册中心是最常见的服务发现解决方案
基本机制
服务提供者在服务启动时,将服务名称、IP 地址、访问端口以及其他服务元数据信息注册到注册中心
注册中心与服务提供者无法维持心跳探测时会将服务从注册中心剔除
服务消费者从注册中心获取服务提供者的最新信息时,可以使用定期拉取和事件通知两种方式
服务发现解决方案
CAP 定理
在一个分布式的计算机系统中,只能同时满足一致性(Consistency)、可用性(Availability) 和分区容错性(Partition tolerance)这三个基本特性中的两个,这就是著名的 CAP 定理
一致性(Consisitency)
所有节点都能够在同一时间返回同一份最新的数据副本
可用性(Avalilability)
每次请求都能够返回非错误的响应
分区容错性(Partition Tolerence)
服务器间的通信即使在一定时间内无法保持畅通也不会影响系统继续运行
传统的关系型数据库选择的是 CA,即缺乏分布式的能力
如果选择一致性而牺牲可用性(选择 CP)的话
为了保证多台服务器上的数据一致, 一旦某台服务器宕机,所有的服务器都需要暂停对外提供数据写入服务
如果选择可用性而牺牲一致性(选择 AP)的话
为了保证服务不中断,当某台服务器宕机时,仍然存活的服务器可以选择先将数据写入本地然后直接返回客户端,但这样又将导致多服务器间的数据不一致
高可用
除了要保证本地多节点部署
通常还要跨多机房进行部署
需要在分布式场景下具备自我治愈和调整的能力
Zookeeper
提供了分布式通知和协调、配置管理、命名服务、主节点选举、分布式锁、分布式队列等完善的解决方案
其中分布式通知和协调被广泛用于服务发现
ZooKeeper 本质上来说是一个强一致性的产品,在集群发生分区时它会优先保证一致性而舍弃可用性(CP)
架构
图
Paxos算法和Zab协议
ZooKeeper 通过消息传递保持分布式节点之间的数据一致性
Zab 是 ZooKeeper Atomic Broadcast 的缩写,是专门为 ZooKeeper 而设计的支持崩溃恢复的原子广播协议
Paxos算法
目标是解决分布式一致性问题
它本质上是一个基于消息传递的高度容错的一致性算法
按照少数服从多数的原则最终达成一致
Zab协议
ZooKeeper 基于对 Paxos 进行裁剪的 Zab 协议,实现了一种主备模式的系统架构来保持服务端集群中各个副本之间的数据一致性
原理
三条原则
可靠递交(Reliable delivery)
如果消息 m 能被一台服务器递交,那么它将可以被其他所有的服务器递交
完全有序(Total order)
如果消息 a 在消息 b 之前被一台服务器递交,那么每台服务器都应该在递交消息 b 之前递交消息 a
因果有序(Causal order)
如果消息 a 在消息 b 之前发生,并和消息 b 一起被递交,那么消息 a 将始终在消息 b 之前被执行
核心概念
集群角色
分别是主节点(Leader)、从节点(Follower)和观察者节点(Observer)
观察者节点同样可以提供数据读取 服务,但是它不参与选举和投票,因此它可以在不影响写性能的前提下提升集群的读性能
服务器数量是奇数时被认为是一种最佳实践
会话
客户端与服务端是通过 TCP 建立长连接的
会话在服务发现中是用于探测服务是否存活的重要指标
数据节点
Znode 的数据模式结构是树形的,除根节点之外,其他节点都仅有一个父节点
Znode 将节点分为持久节点和临时节点
持久节点一旦被创建,只要不主动将其移除,那么它将永久存在于 ZooKeeper 的系统中
临时节点则与创建它的客户端会话相关联,一旦会话结束,客户端创建的临时节点也将被自动删除
只有持久节点可以拥有子节点, 临时节点不能拥有子节点
图
还可以为每个节点增加顺序属性
因此,经过排列组合,Znode 分为持久化节点、临时节点、持久化顺序节点和临时顺序节点这四种类型
监听
ZooKeeper 中的监听称为 Watcher
ZooKeeper 允许客户端在其感兴趣的 Znode 上注册监听器,该 Znode 的状态发生变更时,服务端将直接通知客户端进行处理
使用 Zookeeper 实现服务发现
会话、临时节点和临时节点的监听器,与服务发现有着密切的关系
图
图
会话超时时间是一个关键属性,它的长度将影响服务发现的敏感度
如果会话超时时间过长,系统将不会快速感知由服务宕机导致的服务列表变更,从而延长服务的不可用时长
如果会话超时时间过短,系统将会由于网络抖动和延迟变得异常敏感,服务列表可能会随着一次网络延迟而更新
客户端
常见的第三方客户端
ZkClient
Curator
优势与不足
它的优势主要体现在选举和分布式锁等分布式强一致性的场景中
当 ZooKeeper 的主节点因为网络故障与其他节点失去联系而触发整个系统选举时,集群是不可用的,这将导致注册服务体在选举期间瘫痪
Eureka
基于 AP 的服务发现组件
整体架构
它采用了去中心化的设计理念,整个服务集群由对等节点组成,无须像 ZooKeeper 那样选举主节点
集群中失效的节点不会影响正常节点对外提供服务注册和服务查询能力
Eureka 客户端有失效转移的能力,如果在向某个 Eureka 服务器注册服务时发现连接失败, 则会自动切换至其他节点
只要有一台 Eureka 服务器节点还能够正常工作,就无须担心注册中心的可用性
图
Eureka 几乎是最佳的注册中心。它是专门为服务发现而开发的项目,在这一点上,它与 ZooKeeper 和 etcd 有着本质的区别
服务端
Eureka 服务端使用互相注册的方式来实现部署的高可用性
注册在同一个集群中的 Eureka 服务端可以同步彼此之间的信息
Eureka 提供自我保护机制,可以在故障恢复后进行最终一致性状态合并,清理掉错误数据
客户端
Eureka 在客户端处理服务注册与发现
Eureka客户端向Eureka服务端注册自身的服务
在应用程序运行时周期性地发送心跳以更新服务租约
可以将 Eureka 服务端数据缓存至本地,以提高性能和可用性
拓展
除了 ZooKeeper 和 Eureka,etcd 和 Consul 也经常被当作服务发现的注册中心
etcd 与 ZooKeeper 具有相似的架构和功能,不同的是,etcd 使用更为简单的 Raft 算法代替了略为复杂的 Zab 协议
etcd 也是一个 CP 的系统,对一致性的要求强于对可用性的要求
Consul 除了提供服务发现功能,还提供了内存、磁盘使用情况等细粒度服务状态检测功能,以及用于服务配置的键值对存储功能
注册中心对比
图
负载均衡(Load Balance)
它是实现系统高可用、网络流量疏导和扩容的重要手段
负载均衡的本质是通过合理的算法将请求分摊到多个服务节点
DNS 缺乏对服务发现的应对能力,一 旦服务节点的启动和销毁变得更加频繁,DNS 就会无法应对,它的记录和传播速度无跟上服务节点的变化节奏
服务端负载均衡
服务端负载均衡又分为硬件负载均衡和软件负载均衡
硬件负载均衡
硬件负载均衡需要在服务器节点之间安装专用的负载均衡设备,常见的有 F5 等设备
软件负载均衡
常见的有 LVS、Nginx 等
图
四层负载均衡(传输层)
基于 IP 地址和端口号进行负载均衡
通过三层发布的 IP 地址加上四层的端口号来决定哪些流量需要进行负载均衡
四层负载均衡的性能强于七层负载均衡,F5 和 LVS 是四层负载均衡中最常用的产品
七层负载均衡(应用层)
基于 URL 和请求头等应用层信息进行负载均衡
七层负载均衡的优势是,能够充分理解应用层协议的意义,使转发更加灵活
Nginx 是七层负载均衡中最常用的产品
图
优点是对业务开发无侵入性
缺点是负载均衡服务器是整个系统处理的瓶颈
客户端请求需要先发送至负载均衡服务器,再由其进行二次转发,传输效率会受到一定影响
部署架构
图
服务端负载均衡多用于网站前端与后端交互、应用与数据库交互等场景
对于应用后端服务之间的调用,还是通过客户端负载均衡实现的方案居多
客户端负载均衡
微服务架构下,服务的动态伸缩使得服务的实例数量以及 IP 地址经常发生变化,面向静态的服务端负载均衡方案就无法胜任了
客户端负载均衡的最佳实践是与服务发现配合使用
客户端负载均衡是直接被连接至服务端
图
客户端负载均衡的优点
由客户端内部程序实现,无须额外部署负载均衡器
客户端和服务端是直接连接的,无须通过服务端负载均衡器进行二次转发,无网络间传输带来的损耗
无须考虑中心节点的高可用性
客户端负载均衡方案能够更加充分地利用服务发现的优势,进而提升整个服务集群的弹性伸缩能力
客户端负载均衡的缺点
使应用程序的复杂度增加
并且无法做到异构语言之间的负载均衡透明化
无中间节点的架构模型,以及过多的客户端与服务端的网状交叉访问,也会造成客户端和服务端节点连接数量的增加
对客户端是不透明的,客户端需要知道服务器端的服务列表,并且自行决定需要发送请求的目标地址
Ribbon
可以与服务发现的注册中心有效地整合在一起
还提供了连接超时和重试的能力
图
五个核心接口用于实现其负载均衡策略
ILoadBalancer
用于定义负载均衡器的操作,是负载均衡器的入口。由 IRule、IPing、ServerList 以及 ServerListFilter 组成
IRule
用于定义负载均衡规则
IPing
用于定义与服务端通信的方式,进一步判断服务存活与否
ServerList
这是一组服务器对象的集合,用于告诉 Ribbon 需要进行负载均衡的目标服务器列表
ServerListFilter
这是一组服务器对象的集合,允许动态获取具有所需特征的服务器列表
限流(又称为流量整形(Traffic Shaping))
能够平滑网络上的突发流量,并将突发流量整形为一个稳定的流量供网络使用
主要目的是保护后端的服务节点不被突然到来的流量洪峰冲垮
限流通过对一个时间窗口内的请求流量进行限速来实施对系统的保护,一旦达到限速阈值,则进入限流之后的处理流程
限流之后的操作主要有以下三种
拒绝服务
直接定向到错误页面,或告知用户当前资源已经没有了
排队等待
将客户端请求放入队列,在服务端有资源处理时再从队列中获取请求处理, 处理完毕再返回客户端
客户端通常会设置超时等待时间,请求失效后再次发起请求即可
应用降级
提供默认行为或数据,如默认显示无任何评论、库存有货等
限流算法
计数器限流算法
常用于限制服务端资源而非客户端请求
服务端资源包括数据库连接池和线程池等
不要将其浪费在精确控制海量用户请求的场景
漏桶算法(Leaky Bucket)
图
主要用于控制其他系统的回调洪峰
令牌桶算法(Token Bucket)
图
针对用户洪峰
限流实现方案
客户端限流
通过限制客户端对服务端的访问来实现的
Guava 类库中有一个 RateLimiter 类
客户端限流作为第一道屏障可以将多余的流量丢弃以节省带宽,服务端限流或接入端限流作为最终屏障,可以保证独立服务的健壮性
服务端限流
在服务端采取的对资源进行保护的限流方案
如 Tomcat、Dubbo
如 MySQL、MongoDB、Redis 等
除了中间件和数据库产品,也有独立的服务端限流组件
Zuul
它能够在服务化架构中提供动态路由、监控、安全验证等边缘服务
可以通过加载动态过滤器的机制来实现各项功能
安全校验
用于识别面向资源的验证
精确路由
以更加灵活的方式将请求路由至后端服务集群
精准限流
以更加灵活的方式弃用超过阈值的请求
压力测试
通过编程方式逐渐增加指向集群的负载流量
Metrics统计
在网关中追踪和统计访问数据
Zuul 定义了四种标准的过滤器类型,对应请求的生命周期
Pre
请求被路由前调用,典型的使用场景是身份验证、精准路由以及记录调试等
Routing
将请求路由至业务应用时调用,典型的使用场景是使用 Ribbon 请求后端服务
post
请求执行完毕后调用,典型的使用场景是收集调用耗时信息
error
发生错误时调用
图
接入端限流
接入端是流量的入口,通常是通过负载均衡服务器来实现的,比如 F5 和 Nginx
接入端处理负载的能力远远高于应用端
四层负载均衡器只能无差别限流,无法根据业务需要灵活地将某些流量放入后端系统
四层负载均衡器通常完成紧急情况下的限流
比如当后端服务集群完全崩溃时,通过 F5 阻挡住大部分的流量,可以让整个集群有机会恢复启动,而不至于在每启动一个服务实例时都立刻被流量洪峰所压垮
限流通常是通过七层负载均衡器来实现的
Nginx 作为当前最常用的七层负载均衡器,内置了连接数限流和请求限流两个模块
ngx_http_limit_conn_module
ngx_http_limit_req_module
OpenResty
限流的维度与粒度
限流维度
可以理解为用于限流的关键字段
客户端访问的 IP地址
请求的目标 URL
用户令牌
用户组
设备信息
限流粒度
集群粒度
一般用作兜底的保护措施,使系统不会被突然流量洪峰压垮
服务粒度
在服务化的体系结构中最为常见
接口粒度
熔断(也称为自动停盘机制(circuit breaker))
概述
属于服务化中流量调控的范畴
在流量过载的情况下禁止客户端对服务端进行访问,是熔断的目的所在
限流是允许部分流量通过,而熔断则是完全禁止某客户端访问后端服务,它们的目的都是防止流量洪峰压垮整个集群
熔断还可以防止连锁失效(Cascading Failure)
熔断的粒度越细越好
熔断器模式
熔断器有三种状态
关闭状态(Closed)
这时熔断器对应用程序的请求无任何干涉
开启状态(Open)
禁止应用程序访问远端服务时的状态
等待超时结束后自动切换为半开启状态
半开启状态(Half-Open)
允许少量请求调用服务,如果调用结果符合预期,则认为服务端的问题已被修正,此时熔断器会切换至关闭状态
如果仍有调用失败或超时现象,则认为服务端的问题仍然存在,此时熔断器会切换至开启状态,并重新计时
Hystrix
它提供了熔断、隔离、失效转移、缓存和监控等功能,能够在依赖出现问题时保证系统的可用性
Hystrix 采用了舱壁隔离模式
两种解决方案
线程隔离
支持异步调用
信号量隔离
不支持异步调用
合理地使用熔断可以让系统更加具有柔性,在系统从错误中恢复时提供流量保护,并且减少错误对系统性能的影响
第6章 侵入式服务治理方案
Dubbo(TODO)
Spring Cloud
Spring Cloud 遵循以下设计理念
约定优于配置
提供声明式的元注解配置方式,隐藏组件具体实现的复杂度
提供丰富的组件
灵活的解耦
Neflix OSS
开发脚手架 Spring Boot
自动装配
Spring Boot 对 Spring 进行封装,通过约定优于配置以及元注解驱动的设计理念可以简单地开发出风格一致的应用程序
@SpringBootApplication 是@Configuration、@EnableAutoConfiguration 和@ComponentScan 这三个注解的组合
@Configuration
用于标识目标类可以作为 Spring 容器 Bean 的定义来源
@EnableAutoConfiguration
可以自动配置 Spring 上下文
@ComponentScan
会自动扫描包含 Spring 元注解在内的各个 Bean 并自动装配
暴露端点
Spring Boot 提供的 Actuator 模块用于方便地暴露应用自身的信息,以便监控与管理
/beans
bean 的运行时报告
/env
环境属性信息
/autoconfig
自动化配置信息
/configprops
属性配置信息
/mappings
Spring MVC 映射配置信息
/metrics
返回当前应用的各项度量指标,包括系统负载、内存使用情况、JVM 堆使用情况、线程运行状况、Web 容器使用情况等
/dump
暴露应用的线程快照
/trace
暴露 HTTP 请求的跟踪信息
/health
暴露应用的各种健康指标
/shutdown
用于优雅关闭运行中的应用
服务发现
通过与注册中心协调来实现服务发现
使用 Eureka
使用 ZooKeeper
使用 Consul
负载均衡
Ribbon 组件,更加方便地提供了客户端负载均衡的解决方案。它可以自动从 Eureka 中获取服务提供者列表,将负载均衡与服务发现结合起来应用
Ribbon 的服务列表将由 Eureka 负 责自动填充和维护
熔断
Hystrix 作为微服务的熔断器,并通过它实现熔断后的业务降级
远程通信
采用当前较为流行的 RESTful API 进行服务之间的交互
Feign 是一个声明式的 Web 服务客户端
它采用Spring Web的HttpMessageConverters将网络间传输的消息自动编码和解码成对象或 JSON 字符串
Feign 还整合了同为 Netflix OSS 的 Ribbon 和 Hystrix,让使用 Feign 的应用具备负载均衡和熔断的能力,Ribbon 也可以过自动整合 Eureka 让应用同时具备服务发现能力
通过@FeignClient 声明的接口在访问目标服务时无须编写实现代码
Feign 配置项有
Decoder
Encoder
Logger
Contract
Feign.Builder
Client
Logger.Level
Retryer
ErrorDecoder
Request.Options
Collection<RequestInterceptor>
SetterFactory
总结
侵入式服务框架会或多或少地改变业务应用的开发方式
会增加应用开发的复杂度
第8章 跨语言服务治理方案 Service Mesh
Service Mesh 概述
Service Mesh 是新兴的微服务架构,被誉为下一代微服务
Service Mesh 的由来
Service Mesh 的定义
Linkerd 是业界第一个 Service Mesh 项目
服务网格是一个基础设施层,用于处理服务间通信。现代云原生应用有着复杂的服务拓扑结构,服务网格负责在这些拓扑结构中实现请求的可靠传递。在实践中,服务网格通常被实现为一组轻量级网络代理,它们与应用程序部署在一起,对应用程序是透明的
Service Mesh 详解
单个服务调用
图
通过在请求调用的路径中增加Sidecar,将原本由客户端完成的复杂功能下沉到 Sidecar 中,实现对客户端的简化和服务间通信控制权的转移
多个服务调用
图
大量服务调用
图
Service Mesh 定义回顾
抽象
Service Mesh 是一个抽象层,负责完成服务间通信
Service Mesh 将这些功能从应用中剥离出来,形成了一个单独的通信层,并将其下沉到基础设施层
功能
Service Mesh 负责实现请求的可靠传递
部署
Service Mesh 在部署上体现为轻量级网络代理,以 Sidecar 模式和应用程序一对 一部署,两者之间的通信是远程调用的,但是要通过 Localhost
透明
Service Mesh 对应用程序是透明的,其功能实现完全独立于应用程序
好处
这样带来的一个巨大优势是,Service Mesh 可以独立部署升级、扩展功能、修复缺陷, 而不必改动应用程序
图
Service Mesh 演进历程
远古时代的案例
开发人员需要在应用代码里处理网络通信的细节问题,比如数据包顺序、流量控制等问题
将业务逻辑和底层网络通信细节解
微服务时代的现状
在实现微服务的时候要处理一系列的比较基础和通用的事项,如服务发现;在得到服务器实例列表之后再做负载均衡;为了保护服务器要进行熔断、重试等
为了简化开发,避免代码重复,我们选择使用类库,如经典的 Netflix OSS 套件
侵入式框架的痛点
这些类库还是渗透进了打包部署之后的业务应用程序,和业务应用程序运行在同一进程内
痛点
痛点 1:门槛高
图
痛点 2:功能不全
图
痛点 3:无法跨语言
图
痛点 4:升级困难
图
解决问题的思路
图
问题的根源在哪里?
这些问题都属于服务间通信的范畴,和应用本身的实现逻辑无关
我们的目标是什么?
为了保证将客户端发出的业务请求发送到正确的目的地
服务间通信的本质是什么?
服务间通信实现的是请求的可靠传递
有什么内容是普适的?
适用于所有的语言、框架、组织,这些问题对于任何一个微服务都是同样存在的
Proxy 模式的探索
Proxy 模式来隔离客户端和服务器端
Nginx、HAProxy、Apache 等 HTTP 反向代理
在服务器端和客户端 之间插入一个中间层来完成请求转发的功能,避免两者直接通信
Sidecar 模式的出现
受限于 Proxy 的功能不足,在参考 Proxy 模式的基础上,又陆陆续续出现了一些使用 Sidecar 模式的产品
如 Netflix 的 Prana
Sidecar 的基本思路是,看齐原来侵入式框架在客户端实现的各种功能,通过增加一个 Proxy 实现请求转发,然后直接重用原有的客户端类库
Sidecar 模式和 Proxy 模式的差异主要体现在功能是否齐全上
Proxy只具备基本的简单功能
Sidecar具备侵入式框架的所有功能
第一代ServiceMesh
Linkerd
Envoy
Service Mesh 和 Sidecar 的差异在于是否通用
Sidecar为特定的基础设施而设计,只能运行在原有环境中
ServiceMesh在Sidecar的基础上解决了通用性问题,可以不受限于原有环境
Sidecar 通常是可选的,允许直连
在 Service Mesh 中,由于要求完全掌控所有流量,所以所有请求都必须通过 Service Mesh 进行转发,而不能选择直连方式
第二代ServiceMesh Conduit
第二代 Service Mesh 和第一代 Service Mesh 的差异在于是否有控制平面
第一代 Service Mesh 只有数据平面(Sidecar),所有的功能都在 Sidecar 中实现
第二代 Service Mesh 增加了控制平面,带来了远超第一代的控制力,功能也更加丰富
Istio 最大的创新在于,它为 Service Mesh 带来了前所未有的控制力
以 Sidecar 方式部署的 Service Mesh 控制了服务间所有的流量
Istio增加了控制面板来控制系统中所有的Sidecar
Istio能够控制所有的流量,即控制系统中所有请求的发送
Istio
Service Mesh 市场竞争
Istio
Istio 概述
一个连接,管理和保护微服务的开放平台
Istio 有哪些功能
连接
智能控制服务之间的流量和 API 调用,进行一系列测试,并通过红/黑部署逐 步升级
保护
通过托管身份验证、授权和服务之间通信加密,自动保护服务
控制
应用策略并确保其执行,使得资源在消费者之间得到公平分配
观测
通过自动跟踪、监控和记录所有服务,了解正在发生的情况
Istio 的设计目标
最大化透明度
增量
可移植性
策略一致性
架构和核心组件
Istio 分为数据平面和控制平面两个部分
数据平面
是以 Sidecar 方式部署的智能代理,Istio 默认集成的是 Envoy。数据平面用来控制微服务之间的网络通信,以及和 Mixer 模块的通信
控制平面
负责管理和配置数据平面,控制数据平面的行为
如代理路由流量、实施策略、收集遥测数据、加密认证等
包含 Pilot(领航员)、Mixer(混合器)、Citadel(大本营) 三个主要组件
图
Envoy(通信)
主要提供服务间通信的能力
还提供了和网络通信直接相关的各种功能
服务发现(从 Pilot 得到服务发现信息)
负载均衡
健康检查
熔断
高级路由(路由规则由 Polit 下发)
基于百分比的流量拆分
加密和认证
故障注入
还要完成对请求属性的提取
这些属性可以通过 Istio Proxy 的 Mixer Filter 发 送给 Mixer,用于执行策略决策、配额检查等行为
Mixer(策略控制、遥测手机)
负责提供策略控制和遥测收集的组件
职责主要有以下三点
Check(也称为 precondition,前置条件检查)
允许服务在响应来自服务消费者的请求之前验证一些前置条件。前置条件包括认证、黑白名单、ACL 检查等
Quota
使服务能够在多个维度上分配和释放配额,典型例子是限速
Report
遥测报告,使服务能够上报日志和监控,通常包括Metrics、Logging、Distribution Trace
Mixer 和 Envoy 的交互
图
在传统设计中,服务直接与访问控制系统、遥测捕获系统、配额执行系统、计费系统等后端系统集成时容易产生硬耦合
为了避免应用程序的微服务和基础设施的后端服务之间直接耦合,Istio 提供了 Mixer 作为两者的通用中介层
Mixer 将策略决策从应用层移出并用配置替代,由运维人员控制
应用程序不再直接与特 定后端集成在一起,而是与 Mixer 进行简单的集成,然后 Mixer 负责与后端系统连接
Mixer 的设计目标是减少业务系统的复杂性, 将策略逻辑从业务的微服务代码转移到 Mixer 中, 并且改为由运维人员控制
设计理念
图
Mixer 提供了以下能力
后端抽象
Mixer 隔离 Istio 的其余部分和各个基础设施后端的实现细节
中介
Mixer 允许运维人员对服务网格和基础设施后端之间的所有交互进行细粒度的控制
适配器的设计和实现,使 Mixer 能够对外暴露一致的 API,而与具体使用的后端基础设施直接耦合
Pilot(服务发现、流量管理、路由规则)
承担的主要职责是向Envoy Sidecar发送服务发现信息和各种流量管理及路由规则
另外,Pilot 对外向更高层的用户暴露高级的 Rules API,用于接收控制流量行为的高级路由规则
Pilot 与 Envoy 的交互
图
各个组成部分及职责
Envoy API
负责和Envoy通信,将服务发现信息和流量控制规则发送给Envoy
Abstract Model
Pilot 定义的服务抽象模型,可以从特定平台细节中解耦,为跨平台提供基础
Platform Adapter
上述抽象模型的实现版本, 用于对接外部的不同平台,如 Kubernetes、 Mesos 等
RulesAPI
提供接口给外部调用,以管理Pilot,包括命令行工具Istioctl以及未来可能出现的第三方管理界面
可移植性便是 Pilot 的设计重点
Citadel(安全)
负责安全性的组件
安全
加密
为了不泄露信息,并且抵御中间人攻击,需要对服务间通信进行加密
访问控制
不是每个服务都容许被任意访问,因此需要提供灵活的访问控制,如双向TLS 和细粒度的访问策略
审计
如果需要审核系统中哪些用户做了什么,则需要提供审计功能
由于服务间通信都在Sidecar 的控制当中,因此,要想实现加密,只需要在 Sidecar 之间实现即可
图
组件配合
Citadel
用于密钥管理和证书管理,下发到Envoy等负责通信转发的组件
Envoy
使用从 Citadel 下发而来的密钥和证书,保障服务间通信的安全
注意在应用 和 Envoy 之间是通过 Localhost 回环地址通信的,通常不用加密
Pilot
将授权策略和安全命名信息分发给Envoy
Mixer
负责管理授权、完成审计等
Istio 支持的安全类功能
流量加密
加密服务间的通信流量
身份认证
通过内置身份和凭证管理提供强大的服务间和最终用户身份认证能力,包括传输身份认证和来源身份认证,支持双向 TLS
授权和鉴权
提供基于角色的访问控制(RBAC),提供命名空间级别、服务级别和方法级别的访问控制
第10章 分布式数据库中间件生态圈 ShardingSphere(TODO)
缘起
核心功能
数据分片
分布式事务
数据库治理
Sharding-JDBC
概述
使用说明
Sharding-Proxy
概述
使用说明
Database Mesh
概述
Service Mesh 回顾
Database Mesh 与 Service Mesh 的异同
Sharding-Sidecar
未来规划
第1章 云原生
企业级开发和互联网开发
SOA、DevOps、容器、CI/CD、微服务、Service Mesh 等概念层出不穷,而 Docker、Kubernetes、Mesos、Spring Cloud、gRPC、Istio 等一系列产品的出现,标志着云时代已真正到来
互联网架构变迁
互联网架构的核心问题
海量用户
产品迅速迭代
7 x 24 小时不间断服务
流量突增
业务组合复杂
衍生出的问题
数据海量
响应延迟
稳定性差
伸缩性差
系统繁多
开发困难
从集中式架构到分布式架构
传统的三层架构模型
分布式架构、SOA和服务化
分布式
垂直伸缩
增加服务器配置
水平伸缩
增加服务器数量
难题
CAP 定理
C(Consisitency)一致性
A(Availability)可用性
P(Partition Tolerance)分区容错性
使用最终一致性代替传统事务的ACID强一致性
SOA 面向服务架构
约等于模块化开发 + 分布式计算
自动化运维
自动化运维工具主要分两类
监控自动化工具
Zabbix
流程自动化工具
Ansible
解放交付的 DevOps
开发部门的驱动力通常是频繁交付新特性,而运维部门则更关注服务的可靠性。两者目标
的不匹配使得部门之间产生了鸿沟,从而降低了业务交付的速度与价值。
的不匹配使得部门之间产生了鸿沟,从而降低了业务交付的速度与价值。
DevOps 在软件开发和交付流程中强调“在产品管理、软件开发以及运维之间进行沟通与协作”
从分布式架构到云原生架构
IaaS(关注硬件基础设施的基础设施即服务)
PaaS(关注软件和中间件平台的平台即服务)
SaaS(关注业务应用的软件即服务)
容器 + 编排调度
新纪元的分水岭-容器技术
Docker 提供了让开发工程师将应用和依赖封装到一个可移植容器中的能力
Docker 通过集装箱式的封装方式,让开发工程师和运维工程师都能够以 Docker 提供的“镜像+分发”的标准化方式发布应用
新纪元的编排与调度系统
Kubernetes
Mesos
Swarm
为云原生应用提供了强有力的编排与调度能力,它们是云平台上的分布式操作系统
架构设计的变革-微服务
每个服务运行在自己的进程中
服务间采用轻量级的通信机制,例如 RESTful API
可以使用不同的开发语言和数据存储技术
自由的将资源分配到所需的应用中,而无需扩展整个应用
围绕业务组织团队,而不是围绕技术组织团队
需要额外处理分布式开发和运维工作包括
配置管理
使用集中化的配置中心来存储配置
服务发现
负载均衡
弹性扩缩容
分布式调用追踪
日志中心
自愈能力
从分布式中间件向云原生中间件变迁,便是本书的重点
什么是云原生
概述
云一般指的是一个提供资源的平台,云计算的本质是按需提供资源和弹性计算
云原生应用即专门为在云平台部署和运行而设计的应用
云原生存在的意义是解放开发和运维
让应用能够利用云平台实现资源的按需分配和弹性伸缩,是云原生应用被重点关注的地方
云原生使得应用本身具有“柔性”,即面对强大压力的缓解能力以及压力过后的恢复能力
从本质上来说,云原生是一种设计模式,它要求云原生应用具备可用性和伸缩性,以及自动化部署和管理的能力,可随处运行,并且能够通过持续集成、持续交付工具提升研发、测试与发布的效率
云原生与十二要素
十二要素核心思想和方法论
将流程自动化和标准化,降低新员工的学习标准
划清与底层操作系统的界限,以保证最大的可移植性
适合部署在现代云平台上,避免对服务器和操作系统进行管理
将开发环境和生存环境的差异降到最低,便于实施持续交付和敏捷开发
应用可以在不改变现有工具、架构、流程的情况下,方便的进行水平伸缩
十二要素具体内容
基准代码(CodeBase)
使用源代码管理工具
Git
依赖(Dependencies)
显示声明第三方依赖
Maven
配置(Config)
将配置存储到环境变量
后端服务(Backing Services)
将后端服务作为松耦合的资源
构建、发布、运行(Buid、Release、Run)
严格分离构建阶段和运行阶段
要严格区分应用的非运行时状态和运行时状态
进程(Processes)
将应用作为无状态的进程运行
会话数据应该保存至 Redis 这样的带有过期时间的缓存中,并作为后端服务提供服务
端口绑定(Port Binding)
通过端口绑定对外发布服务
并发(Concurrency)
能够通过水平伸缩应用程序进程来实现并发
已处理(Disposability)
可以快速启动和优雅关闭应用
开发环境和生产环境等价(Dev/Prod Parity)
要保持开发环境和生产环境等价
日志(Logs)
使用事件流处理日志
应用将日志输出到标准输出(STDOUT),然 后由云平台统一收集并处理
管理进程
将后台管理任务当作一次性进程运行
总结
遵循十二要素的应用程序环境是一次性且可复制的。由于应用程序的状态均通过后端服务
持有,因此无状态的应用有助于编排系统自动化扩展。扩容时,编排系统仅须将应用程序的运
行时环境数量扩充到期望位并直接启动进程即可;缩容时,则需要停止应用进程并删除环境,
无须进行环境状态备份。
持有,因此无状态的应用有助于编排系统自动化扩展。扩容时,编排系统仅须将应用程序的运
行时环境数量扩充到期望位并直接启动进程即可;缩容时,则需要停止应用进程并删除环境,
无须进行环境状态备份。
十二要素进阶
优先考虑 API 设计(API First)
尽量避免废止原有的 API
API 进行改动都需要做到向后兼容,并为每次改动提供唯一的版本号
应尽量避免废止原有的 API,以及修改原有 API 中已经存在的属性,而应该通过增量的方式增加新的功能
通过遥测感知系统状态(Telmetry)
云原生应用需要 通过遥测来感知应用以及服务器本身的状况,暴露尽量多的监测信息为运维工程师或云调度系 统提供判断和处理问题的依据
应用本身则需要提供包括 APM 信息、健康检查、系统日志在 内的采集数据
认证和授权(Authentication and Authorization)
采用 OAuth2 认证和 RBAC 授权等比较 完善的安全机制
云原生与 CNCF
应用定义与开发层
数据库与数据分析
流式处理
软件配置管理
应用定义
持续 集成/持续交付
编排与治理层
调度与编排
调度是将分布式系统中的闲置资源合理分配给需要运行的进程并采用容器进行封装的过程
编排则是对系统中的容器进行健康检查、自动扩缩容、自动重启、滚动发布等的过程
分布式协调与服务发现
服务管理
远程通信
服务治理
反向代理
运行时层
云原生存储
容器
云原生网络
供应保障层
宿主机管理工具
基础设施自动化工具
容器仓库
镜像安全
密钥管理
云设施层
观察与分析
监控
日志
追踪
平台
第3章 配置
本地配置
使用配置文件
配置集中化
代码与配置一同部署的集中式系统模式中存在的问题
配置修改的工作量大
配置修改可能发生遗漏而导致环境不一致
各节点配置不一致的时间差长
配置修改无法动态生效
直接修改配置文本信息产生的错误难于校验
配置中心不但能够解决本地配置的问题,还能够提供额外的便利
配置修改的工作量减少
配置修改不可能遗漏
各节点配置时间差基本一致
配置修改动态生效
可以在输入时进行规则校验,避免常见错误
配置信息可以像业务数据一样被持久化保存
多个系统配合上线时,配置检查、沟通协调变得更加容易
业务应用系统将配置信息放置于应用程序之外,更容易保持应用的无状态化
配置中心和注册中心
注册中心用于分布式系统的服务治理,多用于管理运行在当前集群中的服务的状态,需要随时进行动态更新
关注的是配置本身,相比于状态,配置是更加静态和具象的事物
配置的三个要素
快速传播
配置信息服务节点收到信息后会调整所属应用的行为,这个过程需要尽可能地整齐划一
状态信息则是满足条件或感兴趣的相关服务节点才需要订阅和处理的
变更稀疏
通俗地讲,就是指配置发生变更的情况非常少
环境相关
读性能
采用配置中心的方案之后,由于远程调用而导致的性能下降和配置中心本身的单点访问压力
用什么方式提升读性能并降低配置中心的压力呢?
使用缓存
位于配置中心的集中式缓存
集中式缓存的优点是,每次客户端进行访问时都可以获取到最新的数据,缺点是并未缓解配置中心的访问压力
位于应用端的本地缓存
本地缓存的优点是通过减少远程调用进一步提升了访问效率,并且有效缓解了对配置中心的访问压力。
缺点是数据存在多份,数据的一致性和更新的及时性会受到一定的影响
变更实时性
监听
定时同步
可用性
服务冗余
基于主节点提供服务
此方案的优点是实现简单且更容易维持分布式状况下的数据一致性。缺点是只有主节点可以提供服务,这样会造成资源浪费,而单台节点提供的 TPS 也有最大限制
对于读多写少、数据量和访问量都可控的配置中心来说,基于主节点提供服务是非常适合的解决方案
基于对等节点提供服务
缓存
离线模式
如果配置中心完全不可用, 新的调度任务上线将不能读取到配置
数据一致性
ACID
配置数据并不需要基于 ACID 的事务
BASE
柔性事务场景而言,一致性状态没有时间的保证,因此也不适合用于处理相对敏感的配置信息
状态机同步
通过状态机保证数据一致性的处理方式,无论是在一致性上还是在性能上,都更加适合配置中心
ZAB
Raft
第5章 观察分布式服务(Observability)
层次划分
基础设施层
对云主机、操作系统、云服务进行包括可用性在内的基础指标监控,提供云服务商的基础运维支撑能力
工具层
编排工具的可观察性是微服务体系中的重要一环
DevOps 体系
应用环境层
是指对应用服务器、数据库、消息队列、缓存等中间件组件进行观察
总结
因此对于微服务和云原生应用开发者来说,关注点应集中在应用环境层
核心概念
日志(Logging)
日志描述的是一些不连续的离散事件
ELK
指标(Metrics)
指标是可累加的,它具有原子性
每个指标都是一个逻辑计量单元,体现了一段时间之内相关指标的状态
Prometheus
追踪(Tracing)
是指在单次请求范围内处理信息
任何的数据和元数据信息都被绑定到了系统中的单个事务上
ZipKin
分布式追踪
概述
Dapper
核心实现方法是在分布式请求的上下文中加入 span id 以及 parent id,用于记录请求的上下级关系
图
常见的开源解决方案
Apache ZipKin
OpenTracing(接口协议),类似于 Java 的数据库访问接口 JDBC
Jaeger(猎人)
OpenCensus
应用性能管理与可观测性平台
APM(Application Performance Monitoring,应用性能管理)
分布式追踪
非侵入式的语言探针
轻量化
低延迟分析
Apache SkyWalking
Skywalking5 核心架构(APM)
基于语言探针的 APM 解决方案
主要由探针层、分析层、可视化层这三部分组成
探针层
采集并上报观测数据
分析层
提供数据汇集能力、数据分析能力、数据存 储能力和查询能力
可视化层
即 UI 层
图
Skyewalking6 可观测性分析平台(OAP)
三个层次
可观察性分析平台(Observability Analysis Platform,OAP)
不局限于以往的基于多语言探针的思想,它面向多数据源
图
两个维度
Tracing追踪链路数据
Metric指标数据
三个层次
Receiver接收器
针对不同协议提供解析和适配的能力
Analysis Core 分析内核
提供面向 Source 分析源的流式分析方法,并派生出 OAL (Observability Analysis Language,可观察性分析语言)来描述流式分析
Query Core 查询内核
基于拓扑结构、基础数据和指标数据等多个维度提供基于 GraphQL 的查询,为页面和第三方系统集成提供支持
SkyWalking OAL
OAP 助力 Service Mesh
做到透明观测, 实现监控
图
可切换存储
提供了标准的存储层模块接口,并原生提供了多种存储方案
存储模块需要具备以下基本能力
结构化数据存储能力和更新能力
大字段存储能力
直接命中查询能力
聚合查询和排序能力
可切换视图 UI
GraphQL 是一种运行时数据查询 API 风格
具备数据类型定义能力
单次请求返回多次查询结果
第7章 云原生生态的基石 Kubernetes
Kubernetes 架构
谈到云计算就必然谈到分布式,Kubernetes 作为云原生计算的基础组件,本身也采用了分布式架构
图
几个核心组件
etcd
协同存储,负责保存整个集群的状态,通常会部署奇数个节点以保证高可用性
API
提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制
Controller manager
负责维护集群的状态,执行故障检测、自动扩展、滚动更新等操作
Scheduler
负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上
Kubelet
作为工作节点负责维护容器的生命周期,同时也负责对容器存储接口(CSI) 和容器网络接口(CNI)进行管理
容器运行时(Docker)
负责镜像管理,实现 Pod 和容器的真正运行
Proxy
负责提供集群内部的服务发现和负载均衡
推荐使用的插件
CoreDNS
负责为整个集群提供DNS服务
Ingress Controller
负责为服务提供外网入口
Prometheus
负责资源监控
Dashboard
负责提供GUI
Federation
负责提供跨可用区的集群
分层设计理念及架构模型
图
包含以下几部分
核心层
Kubernetes 的核心,负责对外提供 API 构建高层应用,对内提供插件式应用执行环境
应用层
负责部署和路由
可部署的应用包括无状态应用、有状态应用、批处理任务、集群应用等
路由的类型主要有服务发现、DNS 解析等
管理层
负责自动化(如自动扩展、动态部署等),以及策略(RBAC、ResourceQuota、 NetworkPolicy 等)管理
接口层
主要包括 kubectl 命令行工具、客户端 SDK 等用于客户端操作的库,以及集群联邦等实用工具
云原生生态系统
接口层之上的负责容器集群管理调度的生态系统
Kubernetes 内部
CRI(容器运行时接口)
CNI(容器网络接口)
CSI(容器存 储接口)
镜像仓库
云供应商
身份供应商
设计哲学
并未采用常见的命令式设计模式,而是采用了声明式设计模式
运行云原生应用程序的基础设施与传统应用程序的基础设施不同。基础设施不仅仅提供了应用运行所需的资源,还承担了许多对应用程序进行生命周期管理时的责任
采用 CRD 声明式配置方式时,用户可以利用 Kubernetes 的原语快速编排出一个云原生应用
总结
云原生应用程序通过将应用分解为更小的服务来降低代码复杂性,并且可以借助统一的日 志、监控、审计平台加强应用程序的可观察性
Kubernetes 中的原语
Kubernetes 中的对象
在 Kubernetes 系统中,对象是持久化的条目
描述了如下信息
哪些容器化应用在运行,运行在哪个 Node 上
与应用表现相关的策略
重启策略
升级策略
容错策略
Kubernetes 中的对象是“目标状态的声明”
常见对象
资源对象
Pod 、 ReplicaSet 、 ReplicationController 、 Deployment 、 StatefulSet 、 DaemonSet 、 Job 、 CronJob 、 HorizontalPodAutoscaling、Node、Namespace、Service、Ingress、Label、CustomResourceDefinition
存储对象
Volume、PersistentVolume、PersistentVolumeClaim、Secret、ConfigMap
策略对象
SecurityContext、ResourceQuota、LimitRange
身份对象
ServiceAccount、Role、ClusterRole
对象的期望状态与实际状态
spec 描述了对象的期望状态
由用户通过配置提供
status 描述了对象的实际状态
由 Kubernetes 负责提供和更新
描述Kubernetes对象
Pod 是 Kubernetes 中的基本调度单位,就像每个应用容器一样,Pod 也是非持久的实体
apiVersion
创建该对象所使用的KubernetesAPI版本
kind
期望创建的对象类型
metadata
帮助识别对象唯一性的数据,包括一个 name 字符串、UID 和可选的 Namespace
服务发现与负载均衡
针对容器的服务发现与负载均衡机制
提供了 Service 资源,并通过 kube-proxy 配合云供应商来适应不同的应用场景
Kubernetes 中的服务发现和 负载均衡机制大致可以分为以下几种
Service
提供集群内部的负载均衡,并借助云供应商提供的负载均衡 器将集群服务暴露出来供外部客户端访问
Ingress
依然使用Service提供集群内部的负载均衡,但是要自定义负载均衡器让外部 客户端访问集群的服务
Traefik Ingress Controller
Nginx Ingress Controller
Custom Load Balancer
采用自定义负载均衡替代 kube-proxy
安全性与权限管理
网络隔离
需要使用基于 CNI 协议的网络插件,如 Flannel、Calico
资源隔离
Pod 就是资源隔离和调度的最小单位,同时, Kubernetes 还能够使用 Namespace 限制用户空间和资源限额
身份隔离
使用 RBAC(Role-Based Access Control,基于角色的权限访问控制)对多租户的身份和权限进行控制
图
各层
Container
容器内核级别的资源隔离和安全性隔离
Pod
Namespace以及共享网络的隔离
使用如Flannel、Calico等网络插件可以为每个 Pod 分配一个集群内唯一的 IP 地址,实现跨节点的互联访问
使用 Service Account 可 以为 Pod 分配账户,使用 RBAC 可以为 Pod 分配角色和绑定权限
Sandbox
运行节点级别的资源隔离,是对最小资源调度单位的抽象
Node
网络隔离。每个节点间的网络是隔离的,可以使用节点的IP地址互联
Cluster
元数据隔离。使用Federation可以将不同的集群联合在一起
Sidecar 设计模式
Sidecar 进程与主应用程序是松散耦合的,它的主要理念是将代理进程作为 Sidecar,并且与应用程序运行在同一个 Pod 中
优势(?)
透明化
Pod 中的容器对基础设施可见,以便基础设施能够为这些容器提供服务,例如进程管理和资源监控
解耦软件依赖
每个容器都能够进行版本管理,并且可以独立地编译和发布
使用便捷
用户不必运行自己的进程管理器,也无须担心信号传播错误等问题
效率提升
由基础设施承担更多工作,容器会变得更加轻量级
应用 Kubernetes
Kubernetes 作为云上的操作系统,能够保证用户的应用在不同的云环境中使用相同的描述语言
遗留应用迁移至 Kubernetes 集群
将原有的应用拆解为服务
定义服务接口/API 的通信方式
编写启动脚本作为容器进程的入口
准备应用的配置文件
将应用容器化并制作容器镜像
准备 Kubernetes 所使用的 YAML 文件
如果有外置的配置文件,需要创建 ConfigMap 或 Secret 存储
Kubernetes 与云原生生态
Istio 这样的 Service Mesh 技术便成了解决云原生中的连接、保护、控制和观察服务的重要基础设施
缺失一个重要能力——微服务治理能力
本身并未提供 CI/CD 流程
让云应用具有跨云无缝迁移的能力
如果 Kubernetes 被企业大量采用,将会重塑企业的 IT 价值,使 IT 成为影响业务速度和健壮性的中流砥柱
未来趋势
引入服务网格(Service Mesh)
在 Kubernetes 上实现微服务架构以及进行服务治理时, Service Mesh 是必需的组件
落地无服务器架构(Serverless)
以 FaaS 为代表的无服务器架构将会逐渐流行
加强数据服务承载能力
我们可以通过在 Kubernetes 上运行大数据应用来实现这一点
简化应用部署与运维
包括简化云应用的监控、日志收集分析等
第9章 云原生数据架构
关系型数据库尚能饭否
优势
开发优势
面向 SQL
各种开发语言对关系型数据库的支持也十分完善
JDBC 是 Java 语言访问数据库的标准接口
对象关系映射(ORM)框架用于解决关系对象模型阻抗不匹配的问题
ORM 框架大多采用 JDBC 封装,对各个关系型数据库的兼容性非常高
运维优势
比较容易地招聘到相应的数据库管理员(DBA)
有完善的生态圈,实现数据备份、性能监测分析等功能
系统优势
基于 MVCC 的数据库引擎在性能和正确性上能做到很好的平衡
通过 B+Tree 索引大幅提升查询的效率
基于 ACID 的事务是关系型数据库带给应用系统的又一强力保障
ACID
原子性(Atomicity)
位于同一事务中的所有操作要么全部完成(提交),要么全部不完成(回滚), 不能停滞在某个中间环节
一致性(Consistency)
数据库中的数据应满足完整性约束,并且数据库的中间状态不应在事务之外被感知
隔离性(Isolation)
多事务并发执行时,彼此不应相互影响
持久性(Durability)
事务完成后,该事务对数据库的所有更改将被持久保存在数据库中
不足
单节点并发访问量受限
由于数据库中存储的数据是有状态的,因此很难像服务一样任意拆分和扩容
单一的数据库节点承载大量服务节点的查询和更新请求,这并非一个对等的架构部署模式
单节点数据承载量受限
数据量越大, 用于查询数据所创建的索引的深度就越深
索引越深, I/O 的访问次数就越多
分布式事务性能衰退严重
将数据库拆分之后,需要使用分布式事务代替本地事务
基于 XA 的分布式事务采用两阶段提交方式,在准备阶段即锁定资源,直至整个事务结束。在系统并发度增加时,性能会急剧下降
未达预期的 NoSQL
键值数据库
Redis
面对通过主键进行查询的场景,Redis 的效率非常高,但对于内容查询则无能为力
Redis 提供了集群处理的能力,可以将数据分散至不同的节点,有效解决了单一节点的访问量瓶颈
Redis 事务提供了一次性的、按顺序的、不可中断的命令执行机制
即使事务中的部分命令执行失败也无法回滚,因此 Redis 的事务与数据库领域中的事务并不是一一对应的
文档数据库
MongoDB
拥有自由度极高的 Schema 模型,可以方便地与 JSON 数据进行映射
没有静态定义的表结构
使得开发工程师能够十分方便地修改程序逻辑,无须考虑由于数据库表结构变更导致的锁表问题
可以根据需要查找的内容建立索引以提升效率
可以将数据自动分片,并且能够透明化分片之间的负载均衡和失效转移
内置了 GridFS,支持大数据集的存储
MongoDB 支持 ACID 事务
列簇数据库
HBase
专门用于处理海量数据的分布式数据库
通过行键和列族来确定一条记录,每个列族中的属性是不固定的,这一点与文档数据库类似
能够自动切分数据,使得数据存储自动具有水平扩展的能力
HBase 的数据存储在 HDFS 这样的分布式文件系统中,对海量数据的支持是最好的
HBase 更加适合写多读少的应用
不支持 ACID 事务,只能通过行键来查询数据
图数据库
处理图关系的数据库
总结、对比
图
大多对分布式数据库所需的分片和数据迁移功能支持得非常好,在海量数据和高并发支持方面,性能强于传统的关系型数库
不同的 NoSQL 数据库都有自己的查询语言,相比于 SQL,制定应用程序标准接口难上加难
NoSQL 也无法提供 ACID 事务操作,因此很多企业无法放心将 NoSQL 应用于核心业务系统中
冉冉升起的 NewSQL
介绍
是各种具有分布式可扩展功能的数据库的简称
NewSQL 继承了 NoSQL 对海量数据的处理能力
同时还保持了传统关系型数据库对 SQL 和 ACID 事务的支持
关注重点在于混合式(Hybrid)数据库,更倾向于找寻不再区分 OLTP 与 OLAP 的多模式数据库构建方案
新架构(New Architecture)
面向分布式架构而设计的数据库系统
该系统一般使用 share-nothing 架构
多节点并发控制
高度容错的自动化数据副本复制
流量控制
分布式查询处理
典型产品
Goole Spanner
TiDB
透明化分片中间件(Transparent Sharding Middleware)
允许应用将数据分片写入多数据节点,但数据节点仍然采用面向单数据节点的关系型数据库
透明化分片中间件使用中心组件来路由数据操作请求、协调事务、管理数据分布以及复制数据副本
透明化分片中间件的核心优势是兼容性强,它可以低成本地在系统现有的单机关系型数据库与分片中间件之间切换,无须开发者进行任何代码上的改动
透明化分片中间件 NewSQL 的目的是,充分合理地在分布式场景下利用传统关系型数据库的计算和存储能力,而非实现一个全新的关系型数据库
“在原有基础上增加,而非颠覆”是这类 NewSQL 产品的核心理念
整体运行效率略逊于新架构 NewSQL
云数据库(Database-as-a-Service)
由云计算公司所提供的云数据库产品
典型产品
Apache ShardingSphere
云数据库使用成本最低,工程师无须考虑数据库的任何细节问题,对中小型企业来说是理想的解决方案
总结
新架构数据库的关注点是彻底革新
透明化分片中间件数据库的关注点是增量
云数据库的关 注点是屏蔽用户使用细节
云原生数据库中间件的核心功能
数据分片
从性能方面来说
索引深度的增加也将使磁盘访问的 I/O 次数增加,进而导致查询性能下降
高并发访问请求也使得集中式数据库成为系统的最大瓶颈
从可用性方面来讲
而单一的数据节点,或者简单的主从架构,已经越来越难以承担,数据库的可用性已成为整个系统的关键
服务化的无状态型应用能够做到较低成本下的随意扩容,这必然导致系统的最终压力都落在数据库之上
从运维成本方面考虑
数据备份和恢复的时间成本都将随着数据量的增大而愈发不可控
数据分片是指按照某个维度将存放在单一数据库中的数据分散地存放至多个数据库或表中,这样可以达到提升性能瓶颈及可用性的效果
数据分片的有效手段是对关系型数据库进行分库和分表
分库还能够有效地分散对数据库单点的访问量
分表虽然无法缓解数据库的压力,但却有可能将分布式事务转化为本地事务,一旦涉及跨库更新,分布式事务的引入将会使问题变得复杂
分库和分表均可以有效地避免由数据量超过可承受阈值而产生的查询瓶颈
使用多主多从的分片方式可以有效地避免数据单点问题,从而提升数据架构的可用性
数据分片的方式
对流量进行疏导来应对高访问量,是应对高并发和海量数据系统的有效手段
为了缓解读写压力,也可以采用读写分离的方式拆分数据库
垂直分片
按照业务进行拆分的数据分片方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用
按照业务将表进行归类,分布到不同的数据库中,从而将压力分担至不同的数据库
垂直分片可以缓解数据量和访问量增长带来的问题,但无法从根本上解决问题
如果垂直拆分之后,表中的数据量依然超过单节点所能承载的阈值,则需要通过水平分片来进一步处理
图
水平分片
根据某种规则,通过某个字段(或某几个字段)将数据分散至多个库或表中,每个分片仅包含数据的一部分
例如,根据主键分片,偶数主键的记录放入一个库(表),奇数主键的记录放入另一个库(表)
图
水平分片从理论上突破了单机数据量处理的瓶颈,并且扩展起来相对自由,是分库和分表的标准解决方案
读写分离
应将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作
有效避免由数据更新导致的行锁问题,使整个系统的查询性能得到极大的改善
读写分离是根据SQL语义 的分析结果将读操作和写操作分别路由至主库与从库的
图
读写分离的数据节点中的数据内容是一致的,而水平分片的每个数据节点中的数据内容并不相同
将水平分片和读写分离联合使用,能够更加有效地提升系统性能
数据分片面临的挑战
数据分片解决了性能、可用性以及单点备份恢复等问题
应用开发工程师和数据库管理员的工作变得异常繁重
在单节点数据库中能够正确运行的 SQL,并不一定能够在分片之后的数据库中正确运行
分表会导致表名称被修改
分页、排序、聚合分组等操作不正确
跨库事务
合理采用分表,可以在降低单表数据量的情况下尽量使用本地事务,善于使用同库不同表可有效避免分布式事务带来的麻烦
在不能避免跨库事务的场景下,采用最终一致性的柔性事务代替强一致性事务
读写分离虽然可以提升系统的吞吐量和可用性,但同时也带来了数据不一致的问题
数据分片解决方案
全新架构的 NewSQL
重新设计的数据库存储引擎,将同一个逻辑表中的数据存储在分布式文件系统中,并能够利用内存尽量优化存取的效率
数据分片中间件
尽量透明化分库和分表所带来的影响,让使用方尽量像使用一个数据库一样使用水平分片和读写分离之后的数据库集群
数据分片核心流程
协议适配
为了方便将原有的应用程序接入中间件,需要兼容对数据库的访问,因此需要进行数据库协议的适配
SQL 解析
请求路由
SQL 改写
SQL 执行
结果归并
分布式事务
XA 协议
通过一个全局事务管理器与多个资源管理器进行交互
全局事务管理器负责管理全局事务状态和参与事务的资源
资源管理器则负责具体的资源操作
图
使用两阶段提交来保证分布式事务的原子性
将提交过程分为准备阶段和提交/回滚阶段
两阶段提交也是 XA 协议的标准实现
图
基于 XA 协议实现的分布式事务对业务的侵入性很弱
XA 协议能够严格保障事务的 ACID 特性
更 加适用于执行时间确定的短事务
在高并发性能至上的场景中,基于 XA 协议 的分布式事务并不是最佳选择
柔性事务
概述
实现了 ACID 特性的事务称为刚性事务
基于 BASE 事务要素的事务则称为柔性事务
BASE
基本可用(Basically Available)
基本可用保证分布式事务参与方不一定同时在线
柔性状态(Soft state)
柔性状态允许系统状态更新有一定的延时,客户不一定能够察觉
最终一致性(Eventually consistent)
最终一致性通常通过消息可达的方式来保证
在 ACID 事务中,对隔离性的要求很高,在事务执行的过程中必须将所有的资源锁定
柔性事务的理念则是通过业务逻辑将互斥锁操作从资源层面移至业务层面,通过放宽对强一致性的要求来换取系统吞吐量的提升
由于在分布式系统中可能会出现超时重试的情况,因此柔性事务中的操作必须是幂等的,需要通过幂等来避免多次请求所带来的问题
最大努力送达
最简单的柔性事务方案
适合用于“对数据库的操作最终一定能够成功”的场景
自动记录执行失败的 SQL,并反复尝试,直至执行成功
使用最大努力送达方案的柔性事务是没有回滚功能的
优点是无锁定资源时间,性能损耗小
缺点是尝试多次提交失败后无法回滚,它仅适用于事务最终一定能够成功的业务场景
因此它是通过对事务回滚功能的妥协来换取性能提升的
Saga
更适合用于长事务场景
Saga 模型将一个分布式事务拆分为多个本地事务
每个本地事务都有相应的执行模块(Transaction)和补偿模块(Compensation)
任和一个本地事务出错时,都可以通过调用相关的补充方法实现事务的最终一致性
Saga 模型同时支持正向恢复和逆向恢复
正向恢复
是指重试当前失败的事务,它的实现前提是每个子事务最终都能够执行成功
逆向恢复
是指在任意一个子事务失败时补偿所有已完成的事务
Saga 模型没有 XA 协议中的准备阶段,因此事务没有实现隔离性
如果两个 Saga 事务同时操作同一资源则会产生更新丢失、脏数据读取等问题
需要使用 Saga 作为事务管理机制的应用程序,在应用层面加入资源锁定的逻辑了
TCC(Try-Confirm-Cancel)
通过对业务逻辑进行分解来实现分布式事务
需要业务系统提供以下三种业务逻辑
Try
完成业务检查,预留业务所需的资源。Try操作是整个TCC的精髓,可以灵活选择业务资源锁的粒度
Confirm
执行业务逻辑,直接使用Try阶段预留的业务资源,无须再次进行业务检查
Cancel
释放Try阶段预留的业务资源
TCC 模型仅提供两阶段原子提交协议,保证分布式事务的原子性
事务的隔离交给业务逻辑来实现
TCC 模型的隔离性思想
通过对业务的改造将对数据库资源层面加锁上移至对业务层面加锁,从而释放底层数据库锁资源,拓宽分布式事务锁协议,提高系统的并发性
TCC 事务模型的功能最强,但需要应用方负责提供实现 Try、Confirm和 Cancel 操作的三个接口,供事务管理器调用,因此业务方改造的成本较高
TCC 模型对业务的侵入性较强,改造的难度较大
图
消息驱动
消息一致性方案是通过消息中间件保证上下游应用数据操作一致性的
基本思路
将本地操作和发送消息放在同一个本地事务中
下游应用从消息系统订阅该消息,收到消息后执行相应的操作
本质上是依靠消息的重试机制达到最终一致性的
图
消息驱动的缺点是,耦合度高,需要在业务系统中引入消息中间件,将导致系统复杂度增加
总结
技术选型
图
需要开发者合理地在性能与功能之间权衡各种分布式事务
对于分布式系统来说,建议使用“外柔内刚”的设计方案。外柔指的是在跨数据分片的情况下使用柔性事务,保证数据最终一致,并且换取最佳性能;内刚则是指在同一数据分片内使用本地事务,以满足 ACID 特性
数据库治理
基础治理
配置中心
用于集中化配置,动态更新配置,以及下发配置更新通知
注册中心
用于服务发现,这里的服务是指数据库中间层实例本身,通过它可以实现状态监测和自动通知,进而使数据库中间件具备高可用性和自我治愈能力
0 条评论
下一页