滴滴出行架构大神分享:大型微服务框架设计实践

muadgz 2019-06-27

大纲

• 发现问题:服务开发过程中的痛点

• 以史鉴今:从服务框架的演进历程中找到规律

• 大道至简:大型微服务框架的设计要点

• 精雕细琢:框架关键实现细节

复杂业务开发过程中的痛点

痛点

• 时间紧、任务多、团队大、业务增快,如何还能保证架构稳定可靠?

• 研发水平参差不其、项木压力自顾不暇,如何保证质量基线不被突破?

• 公司有各种⼯具平台、 SDK、最佳实践,如何尽可能的在业务中使用?

•用什么“框架”可以解决问题?

从服务框架的演进历程中找到规律

让我们先来看下服务框架的进化史

滴滴出行架构大神分享:大型微服务框架设计实践

标志性的服务框架

Web 服务框架: MVC 架构

• ASP.Net(since 2002):传统 C/S 开发模式在 Web 上的应⽤

• Ruby on Rails(since 2005): MVC 框架的巅峰, “约定⼤于配置”

• Web 服务框架: SaaS 与 RESTful

• Sinatra(since 2007):纯路由框架,诸多框架的灵感源泉

• 微服务框架: RPC 服务

• Thrift(since 2007):开源 IDL-based 框架的⿐祖

• 微服务架构:容器化与 FaaS

• Serverless(since 2015):基于云计算平台,回归框架本质

• Istio(since 2018):专注于解决网络问题

服务框架的演进趋势

服务框架正在演变成新的“操作系统”

• 学习曲线: Exponential Rise(渐进式) → Sigmoid(阶跃式)

• 风格:配置 → 约定 → DSL → 容器化

• 业务代码与框架代码的关系: Is-a → Has-a → Duck-typing

• 工具链: IDE → 代码⽣成器 → 编译器

大型微服务框架的设计要点

站在全局视⻆观察微服务架构

滴滴出行架构大神分享:大型微服务框架设计实践

大型微服务框架的设计目标

框架即一款面向开发人员的效率产品,基于公司的基础设施量身定制

• 目标用户:来不不同背景、具有基本业务研发能⼒的开发者

• 设计要点:让开发人员专注于业务开发本身,无需关注滴滴各种基础设施底层细节

• 设计原则:直观、简洁、智能、个性化

• 预期收益:提升⼈效,降低维护成本;提升整体架构稳定性和可伸缩性;简化技术升级难度

大型微服务框架的设计要点

完全屏蔽业务无关的通用技术细节

• 功能:服务治理、虚拟化、水平扩容、问题定位、性能压测、系统监控、兼容遗留系统……

• 工具链:项目模板、代码生成器、文档生成器、发布打包脚本……

• 设计⻛格: Interceptors、组合模式、依赖注入……

• 让不可靠的调⽤变得可靠

• RPC 调用 ≈ 函数调用

• 访问基础服务 ≈ 访问本地存储

• 服务拆分/合并 ≈ 类拆分/合并

框架关键实现细节

业务实践

业务背景:复杂的业务流程,快速增涨与迭代,异构服务架构,跨国多机房部署

• 核心能力

• 隔离层封装:各种存储、队列、平台服务封装

• 透明支持各种运维基础设施:构建、发布、多机房配置、 metrics

• 提供效率和测试⼯具:⽇志采集、问题⾃动跟踪、全链路压测、 mock、接⼝测试

• 应⽤层协议隔离:⽀持 thrift/http 协议 interceptor

• 工具链:模板、代码⽣成器、依赖管理、版本管理、发布脚本

站在巨人肩膀上:滴滴基础平台建设现状

• Odin:运维平台,提供 metrics 上报、多维度监控、报警、服务树等功能

• 把脉:日志平台,提供日志采集通道、基于 traceid 的全链路⽇志查询能⼒

• DiSF:服务注册平台,提供高可用的服务名字服务、管理服务分组

• RDS:提供高可用、透明水平扩展的 MySQL 集群,支持数据总线、分身等能力

• DDMQ:低延迟高可用的消息队列服务,单机 TPS 吞吐超过百万,支持延时消息

• Fusion:基于 rocksdb 的高性能高可用分布式持久化存储方案,完全兼容 Redis 协议

• 弹性云:基于 k8s,高效、可伸缩的集群管理平台,服务自动容错,基础设施免运维

整体架构

滴滴出行架构大神分享:大型微服务框架设计实践

实现要点:框架与业务正交

实现思路

• 传统框架的 MVC、 middleware、 AOP、执行流程……都不存在也不需要

