Netflix 架构

1997 年 8 月,Reed Hastings 和 Marc Randolph 在加州 Scott Valley 的一家 Blockbuster(百视达,美国当时最大的 DVD 租赁连锁)对面,创办了 Netflix。和 Blockbuster 靠门店租 DVD 不同,Netflix 最初采用邮寄租赁的模式——用户在网上选片,Netflix 把 DVD 寄到家,看完再寄回来。这在当时是一个完全反直觉的商业模式:没有门店,没有逾期罚款(包月制),还免运费。

Netflix 的商业模式经历了两次彻底转型:第一次是从邮寄 DVD 到在线流媒体(2007 年);第二次是从单体应用到微服务(2008-2015 年)。这两次转型,每一次都是被迫的——不是主动求变,而是被技术和市场的变化推着往前走。

本篇文章聚焦的是第二次转型,以及 Netflix 在这之后建立起来的全球流媒体技术体系。

公司画像

Netflix 的核心业务是流媒体视频订阅,目前在全球拥有超过 2.6 亿订阅用户,覆盖 190 多个国家和地区。每天的峰值带宽超过 100 Tbps(相当于每秒传输 12.5TB 数据,足以拷贝 2500 张蓝光 DVD)。

Netflix 的业务有几个显著特点:

  • 流量潮汐效应:用户观看集中在晚间时段(美国时间晚上 8-11 点),峰谷流量比可达 10:1
  • 实时性要求高:视频播放卡顿超过 2 秒,用户流失率显著上升
  • 内容推荐关键:Netflix 80% 以上的用户观看行为来自推荐系统,而非主动搜索
  • 全球化复杂度:不同地区的网络条件、版权内容、监管要求都不同,需要差异化的技术方案

这些特点叠加在一起,构成了 Netflix 技术挑战的核心:如何用稳定的服务质量,在全球范围内,每天为数亿用户提供流畅的视频流,同时保持每天数亿次的推荐计算?

架构演进时间线

时间阶段核心技术解决的核心问题
1997-2007DVD 邮寄时代Oracle RAC + 单体应用快速搭建租赁业务
2008流媒体转型CDN + 转码流水线视频在线播放的基础设施
2008-2015微服务化改造Eureka + Hystrix + Zuul + Cassandra单体故障导致的全站宕机
2015-至今全球化与智能化Chaos Engineering + Open Connect + 推荐系统升级全球分发与内容个性化

第一阶段:DVD 时代的单体架构(1997-2007)

起步:反直觉的商业模式

Netflix 创立之初,美国的 DVD 租赁市场被 Blockbuster 垄断。Blockbuster 的商业模式是靠逾期罚款赚钱——如果你晚还一天 DVD,就罚你几美元。据统计,Blockbuster 10% 的收入来自逾期罚款。

Netflix 的包月制($19.99/月,无限租,不罚逾期费)直接颠覆了这个模式。用户不再需要担心还 DVD 的时间,也不用担心被罚款。这个模式后来被证明是对的——Netflix 的用户留存率远高于 Blockbuster。

但 Netflix 的创始团队很清楚,光靠商业模式创新不够。邮寄 DVD 的物流成本很高,CDN(内容分发网络)技术还不成熟,Netflix 也没有能力自建基础设施。所以最初十年,Netflix 的技术架构相对保守:标准的 Oracle RAC 数据库,跑一个 PHP 单体应用,处理用户订阅、DVD 库存管理、物流调度等核心业务。

2008 年的数据库宕机事件

2008 年,一次数据库故障让 Netflix 的整个在线服务瘫痪了三天。用户在网站上无法登录,无法下单,只能打电话到客服。

这次故障成为 Netflix 技术史上的转折点。Reed Hastings 在事后复盘会上说了一句后来被广泛引用的话:「我们不能把所有服务放在一个篮子里。」

从那之后,Netflix 启动了 SOA(面向服务架构)改造,逐步将单体应用拆分为独立的服务。这次改造花了 7 年才真正完成,期间 Netflix 经历了从 SOA 到微服务的概念演进,也开源了大量自己开发的基础组件。

