微服务架构体系.png

杨波老师曾在ebay、携程等公司从事基础框架研发,对微服务架构体系有独到的见解。近期学习了杨波老师的微服务架构体系课程,并简单整理了一些笔记,供大家学习参考。

0 简介

由于时间有限,这里只能对杨波老师的课程做简单总结,对于其中细节不能再单独拎出来细讲。对于微服务架构体系中的各种模块,大家可以在笔记的基础上,对感兴趣的模块自行深度学习。

1 安全接入

1.1 问题域

1.1.1 开放系统间授权

资源拥有者使用第三方应用,第三方应用需要访问资源拥有者的资源。具体情况如:社交联合登录、开放API平台等。

解决的办法有三种:

  1. 直接在第三方应用中输入用户名密码。(不合适)
  2. 提供access key给到第三方应用。(不合适)
  3. 针对特定应用,在用户允许的情况下发放特殊令牌,通过令牌访问第三方应用。

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中重要的组成部分,整个获取流程如下:

  1. 第三方应用尝试从授权服务器获取Token
  2. 授权服务器询问用户是否允许授权给第三方应用
  3. 用户同意
  4. 授权服务器颁发Token给第三方应用

OAuth2.0 有以下优点:

  1. 更安全,第三方应用不接触密码
  2. 广泛应用和传播,不用担心通用问题
  3. 短寿命的Token,支持过期策略
  4. 资源服务器和授权服务器解耦
  5. 集中式授权,简化第三方应用接入难度
  6. HTTP/JSON友好,易于在不同环境使用
  7. 考虑多种使用场景
  8. 用户可以设置多种信任级别

同时,OAuth2.0 也有一些缺点:

  1. 起草时利益方太多,导致协议框架太宽泛,造成各种实现的兼容性和互操作性差
  2. 不是一个认证协议

1.3 OAuth2.0 令牌类型

  1. 访问令牌:用于代表用户直接去访问资源的令牌
  2. 刷新令牌:用于去授权服务器获取一个新的访问令牌
  3. 授权码:用户给予的一个编码,用于换取访问令牌和刷新令牌
  4. Bearer Token:现钞,不用校验客户端的令牌
  5. Proof Of Possession(PoP) Token:需要检验客户端的令牌

1.4 OAuth2.0 授权模式与选型

rfc6749中描述了OAuth2.0协议框架标准,其中将授权分为四种模式

1.4.1 授权码模式

授权码模式.png

  1. 客户通过浏览器访问第三方应用,第三方应用要求浏览器携带client_identifier和回调地址跳转到授权服务器
  2. 客户在授权服务器上做认证
  3. 认证通过后,授权服务器要求浏览器携带授权码调转到指定的回调地址,第三方应用由此拿到授权码
  4. 第三方应用拿到授权码后,从后端向授权服务器发送携带授权码和另一个回调地址的请求
  5. 授权服务器检验授权码通过后,回调另一个地址,并携带上访问Token和可选的刷新Token

1.4.2 简化模式

简化模式.png

  1. 客户通过浏览器访问第三方应用,第三方应用要求浏览器携带client_identifier和回调地址跳转到授权服务器
  2. 客户在授权服务器上做认证
  3. 认证通过后,授权服务器要求浏览器携带访问Token的Fragment调转到指定的回调地址,第三方应用由此拿到存放Token的Fragment
  4. 第三方应用要求浏览器访问资源服务器脚本
  5. 资源服务器返回脚本,浏览器执行脚本,得到访问Token

1.4.3 密码模式

密码模式.png

  1. 客户填写密码给到客户端
  2. 客户端拿着密码去访问授权服务器
  3. 授权服务器返回访问Token

1.4.4 客户端模式

客户端模式.png

  1. 客户端访问授权服务器
  2. 授权服务器返回访问Token

1.4.5 Token的刷新

除了四种授权模式外,额外还有一个刷新流程需要学习一下。

2020-06-27T08:38:41.png

  1. 第三方应用得到授权,拿到访问Token和刷新Token
  2. 第三方应用在使用访问Token访问资源服务器后
  3. 资源服务器告诉第三方应用访问Token失效
  4. 第三方应用拿着刷新Token访问授权服务器
  5. 授权服务器返回新的访问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

  1. 第三方应用通过授权服务器获取访问令牌,然后调用API网关。
  2. API网关接受第三方应用的访问令牌,并访问授权服务器转为JWT。
  3. 微服务通过API网关对外暴露,访问需要携带JWT。

1.6.2 方案2

  1. 第三方应用通过授权服务器获取加密JWT,然后调用API网关。
  2. API网关接受加密的JWT,并对其解密。
  3. 微服务通过API网关对外暴露,访问需要携带解密的JWT。

1.6.3 方案3(推荐)

  1. 第三方应用通过授权服务器获取访问令牌,然后调用API网关。
  2. 授权服务器在返回访问令牌时,将对应的JWT存放在redis中。
  3. API网关接受访问令牌,并访问redis获取JWT。
  4. 微服务通过API网关对外暴露,访问需要携带JWT。

1.6.4 生产环境监控

业务指标监控

  1. 登陆次数
  2. 授权次数
  3. 颁发访问/刷新令牌次数
  4. 活跃令牌数
  5. 令牌校验次数
  6. 令牌吊销次数
  7. client注册数

性能指标

  1. authorize 授权接口
  2. token 获取token接口
  3. introspect 用户信息获取接口
  4. revoke 注销token接口

1.7 参考资源

OAuth2.0/OIDC开源产品

  • RedHatKeyCloak
  • Apereo CAS
  • Identity Server C#
  • OpenID Connect Java Spring Server
  • Spring Security OAuth2