• 框架是一个执行环境,由一堆不关联的基础库组成高度可扩展,业务可独立于框架运行

如何实现

• 提供工具链,用于生成最初的项⽬模板并通过代码生成器实现类似 AOP 的效果

• 基于 Go interface 的 duck-typing 特性和运行时反射,动态生成业务路由

收益

• 业务开发⽆需关注框架本身

• 框架本身的升级可以做到完全透明,方便所有服务统一框架版本

框架的启动逻辑

滴滴出行架构大神分享:大型微服务框架设计实践

实现要点:隔离层屏蔽业务与底层的联系

如何实现

• 为所有基础服务(mysql/redis/mq/es/...)定义 interface,业务只允许调用 interface 的方法

• 基于 SPI 设计思路,提供基础服务的工厂,动态实例化对应 interface

收益

• 可透明的升级服务驱动,快速在大量服务中实现共性逻辑或者修复共性问题

• 透明的管理基础服务的资源(长连接、 mysql cursor 等),避免出现资源泄露

• 统⼀控制重试、超时、服务发现、故障摘除逻辑,业务⽆感知且不易出错,提升整体稳定性

• 对所有基础服务提供了 mock 能力,可以实现 AOP 能力

案例: Redis 接口设计

滴滴出行架构大神分享:大型微服务框架设计实践

实现要点:协议劫持

如何实现

• HTTP 协议:包装 http.Handler,用责任链模式处理 http.Request 和 http.ResponseWriter

• RPC 协议:劫持协议序列化流程,用FSM(有限状态机)来跟踪序列化过程并适时修改数据

收益

• 将业务数据和服务框架数据充分隔离,避免互相⼲扰

• 实现接口热补丁和 in/out 数据录制与重放,方便测试

• 可透明的增强服务能力,为实现跨服务边界的 context 打好基础

使用FSM 劫持 thrift protocol

FSM 实现思路

• 利用 Go interface 特性,实现一个 interfaceproxy,代理并劫持部分感兴趣的接口

• 维护一个 FSM 状态机,当 protocol read/write走到感兴趣的地⽅时候篡改 read/write 数据

滴滴出行架构大神分享:大型微服务框架设计实践

实现要点:跨服务边界的 context

如何实现

• 实现符合 context.Context 接口的自定义 context,支持序列化与反序列化,支持超时控制

• 结合协议劫持,透明的从服务框架数据中提取必要信息进行反序列化,并在所有 RPC 调用前

透明的将最新 context 序列化并放在服务框架数据中传输给下游

• 需要重新实现一个基于时间片轮转的低精度 time.Timer,提升并发效率并避免 timer 泄露

收益

• 可透明的在服务间传递上下文信息,从而实现流量染色、调用跟踪、防雪崩等功能

低精度 timer 实现原理

滴滴出行架构大神分享:大型微服务框架设计实践

实现要点:防雪崩

如何实现

• 通过跨服务边界的 context 来传递上游超时预期,并不断记录各个环节耗时

• 一旦框架发现自己的可用时间已经耗尽,主动终止后续 rpc 调⽤并快速返回,防止请求积压

收益

• 从根本上避免请求堆积造成的雪崩

跨服务边界的超时时间控制

超时信息如何跨服务边界传递

• 超时时间由最上游设置,框架捕捉到超时信息并将时间记录在 trace 里透明的传播到下游每一个服务节点

• 每个节点从接收到请求开始后计时,自动计算自己消耗的时间并计算当前调用链路总耗时

• 当链路总耗时超过超时时间,自动 fail-fast,快速返回失败信息

• 利⽤ Go context deadline 只会缩短不会提前的特性,方便的用 context 管理超时

• 避免服务器之间的时钟差异影响计时,始终使用时间差来记录号是,而不使用绝对 deadline

业务收益

支撑规模

• 涉及开发人员近 100 人,上线 70+ 服务,国内外双机房部署

• 可支撑百万级日订单规模、万级并发长连接

业务收益

• 零成本:透明接⼊公司运维、发布、日志、压测等平台,支持服务注册发现、弹性伸缩等能力

• 零事故:从未出现因为单点故障造成的全局稳定性事故

• 高质量:快速实现全链路压测常态化,透明实现多环境部署、单测、集成测试等

• 易维护:透明升级各种 driver,简化代码依赖管理过程

未来计划

提升开发者体验

• 命令行工具,用于整合各种工具

• 进一步与滴滴线上线下环境整合

• 整合更多的公司服务和框架

相关推荐