第二阶段:流媒体转型(2008-2015)

为什么要做 Open Connect CDN

2007 年,Netflix 推出了在线流媒体服务,用户可以直接在电脑上观看电影,不需要等 DVD 寄到家。这个转型带来了全新的技术挑战:

视频分发是带宽密集型业务。一部 1080P 的电影大约 4-8GB,一部 4K 电影可达 20GB 以上。如果所有用户都从 Netflix 的中心服务器下载,Netflix 需要采购和运维数量惊人的带宽资源。

Netflix 做了一个关键决策:自建 CDN(Open Connect)。Netflix 与全球各大 ISP(互联网服务提供商,如 AT&T、Comcast、Verizon 等)合作,在 ISP 的机房内放置 Netflix 的专用服务器(称为 Open Connect Appliance,简称 OCA)。用户访问 Netflix 时,请求会被路由到最近的 OCA 服务器,直接从 ISP 内部网络提供视频内容。

用户请求 → ISP 路由 → OCA 服务器(就近提供视频)

               ISP 内部网络,无跨运营商带宽成本

这样做的好处:

  • 大幅降低 Netflix 的带宽采购成本(ISP 内部流量成本接近零)
  • 用户体验更好(不需要跨运营商访问,减少延迟和缓冲)
  • 对 ISP 也有好处(用户在 Netflix 看视频而不是下载 P2P,ISP 的网络更稳定)

Open Connect CDN 的设计哲学:让视频尽可能靠近用户。这是 CDN 行业的核心理念,但 Netflix 把这个理念执行到了极致——不是把服务器放在几个大的 CDN 节点,而是直接下沉到数百个 ISP 的机房。

视频转码流水线

用户上传的内容需要转码成多种格式和质量等级(1080P、4K、HDR、Dolby Vision 等),以适应不同设备和网络条件。Netflix 的转码系统每天处理数万小时的视频内容。

// 视频转码任务调度(简化示例)
@Service
public class TranscodeJobService {
    @Autowired private VideoRepository videoRepository;
    @Autowired private TranscodeWorkerPool workerPool;

    public void submitTranscodeJob(Long videoId, TranscodeProfile profile) {
        Video video = videoRepository.findById(videoId);

        // 创建转码任务
        TranscodeJob job = new TranscodeJob();
        job.setVideoId(videoId);
        job.setSourceFile(video.getOriginalFile());
        job.setTargetProfile(profile);
        job.setPriority(calculatePriority(video));
        job.setStatus(JobStatus.PENDING);
        jobRepository.save(job);

        // 提交到 worker 池
        workerPool.submit(job);
    }

    // 热门内容优先转码,新内容可以排队等待
    private int calculatePriority(Video video) {
        if (video.getPredictedPopularity() > 0.8) {
            return 100; // 高优先级,10 分钟内必须完成
        } else if (video.getPredictedPopularity() > 0.5) {
            return 50;  // 中优先级
        } else {
            return 10;  // 低优先级,可以排队等待
        }
    }
}

第三阶段:微服务化改造(2008-2015)

为什么微服务对 Netflix 是必然

Netflix 的微服务化不是赶时髦,而是被逼出来的。2011 年,Netflix 在 AWS 上部署了第一批微服务,到 2015 年底,Netflix 的微服务数量超过 700 个,每个服务有独立的团队负责。

Netflix 推动微服务的几个关键驱动因素:

独立部署:不同的业务功能变更频率不同。推荐算法可能每周更新一次,用户注册功能可能几个月才改一次。如果都放在同一个应用里,任何一个功能的部署都需要全量测试,发布周期被拖得很长。

弹性扩展:视频播放服务和用户推荐服务的负载特征完全不同——播放服务在晚间高峰,而推荐服务的计算在白天也很繁忙。独立服务可以独立扩缩容,不需要按照最坏情况配置资源。

故障隔离:2011 年 10 月,Netflix 经历了一次大规模 AWS EC2 中断事件。由于服务已经做了故障隔离,Netflix 只影响了一部分功能,大部分用户仍然可以正常观看视频。如果当时还是单体架构,整个服务可能全部宕机。

