Netflix 的微服务演进之路

背景

 

Netflix 是全球领先的视频网站,影片类型包括好莱坞制作,独立制作电影,本地电影等等,自主研发了“纸牌屋”等知名的电视剧。全球有8千多万的订阅会员,覆盖190个国家(暂未覆盖中国…),支持一千多种设备类型。Netflix 是 AWS 服务的重度用户,在 AWS 上有数万台虚机。在 DevOps 领域,Netflix 是业界的先驱,他们为 Spring Cloud Netflix 社区贡献了大量优秀的开源软件,例如 Eureka,Zuul,Turbine,Hystrix 等等。

遇到的挑战

 

Netflix 认为,他们之前的应用架构是典型的巨石应用。虽然实现了应用层面的多活,但是仍然使用单一巨大的代码库,单一巨大的数据库,如果数据库挂了,整个系统都将瘫痪。微服务架构是什么?来复习下 Martin Fowler 的定义: 

里面提到几个重要的关键字:多个微服务,独立进程,轻量的通讯机制(通常是 HTTP)。 
Netflix 任务微服务必须具备以下能力:

服务的关注点分离:

一个服务不能即处理用户信息,又处理订单的信息,服务要实现模块化,并且需要封装内部的接口,对外提供服务。

 

服务的水平扩展性:

服务能否顺利的水平扩展?扩展一个服务需要多少时间?在服务水平扩展之后,如何将流量分流到新的节点?

 

虚拟化和弹性计算的能力:

需要能够实现自动化运维,按需的创建计算环境。

 

 

1.Netflix 微服务架构

 

上图是整个 Netflix 的服务调用关系图谱。左侧是 Edge 服务,包含:

  • ELB (Elastic Load Balance)- 用于做客户端的请求负载分发。
  • Zuul – Netflix 的开源网关组件,用于提供动态路由,监控,故障自愈,安全等服务。
  • API– Netflix 统一调用后端服务的接口层。

 

右侧是中间件服务层:

提供的服务包括:

  • 产品
  1. 产品的 A/B 测试
  2. 订阅服务
  3. 推荐服务
  • 平台
  1. 路由
  2. 服务配置
  3. 加密
一个典型的微服务,应该具备缓存层,服务层,数据层。 

2.Netflix 的痛点

  • 服务间调用失败
服务之间的调用会出现网络延迟,服务失效,调用逻辑错误,以及扩容失败等问题。

  • 服务故障引发雪崩
当核心服务发生故障,会影响到整体系统的可用性。失效的服务会让用户的请求一直等待返回,且一直得不到释放,服务器资源被撑爆。 

解决方案

 

优化方案:熔断器和 FIT 
Hystrix(熔断器)是 Netflix 贡献的开源组件,Netflix 认为,一个服务挂了,应当被立刻发现,系统不再持续调用该不可用的服务而出现超时返回,而是应当立刻调用一个 FallBack 方法进行错误处理。 
Netflix 网站的特点是高峰期并发流量非常高,平时流量低,很多应用上线之后,并没有一个能够完全模拟线上环境流量的测试环境去验证应用的高可用性。为了解决这些问题,Netflix 搭建了 Fault Injection Testing(FIT) 框架做容错性测试。它主要提供3种能力:

  • 模拟线上流量
  • 将真实的线上环境流量推到100%进行压测,看高并发状态下的真实反应。
  • Netflix 的微服务分为2种,一是核心服务,即用户加载应用,观看视频等,另一种是非核心服务。FIT 能够创建一个场景,将所有非核心服务停掉,看用户是否还能享受 Netflix 的核心服务。

 

优化方案: 分布式数据一致性

 

Netflix 的数据需要存储在 AWS 不同的可用区,而一条数据写入多个可用区的数据库存储延迟,那么就有可能部分写失败,Netflix 在 CAP 理论中,选择了最终一致性来解决这个问题。Netflix 使用 Cassandra 作为分布式数据库,当你写入可用区 B 的数据时,Cassandra 会自动复制到其他可用区。你可以使用 Quorum 进行灵活的写策略,比如写入一个节点成功,你就认为整个集群的写入成功,让 Cassandra 帮你做剩下的同步,你也可以设置让整个集群的写入成功,才认为该条数据写成功。优化方案 : 无状态的缓存服务 

传统的缓存服务例如 Squid,即使你可以做到 Squid 基于用户 ID 做 Sharding 来分担高并发的请求,这样每个用户能够访问到自己的缓存,但每个 Sharding 仍然存在单点故障,某一个 Squid 服务挂了,仍然会带来雪崩。Netflix 最初就是使用 Squid 做分片缓存,但当一个分片挂掉是,Netflix 发生了3个小时的宕机。

 

所以 Netflix 采用了多写的分布式缓存 EVCache,EVCache 封装了 MemcacheD 实现分布式缓存。每个 EVCache 客户端会将缓存数据写入多个可用区的缓存服务器,避免缓存服务器的单点故障。而客户端读缓存时,只需从本地可用区进行读取。 
通过 EVCache 集群的搭建,Netflix 支持了3千万次/秒的请求。可是问题又来了,当每个应用都来请求线上的 EVCahce 之后,该服务本身会遇到瓶颈。很多后台任务(Batch),例如推荐服务会频繁访问线上的缓存,于是 Netflix 将线上和线下的缓存分离,这样的优势在于后台任务不会影响到线上的缓存服务。优化方案: 上线检查的 Checklist 

Netflix 在持续的运维工作中,总结出来很多最佳实践,他们在上线之前都有一个检查清单,会覆盖告警,自动化金丝雀发布的分析,自动扩容,ELB 配置,压力测试,蓝绿部署,失败回滚等。 
Netflix 使用 Nebula 构建,Jenkins 做 CI,JFrog Artifactory做制品库的管理(.jar, .deb, Docker 镜像),并且开源了他们的持续部署平台Spinnaker。Spinnaker 能够实现自动化的金丝雀发布,往前能够对接 Jenkins,往后能够对接 AWS,Kubernetes 集群的部署。它能够为金丝雀发布的每个阶段进行自动化评分,评分会包含服务状态,用户反馈,系统异常等信息进行评估,从而用机器决策是否进入到下一个金丝雀发布的阶段。Netflix 公司能够实现快速的发布服务,一个很重要的地方是在于线上服务的容错性做得非常成熟,他们的程序员提交代码也会让线上服务失败。 

从上图可见,在工作日 Netflix 的服务失败时间很高,但由于服务的降级,隔离处理都是自动化完成,所以程序员有充分的自信去提交代码,修复问题。 

总结

 

Netflix 的运维团队为公司提供了强大的基础设施,这为业务开发团队带来了极大的快速发布的能力,实现将 Idea 变成线上服务的时间大大缩短,运维团队在公司的价值大大提升!