~cytrogen/blog-public

ref: 88eebf3dfdd8ab819fa1a84e1976a8a75d5af2b6 blog-public/posts/75ba.html -rw-r--r-- 107.6 KiB
88eebf3dCytrogen Deploy 2026-02-19 08:34:27 4 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
<!DOCTYPE html><html lang="zh" data-theme="dark"><head><meta charset="utf-8"><meta name="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>ByteByteGo 简报翻译【1】:Netflix 如何管理 2.38 亿会员? · Cytrogen 的个人博客</title><meta name="description" content="本文是 ByteByteGo 简报的中文翻译,深入探讨了 Netflix 是如何设计其系统架构来管理全球 2.38 亿会员的。文章详细解析了 Netflix 会员平台的演进过程,从早期的轻量级库到如今由十几个微服务构成的高可用分布式系统。内容涵盖其技术选型(如 CockroachDB、Cassandra、Kafka),并重点介绍了用户注册流程和基于变更数据捕获(CDC)的会员历史追踪机制。通过本文,可以一窥世界顶尖流媒体服务背后,支撑其核心订阅业务的系统设计与实践。"><link rel="icon" href="../favicon.png"><link rel="canonical" href="https://cytrogen.icu/posts/75ba.html"><link rel="webmention" href="https://webmention.io/cytrogen.icu/webmention"><link rel="me" href="https://m.otter.homes/@Cytrogen"><link rel="me" href="https://github.com/cytrogen"><meta name="fediverse:creator" content="@Cytrogen@m.otter.homes"><link rel="preload" href="../fonts/opensans-regular-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous"><style>@font-face {
  font-family: 'Open Sans';
  src: url('../fonts/opensans-regular-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  size-adjust: 107%;
  ascent-override: 97%;
  descent-override: 25%;
  line-gap-override: 0%;
}
</style><script>(function() {
  try {
    // 优先级:用户选择 > 系统偏好 > 默认浅色
    const saved = localStorage.getItem('theme');
    const theme = saved || 
      (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.style.colorScheme = theme;
  } catch (error) {
    // 失败时使用默认主题,不阻塞渲染
    document.documentElement.setAttribute('data-theme', 'light');
  }
})();
</script><link rel="stylesheet" href="../css/ares.css"><script data-netlify-skip-bundle="true">(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const theme = document.documentElement.getAttribute('data-theme');
    const pageWrapper = document.getElementById('page-wrapper');
    if (pageWrapper && theme) {
      pageWrapper.setAttribute('data-theme', theme);
    }
  });
})();