服务划分原则

Netflix 的服务划分有几个核心原则:

按业务边界划分,而非技术层次划分。早期很多公司做 SOA 时,会按「数据访问层」「业务逻辑层」「展示层」来拆分服务。这种拆分的问题是服务之间有大量跨层调用,改一个功能往往要改多个服务。Netflix 的做法是按业务能力划分,每个服务包含从数据到业务逻辑的完整能力。

宁多勿少,允许合并。Netflix 的策略是先拆细,如果发现两个服务之间调用频率过高(说明它们本质上是同一个业务),再合并回来。这个过程花了几年时间才找到合理的颗粒度。

// Netflix 的微服务划分示例

// API Gateway:统一入口
// 职责:路由、认证、限流、协议转换
@RestController
@RequestMapping("/api")
public class ApiGateway {

    @Autowired private MemberServiceClient memberClient;
    @Autowired private PlaybackServiceClient playbackClient;
    @Autowired private RecommendationServiceClient recommendationClient;

    // 获取用户推荐列表
    @GetMapping("/recommendations")
    public List<Video> getRecommendations(@RequestHeader("Authorization") String token) {
        // 1. 验证 Token
        String userId = authService.validateToken(token);

        // 2. 从推荐服务获取个性化推荐
        //    走的是 gRPC(Netflix 内部),而非 HTTP
        List<String> videoIds = recommendationClient.getTopN(userId, 20);

        // 3. 从内容服务获取视频元数据
        List<Video> videos = contentService.getVideos(videoIds);

        // 4. 过滤:已观看、去版权限制等
        return filterService.apply(userId, videos);
    }
}

// Member Service:用户账户管理
// 职责:注册、登录、订阅计划、账单
@Service
public class MemberService {
    public Subscription getSubscription(String userId) {
        return subscriptionRepository.findByUserId(userId);
    }

    public void upgradePlan(String userId, SubscriptionPlan newPlan) {
        // 升级订阅计划
        // 异步通知计费系统
        kafkaTemplate.send("subscription-events",
            userId, new SubscriptionUpgradeEvent(userId, newPlan));
    }
}

// Recommendation Service:推荐系统
// 职责:协同过滤、内容推荐、新内容发现
@Service
public class RecommendationService {
    @Autowired private KafkaTemplate kafkaTemplate;

    // 用户行为事件异步处理
    public void onUserAction(UserAction action) {
        // 发送事件到 Kafka,供下游算法计算使用
        kafkaTemplate.send("user-actions",
            action.getUserId(), action);
    }

    // 实时推荐
    public List<String> getTopN(String userId, int n) {
        // 1. 从缓存获取用户的实时特征
        UserFeatures features = userFeatureCache.get(userId);

        // 2. 协同过滤召回
        List<String> cfCandidates = collaborativeFilter召回(features, n * 2);

        // 3. 内容匹配召回
        List<String> contentCandidates = contentFilter召回(features, n * 2);

        // 4. 混合排序
        return ranker.rank(
            mergeAndDeduplicate(cfCandidates, contentCandidates),
            features, n);
    }
}

服务间通信:同步与异步

Netflix 的服务间通信分为两种模式:

同步通信(gRPC/HTTP):适用于需要实时返回结果的场景。比如用户点开一部电影,需要立即返回播放地址,不能等。

异步通信(Kafka):适用于不需要立即知道结果,或者可以批量处理的场景。比如用户观看行为分析、内容推荐计算、账单生成等。

// 异步事件处理:Kafka 消息消费
@Service
public class UserBehaviorConsumer {

    @KafkaListener(topics = "user-actions", groupId = "recommendation")
    public void onUserAction(ConsumerRecord<String, UserAction> record) {
        UserAction action = record.value();

        // 1. 更新用户观看历史
        viewingHistoryService.record(action);

        // 2. 更新实时推荐特征
        userFeatureService.updateFeatures(action);

        // 3. 如果是付费用户,发送数据给广告系统
        if (subscriptionService.isPaidUser(action.getUserId())) {
            kafkaTemplate.send("paid-user-actions",
                action.getUserId(), action);
        }
    }
}

