1. 微服务结构初探
随着时代和科技发展,系统架构正在持续演进,主要有以下几个影响方面:
- 互联网的爆炸性发展
- 硬件设施的快速发展
- 需求复杂性的多样化
- 开发人员的急剧增加
- 计算机理论及技术的发展
以下为架构演变轴:
1.1 单体架构
在单体架构中,整个应用程序被构建为一个单一的、完整的软件单元,通常由一个大型的、紧密耦合的代码库组成。在单体架构中,所有的功能模块和组件都运行在同一个进程中,共享同一个数据库和资源。这种紧密耦合的设计使得开发、部署和维护相对简单,冗余小,性能最高,但也存在一些潜在的问题,如下:
- debug 困难
- 模块相互影响
- 模块分工、开发流程
1.2 垂直应用架构
按照业务线垂直划分,具有业务独立开发维护的优势,但也存在一些潜在的问题,如下:
- 不同业务存在冗余
- 每个业务还是单体
1.3 分布式架构
抽出业务无关的公共模块。具有业务无关的独立模块单独抽离的优势,但也存在一些潜在的问题,如下:
- 服务模块bug可导致全站瘫疾
- 调用关系复杂
- 不同服务冗余
1.4 SOA架构
SOA架构是面向服务的,优势是服务注册。但也存在一些潜在的问题,如下:
- 整个系统设计中心化
- 需要从上至下设计
- 重构困难
1.5 微服务架构
彻底的服务化。 优势:
- 开发效率
- 业务独立设计
- 自下而上
- 故障隔离 缺点:
- 治理、运维难度
- 观测挑战
- 安全性
- 分布式系统
1.6 微服务架构的核心要素
- 服务治理
- 服务注册
- 服务发现
- 负载均衡
- 扩缩容
- 流量治理
- 稳定性治理
- 可观测性
- 日志采集
- 日志分析
- 监控打点
- 监控大盘
- 异常报警
- 链路追踪
- 安全
- 身份验证
- 认证授权
- 访问令牌
- 审计
- 传输加密
- 黑产攻击
2. 微服务架构原理及特征
2.1 基本概念
以下是一些基本概念
- 服务 (service)。一组具有相同逻辑的运行实体
- 实例 (instance)。一个服务中,每个运行实体即为一个实例
- 实例与进程的关系。
-
- 实例与进程之间没有必然对应关系,可以一个实例对应一个或多个进程(反之不常见)。
- 集群(cluster)。通常指服务内部的逻辑划分,包含多个实例。
- 常见的实例承载形式。进程、VM、k8s pod.....
- 有状态 / 无状态服务。服务的实例是否存储了可持久化的数据(例如磁盘文件)。
- 微服务间通信
- 对于单体服务,不同模块通信只是简单的函数调用。
- 对于微服务,服务间通信意味着网络传输
2.2 服务注册及发现
在代码层面,如何指定调用一个目标服务的地址 (ip:port)? 若是采用DNS
,会存在:
- 本地 DNS 存在缓存,导致延时。
- 负载均衡问题
- 不支持服务实例的探活检查
- 域名无法配置端口。
解决思路
: 新增一个统一的服务注册中心,用于存储服务名到服务实例的映射.
- 服务实例上线及下线过程
2.3 流量特征
- 统一网关入口
- 内网通信多数采用RPC
- 网状调用链路
3. 核心服务治理功能
3.1 服务发布
服务发布 (deployment),即指让一个服务升级运行新的代码的过程。
服务发布的难点:
- 服务不可用
- 服务抖动
- 服务回滚
服务发布的形式:
- 蓝绿部署。旨在实现无缝的版本切换和最小化生产环境中的中断时间。简单,稳定,但需要两倍资源。
- 蓝环境是当前正在运行的生产环境,处理实际用户流量。
- 绿环境是新版本的环境,用于部署和测试新功能和代码。
- 灰度发布(金丝雀发布)。灰度发布通过逐步将新版本或新功能发布给一部分用户或服务器,以便在实际环境中进行测试和验证,从而降低风险并确保系统的稳定性。
3.2 流量治理
在微服务架构下,可以基于地区、集群、实例、请求等维度,对端到端流量的路由路径进行精确控制。
3.3 负载均衡
负载均衡 (Load Balance) 负责分配请求在每个下游实例上的分布。
负载均衡算法:
- Round Robin
- Random
- Ring Hash
- Least Request
3.4 稳定性治理
线上服务总是会出问题的,但与程序的正确性无关。
有以下突发情况:
- 网络攻击
- 流量突增
- 机房断电
- 光纤被挖
- 机器故障
- 网络故障
- 机房空调故障
典型稳定性治理方案:
- 限流
- 熔断
- 过载保护
- 降级
4. 字节跳动服务治理实践
4.1 重试的意义
本地函数调用可能存在异常:
- 参数非法
- OoM (Out Of Memory)
- NPE(Null Pointer Exception)
- 边界 case
- 系统崩溃
- 死循环
- 程序异常退出
远程函数调用可能存在异常:
- 网络抖动
- 下游负载高导致超时
- 下游机器宕机
- 本地机器负载高,调度超时
- 下游熔断、限流
重试可以避免掉偶发的错误,提高 SLA (Service-Level Agreement)
go
func RemoteFunc(ctx context.Context, x int) (int, error) {
ctx2, defer_func := context.WithTimeout(ctx, time.Second)
defer defer_func()
res, err := grpc_client.Calculate(ctx2,x * 2)
return res, err
}
func RemoteFuncRetry(ctx context.Context, x int) (res int, err error)[
for i := 0;i < 3; i++ {
if res, err = RemoteFunc(ctx, x); err == nil [
return
}
}
return
}
重试意义
- 降低错误率。假设单次请求的错误概率为 0.01,那么连续两次错误概率则为 0.0001
- 降低长尾延时。对于偶尔耗时较长的请求,重试请求有机会提前返回。
- 容忍暂时性错误。某些时候系统会有暂时性异常 (例如网络抖动),重试可以尽量规避。
- 避开下游故障实例。一个服务中可能会有少量实例故障(例如机器故障),重试其他实例可以成功。
4.2 重试的难点
- 幂等性。多次请求可能会造成数据不一致。
- 重试风暴。随着调用深度的增加,重试次数会指数级上涨。
- 超时设置。假设一个调用正常是 1s 的超时时间,如果允许一次重试,那么第一次请求经过多少时间时,才开始重试呢?
4.3 重试策略
- 限制重试比例
- 设定一个重试比例闽值 (例如 1%),重试次数占所有请求比例不超过该闯值
- 放止链路重试
- 链路层面的防重试风暴的核心是限制每层都发生重试。
- 理想情况下只有最下一层发生重试。
- 可以返回特殊的 status 表明"请求失败,但别重试"。
- Hedged requests
- 对于可能超时 (或延时高) 的请求,,重新向另一个下游实例发送一个相同的请求,并等待先到达的响应。
4.4 重试效果验证
验证经过上述重试策略后,在链路上发生的重试放大效应。
5. 结语
微服务架构原理与治理是当今软件开发领域中的热门话题,它提供了一种灵活且可扩展的方法来构建复杂的应用程序。通过将应用程序拆分为小而自治的服务,微服务架构使得团队能够更加敏捷地开发、部署和维护应用程序。同时,治理是确保微服务架构成功实施的关键因素之一,它涉及到服务发现、负载均衡、故障恢复等方面的实践。
在学习微服务架构原理与治理的过程中,我深深地体会到了以下几点:
首先,微服务架构的设计理念强调松耦合和高内聚。每个服务都是独立的、自治的,可以独立开发、部署和扩展。这种模块化的设计使得团队能够更好地应对需求变化,并且能够更加灵活地进行团队组织和协作。
其次,治理是微服务架构成功实施的重要组成部分。通过使用适当的工具和技术,我们可以实现服务的自动发现、负载均衡、故障恢复等功能。这些治理机制不仅可以提高系统的可用性和性能,还可以简化开发和运维的工作。
另外,微服务架构也带来了一些挑战和复杂性。服务之间的通信、数据一致性、分布式事务等问题需要仔细考虑和解决。同时,微服务架构也需要更强大的基础设施和监控系统来支持。
总的来说,微服务架构原理与治理是一门值得深入学习和实践的领域。它不仅可以提高应用程序的灵活性和可扩展性,还可以促进团队的协作和创新。然而,我们也要认识到微服务架构并不适用于所有场景,需要根据具体情况进行评估和决策。只有在正确的使用和治理下,微服务架构才能发挥出它的优势,为我们构建高效、可靠的应用程序带来更多的机会和挑战。