</script><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 8.1.1"><link rel="alternate" href="atom.xml" title="Cytrogen 的个人博客" type="application/atom+xml">
</head><body><div id="page-wrapper"><a class="skip-link" href="#main-content">跳到主要内容</a><div class="wrap"><header><a class="logo-link" href="../index.html"><img src="../favicon.png" alt="logo"></a><div class="h-card visually-hidden"><img class="u-photo" src="https://cytrogen.icu/favicon.png" alt="Cytrogen"><a class="p-name u-url u-uid" href="https://cytrogen.icu">Cytrogen</a><p class="p-note">Cytrogen 的个人博客,Cytrogen's Blog</p><a class="u-url" rel="me noopener" target="_blank" href="https://m.otter.homes/@Cytrogen">Mastodon</a><a class="u-url" rel="me noopener" target="_blank" href="https://github.com/cytrogen">GitHub</a></div><nav class="site-nav"><div class="nav-main"><div class="nav-primary"><ul class="nav-list hidden-mobile"><li class="nav-item"><a class="nav-link" href="../index.html">首页</a></li></ul><div class="nav-tools"><div class="language-menu"><button class="language-toggle" type="button"><svg class="icon icon-globe" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855A7.97 7.97 0 0 0 10.855 12H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z"></path></svg><span>中文</span></button><div class="language-dropdown"></div></div></div><div class="nav-controls"><div class="more-menu hidden-mobile"><button class="more-toggle" type="button"><span>更多</span><svg class="icon icon-chevron-down" width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 8.825c-.2 0-.4-.1-.5-.2l-3.3-3.3c-.3-.3-.3-.8 0-1.1s.8-.3 1.1 0l2.7 2.7 2.7-2.7c.3-.3.8-.3 1.1 0s.3.8 0 1.1l-3.3 3.3c-.1.1-.3.2-.5.2z"></path></svg></button><div class="more-dropdown"><ul class="dropdown-list"><li class="dropdown-item"><a class="nav-link" href="../archives/index.html">归档</a></li><li class="dropdown-item"><a class="nav-link" href="../categories/index.html">分类</a></li><li class="dropdown-item"><a class="nav-link" href="../tags/index.html">标签</a></li><li class="dropdown-item"><a class="nav-link" href="../about/index.html">关于</a></li><li class="dropdown-item"><a class="nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></div><div class="theme-switcher"><button class="theme-toggle" type="button" role="switch" aria-pressed="false" aria-label="切换主题"><div class="theme-icon moon-icon"><svg class="icon icon-moon" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path></svg></div><div class="theme-icon sun-icon"><svg class="icon icon-sun" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"></path></svg></div></button></div><details class="mobile-menu-details hidden-desktop"><summary class="hamburger-menu" aria-label="nav.menu"><svg class="icon icon-bars" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"></path></svg><span class="menu-text">nav.menu</span></summary><div class="mobile-menu-dropdown"><ul class="mobile-nav-list"><li class="mobile-nav-item"><a class="mobile-nav-link" href="../index.html">首页</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../archives/index.html">归档</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../categories/index.html">分类</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../tags/index.html">标签</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../about/index.html">关于</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></details></div></div></div></nav></header><main class="container" id="main-content" tabindex="-1"><div class="post"><article class="post-block h-entry"><div class="post-meta p-author h-card visually-hidden"><img class="author-avatar u-photo" src="../favicon.png" alt="Cytrogen"><span class="p-name">Cytrogen</span><a class="u-url" href="https://cytrogen.icu">https://cytrogen.icu</a></div><a class="post-permalink u-url u-uid visually-hidden" href="https://cytrogen.icu/posts/75ba.html">永久链接</a><div class="p-summary visually-hidden"><p>开了个新坑,近期从 Instagram 的广告上得知了 ByteByteGo,并且订阅了他们的简报(Newsletter)服务,基本上隔个两三天就会收到他们的简报邮件。其中有些简报让我非常感兴趣,故分享。</p>
<p><strong>2024 年 06 月 25 日</strong>:Netflix 如何管理 2.38 亿会员?<em>(How Netflix Manages 238 Million Memberships?)</em></p></div><div class="visually-hidden"><a class="p-category" href="../categories/%E7%BF%BB%E8%AF%91/">翻译</a><a class="p-category" href="../tags/ByteByteGo/">ByteByteGo</a></div><h1 class="post-title p-name">ByteByteGo 简报翻译【1】:Netflix 如何管理 2.38 亿会员?</h1><div class="post-info"><time class="post-date dt-published" datetime="2024-06-29T02:48:00.000Z">6/28/2024</time><time class="dt-updated visually-hidden" datetime="2026-02-09T17:16:54.681Z"></time></div><div class="post-content e-content"><html><head></head><body><p>开了个新坑,近期从 Instagram 的广告上得知了 ByteByteGo,并且订阅了他们的简报(Newsletter)服务,基本上隔个两三天就会收到他们的简报邮件。其中有些简报让我非常感兴趣,故分享。</p>
<p><strong>2024 年 06 月 25 日</strong>:Netflix 如何管理 2.38 亿会员?<em>(How Netflix Manages 238 Million Memberships?)</em></p>
<span id="more"></span>
<div class="danger">
<p>如果侵权,请联系我删除!</p>
</div>
<h1 id="简报内容"><a class="markdownIt-Anchor" href="#简报内容"></a> 简报内容</h1>
<p><em>免责声明:本文章中的细节来自 Netflix 工程团队的文章 / 演讲。所有架构细节均归功于 Netflix 工程团队。原文链接见文末参考文献部分。我们尝试分析了这些细节,并提供了我们的意见。如果您发现任何不准确或遗漏之处,请留下评论,我们将尽力修正。</em></p>
<p>作为一家基于订阅的流媒体服务公司,Netflix 的主要收入来源是会员业务。目前,Netflix 在全球拥有 2.38 亿名会员,有效管理会员对公司的成功和持续增长至关重要。</p>
<p>Netflix 的会员平台在处理用户订阅的整个生命周期中发挥着至关重要的作用。</p>
<p>会员生命周期由不同阶段和场景组成:</p>
<ul>
<li>注册: 用户可直接或通过 T-Mobile 等合作伙伴渠道注册 Netflix 服务,开始 Netflix 之旅。</li>
<li>计划变更: 现有会员可根据自己的喜好和需求修改订阅计划。</li>
<li>续订: 作为一项订阅服务,Netflix 会自动尝试使用与用户账户相关联的付款方式为用户续订计划。</li>
<li>付款问题: 如果支付网关出现问题或资金不足,用户的账户可能会被暂停或给予宽限期来解决问题。</li>
<li>会员资格暂停或取消: 用户可以选择暂时中止会员资格或永久取消订阅。</li>
</ul>
<p>在下面的章节中,我们将探讨 Netflix 工程团队为支持其会员平台的各种功能和可扩展性而做出的架构决策。</p>
<h2 id="netflix-会员平台的高级架构"><a class="markdownIt-Anchor" href="#netflix-会员平台的高级架构"></a> Netflix 会员平台的高级架构</h2>
<p>在深入了解 Netflix 会员制平台的细节之前,让我们先回顾一下该公司最初的定价架构是如何设计的。</p>
<p>早期,Netflix 的定价模型相对简单,只需管理少量计划和支持基本功能。</p>
<p>为了满足这些最初的要求,Netflix 采用了轻量级的内存库。事实证明,这种方法相当高效,因为定价系统的范围有限,因此设计简单、精简。</p>
<p>下图展示了这一基本架构:</p>
<svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="-8 -49 320.03125 90" style="max-width: 320.03125px;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="100%" id="mermaid-1771490033713"><style>#mermaid-1771490033713{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-1771490033713 .error-icon{fill:#552222;}#mermaid-1771490033713 .error-text{fill:#552222;stroke:#552222;}#mermaid-1771490033713 .edge-thickness-normal{stroke-width:2px;}#mermaid-1771490033713 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1771490033713 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1771490033713 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1771490033713 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1771490033713 .marker{fill:#333333;stroke:#333333;}#mermaid-1771490033713 .marker.cross{stroke:#333333;}#mermaid-1771490033713 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-1771490033713 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-1771490033713 .cluster-label text{fill:#333;}#mermaid-1771490033713 .cluster-label span,#mermaid-1771490033713 p{color:#333;}#mermaid-1771490033713 .label text,#mermaid-1771490033713 span,#mermaid-1771490033713 p{fill:#333;color:#333;}#mermaid-1771490033713 .node rect,#mermaid-1771490033713 .node circle,#mermaid-1771490033713 .node ellipse,#mermaid-1771490033713 .node polygon,#mermaid-1771490033713 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-1771490033713 .flowchart-label text{text-anchor:middle;}#mermaid-1771490033713 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-1771490033713 .node .label{text-align:center;}#mermaid-1771490033713 .node.clickable{cursor:pointer;}#mermaid-1771490033713 .arrowheadPath{fill:#333333;}#mermaid-1771490033713 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-1771490033713 .flowchart-link{stroke:#333333;fill:none;}#mermaid-1771490033713 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-1771490033713 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-1771490033713 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-1771490033713 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-1771490033713 .cluster text{fill:#333;}#mermaid-1771490033713 .cluster span,#mermaid-1771490033713 p{color:#333;}#mermaid-1771490033713 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1771490033713 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-1771490033713 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="6" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490033713_flowchart-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490033713_flowchart-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490033713_flowchart-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490033713_flowchart-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490033713_flowchart-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490033713_flowchart-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#mermaid-1771490033713_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-A LE-B" id="L-A-B-0" d="M71.141,16.5L75.307,16.5C79.474,16.5,87.807,16.5,95.257,16.5C102.707,16.5,109.274,16.5,112.557,16.5L115.841,16.5"></path><path marker-end="url(#mermaid-1771490033713_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-B LE-C" id="L-B-C-0" d="M159.516,16.5L163.682,16.5C167.849,16.5,176.182,16.5,183.632,16.5C191.082,16.5,197.649,16.5,200.932,16.5L204.216,16.5"></path></g><g class="edgeLabels"><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(35.5703125, 16.5)" data-id="A" data-node="true" id="flowchart-A-0" class="node default default flowchart-label"><rect height="33" width="71.140625" y="-16.5" x="-35.5703125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-28.0703125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="56.140625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">Netflix 服务</span></div></foreignObject></g></g><g transform="translate(140.328125, 16.5)" data-id="B" data-node="true" id="flowchart-B-1" class="node default default flowchart-label"><rect height="33" width="38.375" y="-16.5" x="-19.1875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-11.6875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="23.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员服务</span></div></foreignObject></g></g><g transform="translate(256.7734375, 16.5)" data-id="C" data-node="true" id="flowchart-C-3" class="node default default flowchart-label"><rect height="33" width="94.515625" y="-16.5" x="-47.2578125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-39.7578125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="79.515625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> Netflix 计划和定价库</span></div></foreignObject></g></g></g></g></g><text class="flowchartTitleText" y="-25" x="152.015625"> Netflix 定价服务初步设计</text></svg>
<p>随着 Netflix 全球业务的扩展和产品的多样化,最初为定价架构提供服务的轻量级内存库变得不敷使用。</p>
<p>定价目录的复杂性和范围不断扩大,其在多个应用程序中的重要性也与日俱增,这给运营带来了挑战。该库的规模和依赖性使其难以维护和扩展,因此有必要过渡到更强大和可扩展的架构。</p>
<p>下图显示了 Netflix 现代会员制平台的高层架构:</p>
<svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="-8 -49 743.4765625 652.8255615234375" style="max-width: 743.4765625px;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="100%" id="mermaid-1771490040231"><style>#mermaid-1771490040231{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-1771490040231 .error-icon{fill:#552222;}#mermaid-1771490040231 .error-text{fill:#552222;stroke:#552222;}#mermaid-1771490040231 .edge-thickness-normal{stroke-width:2px;}#mermaid-1771490040231 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1771490040231 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1771490040231 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1771490040231 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1771490040231 .marker{fill:#333333;stroke:#333333;}#mermaid-1771490040231 .marker.cross{stroke:#333333;}#mermaid-1771490040231 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-1771490040231 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-1771490040231 .cluster-label text{fill:#333;}#mermaid-1771490040231 .cluster-label span,#mermaid-1771490040231 p{color:#333;}#mermaid-1771490040231 .label text,#mermaid-1771490040231 span,#mermaid-1771490040231 p{fill:#333;color:#333;}#mermaid-1771490040231 .node rect,#mermaid-1771490040231 .node circle,#mermaid-1771490040231 .node ellipse,#mermaid-1771490040231 .node polygon,#mermaid-1771490040231 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-1771490040231 .flowchart-label text{text-anchor:middle;}#mermaid-1771490040231 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-1771490040231 .node .label{text-align:center;}#mermaid-1771490040231 .node.clickable{cursor:pointer;}#mermaid-1771490040231 .arrowheadPath{fill:#333333;}#mermaid-1771490040231 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-1771490040231 .flowchart-link{stroke:#333333;fill:none;}#mermaid-1771490040231 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-1771490040231 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-1771490040231 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-1771490040231 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-1771490040231 .cluster text{fill:#333;}#mermaid-1771490040231 .cluster span,#mermaid-1771490040231 p{color:#333;}#mermaid-1771490040231 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1771490040231 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-1771490040231 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="6" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490040231_flowchart-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490040231_flowchart-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490040231_flowchart-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490040231_flowchart-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490040231_flowchart-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490040231_flowchart-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-A LE-B" id="L-A-B-0" d="M27.953,33L27.953,37.167C27.953,41.333,27.953,49.667,32.574,57.455C37.196,65.244,46.438,72.487,51.06,76.109L55.681,79.731"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-B LE-C" id="L-B-C-0" d="M80.906,116L80.906,120.167C80.906,124.333,80.906,132.667,82.143,147.267C83.38,161.866,85.853,182.733,87.09,193.166L88.327,203.599"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-C LE-D" id="L-C-D-0" d="M90.906,241.863L90.906,253.173C90.906,264.484,90.906,287.104,90.906,303.894C90.906,320.684,90.906,331.642,90.906,337.121L90.906,342.6"></path><path style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-D LE-E" id="L-D-E-0" d="M90.906,440.275L90.906,446.638C90.906,453,90.906,465.726,113.044,481.061C135.182,496.396,179.458,514.342,201.596,523.315L223.734,532.288"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-F LE-E" id="L-F-E-0" d="M256.539,410.588L256.539,421.898C256.539,433.209,256.539,455.83,257.012,470.507C257.485,485.184,258.431,491.918,258.904,495.284L259.377,498.651"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-G LE-E" id="L-G-E-0" d="M388.843,410.588L380.499,421.898C372.154,433.209,355.466,455.83,341.194,472.981C326.922,490.133,315.067,501.816,309.14,507.657L303.212,513.499"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-G LE-H" id="L-G-H-0" d="M402.971,410.588L404.312,421.898C405.653,433.209,408.334,455.83,409.675,475.371C411.016,494.913,411.016,511.376,411.016,519.607L411.016,527.838"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-I LE-B" id="L-I-B-0" d="M133.859,33L133.859,37.167C133.859,41.333,133.859,49.667,129.238,57.455C124.617,65.244,115.374,72.487,110.753,76.109L106.131,79.731"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-J LE-B" id="L-J-B-0" d="M242.688,33L242.688,37.167C242.688,41.333,242.688,49.667,221.725,59.211C200.763,68.754,158.839,79.509,137.877,84.886L116.915,90.263"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-K LE-C" id="L-K-C-0" d="M447.836,103.263L405.999,109.553C364.161,115.842,280.487,128.421,224.888,145.673C169.288,162.925,141.764,184.851,128.001,195.813L114.239,206.776"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-K LE-L" id="L-K-L-0" d="M496.735,116L502.762,120.167C508.79,124.333,520.844,132.667,526.871,147.26C532.898,161.854,532.898,182.708,532.898,193.136L532.898,203.563"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-L LE-M" id="L-L-M-0" d="M532.898,241.863L532.898,253.173C532.898,264.484,532.898,287.104,532.898,301.698C532.898,316.292,532.898,322.859,532.898,326.142L532.898,329.425"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-N LE-C" id="L-N-C-0" d="M645.594,102.396L589.979,108.83C534.363,115.264,423.133,128.132,334.708,147.091C246.283,166.049,180.664,191.099,147.855,203.623L115.045,216.148"></path><path marker-end="url(#mermaid-1771490040231_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-N LE-O" id="L-N-O-0" d="M670.625,116L670.625,120.167C670.625,124.333,670.625,132.667,670.625,140.117C670.625,147.567,670.625,154.133,670.625,157.417L670.625,160.7"></path></g><g class="edgeLabels"><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(27.953125, 16.5)" data-id="A" data-node="true" id="flowchart-A-0" class="node default default flowchart-label"><rect height="33" width="55.90625" y="-16.5" x="-27.953125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-20.453125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="40.90625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员宽限期服务</span></div></foreignObject></g></g><g transform="translate(80.90625, 99.5)" data-id="B" data-node="true" id="flowchart-B-1" class="node default default flowchart-label"><rect height="33" width="61.75" y="-16.5" x="-30.875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-23.375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="46.75"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员状态管理服务</span></div></foreignObject></g></g><g transform="translate(90.90625, 225.36264038085938)" data-id="C" data-node="true" id="flowchart-C-3" class="node default default flowchart-label"><rect height="33" width="38.375" y="-16.5" x="-19.1875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-11.6875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="23.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">订阅服务</span></div></foreignObject></g></g><g transform="translate(90.90625, 394.0879211425781)" data-id="D" data-node="true" id="flowchart-D-5" class="node default default flowchart-label"><circle height="83" width="92.375" r="46.1875" ry="0" rx="0" style=""></circle><g transform="translate(-38.6875, -34)" style="" class="label"><rect></rect><foreignObject style="width: 77.375px; height: 68px;" height="68" width="77.375"><div style="display: table-cell; white-space: nowrap; max-width: 200px;" xmlns="http://www.w3.org/1999/xhtml">
    <span class="nodeLabel markdown-node-label"><p> Cassandra<br>订阅数据库</p></span></div></foreignObject></g></g><g transform="translate(266.5390625, 549.6380615234375)" data-id="E" data-node="true" id="flowchart-E-7" class="node default default flowchart-label"><circle height="83" width="92.375" r="46.1875" ry="0" rx="0" style=""></circle><g transform="translate(-38.6875, -34)" style="" class="label"><rect></rect><foreignObject style="width: 77.375px; height: 68px;" height="68" width="77.375"><div style="display: table-cell; white-space: nowrap; max-width: 200px;" xmlns="http://www.w3.org/1999/xhtml">
    <span class="nodeLabel markdown-node-label"><p> Cassandra<br>订阅历史数据库</p></span></div></foreignObject></g></g><g transform="translate(256.5390625, 394.0879211425781)" data-id="F" data-node="true" id="flowchart-F-8" class="node default default flowchart-label"><rect height="33" width="138.890625" y="-16.5" x="-69.4453125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-61.9453125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="123.890625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> Apache Spark 对账工作</span></div></foreignObject></g></g><g transform="translate(401.015625, 394.0879211425781)" data-id="G" data-node="true" id="flowchart-G-10" class="node default default flowchart-label"><rect height="33" width="50.0625" y="-16.5" x="-25.03125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">订阅历史服务</span></div></foreignObject></g></g><g transform="translate(411.015625, 549.6380615234375)" data-id="H" data-node="true" id="flowchart-H-13" class="node default default flowchart-label"><rect height="33" width="53.25" y="-16.5" x="-26.625" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-19.125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="38.25"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> kafka</span></div></foreignObject></g></g><g transform="translate(133.859375, 16.5)" data-id="I" data-node="true" id="flowchart-I-14" class="node default default flowchart-label"><rect height="33" width="55.90625" y="-16.5" x="-27.953125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-20.453125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="40.90625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> 会员捆绑包服务</span></div></foreignObject></g></g><g transform="translate(242.6875, 16.5)" data-id="J" data-node="true" id="flowchart-J-16" class="node default default flowchart-label"><rect height="33" width="61.75" y="-16.5" x="-30.875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-23.375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="46.75"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员应用商店服务</span></div></foreignObject></g></g><g transform="translate(472.8671875, 99.5)" data-id="K" data-node="true" id="flowchart-K-18" class="node default default flowchart-label"><rect height="33" width="50.0625" y="-16.5" x="-25.03125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员定价服务</span></div></foreignObject></g></g><g transform="translate(532.8984375, 225.36264038085938)" data-id="L" data-node="true" id="flowchart-L-21" class="node default default flowchart-label"><rect height="33" width="61.75" y="-16.5" x="-30.875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-23.375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="46.75"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">计划定价目录服务</span></div></foreignObject></g></g><g transform="translate(532.8984375, 394.0879211425781)" data-id="M" data-node="true" id="flowchart-M-23" class="node default default flowchart-label"><path transform="translate(-56.8515625,-59.36263664332002)" d="M 0,11.908424428880018 a 56.8515625,11.908424428880018 0,0,0 113.703125 0 a 56.8515625,11.908424428880018 0,0,0 -113.703125 0 l 0,94.90842442888001 a 56.8515625,11.908424428880018 0,0,0 113.703125 0 l 0,-94.90842442888001" style=""></path><g transform="translate(-49.3515625, -34)" style="" class="label"><rect></rect><foreignObject style="width: 98.7031px; height: 68px;" height="68" width="98.703125"><div style="display: table-cell; white-space: nowrap; max-width: 200px;" xmlns="http://www.w3.org/1999/xhtml">
    <span class="nodeLabel markdown-node-label"><p> CockroachDB<br>计划和定价表</p></span></div></foreignObject></g></g><g transform="translate(670.625, 99.5)" data-id="N" data-node="true" id="flowchart-N-24" class="node default default flowchart-label"><rect height="33" width="50.0625" y="-16.5" x="-25.03125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">代码兑换服务</span></div></foreignObject></g></g><g transform="translate(670.625, 225.36264038085938)" data-id="O" data-node="true" id="flowchart-O-27" class="node default default flowchart-label"><path transform="translate(-56.8515625,-59.36263664332002)" d="M 0,11.908424428880018 a 56.8515625,11.908424428880018 0,0,0 113.703125 0 a 56.8515625,11.908424428880018 0,0,0 -113.703125 0 l 0,94.90842442888001 a 56.8515625,11.908424428880018 0,0,0 113.703125 0 l 0,-94.90842442888001" style=""></path><g transform="translate(-49.3515625, -34)" style="" class="label"><rect></rect><foreignObject style="width: 98.7031px; height: 68px;" height="68" width="98.703125"><div style="display: table-cell; white-space: nowrap; max-width: 200px;" xmlns="http://www.w3.org/1999/xhtml">
    <span class="nodeLabel markdown-node-label"><p> CockroachDB<br>代码兑换表</p></span></div></foreignObject></g></g></g></g></g><text class="flowchartTitleText" y="-25" x="363.73828125"> Netflix 会员制服务的架构</text></svg>