核心基础设施组件

Eureka:服务注册与发现

Eureka 是 Netflix 开源的服务注册与发现组件,解决了微服务架构中最基础的问题:服务 A 怎么知道服务 B 在哪里?

传统方案是用配置文件维护服务地址列表,但缺点是:服务地址变了需要手动改配置,服务扩容/缩容时配置维护成本极高。

Eureka 的工作原理:

  • 服务注册:每个服务实例启动时,向 Eureka Server 注册自己的 IP、端口、健康状态
  • 心跳续约:服务定期发送心跳,证明自己还活着。如果 Eureka 超过 90 秒没收到某个服务的心跳,就把它从注册表中移除
  • 服务发现:调用方从 Eureka 获取目标服务的实例列表,Eureka Client 内置了负载均衡(默认轮询)
// Eureka 服务注册与发现示例

// 服务提供者启动时注册
@SpringBootApplication
@EnableEurekaClient
public class RecommendationServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(RecommendationServiceApplication.class, args);
        // 启动后自动向 Eureka Server 注册
        // 服务名(spring.application.name)会被用作注册名
    }
}

// 服务消费者调用
@Service
public class ApiGateway {

    // RestTemplate + @LoadBalanced 实现客户端负载均衡
    // Ribbon 会自动从 Eureka 获取可用实例并轮询分发
    @Autowired
    private RestTemplate restTemplate;

    public VideoDetails getVideoDetails(String videoId) {
        // 不用写死 IP:Netflix 建议服务名大写
        // Ribbon 从 Eureka 获取 recommendation-service 的实例列表
        String url = "http://RECOMMENDATION-SERVICE/api/v1/details/" + videoId;
        return restTemplate.getForObject(url, VideoDetails.class);
    }
}

Hystrix / Resilience4j:熔断器

Hystrix 是 Netflix 开源的熔断器组件,2018 年 Netflix 停止维护后,社区 fork 出了 Resilience4j,继续活跃开发。

熔断器的核心思想来自电路保险丝:当某个服务连续失败超过阈值时,「熔断」后续调用,直接返回降级结果,等服务恢复后再逐步放行请求

为什么需要熔断?考虑一个典型场景:推荐服务调用用户画像服务,如果用户画像服务变慢了(响应时间从 10ms 变成 500ms),所有等待中的请求会耗尽推荐服务的线程资源,进而拖垮整个服务链。

Hystrix 解决这个问题的设计:

// Resilience4j 熔断器配置(Netflix 内部已迁移到 Resilience4j)
@Configuration
public class Resilience4jConfig {
    @Bean
    public CircuitBreakerRegistry circuitBreakerRegistry() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            // 滑动窗口类型:基于调用次数(COUNT_BASED)或时间(WINDOW_BASED)
            .slidingWindowType(SlidingWindowType.COUNT_BASED)
            // 滑动窗口大小:统计最近 10 次调用的失败率
            .slidingWindowSize(10)
            // 失败率阈值:超过 50% 则触发熔断
            .failureRateThreshold(50)
            // 熔断持续时间:熔断后 30 秒内拒绝所有请求
            .waitDurationInOpenState(Duration.ofSeconds(30))
            // 半开状态放行数:熔断结束后,先放 3 个请求试试
            .permittedNumberOfCallsInHalfOpenState(3)
            // 最小调用次数:低于此值不计算失败率
            .minimumNumberOfCalls(5)
            .build();

        return CircuitBreakerRegistry.of(config);
    }
}

// 使用熔断器保护服务调用
@Service
public class RecommendationClient {
    private final CircuitBreaker circuitBreaker;
    private final RestTemplate restTemplate;

    public RecommendationClient(CircuitBreakerRegistry registry) {
        // 创建名为 "recommendationService" 的熔断器
        this.circuitBreaker = registry.circuitBreaker("recommendationService");
    }

    public List<Recommendation> getRecommendations(Long userId) {
        // executeSupplier 会自动执行熔断器逻辑
        return circuitBreaker.executeSupplier(() -> {
            return restTemplate.getForObject(
                "http://recommendation-service/api/v1/" + userId,
                List.class
            );
        });
    }