OAuth2.0/OIDC库(客户端用)

OAuth2.0/OIDC SaaS产品

2 配置中心

2.1 配置基本概念

配置的定义是:程序中可根据需要动态调整的变量。配置的形态主要有:硬编码、配置文件、环境变量、启动参数和基于数据库的配置。

根据配置是否可运行时修改,将配置分为静态配置和动态配置。

静态配置中包含环境配置(当前集群环境等)、安全配置(数据库用户名密码等)。

动态配置中,又分为应用配置(超时、线程池、日志级别、限流阈值、黑白名单)、功能开关(灰度开关、蓝绿部署、功能降级、HA高可用开关、DB主备切换)和业务配置(促销规则、贷款额度、订单数量限制)。

对于配置主要有三个方面需要进行治理,第一是权限控制;第二是环境、集群隔离;第三是灰度发布支持。

2.2 问题域与核心需求

问题域

  1. 动态修改配置
  2. 多系统统一格式
  3. 生产环境隔离

核心需求

  1. 应用和配置分离
  2. 配置抽象标准化
  3. 集中式管理配置
  4. 高可用
  5. 实时发布
  6. 支持配置治理

2.3 开关驱动开发

TBD trunk based development(新功能隐藏在功能开关之后),解决branch hell问题。

支持Branch by Abstraction(基于抽象的重构),安全重构,在新代码真正可用前保留旧代码。

优点:

  1. 新功能和代码发布分离,减少发布风险
  2. 可定制A/B测试
  3. 没有分支合并冲突问题

缺点:

  1. 代码入侵性高,技术债,需定期清理
  2. 需要有配置中心配合使用
  3. 需要有devops文化和流程配置

2.4 相关产品

  • 阿里巴巴:diamond(开源)
  • 百度:disconf(开源)
  • 携程:apollo(开源)
  • 360:360Conf
  • netflix:archaius(开源)
  • facebook:gatekeeper

3 服务网关

3.1 核心需求

  1. 单点入口
  2. 路由转发
  3. 限流熔断
  4. 日志监控
  5. 安全认证

3.2 netflix zuul介绍

zuul处理了绝大多数netflix流量,支持1000+种设备类型,每天处理百亿+请求,多区域(regions)部署支持,多集群(20+)部署支持。在携程,部署实例50+,覆盖大量业务场景,日流量超过50E。在拍拍贷,部署实例40+,覆盖多场景,日流量超过5E。

3.3 应用场景介绍

  1. 红蓝部署(精准切流)
  2. 开发者测试分支(特征流量转发)
  3. 埋点测试
  4. 压力测试(流量复制到压力集群)
  5. 调试路由(特殊head等等)
  6. 金丝雀测试
  7. 粘性金丝雀(不会随机挑选新旧集群)
  8. 失败注入
  9. 降级测试
  10. 跨区域高可用(不同区域网关间流量转发)
  11. 防爬防攻击
  12. 健康检查和屏蔽坏节点

3.4 zuul应用架构

zuul应用架构.png

zuul是一个基于过滤器模式的网关,在请求微服务前,调用pre filter,主要用于认证、选路由、日志记录;选好路由后,执行routing filter,处理请求到微服务处理器;在请求完微服务后,调用post filter,在主要是用作增加head头、收集统计和度量、转换响应方式(rpc->http)等功能。在上述三个调用中,但凡抛出异常,均会调用error filter。

3.5 zuul2.0简介

  1. 引入非阻塞异步模式(更适用于IO密集型)(netty实现)
  2. 20%性能提升
  3. HTTP2支持
  4. 自适应重试

3.6 zuul接入最佳实践

  1. 使用AsyncServlet优化连接数
  2. 引入配置中心(apollo),避免网关频繁部署
  3. Hystrix熔断限流(信号量隔离)
  4. 不同服务集群,连接池单独设置
  5. 尽量不引入业务逻辑
  6. 自助路由扩展(另行实现一个管理域,允许开发者自行配置微服务,入参与rpc调用入参对接、返回结果转换等)

3.7 其他网关产品

  1. kong
  2. tyk.io
  3. goku gateway
  4. 小豹API网关

4 调用链监控

4.1 核心需求

  1. 功能发布跟踪
  2. 从大量报错中查找问题原因
  3. 及早发现应用性能问题
  4. 微服务间调用日志串接

devops实践:

  1. 改进系统的前提是系统可测量
  2. 谁构建、谁运行、谁监控

4.2 基本原理

目前市面上的调用链监控产品,大多都源自google的论文:Dapper, a Large-Scale Distributed Systems Tracing Infrastructure

它的核心概念如下:

  • trace:一次分布式调用链路跟踪
  • span:一个方法的调用踪迹
  • annoation:附着在span的日志信息
  • sampling:采样率

整体的处理流程是:在应用端做日志埋点,由服务器上的日志收集系统对埋点日志进行收集,对接存储系统,最后做UI展示。

4.3 使用调用链监控好处

  1. 报表丰富,从各个角度了解系统状态
  2. 故障快速发现和定位
  3. 有助于开发人员树立质量意识
  4. 便于检查系统设计/服务分层合理性
  5. 发现技术债

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工作流程.png

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 指标驱动开发

  1. 开发功能前先定义度量指标
  2. 开发人员辅助埋点
  3. 作为真实性的唯一来源
  4. 各方共同关注关键指标
  5. 使用度量作为决策
  6. 开发持续维护指标

7.3 prometheus架构

prometheus架构.png

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中每阶段时长
  • 失败/成功数

标签: 微服务, 架构

添加新评论