微服务架构体系简介
杨波老师曾在ebay、携程等公司从事基础框架研发,对微服务架构体系有独到的见解。近期学习了杨波老师的微服务架构体系课程,并简单整理了一些笔记,供大家学习参考。
0 简介
由于时间有限,这里只能对杨波老师的课程做简单总结,对于其中细节不能再单独拎出来细讲。对于微服务架构体系中的各种模块,大家可以在笔记的基础上,对感兴趣的模块自行深度学习。
1 安全接入
1.1 问题域
1.1.1 开放系统间授权
资源拥有者使用第三方应用,第三方应用需要访问资源拥有者的资源。具体情况如:社交联合登录、开放API平台等。
解决的办法有三种:
- 直接在第三方应用中输入用户名密码。(不合适)
- 提供access key给到第三方应用。(不合适)
- 针对特定应用,在用户允许的情况下发放特殊令牌,通过令牌访问第三方应用。
1.1.2 应用安全访问
传统应用使用filter+session的机制来完成认证和授权。
在微服务架构中,使用认证授权服务器+基于token的授权来完成认证和授权。
1.1.3 企业内部接入
员工访问系统使用单点登录(SSO),机器间接入采用网络隔离+IP白名单方式。
1.2 OAuth2.0简介
OAuth2.0 是用于Rest/API的代理授权框架,它能够在不暴露用户名密码的情况下,向第三方应用授权,允许其访问用户的资源。可以说它是事实上的安全接入标准。
OAuth2.0 定义了四种角色,分别是:用户,第三方应用,授权服务器,资源服务器。他定义了这样的场景:第三方应用使用API访问用户资源服务器上的用户私有数据。API如果没有权限校验,会被恶意应用随意访问,所以访问API时需要携带Token。因此引入授权服务器,第三方应用从授权服务器获取Token,用以访问资源服务器。
如何获取Token是OAuth2.0中重要的组成部分,整个获取流程如下:
- 第三方应用尝试从授权服务器获取Token
- 授权服务器询问用户是否允许授权给第三方应用
- 用户同意
- 授权服务器颁发Token给第三方应用
OAuth2.0 有以下优点:
- 更安全,第三方应用不接触密码
- 广泛应用和传播,不用担心通用问题
- 短寿命的Token,支持过期策略
- 资源服务器和授权服务器解耦
- 集中式授权,简化第三方应用接入难度
- HTTP/JSON友好,易于在不同环境使用
- 考虑多种使用场景
- 用户可以设置多种信任级别
同时,OAuth2.0 也有一些缺点:
- 起草时利益方太多,导致协议框架太宽泛,造成各种实现的兼容性和互操作性差
- 不是一个认证协议
1.3 OAuth2.0 令牌类型
- 访问令牌:用于代表用户直接去访问资源的令牌
- 刷新令牌:用于去授权服务器获取一个新的访问令牌
- 授权码:用户给予的一个编码,用于换取访问令牌和刷新令牌
- Bearer Token:现钞,不用校验客户端的令牌
- Proof Of Possession(PoP) Token:需要检验客户端的令牌
1.4 OAuth2.0 授权模式与选型
在rfc6749中描述了OAuth2.0协议框架标准,其中将授权分为四种模式。
1.4.1 授权码模式
- 客户通过浏览器访问第三方应用,第三方应用要求浏览器携带client_identifier和回调地址跳转到授权服务器
- 客户在授权服务器上做认证
- 认证通过后,授权服务器要求浏览器携带授权码调转到指定的回调地址,第三方应用由此拿到授权码
- 第三方应用拿到授权码后,从后端向授权服务器发送携带授权码和另一个回调地址的请求
- 授权服务器检验授权码通过后,回调另一个地址,并携带上访问Token和可选的刷新Token
1.4.2 简化模式
- 客户通过浏览器访问第三方应用,第三方应用要求浏览器携带client_identifier和回调地址跳转到授权服务器
- 客户在授权服务器上做认证
- 认证通过后,授权服务器要求浏览器携带访问Token的Fragment调转到指定的回调地址,第三方应用由此拿到存放Token的Fragment
- 第三方应用要求浏览器访问资源服务器脚本
- 资源服务器返回脚本,浏览器执行脚本,得到访问Token
1.4.3 密码模式
- 客户填写密码给到客户端
- 客户端拿着密码去访问授权服务器
- 授权服务器返回访问Token
1.4.4 客户端模式
- 客户端访问授权服务器
- 授权服务器返回访问Token
1.4.5 Token的刷新
除了四种授权模式外,额外还有一个刷新流程需要学习一下。
- 第三方应用得到授权,拿到访问Token和刷新Token
- 第三方应用在使用访问Token访问资源服务器后
- 资源服务器告诉第三方应用访问Token失效
- 第三方应用拿着刷新Token访问授权服务器
- 授权服务器返回新的访问Token和刷新Token
1.4.6 授权模式选型
- 客户端模式:不能有人参与的情况下
- 授权码模式:传统Web服务
- 简化模式:单页应用或桌面应用
- 密码模式:客户端和资源方同属一个组织
1.5 JWT令牌
令牌可以分为两种类型,一种是By reference token(透明令牌)它由随机字符串组成,资源服务器需要到授权服务器验证有效性并获取额外信息;还有一种是By value token(自包含令牌)它包含用户元数据和授权信息的字符串,资源服务器可以通过检查签名判断有效性。
自包含令牌通常的实现为带签名的JSON Web Tokens(JWT),JWT由三部分组成。
第一部分是Header,Header的格式如下:
{
"alg": "HS256",
"typ": "JWT"
}
其中alg表示是通过何种哈希得到signature。
第二部分是Data,其中可以包含任何KV键值对,例如:
{
"name": "John Doe",
"authority": ["login","read"]
}
第三部分是签名signature,signature在授权服务器中生成,其中special_secret是用于保证Token不被伪造的关键手段,生成格式是:
SHA256(base64url(Header).base64url(Data).special_secret)
当得到一个JWT后,可以在jwt.io网站上对JWT进行验证。
1.6 安全接入架构选型
1.6.1 方案1
- 第三方应用通过授权服务器获取访问令牌,然后调用API网关。
- API网关接受第三方应用的访问令牌,并访问授权服务器转为JWT。
- 微服务通过API网关对外暴露,访问需要携带JWT。
1.6.2 方案2
- 第三方应用通过授权服务器获取加密JWT,然后调用API网关。
- API网关接受加密的JWT,并对其解密。
- 微服务通过API网关对外暴露,访问需要携带解密的JWT。
1.6.3 方案3(推荐)
- 第三方应用通过授权服务器获取访问令牌,然后调用API网关。
- 授权服务器在返回访问令牌时,将对应的JWT存放在redis中。
- API网关接受访问令牌,并访问redis获取JWT。
- 微服务通过API网关对外暴露,访问需要携带JWT。
1.6.4 生产环境监控
业务指标监控
- 登陆次数
- 授权次数
- 颁发访问/刷新令牌次数
- 活跃令牌数
- 令牌校验次数
- 令牌吊销次数
- client注册数
性能指标
- authorize 授权接口
- token 获取token接口
- introspect 用户信息获取接口
- revoke 注销token接口
1.7 参考资源
OAuth2.0/OIDC开源产品
- RedHatKeyCloak
- Apereo CAS
- Identity Server C#
- OpenID Connect Java Spring Server
- Spring Security OAuth2
OAuth2.0/OIDC库(客户端用)
- Google OAuth Client Library
- ScribeJava
- Spring Security OAuth
- Nimbus OAuth SDK
- https://oauth.net/code
OAuth2.0/OIDC SaaS产品
2 配置中心
2.1 配置基本概念
配置的定义是:程序中可根据需要动态调整的变量。配置的形态主要有:硬编码、配置文件、环境变量、启动参数和基于数据库的配置。
根据配置是否可运行时修改,将配置分为静态配置和动态配置。
静态配置中包含环境配置(当前集群环境等)、安全配置(数据库用户名密码等)。
动态配置中,又分为应用配置(超时、线程池、日志级别、限流阈值、黑白名单)、功能开关(灰度开关、蓝绿部署、功能降级、HA高可用开关、DB主备切换)和业务配置(促销规则、贷款额度、订单数量限制)。
对于配置主要有三个方面需要进行治理,第一是权限控制;第二是环境、集群隔离;第三是灰度发布支持。
2.2 问题域与核心需求
问题域
- 动态修改配置
- 多系统统一格式
- 生产环境隔离
核心需求
- 应用和配置分离
- 配置抽象标准化
- 集中式管理配置
- 高可用
- 实时发布
- 支持配置治理
2.3 开关驱动开发
TBD trunk based development(新功能隐藏在功能开关之后),解决branch hell问题。
支持Branch by Abstraction(基于抽象的重构),安全重构,在新代码真正可用前保留旧代码。
优点:
- 新功能和代码发布分离,减少发布风险
- 可定制A/B测试
- 没有分支合并冲突问题
缺点:
- 代码入侵性高,技术债,需定期清理
- 需要有配置中心配合使用
- 需要有devops文化和流程配置
2.4 相关产品
- 阿里巴巴:diamond(开源)
- 百度:disconf(开源)
- 携程:apollo(开源)
- 360:360Conf
- netflix:archaius(开源)
- facebook:gatekeeper
3 服务网关
3.1 核心需求
- 单点入口
- 路由转发
- 限流熔断
- 日志监控
- 安全认证
3.2 netflix zuul介绍
zuul处理了绝大多数netflix流量,支持1000+种设备类型,每天处理百亿+请求,多区域(regions)部署支持,多集群(20+)部署支持。在携程,部署实例50+,覆盖大量业务场景,日流量超过50E。在拍拍贷,部署实例40+,覆盖多场景,日流量超过5E。
3.3 应用场景介绍
- 红蓝部署(精准切流)
- 开发者测试分支(特征流量转发)
- 埋点测试
- 压力测试(流量复制到压力集群)
- 调试路由(特殊head等等)
- 金丝雀测试
- 粘性金丝雀(不会随机挑选新旧集群)
- 失败注入
- 降级测试
- 跨区域高可用(不同区域网关间流量转发)
- 防爬防攻击
- 健康检查和屏蔽坏节点
3.4 zuul应用架构
zuul是一个基于过滤器模式的网关,在请求微服务前,调用pre filter,主要用于认证、选路由、日志记录;选好路由后,执行routing filter,处理请求到微服务处理器;在请求完微服务后,调用post filter,在主要是用作增加head头、收集统计和度量、转换响应方式(rpc->http)等功能。在上述三个调用中,但凡抛出异常,均会调用error filter。
3.5 zuul2.0简介
- 引入非阻塞异步模式(更适用于IO密集型)(netty实现)
- 20%性能提升
- HTTP2支持
- 自适应重试
3.6 zuul接入最佳实践
- 使用AsyncServlet优化连接数
- 引入配置中心(apollo),避免网关频繁部署
- Hystrix熔断限流(信号量隔离)
- 不同服务集群,连接池单独设置
- 尽量不引入业务逻辑
- 自助路由扩展(另行实现一个管理域,允许开发者自行配置微服务,入参与rpc调用入参对接、返回结果转换等)
3.7 其他网关产品
- kong
- tyk.io
- goku gateway
- 小豹API网关
4 调用链监控
4.1 核心需求
- 功能发布跟踪
- 从大量报错中查找问题原因
- 及早发现应用性能问题
- 微服务间调用日志串接
devops实践:
- 改进系统的前提是系统可测量
- 谁构建、谁运行、谁监控
4.2 基本原理
目前市面上的调用链监控产品,大多都源自google的论文:Dapper, a Large-Scale Distributed Systems Tracing Infrastructure。
它的核心概念如下:
- trace:一次分布式调用链路跟踪
- span:一个方法的调用踪迹
- annoation:附着在span的日志信息
- sampling:采样率
整体的处理流程是:在应用端做日志埋点,由服务器上的日志收集系统对埋点日志进行收集,对接存储系统,最后做UI展示。
4.3 使用调用链监控好处
- 报表丰富,从各个角度了解系统状态
- 故障快速发现和定位
- 有助于开发人员树立质量意识
- 便于检查系统设计/服务分层合理性
- 发现技术债
4.4 相关产品
- open tracing
- open zipkin
- CAT(美团)(概念源于ebayCAL)
- eagleeye(阿里巴巴)
- Hydra(京东)
- apache skywalking
4.5 spring-cloud-sleuth简介
它是spring cloud封装的zipkin兼容客户端tracer,添加traceId和spanId到slf4j的MDC中,支持大多数开源产品埋点
例如hystrix、restTemplate、Feign、message with spring integration、zuul等,支持采样策略。
代码层级的追踪库,老版本基于htrace,新版本基于brave。
5 容错限流
5.1 存在问题
- 微服务整体可用性较低
- 单应用延迟造成高峰期雪崩
5.2 容错理念与模式
容错理念:
- 凡是依赖均会失败
- 凡是资源均会有限制
- 网络不可靠
- 延迟是稳定性杀手
基本容错模式:
- 超时:在调用端设置较短超时时间
- 限流:限制最大并发数
- 熔断:错误数达到指定值时,之后的访问均拒绝(断路器模式)
- 隔离:隔离不同依赖调用(bulkhead舱壁隔离模式)
- 降级:流量较大时, 减少对高风险微服务的调用
弹性理念:当系统被限流降级后,如果流量恢复正常,系统应该恢复到正常状态。
5.3 hystrix 设计原理和工作流程
hystrix是一个自适应的状态反馈机,其中包含了一个基于时间桶和失败率统计的断路器。
5.4 hystrix 基本概念
- SystrixCommand:将需要容错限流的服务包裹在command中
- FailFast:在执行中直接抛异常并直接返回异常给到调用方
- FailSilent:抛出异常后,调用fallback函数返回空或其他结果
- StaticFallback:在抛出异常后,返回默认值
- Fail with Network:抛出异常后,在fallback中调用别的服务,在调用别的服务上也做command包装
- Primary+Secondary withFallBack:在执行中选择性执行主备服务,主备服务也做command包装
- 请求合并:将一段时间内的请求封装起来,打包发给微服务,得到结果后再解包
- 请求缓存:不频繁变动数据缓存,难点在于理解数据,发现数据变动时间
- 线程隔离:对不同服务创建不同的线程池。好处是支持排队、超时和异步调用,适合不受信客户,单应用访问
- 信号量隔离:对一些服务使用同一个信号量计数进行隔离。好处是轻量,无额外开销,适合受信客户、访问量较大场景(网关)
5.5 hystrix dashboard原理
- 单独应用部署,和应用容错限流无关,仅仅展示数据
- 接收hystrix客户端流stream
- 融合多客户端流数据并展示
5.6 跨集群数据聚合
turbine
5.7 网关+hystrix部署方案
- 使用配置中心(apollo)设置hystrix相关变量
- 使用eureka注册网关服务
- 使用turbine聚合流到hystrix dashboard
5.8 spring cloud hystrix 介绍
对command封装,提供注解方式使用hystrix。
5.9 最佳实践
- 网关集中埋点
- 框架集中埋点,避免开发人员直接操作hystrix
- 使用配置中心(apollo)配置变量
- 信号量/线程池合理使用,信号量用于网关、线程池用于单应用
- 熔断接入内部告警
- 建议使用spring cloud hystrix
5.10 其他产品介绍
- sentinel(阿里巴巴-限流)
- Polly(C#)
- Go-Hystrix(Go)
- FailSafe
- Resilience4J
6 服务注册发现
6.1 服务注册发现模式
- 集中式代理(Nginx实现),【注册中心+自注册、自发现】变体,解决自动化问题;【注册中心+手动/外部注册】变体,改造成本低
- 服务注册中心+客户端嵌入式代理(Dubbo采用的此类模式)
- 主机独立进程代理(service mesh/服务网格)
6.2 eureka/ribbon 简介
eureka是阿基米德发现浮力定律时的惊叹声,寓意服务发现;ribbon是蝴蝶结的意思,寓意微服务链接,负载均衡
6.3 服务注册发现流程
- eureka服务端处理服务注册和发现
- 微服务服务端将服务注册到eureka服务端
- 微服务客户端从eureka服务端获取服务端列表
- 微服务客户端发起请求,请求中需转义服务地址,使用ribbon挑选出其中一个地址
6.4 eureka自保护介绍
当15分钟内损失15%的微服务心跳,将不再剔除提供者。主要是为了应对eureka服务端与微服务服务端网络连接异常的情况。
6.5 eureka健康检查
- eureka客户端主动上报心跳
- eureka客户端也会提供健康检查端点
- 实现HealthCheckHandler定制健康心跳(spring cloud 实现了该handler,并提供disk、hystrix容错限流等检查点)
6.6 服务注册发现延迟问题
- 微服务注册延迟30s(先注册start状态,等待30s后开始提交健康状态)
- eureka服务端响应延迟30s,当微服务上服务端提交注册后,并不会立刻出现在服务列表中,eureka服务端的缓存30s才会刷新一次
- eureka客户端更新延迟30s,客户端间隔30s才会向服务器发送一次获取服务列表请求。
- rebbon服务列表更新延迟30s,rebbon内部维护了一个服务列表缓存,该缓存30s刷新一次。
整体服务上线后,最长需等待两分钟才能生效。
6.7 其他相关产品
- consul
- zookeeper
- etcd
7 微服务监控
7.1 存在问题
- 运维人员没有应用级监控能力
- 开发人员只实现需求,没有devops和度量意识
- 应用监控空白,对应用状态无感知
- 对关键业务指标无感知,很多功能开发了也没有人使用
7.2 MDD 指标驱动开发
- 开发功能前先定义度量指标
- 开发人员辅助埋点
- 作为真实性的唯一来源
- 各方共同关注关键指标
- 使用度量作为决策
- 开发持续维护指标
7.3 prometheus架构
7.3.1 数据输入
prometheus的数据采集采用拉模式,它可以从监控对象直接获取信息、或使用exporter间接采集;针对短周期的batch job,可以使用push gateway,job将信息推到push gateway,prometheus会从gateway中获取信息;此外,prometheus还支持从别的prometheus服务端拉取数据。
7.3.2 监控对象发现
prometheus如何动态调整监控对象?它可以和DNS、K8S、Consul或其他定制化的服务结合,从而调整监控对象。
7.3.3 服务端
prometheus服务端内部有三个模块。
第一个模块是数据采集模块,从监控对象发现中获取监控对象,然后拉取数据。
第二个模块是数据存储模块,prometheus实现了一个时序数据库,部分数据存放在内存中,部分数据存放在磁盘中。
第三个模块是数据查询模块,prometheus拥有自定义的promQL语言,通过promQL语言,可以查询时序数据库中的数据。
7.3.4 alert manager
prometheus服务端会计算是否需要告警,如果需要告警,则将告警信息发送到alert manager,由管理器决定将告警发送到哪里。
7.3.5 信息展示
如果想查看获取prometheus中的数据,可以使用自带的web ui,也可以使用第三方的grafana,也可以使用api client调用。
7.4 最佳实践
重点关注指标:
- rt
- qps
- error rate
- cpu/mem/disk
在线服务重点关注:
- rt
- qps
- error
- cache
离线队列服务重点关注:
- utilization 利用率
- satuation 饱和度
- error
批处理任务重点关注:
- job时长
- job中每阶段时长
- 失败/成功数