    // 降级方法:熔断打开时调用的兜底逻辑
    public List<Recommendation> fallback(Long userId, Throwable throwable) {
        log.warn("Recommendation service failed: {}, fallback triggered",
            throwable.getMessage());

        // 策略一:返回全局热门内容(不依赖个性化服务)
        return getGlobalTrending();

        // 策略二:返回用户上次观看的同类型内容
        // return getRecentlyWatchedSimilar(userId);
    }
}

Zuul:API 网关

Zuul 是 Netflix 的 API 网关,承担了所有外部流量的入口职责:

  • 请求路由:根据 URL 将请求转发到对应的后端服务
  • 认证授权:验证 JWT Token,检查用户权限
  • 限流熔断:在网关层拦截异常流量,保护后端服务
  • 灰度发布:支持按用户 ID 或流量比例路由到不同版本
  • 日志收集:记录每个请求的完整调用链,供 Dapper 追踪
// Zuul 过滤器示例:请求限流
@Component
public class RateLimitFilter extends ZuulFilter {

    private final RateLimiter rateLimiter = RateLimiter.create(10000.0);

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE; // 前置过滤器
    }

    @Override
    public int filterOrder() {
        return 0; // 优先级最高
    }

    @Override
    public boolean shouldFilter() {
        return true; // 对所有请求生效
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        String userId = ctx.getRequest().getHeader("X-User-Id");

        // 令牌桶限流:每秒允许 10000 个请求
        if (!rateLimiter.tryAcquire()) {
            ctx.setSendZuulResponse(false); // 拦截请求
            ctx.setResponseStatusCode(429); // Too Many Requests
            ctx.setResponseBody("Rate limit exceeded");
        }

        return null;
    }
}

稳定性保障实践

Chaos Engineering:主动发现系统脆弱点

Netflix 在 2011 年上线了 Chaos Monkey,这个工具会在工作时间随机关闭生产环境的服务器。听起来很疯狂,但背后的逻辑是:与其等着故障发生,不如主动制造故障,提前验证系统的容错能力

后来 Netflix 升级为 Chaos Kong——模拟整个 AWS 可用区(Region)级别的故障。在一个大规模 AWS 中断事件中,Chaos Kong 验证了 Netflix 的多区域容灾能力确实有效。

# Chaos Monkey 配置示例
# chaos_monkey.conf
enabled = true
# 工作时间随机终止服务器
mean_wait_between_failures = 10m
# 10% 的概率杀死 ASG 中的服务器
kill_probability = 0.1
# 只在工作时间执行,周末不搞事
business_hours_only = true

Chaos Engineering 的核心流程:

  1. 定义稳态假设:系统正常运行时,关键指标应该是什么样的?(如:p99 延迟 < 200ms,成功率 > 99.9%)
  2. 注入故障:随机关闭服务器、注入网络延迟、模拟服务超时
  3. 观察系统行为:指标是否仍在稳态范围内?
  4. 迭代改进:如果发现系统无法承受故障,修复后再次实验

灰度发布策略

Netflix 每天都会上线新版本服务,靠的是一套成熟的灰度发布体系:

  • Canary Release:新版本先部署到 1-5% 的流量,观察 30 分钟到 2 小时
  • 监控指标:重点关注错误率、延迟、CPU 使用率是否异常
  • 自动回滚:如果指标超出基线阈值,自动回滚到上一版本
  • 逐步放量:确认无误后,按 5% → 20% → 50% → 100% 的节奏逐步放量

架构启示

启示一:微服务不是银弹

Netflix 的微服务化花了 7 年,代价巨大。过程中遇到了:

  • 服务间通信的复杂性爆炸(700+ 个服务,调用链路最深达 20 层)
  • 分布式事务的噩梦(跨服务的数据一致性无法用本地事务保证)
  • 运维复杂度的指数增长(每个服务需要独立监控、告警、部署、回滚)

Netflix 的经验:微服务拆分要谨慎。如果你的团队小于 20 人,业务复杂度不高,单体应用完全够用。不要因为「微服务很酷」就盲目拆分。

启示二:稳定性是核心竞争力

Netflix 的 SLA 是 99.99%,即每年允许的故障时间只有 52 分钟。这不是靠运气,而是靠大量主动投入:

  • Chaos Engineering 主动发现脆弱点
  • 全链路监控覆盖每一个服务调用
  • 完善的 runbook(故障处理手册),每个常见故障都有标准化处理流程
  • 定期的 GameDay(全员参与的故障演练)

对普通项目的启发:稳定性保障需要投入资源,但不能只靠运维团队。开发团队在设计系统时就要考虑故障场景,「Design for Failure」是 Netflix 的核心理念。

启示三:开源是最好的技术营销

Netflix 开源的 Eureka、Hystrix、Zuul、Ribbon、Cassandra 等组件,被全球数百万开发者使用。这些组件的质量经过了 Netflix 自身生产环境的验证,因此可信度极高。

开源给 Netflix 带来的收益:

  • 社区贡献代码:Hystrix 的熔断器思想来自社区反馈,Eureka 的服务发现机制被 Spring Cloud 广泛借鉴
  • 技术影响力:Netflix 成为微服务领域的标杆,吸引顶尖工程师加入
  • 生态绑定:用 Spring Cloud 开发微服务,几乎离不开 Netflix 的组件

术语表

术语类型说明
Reed Hastings人名Netflix 联合创始人兼 CEO,1997 年与 Marc Randolph 共同创办 Netflix,曾任加州教育局专员
Marc Randolph人名Netflix 联合创始人,首任 CEO,后离开 Netflix 创办其他公司
Blockbuster公司名美国最大的 DVD 租赁连锁,2010 年破产,被 Netflix 的邮寄模式击败
OCA(Open Connect Appliance)专有名词Netflix 自建 CDN 的专用服务器,放置在 ISP 机房内,直接从 ISP 内部网络提供视频内容
CDN(Content Delivery Network)技术名词内容分发网络,通过在全球部署缓存节点,让用户就近获取内容,减少骨干网络压力
Chaos Monkey技术名词Netflix 开源的工具,随机关闭生产环境服务器,验证系统容错能力
SLA(Service Level Agreement)技术名词服务等级协议,定义服务的可用性、响应时间等指标的承诺值
Canary Release技术名词金丝雀发布,新版本先小流量验证,确认无误后再全量发布
gRPC技术名词Google 开源的高性能 RPC 框架,基于 HTTP/2 和 Protocol Buffers,支持多语言
Cassandra技术名词Apache 顶级项目,分布式 NoSQL 数据库,Facebook 开发,Netflix 用来存储用户事件数据
JWT(JSON Web Token)技术名词用于在各方之间安全传递声明的开放标准,常用于 API 认证
令牌桶算法(Token Bucket)算法名词限流算法的一种,以固定速率往桶里放令牌,请求来了消耗令牌,令牌不够则拒绝

总结

Netflix 是微服务架构领域最系统化的实践者,它的历史贡献不仅在于自己的系统做得好,更在于把踩过的坑变成了开源工具,让整个行业受益。

演进脉络

  • 1997-2007:DVD 邮寄时代的单体架构
  • 2008:数据库宕机推动 SOA 改造,流媒体转型
  • 2008-2015:微服务化完成,开源 Eureka + Hystrix + Zuul
  • 2015-至今:Chaos Engineering 保障全球高可用,Open Connect CDN 优化分发成本

核心技术组件

  • Eureka:服务注册与发现,解决了服务定位问题
  • Hystrix / Resilience4j:熔断器,防止故障级联扩散
  • Zuul:API 网关,统一处理认证、限流、灰度
  • Open Connect CDN:自建 CDN,让视频下沉到 ISP 机房

对普通项目的启发

  • 微服务拆分要量力而行,团队规模是重要参考
  • 稳定性需要主动投入,「Design for Failure」要从设计阶段开始
  • 善用 Netflix 开源组件,可以快速搭建生产级的微服务基础设施