<p>会员平台由十几个微服务组成,旨在支持四个九(99.99%)的可用性。</p>
<p>这种高可用性要求源于该平台在各种面向用户的流程中发挥的关键作用。如果任何服务出现宕机,都会直接影响用户体验。</p>
<p>该平台支持多个关键功能:</p>
<ul>
<li>当用户点击 Netflix 上的播放按钮时,会直接呼叫会员系统,以确定与其计划相关的服务质量。用户允许的并发流和支持的设备等因素都在考虑之列。由于 Netflix 每天都要处理数十亿个流媒体请求,因此该流程处理的流量最大。</li>
<li>当用户访问其账户页面时会触发会员流。更改计划、管理额外会员和取消会员资格等操作会直接与会员服务交互。</li>
<li>该平台是任何特定时间点会员总数的权威来源。它发出事件并写入持久存储,Netflix 内部和外部的下游分析系统都会使用该存储。</li>
</ul>
<p>架构图的要点如下:</p>
<ul>
<li>会员平台在全球范围内管理会员计划和定价目录,不同地区的计划和定价目录各不相同。计划定价目录服务处理基于特定地点产品的规则管理。</li>
<li>两个 CockroachDB 数据库用于存储计划定价和代码兑换信息。会员定价服务支持会员操作,如更改计划或添加额外会员。</li>
<li>专门的微服务负责处理与合作伙伴的互动,包括捆绑激活、注册以及与苹果应用商店等平台的集成。</li>
<li>会员数据存储在 Cassandra 数据库中,该数据库支持订阅服务和历史跟踪服务。</li>
<li>该平台不仅满足 2.38 亿活跃会员的需求,还关注前会员和重新加入会员的体验。</li>
<li>该平台生成的数据将提供给下游消费者,以便他们了解注册情况和收入预测。</li>
</ul>
<p>Netflix 选择使用 CockroachDB 和 Cassandra 很有意思。</p>
<p>CockroachDB 具有很强的一致性,适合处理计划定价信息等关键数据,而 Cassandra 则是一种高度可扩展的 NoSQL 数据库,适合处理大量会员数据。</p>
<p>此外,99.99% 的可用性也表明 Cassandra 非常注重弹性和容错性。无论如何,Netflix 以其全面的混乱工程实践而闻名,它可以主动测试系统的弹性。</p>
<h2 id="注册流程"><a class="markdownIt-Anchor" href="#注册流程"></a> 注册流程</h2>
<p>一旦用户开始 Netflix 之旅,他们就会遇到选择计划的选项。</p>
<p>由于货币、定价和可用计划存在地域差异,因此准确呈现计划选择页面至关重要。Netflix 的会员制平台可确保根据用户的位置和设备类型向其展示适当的选项。</p>
<p>下图显示了 Netflix 注册流程的详细步骤以及在流程中触发的服务:</p>
<svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="-8 -8 441.62890625 706.8492431640625" style="max-width: 441.62890625px;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="100%" id="mermaid-1771490042702"><style>#mermaid-1771490042702{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-1771490042702 .error-icon{fill:#552222;}#mermaid-1771490042702 .error-text{fill:#552222;stroke:#552222;}#mermaid-1771490042702 .edge-thickness-normal{stroke-width:2px;}#mermaid-1771490042702 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1771490042702 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1771490042702 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1771490042702 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1771490042702 .marker{fill:#333333;stroke:#333333;}#mermaid-1771490042702 .marker.cross{stroke:#333333;}#mermaid-1771490042702 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-1771490042702 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-1771490042702 .cluster-label text{fill:#333;}#mermaid-1771490042702 .cluster-label span,#mermaid-1771490042702 p{color:#333;}#mermaid-1771490042702 .label text,#mermaid-1771490042702 span,#mermaid-1771490042702 p{fill:#333;color:#333;}#mermaid-1771490042702 .node rect,#mermaid-1771490042702 .node circle,#mermaid-1771490042702 .node ellipse,#mermaid-1771490042702 .node polygon,#mermaid-1771490042702 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-1771490042702 .flowchart-label text{text-anchor:middle;}#mermaid-1771490042702 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-1771490042702 .node .label{text-align:center;}#mermaid-1771490042702 .node.clickable{cursor:pointer;}#mermaid-1771490042702 .arrowheadPath{fill:#333333;}#mermaid-1771490042702 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-1771490042702 .flowchart-link{stroke:#333333;fill:none;}#mermaid-1771490042702 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-1771490042702 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-1771490042702 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-1771490042702 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-1771490042702 .cluster text{fill:#333;}#mermaid-1771490042702 .cluster span,#mermaid-1771490042702 p{color:#333;}#mermaid-1771490042702 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1771490042702 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-1771490042702 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="6" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490042702_flowchart-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490042702_flowchart-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490042702_flowchart-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490042702_flowchart-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490042702_flowchart-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490042702_flowchart-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-A LE-B" id="L-A-B-0" d="M198.328,29.34L183.236,35.617C168.143,41.893,137.958,54.447,122.866,65.507C107.773,76.567,107.773,86.133,107.773,90.917L107.773,95.7"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-B LE-C" id="L-B-C-0" d="M91.697,134L86.176,139.667C80.655,145.333,69.613,156.667,64.091,169.889C58.57,183.111,58.57,198.222,58.57,205.778L58.57,213.333"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-B LE-D" id="L-B-D-0" d="M127.532,134L134.318,139.667C141.103,145.333,154.675,156.667,161.46,167.117C168.246,177.567,168.246,187.133,168.246,191.917L168.246,196.7"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-A LE-E" id="L-A-E-0" d="M216.823,33L212.571,38.667C208.32,44.333,199.816,55.667,199.286,66.293C198.756,76.92,206.199,86.84,209.921,91.801L213.642,96.761"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-E LE-A" id="L-E-A-0" d="M241.583,101L245.835,95.333C250.087,89.667,258.59,78.333,259.12,67.707C259.651,57.08,252.207,47.16,248.486,42.199L244.764,37.239"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-A LE-F" id="L-A-F-0" d="M260.078,30.347L273.699,36.456C287.319,42.565,314.56,54.782,328.18,65.675C341.801,76.567,341.801,86.133,341.801,90.917L341.801,95.7"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-F LE-G" id="L-F-G-0" d="M341.801,134L341.801,139.667C341.801,145.333,341.801,156.667,341.801,169.363C341.801,182.059,341.801,196.118,341.801,203.147L341.801,210.177"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-G LE-H" id="L-G-H-0" d="M328.144,248.477L321.595,256.39C315.045,264.303,301.946,280.128,295.397,292.824C288.848,305.521,288.848,315.087,288.848,319.871L288.848,324.654"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-H LE-I" id="L-H-I-0" d="M288.848,384.402L288.848,390.068C288.848,395.735,288.848,407.068,288.848,417.518C288.848,427.968,288.848,437.535,288.848,442.318L288.848,447.102"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-G LE-M" id="L-G-M-0" d="M355.458,248.477L362.007,256.39C368.556,264.303,381.655,280.128,388.205,294.612C394.754,309.095,394.754,322.236,394.754,328.807L394.754,335.378"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-M LE-N" id="L-M-N-0" d="M394.754,373.678L394.754,381.132C394.754,388.586,394.754,403.494,394.754,417.518C394.754,431.543,394.754,444.684,394.754,451.255L394.754,457.825"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;stroke-width:2px;stroke-dasharray:3;" class="edge-thickness-normal edge-pattern-dotted flowchart-link LS-I LE-J" id="L-I-J-0" d="M288.848,506.849L288.848,512.516C288.848,518.183,288.848,529.516,288.848,539.966C288.848,550.416,288.848,559.983,288.848,564.766L288.848,569.549"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-J LE-K" id="L-J-K-0" d="M275.504,601.398L268.543,606.64C261.582,611.882,247.66,622.365,240.765,630.974C233.87,639.583,234.002,646.317,234.068,649.683L234.134,653.05"></path><path marker-end="url(#mermaid-1771490042702_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-J LE-L" id="L-J-L-0" d="M302.191,601.398L309.152,606.64C316.113,611.882,330.035,622.365,337.062,630.974C344.089,639.583,344.221,646.317,344.287,649.683L344.353,653.05"></path></g><g class="edgeLabels"><g transform="translate(107.7734375, 67)" class="edgeLabel"><g transform="translate(-26.4296875, -9)" class="label"><foreignObject height="18" width="52.859375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel">1. 检索计划详情</span></div></foreignObject></g></g><g transform="translate(58.5703125, 168)" class="edgeLabel"><g transform="translate(-58.5703125, -9)" class="label"><foreignObject height="18" width="117.140625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 2. 基于区域、设备类型的加载和读取计划</span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g transform="translate(191.3125, 67)" class="edgeLabel"><g transform="translate(-20.5859375, -9)" class="label"><foreignObject height="18" width="41.171875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 3. 展示选项</span></div></foreignObject></g></g><g transform="translate(267.09375, 67)" class="edgeLabel"><g transform="translate(-35.1953125, -9)" class="label"><foreignObject height="18" width="70.390625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 4. 会员选择了一个计划</span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g transform="translate(341.80078125, 168)" class="edgeLabel"><g transform="translate(-26.4296875, -9)" class="label"><foreignObject height="18" width="52.859375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 5. 会员服务启动</span></div></foreignObject></g></g><g transform="translate(288.84765625, 295.9538764953613)" class="edgeLabel"><g transform="translate(-26.4296875, -9)" class="label"><foreignObject height="18" width="52.859375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 5. 持续订阅信息</span></div></foreignObject></g></g><g transform="translate(288.84765625, 418.40157318115234)" class="edgeLabel"><g transform="translate(-26.4296875, -9)" class="label"><foreignObject height="18" width="52.859375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 6. 写入历史服务</span></div></foreignObject></g></g><g transform="translate(394.75390625, 295.9538764953613)" class="edgeLabel"><g transform="translate(-20.5859375, -9)" class="label"><foreignObject height="18" width="41.171875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 7. 通知计费</span></div></foreignObject></g></g><g transform="translate(394.75390625, 418.40157318115234)" class="edgeLabel"><g transform="translate(-20.5859375, -9)" class="label"><foreignObject height="18" width="41.171875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 8. 生成发票</span></div></foreignObject></g></g><g transform="translate(288.84765625, 540.8492698669434)" class="edgeLabel"><g transform="translate(-20.5859375, -9)" class="label"><foreignObject height="18" width="41.171875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 9. 发布事件</span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(229.203125, 16.5)" data-id="A" data-node="true" id="flowchart-A-0" class="node default default flowchart-label"><rect height="33" width="61.75" y="-16.5" x="-30.875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-23.375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="46.75"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">增长工程应用程序</span></div></foreignObject></g></g><g transform="translate(107.7734375, 117.5)" data-id="B" data-node="true" id="flowchart-B-1" class="node default default flowchart-label"><rect height="33" width="50.0625" y="-16.5" x="-25.03125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员计划目录</span></div></foreignObject></g></g><g transform="translate(58.5703125, 231.97693824768066)" data-id="C" data-node="true" id="flowchart-C-3" class="node default default flowchart-label"><circle height="33" width="26.6875" r="13.34375" ry="0" rx="0" style=""></circle><g transform="translate(-5.84375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="11.6875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">规则</span></div></foreignObject></g></g><g transform="translate(168.24609375, 231.97693824768066)" data-id="D" data-node="true" id="flowchart-D-5" class="node default default flowchart-label"><path transform="translate(-35.0625,-29.976937860345934)" d="M 0,8.984625240230622 a 35.0625,8.984625240230622 0,0,0 70.125 0 a 35.0625,8.984625240230622 0,0,0 -70.125 0 l 0,41.98462524023062 a 35.0625,8.984625240230622 0,0,0 70.125 0 l 0,-41.98462524023062" style=""></path><g transform="translate(-27.5625, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="55.125"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> SKUDB</span></div></foreignObject></g></g><g transform="translate(229.203125, 117.5)" data-id="E" data-node="true" id="flowchart-E-7" class="node default default flowchart-label"><rect height="33" width="50.0625" y="-16.5" x="-25.03125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> 选择计划页面</span></div></foreignObject></g></g><g transform="translate(341.80078125, 117.5)" data-id="F" data-node="true" id="flowchart-F-11" class="node default default flowchart-label"><rect height="33" width="38.375" y="-16.5" x="-19.1875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-11.6875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="23.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">付款页面</span></div></foreignObject></g></g><g transform="translate(341.80078125, 231.97693824768066)" data-id="G" data-node="true" id="flowchart-G-13" class="node default default flowchart-label"><rect height="33" width="50.0625" y="-16.5" x="-25.03125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员状态服务</span></div></foreignObject></g></g><g transform="translate(288.84765625, 357.17772483825684)" data-id="H" data-node="true" id="flowchart-H-15" class="node default default flowchart-label"><path transform="translate(-25.03125,-27.2238486254909)" d="M 0,7.149232416993931 a 25.03125,7.149232416993931 0,0,0 50.0625 0 a 25.03125,7.149232416993931 0,0,0 -50.0625 0 l 0,40.14923241699393 a 25.03125,7.149232416993931 0,0,0 50.0625 0 l 0,-40.14923241699393" style=""></path><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员订阅服务</span></div></foreignObject></g></g><g transform="translate(288.84765625, 479.62542152404785)" data-id="I" data-node="true" id="flowchart-I-17" class="node default default flowchart-label"><path transform="translate(-25.03125,-27.2238486254909)" d="M 0,7.149232416993931 a 25.03125,7.149232416993931 0,0,0 50.0625 0 a 25.03125,7.149232416993931 0,0,0 -50.0625 0 l 0,40.14923241699393 a 25.03125,7.149232416993931 0,0,0 50.0625 0 l 0,-40.14923241699393" style=""></path><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员历史服务</span></div></foreignObject></g></g><g transform="translate(394.75390625, 357.17772483825684)" data-id="M" data-node="true" id="flowchart-M-19" class="node default default flowchart-label"><rect height="33" width="61.75" y="-16.5" x="-30.875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-23.375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="46.75"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">计费工程应用程序</span></div></foreignObject></g></g><g transform="translate(394.75390625, 479.62542152404785)" data-id="N" data-node="true" id="flowchart-N-21" class="node default default flowchart-label"><rect height="33" width="50.0625" y="-16.5" x="-25.03125" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员定价服务</span></div></foreignObject></g></g><g transform="translate(288.84765625, 591.3492698669434)" data-id="J" data-node="true" id="flowchart-J-23" class="node default default flowchart-label"><rect height="33" width="26.6875" y="-16.5" x="-13.34375" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-5.84375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="11.6875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">事件</span></div></foreignObject></g></g><g transform="translate(233.73828125, 674.3492698669434)" data-id="K" data-node="true" id="flowchart-K-25" class="node default default flowchart-label"><polygon style="" transform="translate(-25.03125,16.5)" class="label-container" points="0,0 50.0625,0 50.0625,-33 0,-33 0,0 -8,0 58.0625,0 58.0625,-33 -8,-33 -8,0"></polygon><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">数据科学管道</span></div></foreignObject></g></g><g transform="translate(343.95703125, 674.3492698669434)" data-id="L" data-node="true" id="flowchart-L-27" class="node default default flowchart-label"><polygon style="" transform="translate(-19.1875,16.5)" class="label-container" points="0,0 38.375,0 38.375,-33 0,-33 0,0 -8,0 46.375,0 46.375,-33 -8,-33 -8,0"></polygon><g transform="translate(-11.6875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="23.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">消息管道</span></div></foreignObject></g></g></g></g></g></svg>
<p>下面将详细介绍每个步骤:</p>
<ol>
<li>首先,用户通过 Netflix 的增长工程应用程序选择计划。计划详情从会员计划目录服务中获取,该服务由 CockroachDB 支持。</li>
<li>会员计划目录服务根据预定义的地区和设备类型规则加载和读取计划。</li>
<li>然后将检索到的计划展示给用户,让他们根据自己的偏好和预算做出明智的决定。</li>
<li>用户选择计划后,流程会进入付款确认屏幕。在这里,用户提供付款详情并确认订阅。</li>
<li>确认后,用户点击 <code>Start Membership</code> 按钮,触发会员状态服务。该服务会将相关信息(如所选计划、价格层级和国家)保存到 Cassandra 数据库中。</li>
<li>会员状态服务还会将付款情况通知计费工程应用程序。</li>
<li>计费工程应用程序根据从会员定价服务获取的注册数据生成发票。</li>
<li>会员数据会同时写入会员历史服务,以确保全面记录用户的订阅历史。</li>
<li>发布事件以提示激活会员资格。这些事件会触发信息管道,负责向用户发送欢迎邮件,并通知下游系统以进行分析。</li>
</ol>
<h2 id="如何跟踪会员历史"><a class="markdownIt-Anchor" href="#如何跟踪会员历史"></a> 如何跟踪会员历史?</h2>
<p>在 Netflix 会员平台的早期阶段,会员历史和数据是通过应用程序级事件来跟踪的。</p>
<p>虽然这种方法在初期已经足够,但随着 Netflix 的扩张和会员数据复杂性的增加,显然需要一种更精细、更持久的数据跟踪解决方案。</p>
<p>为了满足这一需求,Netflix 开发了基于变更数据捕获(CDC)模式的强大解决方案。</p>
<p>作为参考,CDC 是一种设计模式,可直接捕获对数据库所做的更改,并将这些更改传播到下游系统进行进一步处理或分析。</p>
<p>下图显示了 CDC 流程的工作原理:</p>
<svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="-8 -49 513.328125 326" style="max-width: 513.328125px;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="100%" id="mermaid-1771490045152"><style>#mermaid-1771490045152{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-1771490045152 .error-icon{fill:#552222;}#mermaid-1771490045152 .error-text{fill:#552222;stroke:#552222;}#mermaid-1771490045152 .edge-thickness-normal{stroke-width:2px;}#mermaid-1771490045152 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1771490045152 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1771490045152 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1771490045152 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1771490045152 .marker{fill:#333333;stroke:#333333;}#mermaid-1771490045152 .marker.cross{stroke:#333333;}#mermaid-1771490045152 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-1771490045152 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-1771490045152 .cluster-label text{fill:#333;}#mermaid-1771490045152 .cluster-label span,#mermaid-1771490045152 p{color:#333;}#mermaid-1771490045152 .label text,#mermaid-1771490045152 span,#mermaid-1771490045152 p{fill:#333;color:#333;}#mermaid-1771490045152 .node rect,#mermaid-1771490045152 .node circle,#mermaid-1771490045152 .node ellipse,#mermaid-1771490045152 .node polygon,#mermaid-1771490045152 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-1771490045152 .flowchart-label text{text-anchor:middle;}#mermaid-1771490045152 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-1771490045152 .node .label{text-align:center;}#mermaid-1771490045152 .node.clickable{cursor:pointer;}#mermaid-1771490045152 .arrowheadPath{fill:#333333;}#mermaid-1771490045152 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-1771490045152 .flowchart-link{stroke:#333333;fill:none;}#mermaid-1771490045152 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-1771490045152 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-1771490045152 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-1771490045152 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-1771490045152 .cluster text{fill:#333;}#mermaid-1771490045152 .cluster span,#mermaid-1771490045152 p{color:#333;}#mermaid-1771490045152 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1771490045152 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-1771490045152 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="6" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490045152_flowchart-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490045152_flowchart-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490045152_flowchart-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490045152_flowchart-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490045152_flowchart-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490045152_flowchart-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"><g id="目标系统" class="cluster default flowchart-label"><rect height="269" width="193.890625" y="0" x="303.4375" ry="0" rx="0" style=""></rect><g transform="translate(388.6953125, 0)" class="cluster-label"><foreignObject height="18" width="23.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">目标系统</span></div></foreignObject></g></g><g id="交易" class="cluster default flowchart-label"><rect height="103" width="170.90625" y="83" x="82.53125" ry="0" rx="0" style=""></rect><g transform="translate(162.140625, 83)" class="cluster-label"><foreignObject height="18" width="11.6875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">交易</span></div></foreignObject></g></g></g><g class="edgePaths"><path marker-end="url(#mermaid-1771490045152_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-A LE-B" id="L-A-B-0" d="M32.531,134.5L36.698,134.5C40.865,134.5,49.198,134.5,57.531,134.5C65.865,134.5,74.198,134.5,81.648,134.5C89.098,134.5,95.665,134.5,98.948,134.5L102.231,134.5"></path><path marker-end="url(#mermaid-1771490045152_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-B LE-C" id="L-B-C-0" d="M140.063,134.5L144.229,134.5C148.396,134.5,156.729,134.5,164.179,134.5C171.629,134.5,178.196,134.5,181.479,134.5L184.763,134.5"></path><path marker-end="url(#mermaid-1771490045152_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-C LE-D" id="L-C-D-0" d="M228.438,134.5L232.604,134.5C236.771,134.5,245.104,134.5,253.438,134.5C261.771,134.5,270.104,134.5,278.438,134.5C286.771,134.5,295.104,134.5,302.554,134.5C310.004,134.5,316.571,134.5,319.854,134.5L323.138,134.5"></path><path marker-end="url(#mermaid-1771490045152_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-D LE-E" id="L-D-E-0" d="M370.186,118L377.621,106.917C385.056,95.833,399.927,73.667,410.645,62.583C421.364,51.5,427.93,51.5,431.214,51.5L434.497,51.5"></path><path marker-end="url(#mermaid-1771490045152_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-D LE-F" id="L-D-F-0" d="M389.797,134.5L393.964,134.5C398.13,134.5,406.464,134.5,414.401,134.5C422.338,134.5,429.878,134.5,433.648,134.5L437.419,134.5"></path><path marker-end="url(#mermaid-1771490045152_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-D LE-G" id="L-D-G-0" d="M370.186,151L377.621,162.083C385.056,173.167,399.927,195.333,410.645,206.417C421.364,217.5,427.93,217.5,431.214,217.5L434.497,217.5"></path></g><g class="edgeLabels"><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(456.0625, 51.5)" data-id="E" data-node="true" id="flowchart-E-7" class="node default default flowchart-label"><rect height="33" width="32.53125" y="-16.5" x="-16.265625" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-8.765625, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="17.53125"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">数据库</span></div></foreignObject></g></g><g transform="translate(359.1171875, 134.5)" data-id="D" data-node="true" id="flowchart-D-5" class="node default default flowchart-label"><rect height="33" width="61.359375" y="-16.5" x="-30.6796875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-23.1796875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="46.359375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> CDC 进程</span></div></foreignObject></g></g><g transform="translate(456.0625, 134.5)" data-id="F" data-node="true" id="flowchart-F-9" class="node default default flowchart-label"><rect height="33" width="26.6875" y="-16.5" x="-13.34375" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-5.84375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="11.6875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">缓存</span></div></foreignObject></g></g><g transform="translate(456.0625, 217.5)" data-id="G" data-node="true" id="flowchart-G-11" class="node default default flowchart-label"><rect height="33" width="32.53125" y="-16.5" x="-16.265625" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-8.765625, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="17.53125"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">数据仓</span></div></foreignObject></g></g><g transform="translate(209.25, 134.5)" data-id="C" data-node="true" id="flowchart-C-3" class="node default default flowchart-label"><rect height="33" width="38.375" y="-16.5" x="-19.1875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-11.6875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="23.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">交易日志</span></div></foreignObject></g></g><g transform="translate(123.796875, 134.5)" data-id="B" data-node="true" id="flowchart-B-1" class="node default default flowchart-label"><rect height="33" width="32.53125" y="-16.5" x="-16.265625" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-8.765625, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="17.53125"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">交易表</span></div></foreignObject></g></g><g transform="translate(16.265625, 134.5)" data-id="A" data-node="true" id="flowchart-A-0" class="node default default flowchart-label"><path transform="translate(-16.265625,-24.243999206506643)" d="M 0,5.162666137671097 a 16.265625,5.162666137671097 0,0,0 32.53125 0 a 16.265625,5.162666137671097 0,0,0 -32.53125 0 l 0,38.162666137671096 a 16.265625,5.162666137671097 0,0,0 32.53125 0 l 0,-38.162666137671096" style=""></path><g transform="translate(-8.765625, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="17.53125"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">数据库</span></div></foreignObject></g></g></g></g></g><text class="flowchartTitleText" y="-25" x="248.6640625"> CDC 如何工作?</text></svg>
<p>采用类似 CDC 的方法可确保对会员数据源所做的所有 delta 更改都记录在一个仅有附件的日志系统中,该日志系统由 Cassandra 数据库提供支持。</p>
<p>下图显示了 Netflix 会员平台的历史数据流:</p>
<svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="-8 -49 279.2109375 533.1976928710938" style="max-width: 279.2109375px;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="100%" id="mermaid-1771490048193"><style>#mermaid-1771490048193{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-1771490048193 .error-icon{fill:#552222;}#mermaid-1771490048193 .error-text{fill:#552222;stroke:#552222;}#mermaid-1771490048193 .edge-thickness-normal{stroke-width:2px;}#mermaid-1771490048193 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1771490048193 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1771490048193 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1771490048193 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1771490048193 .marker{fill:#333333;stroke:#333333;}#mermaid-1771490048193 .marker.cross{stroke:#333333;}#mermaid-1771490048193 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-1771490048193 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-1771490048193 .cluster-label text{fill:#333;}#mermaid-1771490048193 .cluster-label span,#mermaid-1771490048193 p{color:#333;}#mermaid-1771490048193 .label text,#mermaid-1771490048193 span,#mermaid-1771490048193 p{fill:#333;color:#333;}#mermaid-1771490048193 .node rect,#mermaid-1771490048193 .node circle,#mermaid-1771490048193 .node ellipse,#mermaid-1771490048193 .node polygon,#mermaid-1771490048193 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-1771490048193 .flowchart-label text{text-anchor:middle;}#mermaid-1771490048193 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-1771490048193 .node .label{text-align:center;}#mermaid-1771490048193 .node.clickable{cursor:pointer;}#mermaid-1771490048193 .arrowheadPath{fill:#333333;}#mermaid-1771490048193 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-1771490048193 .flowchart-link{stroke:#333333;fill:none;}#mermaid-1771490048193 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-1771490048193 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-1771490048193 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-1771490048193 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-1771490048193 .cluster text{fill:#333;}#mermaid-1771490048193 .cluster span,#mermaid-1771490048193 p{color:#333;}#mermaid-1771490048193 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1771490048193 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-1771490048193 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="6" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490048193_flowchart-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490048193_flowchart-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490048193_flowchart-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart" id="mermaid-1771490048193_flowchart-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490048193_flowchart-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart" id="mermaid-1771490048193_flowchart-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#mermaid-1771490048193_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-A LE-B" id="L-A-B-0" d="M106.797,33L106.797,38.667C106.797,44.333,106.797,55.667,106.797,66.117C106.797,76.567,106.797,86.133,106.797,90.917L106.797,95.7"></path><path marker-end="url(#mermaid-1771490048193_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-B LE-C" id="L-B-C-0" d="M81.766,146.36L75.836,153.541C69.906,160.722,58.047,175.085,52.117,187.05C46.188,199.014,46.188,208.581,46.188,213.364L46.188,218.148"></path><path marker-end="url(#mermaid-1771490048193_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-B LE-D" id="L-B-D-0" d="M131.828,146.36L137.758,153.541C143.688,160.722,155.547,175.085,161.477,190.21C167.406,205.336,167.406,221.223,167.406,229.167L167.406,237.111"></path><path marker-end="url(#mermaid-1771490048193_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-D LE-E" id="L-D-E-0" d="M148.813,294.496L142.784,303.717C136.756,312.938,124.698,331.381,118.669,345.385C112.641,359.389,112.641,368.956,112.641,373.739L112.641,378.523"></path><path marker-end="url(#mermaid-1771490048193_flowchart-pointEnd)" style="fill:none;" class="edge-thickness-normal edge-pattern-solid flowchart-link LS-D LE-F" id="L-D-F-0" d="M185.999,294.496L192.028,303.717C198.057,312.938,210.114,331.381,216.143,350.333C222.172,369.285,222.172,388.748,222.172,398.479L222.172,408.21"></path></g><g class="edgeLabels"><g transform="translate(106.796875, 67)" class="edgeLabel"><g transform="translate(-38.1171875, -9)" class="label"><foreignObject height="18" width="76.234375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel">1. 计费合作伙伴更新请求</span></div></foreignObject></g></g><g transform="translate(46.1875, 189.44769668579102)" class="edgeLabel"><g transform="translate(-41.0390625, -9)" class="label"><foreignObject height="18" width="82.078125"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 2. 更新客户的账单合作伙伴</span></div></foreignObject></g></g><g transform="translate(167.40625, 189.44769668579102)" class="edgeLabel"><g transform="translate(-35.1953125, -9)" class="label"><foreignObject height="18" width="70.390625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 3. 为客户添加更新数据</span></div></foreignObject></g></g><g transform="translate(112.640625, 349.822696685791)" class="edgeLabel"><g transform="translate(-38.1171875, -9)" class="label"><foreignObject height="18" width="76.234375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 4. 将数据插入会员历史表</span></div></foreignObject></g></g><g transform="translate(222.171875, 349.822696685791)" class="edgeLabel"><g transform="translate(-41.0390625, -9)" class="label"><foreignObject height="18" width="82.078125"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"> 5. 发出更新会员资格的事件</span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(106.796875, 16.5)" data-id="A" data-node="true" id="flowchart-A-0" class="node default default flowchart-label"><rect height="33" width="38.375" y="-16.5" x="-19.1875" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-11.6875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="23.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员服务</span></div></foreignObject></g></g><g transform="translate(106.796875, 128.2238483428955)" data-id="B" data-node="true" id="flowchart-B-1" class="node default default flowchart-label"><path transform="translate(-25.03125,-27.2238486254909)" d="M 0,7.149232416993931 a 25.03125,7.149232416993931 0,0,0 50.0625 0 a 25.03125,7.149232416993931 0,0,0 -50.0625 0 l 0,40.14923241699393 a 25.03125,7.149232416993931 0,0,0 50.0625 0 l 0,-40.14923241699393" style=""></path><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel">会员订阅服务</span></div></foreignObject></g></g><g transform="translate(46.1875, 269.635196685791)" data-id="C" data-node="true" id="flowchart-C-3" class="node default default flowchart-label"><circle height="33" width="92.375" r="46.1875" ry="0" rx="0" style=""></circle><g transform="translate(-38.6875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="77.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> Cassandra</span></div></foreignObject></g></g><g transform="translate(167.40625, 269.635196685791)" data-id="D" data-node="true" id="flowchart-D-5" class="node default default flowchart-label"><path transform="translate(-25.03125,-27.2238486254909)" d="M 0,7.149232416993931 a 25.03125,7.149232416993931 0,0,0 50.0625 0 a 25.03125,7.149232416993931 0,0,0 -50.0625 0 l 0,40.14923241699393 a 25.03125,7.149232416993931 0,0,0 50.0625 0 l 0,-40.14923241699393" style=""></path><g transform="translate(-17.53125, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="35.0625"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> 会员历史服务</span></div></foreignObject></g></g><g transform="translate(112.640625, 430.010196685791)" data-id="E" data-node="true" id="flowchart-E-7" class="node default default flowchart-label"><circle height="33" width="92.375" r="46.1875" ry="0" rx="0" style=""></circle><g transform="translate(-38.6875, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="77.375"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> Cassandra</span></div></foreignObject></g></g><g transform="translate(222.171875, 430.010196685791)" data-id="F" data-node="true" id="flowchart-F-9" class="node default default flowchart-label"><rect height="33" width="26.6875" y="-16.5" x="-13.34375" ry="0" rx="0" style="" class="basic label-container"></rect><g transform="translate(-5.84375, -9)" style="" class="label"><rect></rect><foreignObject height="18" width="11.6875"><div style="display: inline-block; white-space: nowrap;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"> 事件</span></div></foreignObject></g></g></g></g></g><text class="flowchartTitleText" y="-25" x="131.60546875">新会员历史记录流程</text></svg>
<p>让我们来看看这个过程的步骤:</p>
<ol>
<li>假设有一个修改会员计费伙伴的更新请求。会员订阅服务接收到该请求。</li>
<li>会员订阅服务会处理该请求并更新 Cassandra 数据库中的相关信息。</li>
<li>除了更新主数据库外,用户的更新数据还会附加到会员历史服务中。该服务负责维护会员数据所有更改的历史记录。</li>
<li>会员历史服务将附加数据插入会员历史表。该表是历史数据的持久存储。</li>
<li>最后会发出一个事件,通知下游系统有关成员资格更新的信息。这样,其他服务和流程就能对变化做出反应,并执行任何必要的操作。</li>
</ol>
<p>这种设计有多种好处:</p>
<ul>
<li><strong>详细调试</strong>:通过维护会员数据更改的全面历史记录,系统可以进行详细的调试和故障排除。开发人员可以追溯事件发生的顺序,了解数据是如何演变的。</li>
<li><strong>事件回放和对账</strong>:日志系统的只附加特性允许重放事件。在数据损坏或不一致的情况下,系统可以通过从已知的良好状态重放事件来调节数据。</li>
<li><strong>客户服务分析</strong>:会员历史记录服务捕获的历史数据使客户服务分析变得简单。</li>
</ul>
<h2 id="netflix-会员平台的技术足迹"><a class="markdownIt-Anchor" href="#netflix-会员平台的技术足迹"></a> Netflix 会员平台的技术足迹</h2>
<p>Netflix 会员制平台的技术架构可大致分为两个主要领域:开发和运营 / 监控。</p>
<p>让我们来详细了解每个领域。</p>
<h4 id="开发堆栈"><a class="markdownIt-Anchor" href="#开发堆栈"></a> 开发堆栈</h4>
<p>Netflix 会员制平台的开发堆栈可以用以下要点来描述:</p>
<ul>
<li>Netflix 的架构经过优化,可处理每秒高读取请求(RPS),以支持其庞大的用户群。</li>
<li>会员平台由超过 12 个微服务组成,这些微服务在 HTTP 层使用 gRPC 进行通信。通常情况下,该平台每秒可处理 300-400 万个请求。为了支持如此大的处理量,Netflix 采用了 gRPC 层的客户端缓存和整个记录的内存缓存等技术,以防止 CockroachDB 成为单点故障。</li>
<li>会员平台使用的主要编程语言是带有 Spring Boot 的 Java。不过,在某些重写场景中,Netflix 正在逐步过渡到 Kotlin。</li>
<li>Kafka 在消息传递和与其他团队(如消息传递和下游分析)的接口方面发挥着关键作用。这确保了不同系统间的顺畅通信和数据流。</li>
<li>Netflix 利用 Spark 和 Flink 对其大数据执行离线对账任务。这些对账工作对于保持会员平台内各种记录系统(如订阅和会员历史数据库)之间的数据一致性和一致性至关重要。数据的准确性还延伸到外部系统,确保整个生态系统的状态一致。</li>
<li>为确保在线系统的数据一致性,Netflix 采用了轻量级事务并使用 Cassandra 等数据库。这种方法保证了不同服务间数据的完整性和可靠性。</li>
</ul>
<h4 id="运行和监测"><a class="markdownIt-Anchor" href="#运行和监测"></a> 运行和监测</h4>
<p>Netflix 非常重视可观察性和监控,以确保其会员平台的顺利运行:</p>
<ul>
<li>广泛的日志记录、仪表板和分布式跟踪机制可实现快速的错误检测和解决。在 Netflix 复杂的微服务环境中,这些工具对于发现和排除故障至关重要。</li>
<li>设置了生产警报,以跟踪运营指标并保证最佳服务水平。</li>
<li>利用运营数据来推动机器学习模型,从而增强异常检测并启用自动问题解决流程。所有这些都是为了努力为用户提供不间断的流媒体体验。</li>
<li>Netflix 利用 Kibana 和 Elasticsearch 等工具创建仪表板并分析日志数据。在错误率激增的情况下,这些仪表盘可让团队快速识别导致问题的特定端点,并采取纠正措施。</li>
</ul>
<h2 id="结论"><a class="markdownIt-Anchor" href="#结论"></a> 结论</h2>
<p>总之,Netflix 的会员平台是公司成功的关键组成部分,使其能够管理用户订阅的整个生命周期。该平台已从一个简单、轻量级的库发展成为一个强大、可扩展的架构,每秒可处理数百万个请求。</p>
<p>需要记住的一些关键要点如下:</p>
<ul>
<li>会员平台负责管理用户注册、计划变更、续订和取消。</li>
<li>它采用微服务架构,使用 CockroachDB 和 Cassandra 等数据库存储会员数据。</li>
<li>该平台使用 CDC 捕获并存储会员数据的历史变化,用于调试、事件重放和分析。</li>
</ul>
<h4 id="参考文献"><a class="markdownIt-Anchor" href="#参考文献"></a> 参考文献</h4>
<ul>
<li><a target="_blank" rel="noopener" href="https://substack.com/redirect/19db3f26-2ee5-4ff7-ba91-dd7977f4d927?j=eyJ1IjoiM241d3VpIn0.zuhQr2h0bppviCayt6ZoA-2zzi3IsOcAKWBL0Cs6t9I">Managing 238M memberships at Netflix</a></li>
<li><a target="_blank" rel="noopener" href="https://substack.com/redirect/3817e491-2856-4a03-8706-a2c103787021?j=eyJ1IjoiM241d3VpIn0.zuhQr2h0bppviCayt6ZoA-2zzi3IsOcAKWBL0Cs6t9I">Netflix』s Membership Platform</a></li>
<li><a target="_blank" rel="noopener" href="https://substack.com/redirect/2c62bed5-a3b0-480d-90b0-620f99ff1c08?j=eyJ1IjoiM241d3VpIn0.zuhQr2h0bppviCayt6ZoA-2zzi3IsOcAKWBL0Cs6t9I">Presentation on Managing 238M memberships at Netflix</a></li>
</ul>
</body></html></div></article></div></main><footer><div class="paginator"><a class="prev" href="49c3.html">上一篇</a><a class="next" href="369.html">下一篇</a></div><!-- Webmention 显示区域--><div class="webmention-section webmention-empty" data-page-url="posts/75ba.html" data-full-url="https://cytrogen.icu/posts/75ba.html" data-mode="static">
              <h3 class="webmention-title">Webmentions (<span class="webmention-count">0</span>)</h3>
              <div class="webmention-list"></div>
              <span>暂无 Webmentions</span>
            </div><div class="copyright"><p class="footer-links"><a href="../friends/index.html">友链</a><span class="footer-separator"> ·</span><a href="../links/index.html">邻邦</a><span class="footer-separator"> ·</span><a href="../contact/index.html">联络</a><span class="footer-separator"> ·</span><a href="../colophon/index.html">营造记</a><span class="footer-separator"> ·</span><a href="../atom.xml">RSS订阅</a></p><p>© 2025 - 2026 <a href="https://cytrogen.icu">Cytrogen</a>, powered by <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/cytrogen/hexo-theme-ares" target="_blank">hexo-theme-ares</a>.</p><p><a href="https://blogscn.fun" target="_blank" rel="noopener">BLOGS·CN</a></p></div></footer></div></div><a class="back-to-top" href="#top" aria-label="返回顶部"><svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M3.293 9.707a1 1 0 010-1.414L9.586 2a2 2 0 012.828 0l6.293 6.293a1 1 0 01-1.414 1.414L11 3.414V17a1 1 0 11-2 0V3.414L2.707 9.707a1 1 0 01-1.414 0z"></path></svg></a><script>document.addEventListener('DOMContentLoaded', function() {
  const codeBlocks = document.querySelectorAll('figure.highlight');
  
  codeBlocks.forEach(block => {
    let caption = block.querySelector('figcaption');
    if (!caption) {
      caption = document.createElement('figcaption');
      block.insertBefore(caption, block.firstChild);
    }

    const info = document.createElement('div');
    info.className = 'info';
    
    const filename = caption.querySelector('span');
    if (filename) {
      filename.className = 'filename';
      info.appendChild(filename);
    }
    
    const lang = block.className.split(' ')[1];
    if (lang) {
      const langSpan = document.createElement('span');
      langSpan.className = 'lang-name';
      langSpan.textContent = lang;
      info.appendChild(langSpan);
    }

    const sourceLink = caption.querySelector('a');
    if (sourceLink) {
      sourceLink.className = 'source-link';
      info.appendChild(sourceLink);
    }

    const actions = document.createElement('div');
    actions.className = 'actions';

    const codeHeight = block.scrollHeight;
    const threshold = 300;

    if (codeHeight > threshold) {
      block.classList.add('folded');
      
      const toggleBtn = document.createElement('button');
      toggleBtn.textContent = '展开';
      toggleBtn.addEventListener('click', () => {
        block.classList.toggle('folded');
        toggleBtn.textContent = block.classList.contains('folded') ? '展开' : '折叠';
      });
      actions.appendChild(toggleBtn);
    }

    const copyBtn = document.createElement('button');
    copyBtn.textContent = '复制';
    copyBtn.addEventListener('click', async () => {
      const codeLines = block.querySelectorAll('.code .line');
      const code = Array.from(codeLines)
        .map(line => line.textContent)
        .join('\n')
        .replace(/\n\n/g, '\n');
      
      try {
        await navigator.clipboard.writeText(code);
        copyBtn.textContent = '已复制';
        copyBtn.classList.add('copied');
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
          copyBtn.classList.remove('copied');
        }, 3000);
      } catch (err) {
        console.error('复制失败:', err);
        copyBtn.textContent = '复制失败';
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
        }, 3000);
      }
    });
    actions.appendChild(copyBtn);

    caption.innerHTML = '';
    caption.appendChild(info);
    caption.appendChild(actions);

    const markedLines = block.getAttribute('data-marked-lines');
    if (markedLines) {
      const lines = markedLines.split(',');
      lines.forEach(range => {
        if (range.includes('-')) {
          const [start, end] = range.split('-').map(Number);
          for (let i = start; i <= end; i++) {
            const line = block.querySelector(`.line-${i}`);
            if (line) line.classList.add('marked');
          }
        } else {
          const line = block.querySelector(`.line-${range}`);
          if (line) line.classList.add('marked');
        }
      });
    }
  });
});</script><script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script"></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const themeToggle = document.querySelector('.theme-toggle');
    
    if (!themeToggle) return;
    
    const getCurrentTheme = () => {
      return document.documentElement.getAttribute('data-theme') || 'light';
    };
    
    const updateUI = (theme) => {
      const isDark = theme === 'dark';
      themeToggle.setAttribute('aria-pressed', isDark.toString());
    };
    
    const setTheme = (theme) => {
      document.documentElement.setAttribute('data-theme', theme);
      document.documentElement.style.colorScheme = theme;
      
      const pageWrapper = document.getElementById('page-wrapper');
      if (pageWrapper) {
        pageWrapper.setAttribute('data-theme', theme);
      }
      
      // Find and remove the temporary anti-flicker style tag if it exists.
      // This ensures the main stylesheet takes full control after the initial load.
      const antiFlickerStyle = document.getElementById('anti-flicker-style');
      if (antiFlickerStyle) {
        antiFlickerStyle.remove();
      }
      
      localStorage.setItem('theme', theme);
      updateUI(theme);
    };
    
    const toggleTheme = () => {
      const current = getCurrentTheme();
      const newTheme = current === 'light' ? 'dark' : 'light';
      setTheme(newTheme);
    };
    
    updateUI(getCurrentTheme());
    
    themeToggle.addEventListener('click', toggleTheme);
    
    if (window.matchMedia) {
      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
      mediaQuery.addEventListener('change', function(e) {
        if (!localStorage.getItem('theme')) {
          const theme = e.matches ? 'dark' : 'light';
          setTheme(theme);
        }
      });
    }
  });
})();
</script><script src="../js/details-toggle.js" defer></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const backToTopBtn = document.querySelector('.back-to-top');
    
    if (!backToTopBtn) return;
    
    const toggleButtonVisibility = () => {
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const shouldShow = scrollTop > 200;
      
      if (shouldShow) {
        backToTopBtn.classList.add('is-visible');
      } else {
        backToTopBtn.classList.remove('is-visible');
      }
    };
    
    let ticking = false;
    const handleScroll = () => {
      if (!ticking) {
        requestAnimationFrame(() => {
          toggleButtonVisibility();
          ticking = false;
        });
        ticking = true;
      }
    };
    
    const scrollToTop = (event) => {
      event.preventDefault();
      window.scrollTo({
        top: 0,
        behavior: 'smooth'
      });
    };
    
    window.addEventListener('scroll', handleScroll);
    backToTopBtn.addEventListener('click', scrollToTop);
    
    toggleButtonVisibility();
  });
})();</script></body></html>