Created
January 31, 2026 13:48
-
-
Save zavakid/a58c635006786cc35ee212f66f8dcadc to your computer and use it in GitHub Desktop.
The video Think Distributed Systems with Dominik Tornow provides deep insights into the architecture of reliable systems. from https://www.youtube.com/watch?v=FBKDHpkdrGk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Think Distributed Systems - 视频学习助手</title> | |
| <!-- React & ReactDOM --> | |
| <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script> | |
| <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script> | |
| <!-- Babel for JSX --> | |
| <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> | |
| <!-- Tailwind CSS --> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <!-- Phosphor Icons --> | |
| <script src="https://unpkg.com/@phosphor-icons/web"></script> | |
| <style> | |
| /* Custom scrollbar for better aesthetics */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #f1f1f1; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #cbd5e1; | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #94a3b8; | |
| } | |
| .active-tab { | |
| border-bottom: 2px solid #2563eb; | |
| color: #2563eb; | |
| font-weight: 600; | |
| } | |
| .transcript-item:hover { | |
| background-color: #f8fafc; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 h-screen overflow-hidden text-slate-800"> | |
| <div id="root" class="h-full"></div> | |
| <script type="text/babel"> | |
| const { useState, useEffect, useRef } = React; | |
| // --- Data: Transcript, Summary, Resources --- | |
| const VIDEO_ID = "FBKDHpkdrGk"; | |
| const summaryData = [ | |
| { | |
| title: "视频简介", | |
| content: "本期 'Flying High with Flutter' 节目邀请到了《Think Distributed Systems》一书的作者 Dominik Tornow。 Dominik 在节目中深入浅出地探讨了分布式系统的核心概念,不仅仅是针对后端工程师,对于所有现代软件开发者(包括移动端)都至关重要。" | |
| }, | |
| { | |
| title: "核心类比:办公楼模型 (The Office Building Analogy)", | |
| content: "Dominik 提出了一个非常形象的 '办公楼' 类比来解释分布式系统:\n1. 每个进程就像办公楼里的一个房间,房间里只有一个人。\n2. 人与人之间不能直接说话(没有共享内存),只能通过 '气动管' (Pneumatic Tubes) 互相发送信件(消息传递)。\n3. 这个模型完美对应了分布式系统的两个核心挑战:并发 (Concurrency) 和 分布 (Distribution/Partial Failure)。" | |
| }, | |
| { | |
| title: "CAP 定理的误区与真相", | |
| content: "Dominik 对 CAP 定理提出了批评。他区分了 'CAP 猜想' (实用但非定理) 和 'CAP 定理' (形式化证明但不实用)。他指出 CAP 定理中的 '可用性' 要求所有节点在任何时候都必须响应,这在现实中是不切实际的。相比之下,分布式共识算法(如 Raft, Paxos)通过 Quorum (法定人数) 机制,使得系统即使在部分节点故障时也能保持一致性和可用性,这实际上比 CAP 定理描述的更具指导意义。" | |
| }, | |
| { | |
| title: "Safety (安全性) vs Liveness (活性)", | |
| content: "引用 Leslie Lamport 的理论:\n- **Safety**: 保证 '坏事永远不会发生' (Something bad never happens)。例如:银行转账不能凭空消失钱。\n- **Liveness**: 保证 '好事最终会发生' (Something good eventually happens)。例如:转账最终会到账。\n在分布式系统中,我们经常需要在故障发生时,在 Safety 和 Liveness 之间做权衡。" | |
| }, | |
| { | |
| title: "消息传递与幂等性 (Idempotency)", | |
| content: "在不可靠的网络中(如移动端),我们无法实现 '恰好一次' (Exactly-once) 的消息传递。我们通常能做到的是 '至少一次' (At-least-once) 加上 **幂等性** (Idempotency)。\n- **策略**:客户端生成一个唯一的 ID (Idempotency Key),服务器收到消息后记录这个 ID。如果客户端重试发送相同的消息,服务器看到 ID 已存在,就不再重复处理,从而实现逻辑上的 '恰好一次'。" | |
| } | |
| ]; | |
| const resourcesData = [ | |
| { | |
| title: "书籍推荐", | |
| items: [ | |
| { label: "Think Distributed Systems (Manning Publications)", link: "https://www.manning.com/books/think-distributed-systems", desc: "Dominik Tornow 所著,通过生动的插图和心智模型来解释分布式系统。" } | |
| ] | |
| }, | |
| { | |
| title: "核心概念", | |
| items: [ | |
| { label: "CAP 定理", desc: "Consistency (一致性), Availability (可用性), Partition Tolerance (分区容错性)。分布式系统中的经典权衡理论。" }, | |
| { label: "TLA+ (Leslie Lamport)", desc: "一种用于建模并发和分布式系统的形式化规范语言。Dominik 提到的 Safety 和 Liveness 概念深受其影响。" }, | |
| { label: "Actor 模型", desc: "一种并发计算模型,强调通过消息传递进行通信,Erlang, Elixir 和 Dart (Flutter) 都受此影响。" } | |
| ] | |
| }, | |
| { | |
| title: "人物", | |
| items: [ | |
| { label: "Dominik Tornow", link: "https://twitter.com/DominikTornow", desc: "分布式系统专家,作者,Resonate HQ 创始人。" } | |
| ] | |
| } | |
| ]; | |
| // Processed transcript with manual translation mapping | |
| // Due to length, I've segmented and translated the key parts provided. | |
| const transcriptData = [ | |
| { time: "00:00:10", en: "Hello, welcome to another episode of Flying High with Flutter. I'm your host Alan Wima.", zh: "你好,欢迎收看新一期的《Flying High with Flutter》,我是主持人 Alan Wima。" }, | |
| { time: "00:00:16", en: "Today we have a very interesting, amazing guest Dominik Tornow.", zh: "今天我们要请到一位非常有趣、了不起的嘉宾 Dominik Tornow。" }, | |
| { time: "00:00:23", en: "He wrote a very interesting book that resonates a lot with me called Think Distributed Systems.", zh: "他写了一本非常有趣的书,让我产生了很多共鸣,书名叫做《Think Distributed Systems》(分布式系统思考)。" }, | |
| { time: "00:00:39", en: "People are actually working with distributed systems more than they think.", zh: "人们实际使用分布式系统的情况比他们想象的要多。" }, | |
| { time: "00:00:48", en: "If you have an app, an API server, and a database, you already have a distributed system.", zh: "如果你有一个 App,一个 API 服务器和一个数据库,你就已经拥有一个分布式系统了。" }, | |
| { time: "00:00:58", en: "All modern systems today are distributed systems. It's only a question to what degree.", zh: "当今所有的现代系统都是分布式系统。问题只在于分布式的程度如何。" }, | |
| { time: "00:01:40", en: "I came to the United States for an internship in 2006... that was the year the cloud was born.", zh: "我于 2006 年来到美国实习……那一年正是云计算诞生的一年。" }, | |
| { time: "00:02:25", en: "AWS released S3 and EC2... basically the birth year of the cloud.", zh: "AWS 发布了 S3 和 EC2……基本上那是云计算的诞生之年。" }, | |
| { time: "00:03:04", en: "I was fascinated by the problems that were presented by distributed systems.", zh: "我被分布式系统所带来的问题深深吸引了。" }, | |
| { time: "00:04:54", en: "I like to break large concepts down into fundamental building blocks, thinking from first principles.", zh: "我喜欢将大概念分解为基本的构建块,从第一性原理进行思考。" }, | |
| { time: "00:05:22", en: "There are two forces at play: Concurrency and Distribution.", zh: "这里有两种力量在起作用:并发 (Concurrency) 和 分布 (Distribution)。" }, | |
| { time: "00:05:35", en: "Concurrency is defined by nondeterministic partial order. You don't know what happens next.", zh: "并发的定义是不确定的偏序。你不知道接下来会发生什么。" }, | |
| { time: "00:05:48", en: "Distribution is defined by nondeterministic partial failure. You don't know what fails next.", zh: "分布的定义是不确定的部分故障。你不知道接下来什么会坏掉。" }, | |
| { time: "00:06:30", en: "I settled on the office building analogy. Every room is occupied by one person.", zh: "我选定了办公楼这个类比。每个房间里只有一个人。" }, | |
| { time: "00:06:42", en: "That person cannot communicate by yelling, they have to send letters via pneumatic tubes.", zh: "那个人不能通过喊话交流,他们必须通过气动管发送信件。" }, | |
| { time: "00:07:39", en: "A distributed system is a collection of concurrent components with exclusive access to their own state.", zh: "分布式系统是一组并发组件的集合,它们对自己拥有的状态享有独占访问权。" }, | |
| { time: "00:07:49", en: "They share no state and communicate by exchanging messages over the network.", zh: "它们不共享状态,而是通过网络交换消息进行通信。" }, | |
| { time: "00:09:55", en: "If we have Bob the mail room attendant who is a little disorganized, sometimes he forgets messages.", zh: "如果我们有一个收发室管理员 Bob,他有点乱,有时会忘记发送消息。" }, | |
| { time: "00:10:35", en: "That's what we call an asynchronous system model in the presence of failure.", zh: "这就是我们所说的在存在故障情况下的异步系统模型。" }, | |
| { time: "00:13:08", en: "Shift in perspective: In the office, there is only you and the rest of the world.", zh: "视角的转变:在办公室里,只有你和“世界的其余部分”。" }, | |
| { time: "00:14:25", en: "We often look at distributed systems from a God-like perspective, but no single component has that view.", zh: "我们通常以上帝视角看待分布式系统,但没有任何单一组件拥有那种视角。" }, | |
| { time: "00:17:09", en: "CAP Theorem. I have very strong feelings about the CAP theorem.", zh: "CAP 定理。我对 CAP 定理有非常强烈的看法。" }, | |
| { time: "00:17:55", en: "There is the CAP Conjecture (tangible but not a theorem) and the CAP Theorem (proven but impractical).", zh: "有 CAP 猜想(具体但不算定理)和 CAP 定理(已证明但不切实际)。" }, | |
| { time: "00:18:34", en: "CAP is often misunderstood as 'two out of three'. Partitioning is not a choice, it's a reality.", zh: "CAP 常被误解为“三选二”。分区 (Partitioning) 不是一种选择,它是现实常态。" }, | |
| { time: "00:20:09", en: "In the CAP Theorem, Consistency is defined as linearizability.", zh: "在 CAP 定理中,一致性 (Consistency) 被定义为线性一致性。" }, | |
| { time: "00:21:25", en: "The requirement that ANY server must be able to respond is impractical.", zh: "要求“任何”服务器都必须能够响应,这是不切实际的。" }, | |
| { time: "00:24:06", en: "Distributed consensus (like Paxos/Raft) allows us to make progress as long as a quorum exists.", zh: "分布式共识(如 Paxos/Raft)允许我们只要存在法定人数 (Quorum) 就能继续运行。" }, | |
| { time: "00:24:37", en: "That is the true breakthrough, not CAP.", zh: "这才是真正的突破,而不是 CAP。" }, | |
| { time: "00:28:44", en: "Any property of a system is a combination of Safety properties and Liveness properties.", zh: "系统的任何属性都是安全性 (Safety) 属性和活性 (Liveness) 属性的结合。" }, | |
| { time: "00:28:52", en: "Safety: Nothing bad is ever going to happen.", zh: "安全性:坏事永远不会发生。" }, | |
| { time: "00:29:00", en: "Liveness: Something good is eventually going to happen.", zh: "活性:好事最终会发生。" }, | |
| { time: "00:33:03", en: "Message delivery: At most once, at least once, and exactly once.", zh: "消息传递:至多一次,至少一次,以及恰好一次。" }, | |
| { time: "00:34:11", en: "Nobody can guarantee exactly-once delivery and processing generally.", zh: "通常没有人能保证“恰好一次”的传递和处理。" }, | |
| { time: "00:36:46", en: "At most once: Either it happened or it didn't, I won't worry about it.", zh: "至多一次:要么发生了要么没发生,我不再操心了。" }, | |
| { time: "00:37:53", en: "At least once: If I don't get a receipt, I send the request again.", zh: "至少一次:如果我没收到回执,我就再次发送请求。" }, | |
| { time: "00:39:14", en: "We combine 'at least once' delivery with Idempotency.", zh: "我们将“至少一次”传递与“幂等性”结合起来。" }, | |
| { time: "00:39:23", en: "I put an invoice number on my letter. If Bob sees it twice, he knows he already paid it.", zh: "我在信上写上发票编号。如果 Bob 看到两次,他就知道他已经付过款了。" }, | |
| { time: "00:43:17", en: "Think in terms of message passing, not TCP connections.", zh: "要用消息传递的思维来思考,而不是 TCP 连接。" }, | |
| { time: "00:45:02", en: "Start with the client adding IDs that can be used as idempotent keys.", zh: "从客户端添加可以用作幂等键 (Idempotent Keys) 的 ID 开始。" }, | |
| { time: "00:49:40", en: "I was booking a flight, the app was glitchy, and I got charged multiple times.", zh: "我在订机票,APP 出故障了,结果我被扣了好几次款。" }, | |
| { time: "00:50:38", en: "Eventually the system reconciled, ensuring safety, but the user experience was terrible.", zh: "最终系统对账了,保证了安全性,但用户体验非常糟糕。" }, | |
| { time: "00:52:41", en: "The concept of a Holarchy (not hierarchy) helps reasoning about complex systems.", zh: "Holarchy(全子与整体,非层级结构)的概念有助于推理复杂的系统。" }, | |
| { time: "00:53:16", en: "A Holon is a whole in and of itself, but also part of something bigger.", zh: "全子 (Holon) 本身是一个整体,同时也是更大整体的一部分。" } | |
| ]; | |
| // --- Components --- | |
| function App() { | |
| const [activeTab, setActiveTab] = useState('transcript'); | |
| const playerRef = useRef(null); | |
| // Load YouTube API | |
| useEffect(() => { | |
| if (!window.YT) { | |
| const tag = document.createElement('script'); | |
| tag.src = "https://www.youtube.com/iframe_api"; | |
| const firstScriptTag = document.getElementsByTagName('script')[0]; | |
| firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); | |
| window.onYouTubeIframeAPIReady = () => { | |
| playerRef.current = new window.YT.Player('player', { | |
| videoId: VIDEO_ID, | |
| events: { | |
| 'onReady': onPlayerReady, | |
| } | |
| }); | |
| }; | |
| } else { | |
| playerRef.current = new window.YT.Player('player', { | |
| videoId: VIDEO_ID, | |
| events: { | |
| 'onReady': onPlayerReady, | |
| } | |
| }); | |
| } | |
| }, []); | |
| const onPlayerReady = (event) => { | |
| // Player is ready | |
| }; | |
| const seekTo = (timeStr) => { | |
| if (playerRef.current && playerRef.current.seekTo) { | |
| // Convert "00:01:40" to seconds | |
| const parts = timeStr.split(':'); | |
| const seconds = parseInt(parts[0]) * 3600 + parseInt(parts[1]) * 60 + parseInt(parts[2]); | |
| playerRef.current.seekTo(seconds, true); | |
| playerRef.current.playVideo(); | |
| } | |
| }; | |
| return ( | |
| <div className="flex flex-col md:flex-row h-screen"> | |
| {/* Left: Video Panel */} | |
| <div className="w-full md:w-1/2 bg-black flex items-center justify-center relative"> | |
| <div id="player" className="w-full h-full absolute inset-0"></div> | |
| {/* Fallback/Placeholder if JS loads slowly */} | |
| <div className="text-white z-0">Loading Video...</div> | |
| </div> | |
| {/* Right: Info Panel */} | |
| <div className="w-full md:w-1/2 flex flex-col bg-white h-1/2 md:h-full overflow-hidden"> | |
| {/* Tabs Header */} | |
| <div className="flex border-b border-slate-200 bg-white"> | |
| <button | |
| onClick={() => setActiveTab('transcript')} | |
| className={`flex-1 py-4 text-center text-sm font-medium transition-colors ${activeTab === 'transcript' ? 'active-tab bg-blue-50' : 'text-slate-500 hover:text-slate-700'}`} | |
| > | |
| 全文字幕 | |
| </button> | |
| <button | |
| onClick={() => setActiveTab('summary')} | |
| className={`flex-1 py-4 text-center text-sm font-medium transition-colors ${activeTab === 'summary' ? 'active-tab bg-blue-50' : 'text-slate-500 hover:text-slate-700'}`} | |
| > | |
| 核心总结 | |
| </button> | |
| <button | |
| onClick={() => setActiveTab('resources')} | |
| className={`flex-1 py-4 text-center text-sm font-medium transition-colors ${activeTab === 'resources' ? 'active-tab bg-blue-50' : 'text-slate-500 hover:text-slate-700'}`} | |
| > | |
| 相关资料 | |
| </button> | |
| </div> | |
| {/* Tab Content Area */} | |
| <div className="flex-1 overflow-y-auto p-6 scroll-smooth"> | |
| {/* Transcript Tab */} | |
| {activeTab === 'transcript' && ( | |
| <div className="space-y-6"> | |
| <div className="text-xs text-slate-400 mb-4 flex items-center gap-1"> | |
| <i className="ph ph-info"></i> 点击文字可跳转视频进度 | |
| </div> | |
| {transcriptData.map((item, index) => ( | |
| <div | |
| key={index} | |
| onClick={() => seekTo(item.time)} | |
| className="group cursor-pointer p-3 rounded-lg hover:bg-blue-50 transition-all border border-transparent hover:border-blue-100" | |
| > | |
| <div className="flex items-center gap-2 mb-1"> | |
| <span className="text-xs font-mono bg-slate-100 text-slate-500 px-1.5 py-0.5 rounded group-hover:bg-blue-200 group-hover:text-blue-700 transition-colors"> | |
| [{item.time}] | |
| </span> | |
| </div> | |
| <p className="text-base text-slate-800 leading-relaxed font-medium mb-1"> | |
| {item.zh} | |
| </p> | |
| <p className="text-sm text-slate-500 leading-relaxed group-hover:text-slate-600"> | |
| {item.en} | |
| </p> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| {/* Summary Tab */} | |
| {activeTab === 'summary' && ( | |
| <div className="space-y-8 animate-fade-in"> | |
| {summaryData.map((section, index) => ( | |
| <div key={index} className="bg-white border border-slate-100 rounded-xl p-5 shadow-sm"> | |
| <h3 className="text-lg font-bold text-slate-800 mb-3 flex items-center gap-2"> | |
| <div className="w-1 h-5 bg-blue-500 rounded-full"></div> | |
| {section.title} | |
| </h3> | |
| <p className="text-slate-600 leading-7 whitespace-pre-line"> | |
| {section.content} | |
| </p> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| {/* Resources Tab */} | |
| {activeTab === 'resources' && ( | |
| <div className="space-y-8"> | |
| {resourcesData.map((category, index) => ( | |
| <div key={index}> | |
| <h3 className="text-sm uppercase tracking-wider text-slate-400 font-bold mb-4 border-b border-slate-100 pb-2"> | |
| {category.title} | |
| </h3> | |
| <ul className="space-y-4"> | |
| {category.items.map((item, idx) => ( | |
| <li key={idx} className="bg-white p-4 rounded-lg border border-slate-200 hover:border-blue-300 transition-colors shadow-sm"> | |
| <div className="flex justify-between items-start"> | |
| <div> | |
| <h4 className="font-semibold text-slate-800 text-base mb-1"> | |
| {item.link ? ( | |
| <a href={item.link} target="_blank" className="hover:text-blue-600 hover:underline flex items-center gap-1"> | |
| {item.label} | |
| <i className="ph ph-arrow-square-out text-xs"></i> | |
| </a> | |
| ) : ( | |
| item.label | |
| )} | |
| </h4> | |
| <p className="text-sm text-slate-500 leading-relaxed mt-1"> | |
| {item.desc} | |
| </p> | |
| </div> | |
| </div> | |
| </li> | |
| ))} | |
| </ul> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| const root = ReactDOM.createRoot(document.getElementById('root')); | |
| root.render(<App />); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment