这是我参与「第三届青训营 -后端场」笔记创作活动的第14篇笔记
微服务架构
系统架构演进
单体架构
单体架构:all in one process
优势:
性能最高
冗余小
劣势:
debug困难
模块互相影响
模块分工困难、开发流程久
垂直应用架构
垂直应用架构:按照业务线进行划分
优势:
业务独立开发维护
劣势:
不同业务存在冗余
每个业务还是单体
分布式架构
分布式架构:抽出业务无关的公共模块
优势:
业务无关的独立服务
劣势:
服务模块bug可导致全站瘫痪,只要服务层中有一个模块出现bug就会导致全站瘫痪
调用关系复杂,没有完全的将服务与业务进行松耦合
不同服务之间存在冗余
SOA架构
SOA架构:Service Oriented Architecture 面向服务的思想
优势:
多了一个服务注册中心,彻底将业务与服务进行松耦合
劣势:
整个系统设计是中心化的
需要从上至下设计
重构困难
微服务架构
微服务架构:彻底的服务化
优势:
开发效率大大提高
由于每一个模块都是一个服务,业务可以独立设计,不用关心与其他模块之间的关联,之间的关联通过微服务框架提供的功能就可以完成
自下而上,由于不是流程式的开发,所以可以随时设计添加一些新的模块,或者是移除一些不需要的模块,重构成本低
故障隔离,由于每个服务都是单独的模块,故障是不会传播给其他模块的,所以有故障隔离的效果
劣势:
治理、运维难度
观测挑战
安全性
分布式系统
微服务架构概览
对于微服务架构,服务是作为一等公民的存在,所以需要一种统一管理服务的能力,也就是服务的配置和治理模块
同时作为一种庞大的分布式架构,链路的追踪与监控是实现服务监控的重要窗口,有了链路追踪监控能力才具备在这个庞大的分布式系统中进行debug的能力
微服务架构核心要素
服务治理:
服务注册、服务发现、负载均衡、扩缩容、流量治理、稳定性治理......
可观测性:
日志采集与分析,对于庞大的分布式架构,日志信息也是非常庞大的,所以一般不会通过直接查看日志文件来定位问题的所在,而是通过这种自动分析日志的能力来帮助找到问题的所在
监控打点
监控大盘
异常报警与链路追踪,当发生问题的时候,异常的报警可以通知服务内部的问题,而链路追踪技术可以横跨多个微服务来帮助排查问题所在
...
安全:
身份认证、认证授权、访问令牌、审计、传输加密、黑产攻击......
微服务架构原理及特征
微服务架构的基本组件
服务(service)
一具有相同逻辑的运行实体
实例(instance)
一个服务中,每个运行实体即为一个实例
实例与进程的关系
实例与进程之间没有必然对应关系,可以一个实例可以对应一个或多个实例(反之不常见)
集群
通常指服务内部的逻辑划分,包含多个实例
常见的实例承载形式
进程、VM、k8s pod...
有状态/无状态服务
服务的实例是否存储了可持久化的数据(例如磁盘文件)
微服务架构的基本原理:服务注册与发现
服务间通信:
对于单体服务,不同模块通信知识简单的函数调用
对于微服务,服务间的通信意味着网络传输
服务注册与发现:
一种实现方式就是硬编码实现,缺点就是耦合性高,并且出现魔法字符
另一种方式就是通过DNS,但是存在以下问题:
本地DNS存在缓存,由于缓存不一致的问题,导致服务调用延时
负载均衡问题,并没有负载均衡的功能
不支持服务实例的探活检查,如果配置一个不存在IP的时候,无法检查配置出错
域名无法配置端口
通过服务注册与发现实现,也就是新增一个统一的服务注册中心,用于存储服务名到服务实例IP的映射,简单来说就相当于一个hashmap
服务先从服务注册中心获取一组目标地址,然后可以使用一种随机方式来进行调用目标服务
服务实例的上线与下线
首先去服务注册中心将目标实例下线,之后在真正的将服务下线
首先将服务真正的服务上线,然后再将服务注册到注册中心
流量特征
统一网关入口
内网通信多数采用RPC
网状调用链路
核心服务治理功能
服务发布
服务发布(deployment),即指让一个服务升级运行新的代码的过程,也就是更新一个服务,将新的服务上线的功能
服务发布的难点:
服务不可用
服务抖动
服务回滚
蓝绿部署:简单稳定,但需要两倍资源
灰度发布(金丝雀发布):需要精确的控制流量,回滚操作比较困难,比如说k8s可以提供这种操作
流量治理
流量治理:在微服务架构下,我们可以基于地区、集群实例、请求等维度,对端到端流量的路由路径进行精确控制
地区维度:比如说在北京地区的实例多于上海地区的实例,也就是北京的处理能力比较强,所以可以将请求更多的分配到北京地区的服务器
集群维度:比如说对于一个服务A,其中有一部分是上线的服务,另一部分是即将上线的服务,这时候可以将流量分一小部分给测试集群用于功能的测试
实例维度:对于请求的实例,被请求的实例可能由于物理机配置不一致,所以能接收处理的流量也不一致,所以可以在保持一致请求处理时延的情况下,将请求均衡的分配给不同的实例
请求维度:对于一个请求,可以是内部人员进行测试的请求,这时候可以在表头打上特殊的表示,将这个请求引流到一个特殊的实例上,用来进行内部开发过程中的测试
负载均衡
负载均衡(Load Balance)负责分配请求在每个下游实例上的分布
常见的LB策略
Round Robin
Random
Ring Hash
Least Request
......
稳定性治理
稳定性治理:线上的服务总是会出问题的,这与程序的正确性无关
网络攻击
流量突增
机房断电
光线被挖
机器故障
网络故障
机房空调故障
......
微服务架构中典型的稳定性治理功能
限流:比如说一个服务B只能处理1000的qps,这时候假如打过来5000的qps,服务B就会拒绝其中的4000qps
熔断:比如说服务A在察觉到服务B由于请求数过多而无法即时的处理请求的时候,服务A就会直接拒绝掉上游的请求,并在此期间服务A将通过不断的重试检测服务B是否恢复
过载保护:比如说服务B监控到了自身的处理请求压力过大,这时候就会全部拒绝到打过来的请求
降级:比如说服务B在监控到了自身处理请求压力过大的时候,这时候就会拒绝不重要的请求,处理重要的请求
服务治理实践
重试的意义
本地函数调用没有重试的必要
远程函数调用还是有重试的必要
远程调用可能出现的异常:网络抖动、下游负载高导致超时、下游机器宕机、本地机器负载高导致调度超时、下游熔断限流
降低错误率
假设单次请求的错误概率为0.01,那么连续两次错误概率为0.0001
降低长尾延时
对于偶尔耗时较长的请求,重试请求有机会提前返回
容忍暂时性错误
某些时候系统会有暂时性异常(例如网络抖动),重试可以尽量规避
避开下游故障实例
一个服务中可能会有少量故障实例(例如机器故障),重试其他实例可以成功
重试的难点
幂等性
只有当一个函数处理的请求具有幂等性的时候,才可以被重试,幂等性是指一个请求发送多次得到的响应是一致的,这里为了不会出现一个请求由于网络超时上游才发送重试请求,导致下游处理了两个相同的请求,比如说转账服务就不能被重试多次,这样会被误认为转账多次
重试风暴
如果调用链路过长,并且经过每个调用链路都会重试三次的话,这样累计起来的请求是非常恐怖的,会引起雪崩现象
超时时间设置
如何设置超时时间,一般来说会以p99的点,也就是在正常情况下一个请求返回的时候所需要的时间的99百分位,作为超时时间
重试的策略
设置重试比例
当大部分请求都成功的时候才有必要去重试,设定重试比例阈值(例如1%),重试次数占所有请求比例不超过该阈值。
防止链路重试
链路层面的防重试风暴的核心是限制每层都发生重试,理想状态下只有下一层发生重试。可以返回特殊的status表明“请求失败,但别重试”
Hedged requests
对于可能超时(或延时高)的请求,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应