微服务架构
系统架构演变历史
单体架构
项目的所有模块均部署在一台机器上。
优势:
-
性能最高
-
冗余小
劣势:
-
大量程序员开发一个程序,导致 debug 困难。
-
不同模块之间互相依赖,互相影响。
-
单个项目仓库几乎无法模块分工,依赖管理,开发流程设置。
垂直应用架构
按照业务线垂直划分成多个模块。
优势:
- 业务独立开发维护。
劣势:
-
不同业务之间存在冗余,无法进行复用。
-
每个业务还是单体架构的。
分布式架构
抽取业务无关的公共模块,分布式独立部署运行。
优势:
- 使得业务无关的服务相互独立
劣势:
-
一个模块服务有问题,可能导致整个系统崩溃。
-
不同模块之间的调用关系错综复杂。
-
不同服务之间依然存在冗余。
SOA ( Service Oriented Architecture ) 架构
SOA 也就是面向服务的架构,开始引入“服务”,“服务注册”的概念。不同服务的调用通过服务注册中心实现。
优势:
- 服务注册
劣势:
-
整个系统从设计上依然是中心化的
-
整个服务架构需要从上至下去设计划分
-
重构困难
微服务架构
彻底的服务化,不同服务模块之间,通过某种通信机制进行互相调用、协作。
优势:
-
不同模块之间完全独立,开发、迭代效率高
-
业务独立设计
-
故障隔离,某个服务模块挂了,不会影响其他服务模块。
缺点:
-
治理、运维难度大
-
服务运行观测挑战
-
安全性
-
分布式系统本身的复杂性
微服务架构核心要素
-
服务治理:服务注册、服务发现、负载均衡、动态扩缩容、流量治理、稳定性治理
-
可观测性:日志采集、日志分析、监控打点、监控大盘、异常报告、链路追踪
-
安全:身份验证、认证授权、访问令牌、审计、传输加密
微服务架构原理及特征
基本概念
-
服务:一组具有相同逻辑的运行实体
-
实例:一个服务中,每个运行实体即为一个实例
-
实例与进程的关系:没有必然对应关系,一个实例可以对应一个或多个进程
-
集群:通常指服务内部的逻辑划分,包含多个实例
-
常见的实例载体:进程, VM, k8s pod ...
-
有状态/无状态服务:服务的实例是否存储了可持久化的数据
-
服务间通信
-
对于单体服务,不同模块通信只是简单的函数调用。
-
对于微服务,服务间通信意味着需要进行网络传输。
-
服务注册及发现
在代码层面,如何指定调用一个目标服务的地址( ip:port )?一个最朴素的想法是直接在代码中写死固定目标服务地址:
client := grpc.NewClient("10.23.45.67:8080") // Service A wants to call service B
但这存在一些问题,服务是有多个实例的,服务实例本身的 ip + port 是动态变化的,所以没有办法在代码中写死 ip + port.
其次,可以想到通过 DNS 来记录目标服务地址。但是 DNS 依然存在一些问题:
-
本地 DNS 存在缓存,导致延时。
-
负载均衡问题。
-
不支持服务实例的探活检查。
-
域名无法配置端口。
于是,正确的解决思路是:新增一个统一的服务注册中心,用于存储服务名到服务实例的映射。
如果现在想下线一个服务实例,不能直接下线,因为还有流量在访问该实例。
于是,先在服务注册中心中删除该服务实例映射,防止新的流量访问该实例。
当该实例上所有请求都处理完毕时,下线该实例。
如果我想上线一个新的实例,首先需要检查以下该实例是否正常。
然后,再将该实例注册到服务注册中心。
核心服务治理功能
服务发布
蓝绿部署
一共有两套系统:一套是正在提供服务系统,标记为“绿色”;另一套是准备发布的系统,标记为“蓝色”。蓝色系统用来做发布前测试,测试过程中发现任何问题,可以直接在蓝色系统上修改,不干扰用户正在使用的系统。
蓝色系统经过反复的测试、修改、验证,确定达到上线标准之后,直接将用户切换到蓝色系统。
切换后的一段时间内,依旧是蓝绿两套系统并存,但是用户访问的已经是蓝色系统。这段时间内观察蓝色系统(新系统)工作状态,如果出现问题,直接切换回绿色系统。
当确信对外提供服务的蓝色系统工作正常,不对外提供服务的绿色系统已经不再需要的时候,蓝色系统正式成为对外提供服务系统,成为新的绿色系统。 原先的绿色系统可以销毁,将资源释放出来,用于部署下一个蓝色系统。
优势:简单、稳定。劣势:需要两倍资源。
灰度发布(金丝雀发布)
在灰度发布开始后,先启动一个新版本应用,但是并不直接将流量切过来,而是测试人员对新版本进行线上测试,启动的这个新版本应用,就是我们的金丝雀。
如果没有问题,那么可以将少量的用户流量导入到新版本上,然后再对新版本做运行状态观察。
当确认新版本运行良好后,再逐步将更多的流量导入到新版本上,在此期间,还可以不断地调整新旧两个版本的运行的服务器副本数量,以使得新版本能够承受越来越大的流量压力。直到将100%的流量都切换到新版本上,最后关闭剩下的老版本服务,完成灰度发布。
如果在灰度发布过程中(灰度期)发现了新版本有问题,就应该立即将流量切回老版本上,这样,就会将负面影响控制在最小范围内。
劣势:回滚难度大,基础设施要求高。
负载均衡
负责分配请求在每个下游实例上的分布。
常见的负载均衡策略:
-
Round Robin 轮询:按照顺序轮流将请求分配给不同的服务器。
-
Random 随机:随机选择服务器来处理请求。
-
Ring Hash 一致性哈希:将哈希空间想象成一个圆环,每个服务节点映射到这个环上,每次请求也映射到这个环上,并按照顺时针顺序(哈希值增大),找到第一个服务节点转发请求。即使,服务节点数量有变化,也无需改变哈希策略。
-
Least Request 最小请求:将请求发送给当前处理连接数最少的服务器。
稳定性治理
限流
设置最大 qps, 超过直接拒接。
熔断
当某个模块服务出现故障而调用失败时,通过直接返回一个默认的错误响应,而不是继续调用该服务,防止整个系统因为单个服务的故障而陷入瘫痪。并在适当的时候自动恢复服务调用。
过载保护
当服务节点 CPU 过载时,直接拒绝大量的请求。
降级
在系统面临高负载或资源紧张的情况下,牺牲非核心功能的可用性,来确保核心功能的正常运行。