《极客时间教程 - 从 0 开始学微服务》笔记
《极客时间教程 - 从 0 开始学微服务》笔记
到底什么是微服务?
微服务定义:由单一应用程序构成的小服务,拥有自己的进程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用 HTTP API 通讯。服务使用最小规模的集中管理(如 Docker)技术,可以用不同编程语言与数据库。
——Martin Fowler 和 James Lewis
单体应用的问题:部署效率低、团队协作成本高、单点故障、发布变慢
服务化:本地方法调用 → 远程方法调用(RPC)
微服务 vs 服务化:
- 服务拆分粒度更细
- 服务独立部署、维护
- 服务治理要求更高
从单体应用走向服务化
什么时候进行服务化拆分?
经验:开发人员超过 10 人(沟通成本变高)即可考虑服务化拆分
服务化拆分的两种姿势
- 纵向拆分:从业务维度拆分。关联密切的业务拆为一个微服务,功能独立的单独拆分
- 横向拆分:从公共功能维度拆分。标准是是否被多个服务公共调用,且依赖资源独立不耦合
服务化拆分的前置条件
- 服务定义:通过接口约定
- 服务发布和订阅:通过服务注册和发现
- 服务监控 + 故障定位:需要链路监控
- 服务治理:超时重试、流量控制
初探微服务架构
微服务通过注册中心实现发布订阅模式。
服务调用基本组件:
- 服务描述:
RESTful API(代表:Swagger)XML(代表:Dubbo)IDL(代表:Thrift、gRPC)
- 注册中心:
- 服务提供者启动时向注册中心注册
- 服务消费者启动时向注册中心订阅
- 注册中心返回地址列表并通知变更
- 服务框架:
- 通信协议:TCP/UDP/HTTP
- 数据传输:同步/异步/多路复用
- 序列化:JDK/JSON/Protobuf/Thrift
- 服务监控:数据采集 → 数据处理 → 数据展示
- 服务追踪:通过
requestId、spanId分别标识一次请求和请求中的某一环节 - 服务治理:超时重试、负载均衡、故障转移、流量控制
如何发布和引用服务?
- RESTful API:用作 HTTP/HTTPS 协议的接口定义(代表:
Eureka) - XML 配置(代表:
Dubbo):- 服务提供者定义并实现接口
- 启动时通过
server.xml暴露接口 - 消费者启动时通过
client.xml引入接口
- IDL 文件(接口描述语言):用作跨语言平台的服务调用(代表:
Thrift、gRPC)
如何注册和发现服务?
三种角色:服务提供者(RPC Server)、服务消费者(RPC Client)、服务注册中心(Registry)
注册中心实现方式
基本 API:服务注册、服务注销、心跳汇报、服务订阅、服务变更查询、服务查询、服务修改
集群部署
注册中心采用集群部署保证高可用,通过分布式一致性协议保证数据一致性。
ZooKeeper 工作原理:
- 每个 Server 内存存储一份数据,Client 读请求可请求任意 Server
- 启动时选举 Leader(
Paxos协议) - Leader 处理数据更新(
ZAB协议) - 更新操作成功当且仅当大多数 Server 成功修改
目录存储
采用层次化目录结构:
- 每个目录叫
znode,有唯一路径标识 znode可包含数据和子znodeznode数据可有多个版本
服务健康状态检测
客户端与服务端维持长连接,生成全局唯一 Session ID,客户端定期发送心跳。超时则 ZooKeeper 认为服务节点不可用,将其删除。
服务状态变更通知
ZooKeeper 支持 Watch 机制,消费者可监听服务提供者节点信息变化。
白名单机制
只有白名单内的 RPC Server 才能调用注册接口,防止测试环境节点意外跑到线上。
如何实现 RPC 远程服务调用?
网络连接建立:
- HTTP 通信:三次握手建立,四次挥手断开
- Socket 通信:服务器监听 → 客户端请求 → 连接确认 → 数据传输
服务端请求处理:BIO、NIO、AIO
数据传输协议:HTTP、Dubbo
序列化方式:JDK、JSON、二进制(Protobuf、Thrift)
如何监控微服务调用?
监控对象:客户端、接口、资源、基础监控
监控指标:请求量、响应时间、错误率
监控维度:全局、机房、单机、时间、重要性
监控关键点:
- 数据采集:主动上报、代理收集
- 数据传输:
UDP、Kafka - 数据处理:
- 全文检索:
Elasticsearch - 时序数据库:
InfluxDB、OpenTSDB - 流计算:
Spark、Storm、Flink
- 全文检索:
- 数据展示
如何追踪微服务调用?
服务追踪的作用
定位系统瓶颈点、优化链路调用、生成网络拓扑、透明传输数据
服务追踪系统原理
经典论文:Dapper, a Large-Scale Distributed Systems Tracing Infrastructure
- traceId:全局唯一请求 ID,在 RPC 调用第一层生成,随每层调用传递,串联整个调用路径
- spanId:标识一次 RPC 调用在分布式请求中的位置(如 0 → 0.1 → 0.1.1 → 0.2),定位上下游依赖
- annotation:业务自定义埋点数据(如用户 UID)
服务追踪系统实现
三层架构:数据采集层(埋点上报)→ 数据处理层(存储与计算)→ 数据展示层(图形化展示)
微服务治理的手段有哪些?
服务调用失败原因:服务提供者自身问题(宕机、进程退出)、网络问题
节点管理
- 注册中心主动摘除:服务提供者定时发送心跳,超时则从服务列表中删除
- 服务消费者摘除:调用失败则从内存中的可用节点列表中移除
负载均衡
随机算法、轮询算法、最少活跃调用算法、一致性 Hash 算法
服务路由
应用场景:灰度发布、多机房就近访问
路由规则配置:
- 静态配置:修改本地配置,上线后生效
- 动态配置:修改注册中心配置,服务消费者下个同步周期更新
服务容错
| 策略 | 说明 | 适用场景 |
|---|---|---|
| FailOver | 失败自动切换 | 幂等调用 |
| FailBack | 失败通知 | 非幂等调用 |
| FailCache | 失败缓存 | 幂等调用 |
| FailFast | 快速失败 | 非幂等调用 |
Dubbo 框架里的微服务组件
服务发布和引用的实践
XML 配置方式的服务发布和引用流程
- 服务提供者定义接口
- 服务提供者发布接口
- 服务消费者引用接口
服务发布和引用的那些坑
如何将注册中心落地?
服务信息存储结构:分组 + 服务名 + 节点信息(节点地址 + 其他信息)
分组维度:核心/非核心、机房、线上/测试环境
注册中心工作流程
服务提供者注册/反注册、服务消费者查询/订阅变更
如何注册节点
- 检查白名单 → 检查 Cluster(接口名) → 检查 Service(分组) → 添加节点信息
如何反注册
- 检查 Service → 检查 Cluster → 删除节点信息 → 更新 Cluster 的
sign值
如何查询节点信息
- 先查
localcache(本机内存) → 再查snapshot(本地快照)
如何订阅服务变更
- 消费者获取服务信息后,本地保留 Cluster 的
sign值 - 定期调用
getSign()对比本地与服务端sign值,不一致则拉取新节点信息并更新localcache和snapshot
注册与发现的常见问题
多注册中心、并行订阅服务、批量反注册、服务变更信息增量更新
开源服务注册中心如何选型?
- 应用内注册与发现:通过 SDK 与注册中心交互(代表:
Eureka) - 应用外注册与发现:通过其他方式间接与注册中心交互(代表:
Consul)
应用内适用于同一技术体系;应用外适用于不同技术体系。
注册中心选型关键:
- 高可用性
- 数据一致性:
- CP 型:牺牲可用性保证强一致性(
ZooKeeper、Etcd、Consul) - AP 型:保证可用性(
Eureka、Nacos)
- CP 型:牺牲可用性保证强一致性(
注册中心主要功能是服务注册和发现,网络问题时可用性需求远高于数据一致性。即使引入不可用节点,也可通过客户端快速失败机制避免,只要实现最终一致性即可。因此推荐 AP 型注册中心。
开源 RPC 框架如何选型?
限定语言 RPC:
Dubbo:仅支持 JavaMotan:仅支持 JavaTars:仅支持 C++Spring Cloud:仅支持 Java
跨语言 RPC:
gRPC:支持 C++、Java、Python、Go、Ruby、PHP 等Thrift:支持 C++、Java、PHP、Python、Ruby、Erlang 等
如何搭建一个可靠的监控系统?
日志解决方案:ELK
时序数据库解决方案:Graphite、TICK和Prometheus
如何搭建一套适合你的服务追踪系统?
代表:Zipkin、PinPoint
如何识别服务节点是否存活?
心跳开关保护机制
问题:服务消费者并发访问注册中心导致带宽打满
方案:即使在网络频繁抖动时,服务消费者也不会同时请求注册中心获取最新节点信息
服务节点摘除保护机制
问题:服务提供者节点被大量摘除导致可用节点不足
方案:设定摘除阈值比例,注册中心不能摘除超过该比例的节点
静态注册中心
如何使用负载均衡算法?
负载均衡算法:随机算法、轮询算法、加权轮询算法、最少活跃连接算法、一致性 Hash 算法
如何使用服务路由?
服务路由:服务消费者根据特定规则选择服务节点,满足特定需求。
应用场景:分组调用、灰度发布、流量切换、读写分离
路由规则:
- 条件路由:排除节点、白名单/黑名单、机房隔离、读写分离
- 脚本路由
路由规则获取方式:本地配置、配置中心管理、动态下发
服务端出现故障时该如何应对?
故障类型与解决方案:
| 故障类型 | 解决方案 |
|---|---|
| 集群故障 | 流量控制(限流、降级) |
| 单 IDC 故障 | 多 IDC 部署(同城多活、异地多活)+ 流量切换(DNS/RPC) |
| 单机故障 | 负载均衡自动摘除 |
服务调用失败时有哪些处理手段?
超时、重试、流量控制
如何管理服务配置?
配置类型:本地配置、配置中心
配置中心代表:Spring Cloud Config、Apollo
如何搭建微服务治理平台?
- 服务管理:服务上下线、节点添加/删除、服务查询、节点查询
- 服务治理:限流、降级、切流量
- 服务监控:问题定位、日志查询
- 服务运维:发布部署、弹性伸缩
微服务架构该如何落地?
(略)
微服务为什么要容器化?
微服务引入的问题:设计复杂、测试复杂、运维困难
微服务容器化运维:镜像仓库和资源调度
容器运维平台组成:镜像仓库、资源调度、容器调度、服务编排
微服务容器化运维:容器调度和服务编排
容器调度系统:Swarm、Mesos、Kubernetes
容器调度要解决的问题:
- 主机过滤:存活过滤、硬件过滤
- 调度策略
- 服务编排
- 服务依赖:Docker Compose
- 服务发现:基于 Nginx、基于注册中心
- 弹性伸缩
微服务容器化运维:微博容器运维平台 DCP
微服务如何实现 DevOps?
- CI(持续集成):代码检查、单元测试、集成测试
- CD(持续部署):自动部署到类生产环境 → 灰度验证 → 线上发布
如何做好微服务容量规划?
容量规划的挑战:服务数量众多、接口表现差异大、集群规模不同、服务间存在依赖
容量规划系统:根据集群最大容量和实际负荷,决定是否需要弹性扩缩容及扩缩容数量。
实施关键:
- 容量评估:
- 压测指标:系统类(CPU/内存/I/O/带宽)、服务类(响应时间/P999耗时/错误率)
- 压测方式:单机压测(日志回放、TCP-Copy)、集群压测
- 调度决策:
- 使用水位线调度:致命线以下立即扩容,安全线以上稳定后缩容
- 扩容:按数量/按比例
- 缩容:逐步缩容
- 避免抖动:多次采集,超过 60% 数据满足条件才真正触发
微服务多机房部署实践
多机房负载均衡:七层 + 四层负载均衡,根据用户就近原则切分流量。
多机房数据同步
主从机房架构
- 主机房处理机更新本机缓存和数据库
- 其他机房缓存通过主机房处理机更新
- 从机房数据库通过
MySQL binlog同步主机房数据
独立机房架构
- 每个机房处理机接收写请求后更新各自缓存
- 只有主机房更新数据库
- 从机房数据库通过
MySQL binlog同步
WMB 消息同步组件:
reship:把本机房的写请求分发给其他机房collector:从其他机房读取写请求并转发给本机房处理机
WMB 实现方案:MQ(维护状态机读写请求)、RPC
多机房数据一致性
微服务混合云部署实践
跨云服务的负载均衡
需将一定比例用户请求路由到云上部署的服务
跨云服务的数据同步
- 网络隔离:内部机房与公有云通过 VPN/专线互通
- 数据库上云:取决于数据隐私性
跨云服务的容器运维
跨云主机管理、跨云服务发现、跨云弹性扩容、跨云服务编排
下一代微服务架构 Service Mesh
为什么需要 Service Mesh:跨语言服务调用、云原生应用服务治理
Service Mesh 的实现原理
实现关键点:
- SideCar:轻量级网络代理,转发服务间调用
- Control Plane:向 SideCar 发送指令,完成服务治理
治理功能:服务发现、负载均衡、请求路由、故障处理、安全认证、监控上报、日志记录、配额控制
Istio:Service Mesh 的代表产品
Istio 整体架构
由两部分组成:
- Proxy(SideCar):与应用程序部署在同一主机,转发调用(支持 HTTP/1.1、HTTP/2、gRPC、TCP)
- Control Plane:与 Proxy 通信实现服务治理,包含三个组件:
Pilot、Mixer、Citadel