diff --git a/article/2021/Interview-with-Ryan-Dahl-Creator-of-Node-js.md b/article/2021/Interview-with-Ryan-Dahl-Creator-of-Node-js.md index e0cecc817c4..9b350775967 100644 --- a/article/2021/Interview-with-Ryan-Dahl-Creator-of-Node-js.md +++ b/article/2021/Interview-with-Ryan-Dahl-Creator-of-Node-js.md @@ -11,13 +11,13 @@ Ryan Dahl 是 Node.js 的创始人以及 Deno JavaScript 和 TypeScript 运行时的早期开发者。我们很高兴有机会与 Ryan 探讨他开发的一些别的项目以及 Deno 面临的主要挑战,了解他对 JavaScript 和 TypeScript 的未来的想法,寻找更多第三方的 Deno 生态系统项目的更多信息,并讨论如果他能时光倒流,他会如何改变 Node.js 的开发! -## 采访 +## 采访正文 -**Evrone:** 您的新项目 Deno 对开发者们产生了很大影响。请问您现在大部分时间都在做些什么? +**Evrone:** 你的新项目 Deno 对开发者们产生了很大影响。请问你现在大部分时间都在做些什么? **Ryan:** 我大部分时间都在研究 Deno!实际上 Deno 是一个相当大的软件集合,我们把它放到可执行文件中。我们现在正在提升 Deno 运行时,同时也在努力将基础架构应用到商业项目中。 -**Evrone:** 您具有使用多种编程语言的实践经验,例如 C、Rust、Ruby、JavaScript 和 TypeScript。您最喜欢使用哪一种语言进行开发? +**Evrone:** 你具有使用多种编程语言的实践经验,例如 C、Rust、Ruby、JavaScript 和 TypeScript。你最喜欢使用哪一种语言进行开发? **Ryan:** 这些天我发现编写 Rust 代码最是有趣。它的学习曲线很是陡峭,并且不适合许多情景,但对于我现在正在研究的东西来说,它真的是完美的,比 C++ 好多了 —— 我坚信我将永远不会再开始一个新的 C++ 项目。Rust 具有如此简单的表达低级机械语言的能力,真的太棒了! @@ -25,11 +25,11 @@ JavaScript 从来不是我最喜欢的语言,它只是最常见的语言,也 在 Deno 中,我们正试图降低将 TypeScript 代码转换为 JavaScript 所固有的复杂性,希望这将使更多开发者能够使用 TypeScript。 -**Evrone:** 渐近类型(Gradual typing)已成功添加到 Python 核心库、PHP 和 Ruby 语言中。您认为将类型添加到 JavaScript 的主要作用是什么? +**Evrone:** 渐近类型(Gradual typing)已成功添加到 Python 核心库、PHP 和 Ruby 语言中。你认为将类型添加到 JavaScript 的主要作用是什么? **Ryan:** 将类型(像 TypeScript 那样)添加到 JavaScript 的成功远远超过了在 Python、PHP 或 Ruby 中所完成的那样。TypeScript 是具有类型的 JavaScript 语言。更好的问题是:是什么在阻止 JavaScript 标准化组织(TC39)采用 TypeScript?通过设计,标准化会缓慢而谨慎地进行。他们首先正在研究提出 Types-As-Comments 的提议,该提议将允许 JavaScript 运行时通过忽略类型来执行 TypeScript 语法。我认为最终 TypeScript(或类似的东西)将作为 JavaScript 标准的一部分被提出,但这需要时间。 -**Evrone:** 作为受人尊敬的 VIM 用户,您如何看待像 VS Code 这样的现代化代码编辑器?他们对老一代开发者友善吗? +**Evrone:** 作为受人尊敬的 VIM 用户,你如何看待像 VS Code 这样的现代化代码编辑器?他们对老一代开发者友善吗? **Ryan:** 与我一起工作的每个人都使用 VS Code。他们喜欢它,而且我想多数人都应该去使用它。 @@ -38,55 +38,55 @@ JavaScript 从来不是我最喜欢的语言,它只是最常见的语言,也 1. 我对它非常熟悉,并且可以用它快速编码。我喜欢在 ssh 和 tmux 上的工作体验,并且享受全屏终端的宁静。 2. 对于软件基础结构来说,基于文本并可以通过简单工具进行访问非常重要。在 Java 世界中,开发者们犯了将 IDE 过多地与该语言的世界联系在一起的错误,从而造成一种情况,即实际上人们被迫使用 IDE 去编写 Java 代码。而通过使用简单的工具,我可以确保我开发的软件不会不必要地依赖 IDE。如果你去尝试使用 grep 而不是跳转到定义(jump-to-definition),那么你会发现,跳转到定义这种太多的间接寻址实在是令人无法忍受。对于我所做的事情,我认为这会带来更好的软件。 -**Evrone:** Deno 运行时展示了修复依赖项管理、安全性等长期存在的问题的可能方法。您是否希望它像是进行实验的 Haskell 那样的场所,还是您想将它作为最佳实践选择,用在一些什么用途上? +**Evrone:** Deno 运行时展示了修复依赖项管理、安全性等长期存在的问题的可能方法。你是否希望它像是进行实验的 Haskell 那样的场所,还是你想将它作为最佳实践选择,用在一些什么用途上? **Ryan:** 千万不要将新颖性误认为是实验性的,Deno 绝对是实用的,它建立在服务器端 JavaScript 已有多年经验的基础上。我和我的同事们致力于构建实用的动态语言运行时。我们围绕依赖项管理和安全性所做的设计选择非常保守。我们可以很容易地引入另一个类似于 NPM 的集中式系统,但是选择了基于 Web 标准 URL 的链接系统。我们当然可以更容易地打开文件系统和网络中的各种安全漏洞,但相反,我们选择像浏览器一样仔细管理访问。 Deno 是新软件 —— 这使得它天生就不适合很多用例。但是 Deno 还是一个大型 Rust 代码库,拥有着强大的速度,稳定可靠且成功率高的 CI 管理以及定期的计划发布。你说这是实验性的,这可不是实验! -**Evrone:** 在 2020 年,大多数软件开发人员大会都变成了“在线”和“虚拟”会议。您是否尝试参加过这种新形式的会议?您对此又有何看法? +**Evrone:** 在 2020 年,大多数软件开发人员大会都变成了“在线”和“虚拟”会议。你是否尝试参加过这种新形式的会议?你对此又有何看法? **Ryan:** 我参加过,但我现在正避免参加线上会议。对我而言,会议最好的部分是“走廊”(指去交际),这恰恰是在线会议所缺失的一个关键方面。我更喜欢在空闲时间以 2 倍的速度观看 YouTube 上的演讲。希望我能在 2021 年晚些时候参加一些非虚拟的会议。 -**Evrone:** 将依赖关系图从一个文件分散到单个源代码文件的想法受到了 Webpack 的拥护,也受到许多开发人员的赞扬。但是依赖性管理具有挑战性,Node.js 从 Common.js 迁移到 ESM 花费了多年的时间。请问您要使用 Deno 解决的主要依赖管理问题是什么? +**Evrone:** 将依赖关系图从一个文件分散到单个源代码文件的想法受到了 Webpack 的拥护,也受到许多开发人员的赞扬。但是依赖性管理具有挑战性,Node.js 从 Common.js 迁移到 ESM 花费了多年的时间。请问你要使用 Deno 解决的主要依赖管理问题是什么? **Ryan:** 浏览器没有借助任何一个 CDN 来分发 JavaScript。网络的分散性是其最大的优势,我不明白为什么这也不能用于服务器端的 JavaScript。因此,我希望 Deno 不依赖于任何中心化的代码数据库。 -**Evrone:** Python 和 JavaScript 正在竞争,PK 究竟谁才是最佳的供新人开发者学习的通用编程语言。您对此有何看法? +**Evrone:** Python 和 JavaScript 正在竞争,PK 究竟谁才是最佳的供新人开发者学习的通用编程语言。你对此有何看法? **Ryan:** 脚本语言非常适合初学者。本质上,Python 和 JavaScript 的语言系统非常相似,只不过语法各异,在语义上也稍微不同。JavaScript 是由国际标准委员会管理的,可以在所有地方运行,并且速度要快一个数量级(将 V8 与 cpython 进行比较时),而且还拥有着更大的用户群。而对于某些领域来说,可用的 Python 库则更多,尤其是在科学计算中。新手程序员想开发的东西因人而异,Python 可能是合适的。但总的来说,我认为 JavaScript 是一种更好的入门语言。 -**Evrone:** 具有一个主线程和小的 `handler` 可调用对象的异步并发范例是 Node.js 的基石之一。现在,这种想法通过新的 `Async / Await` 语法和协程的概念得到了进一步提升。作为平台作者,您如何看待它们及其可用的替代方案,例如 Go Goroutines 或基于 Ruby 线程的并发? +**Evrone:** 具有一个主线程和小的 `handler` 可调用对象的异步并发范例是 Node.js 的基石之一。现在,这种想法通过新的 `Async / Await` 语法和协程的概念得到了进一步提升。作为平台作者,你如何看待它们及其可用的替代方案,例如 Go Goroutines 或基于 Ruby 线程的并发? -**Ryan:** OS 线程无法很好地扩展到高并发应用程序。如果您有许多并发连接,请不要使用 Ruby。 +**Ryan:** OS 线程无法很好地扩展到高并发应用程序。如果你有许多并发连接,请不要使用 Ruby。 Goroutines 非常易于使用,并能够达到最佳性能。与 Go 一样,Node 和 Deno 都是基于非阻塞 IO 和 OS 事件通知系统(`epoll`,`kqueue`)构建的。JavaScript 本质上是一个单线程系统,因此 Node 或 Deno 的单个实例通常无法在不开始创建新实例的情况下利用系统上的所有 CPU 内核。Node / Deno 是 JavaScript 的最佳选择,但在没有其他可能偏向 JavaScript 的其他要求的情况下,Go 最终是高并发系统的更好选择。 -**Evrone:** 在如此激烈的竞争中,您如何看待 JavaScript 和 TypeScript 的未来,尤其是与后端、嵌入式和机器学习(ML)领域有关的未来? +**Evrone:** 在如此激烈的竞争中,你如何看待 JavaScript 和 TypeScript 的未来,尤其是与后端、嵌入式和机器学习(ML)领域有关的未来? **Ryan:** 动态(或“脚本”)语言**非常有用。程序员要解决的问题通常不受 CPU 限制。问题更多是受工程时间限制。能够快速开发和部署更为重要。在动态语言中,JavaScript(纯 JavaScript 或带类型的 JavaScript)是最受欢迎的,也是迄今为止最快的。未来,我相信我们所追求的唯一动态语言将是这种奇怪的、从网络浏览器中衍生出来的进化语言。借助 Deno,我们正在努力消除障碍,在某些很少使用 JS 的地方应用 JS,像是机器学习领域。例如,我们可能会在 Deno 中添加 WebGPU 支持,从而允许简单的开箱即用的 GPU 编程,最终将使 TensorFlow.js 之类的系统能够在 Deno 上运行。 -如前所述,动态语言有其局限性,并不适合所有问题领域。如果您正在对数据库进行编程,则最好使用一种使您对计算机具有最大控制权的语言(例如 Rust 或 C++)进行编写。如果您正在编写高并发性 API 服务器,很难想象有比 Go 更好的选择。 +如前所述,动态语言有其局限性,并不适合所有问题领域。如果你正在对数据库进行编程,则最好使用一种使你对计算机具有最大控制权的语言(例如 Rust 或 C++)进行编写。如果你正在编写高并发性 API 服务器,很难想象有比 Go 更好的选择。 -**Evrone:** 现代操作系统和新的 Deno 运行时引入了精细的权限,以抵消第三方软件和依赖项的安全风险。但是,使用依赖关系的最终用户和开发人员是否有可能在“允许”和“拒绝”应用程序安全性请求时做出正确的决定?您如何看待几年后像我们大多数人一样自动单击“允许一切”的风险,就像我们大多数人现在对网站 Cookie “安全确认”所做的那样? +**Evrone:** 现代操作系统和新的 Deno 运行时引入了精细的权限,以抵消第三方软件和依赖项的安全风险。但是,使用依赖关系的最终用户和开发人员是否有可能在“允许”和“拒绝”应用程序安全性请求时做出正确的决定?你如何看待几年后像我们大多数人一样自动单击“允许一切”的风险,就像我们大多数人现在对网站 Cookie “安全确认”所做的那样? -**Ryan:** 网站 Cookie 弹出窗口不是个最好的类比 —— 它们是相当无用的法律副产品。更好的是内置一个对话框,上面写着“允许该网站访问您的相机”或“允许桌面通知”或“允许该网站查看您的位置”。这些并不是没有用的,这些是相当重要的安全功能。 +**Ryan:** 网站 Cookie 弹出窗口不是个最好的类比 —— 它们是相当无用的法律副产品。更好的是内置一个对话框,上面写着“允许该网站访问你的相机”或“允许桌面通知”或“允许该网站查看你的位置”。这些并不是没有用的,这些是相当重要的安全功能。 -程序员在计算机上运行许多不同的自动化程序,没有人有时间审核他们将要运行的所有代码,也不足以在 Docker 容器中运行所有代码:当您运行 lint 时,是被隔离的吗?不,答案是您必须相信 lint 脚本不会破坏您的系统。我认为允许用户查看并拒绝不必要的系统访问非常合适。 +程序员在计算机上运行许多不同的自动化程序,没有人有时间审核他们将要运行的所有代码,也不足以在 Docker 容器中运行所有代码:当你运行 lint 时,是被隔离的吗?不,答案是你必须相信 lint 脚本不会破坏你的系统。我认为允许用户查看并拒绝不必要的系统访问非常合适。 -**Evrone:** 全新的“全栈”概念促使开发人员同时编写前端代码和后端代码,而使用相同的语言和诸如 TypeScript 之类的共享技术栈在现在变得非常容易。您认为对于许多开发人员来说,将如此多的不同事物纳入他们的日常工作范围是一个好主意吗? +**Evrone:** 全新的“全栈”概念促使开发人员同时编写前端代码和后端代码,而使用相同的语言和诸如 TypeScript 之类的共享技术栈在现在变得非常容易。你认为对于许多开发人员来说,将如此多的不同事物纳入他们的日常工作范围是一个好主意吗? **Ryan:** 降低复杂性总是有益的。程序员必须与之交互的语言、VM、框架和概念越少越好。 -**Evrone:** 您打算如何处理 TypeScript 语言本身的版本更新?在 Node.js 生态系统内,使用 V8 引擎进行 JavaScript 语法更新通常会导致某些程序包无法正常工作。 +**Evrone:** 你打算如何处理 TypeScript 语言本身的版本更新?在 Node.js 生态系统内,使用 V8 引擎进行 JavaScript 语法更新通常会导致某些程序包无法正常工作。 **Ryan:** TypeScript 语言几乎具有完整的功能。依赖于最先进的语言功能的用户可能会遇到不稳定的情况,请不要这样做。 -**Evrone:** 您如何看待软件开发人员的良好教育?我们是否需要具有所有数学,算法和数据结构的“科学”(如“计算机科学”),还是需要其他东西? +**Evrone:** 你如何看待软件开发人员的良好教育?我们是否需要具有所有数学,算法和数据结构的“科学”(如“计算机科学”),还是需要其他东西? **Ryan:** 想要从事编程职业的人应该去大学学习计算机科学。当然,可以获得相关领域的学位(例如电气工程,物理学,数学);有许多非常有能力的工程师根本没有学位。但是,通过花几年的时间学习基础知识并进行许多非常困难的实验,确实可以得到一些好处。 -**Evrone:** 您是否有非常喜欢的已经实施了的第三方 Deno 生态系统项目? +**Evrone:** 你是否有非常喜欢的已经实施了的第三方 Deno 生态系统项目? **Ryan:** 是的,当然有: @@ -97,7 +97,7 @@ Goroutines 非常易于使用,并能够达到最佳性能。与 Go 一样,No - 可视化模块图 - 最小但灵活的静态站点生成器 -**Evrone:** 随着 GitHub 之类的社交平台的推出,个体开发者和大公司现在都可以轻松地使用开源并做出贡献。您认为现在是“开源的黄金时代”,还是认为仍存在着一些潜在的问题? +**Evrone:** 随着 GitHub 之类的社交平台的推出,个体开发者和大公司现在都可以轻松地使用开源并做出贡献。你认为现在是“开源的黄金时代”,还是认为仍存在着一些潜在的问题? **Ryan:** 现在肯定是开源的,许可情况已广为人知并得到了解决。关于维护的激励模型仍然存在未解决的问题,也许 GitHub 赞助商正在朝着这个方向提供帮助。它比以前要好,但是我希望我们能找到一种方法,使维护软件重要部分的人员可以为他们的工作得到独立的报酬。 @@ -113,7 +113,7 @@ Goroutines 非常易于使用,并能够达到最佳性能。与 Go 一样,No **Ryan:** 使用 ES 模块,并查看我们的 Node 兼容性层。 -## 结论 +## 小结 我们很高兴能与 Ryan 对话,进一步地了解他的生活、想法和计划。在 Evrone,我们经常使用 Node.js 为客户构建自定义解决方案。感谢你的阅读! diff --git a/article/2021/from-rxjava-2-to-kotlin-flow-threading.md b/article/2021/from-rxjava-2-to-kotlin-flow-threading.md index 9228a66cfce..70e63993351 100644 --- a/article/2021/from-rxjava-2-to-kotlin-flow-threading.md +++ b/article/2021/from-rxjava-2-to-kotlin-flow-threading.md @@ -103,7 +103,7 @@ Observable.just(loadDataSync()) .subscribe { result -> println(result) } ``` -`just` 参数的值是立即计算的,而不是在订阅时才计算的。这意味着,如果您在主线程上创建此类可观察的对象,那么可能会在主线程上进行大量潜在的计算。虽说订阅将在 `io` 上正确完成,但是 `just` 的值将在订阅之前就被计算出来了。 +`just` 参数的值是立即计算的,而不是在订阅时才计算的。这意味着,如果你在主线程上创建此类可观察的对象,那么可能会在主线程上进行大量潜在的计算。虽说订阅将在 `io` 上正确完成,但是 `just` 的值将在订阅之前就被计算出来了。 解决此问题的方法之一是将你的 `Observable.just` 调用包装到 `Observable.defer` 中,这样调用所执行的所有内容都将在订阅时以及在我们位于 `subscribeOn` 处所声明的调度器上进行计算: @@ -183,7 +183,7 @@ CoroutineScope(Job() + Dispatchers.Main).launch { } ``` -> 现在,我们有了许多与协程相关的概念,可能需要对其进行解释。我们不会深入介绍协程这个功能或是 Kotlin Flow,因此,如果您不熟悉协程,最好先阅读有关协程的文档。 +> 现在,我们有了许多与协程相关的概念,可能需要对其进行解释。我们不会深入介绍协程这个功能或是 Kotlin Flow,因此,如果你不熟悉协程,最好先阅读有关协程的文档。 该示例在某种程度上与 RxJava 部分中使用的示例相同:我们观察了 `io` 的一些变化,然后在 `main` 上打印结果,尽管代码有所不同。让我们找出区别以及它是如何工作的。 @@ -235,7 +235,7 @@ CoroutineScope(Job() + Dispatchers.Main).launch { 如果在带有 `Dispatchers.Main` 的上下文中运行,那么 `calculate` 将在主线程上完成而不是输入输出上运行。 -为了解决这个问题,您可以使用 `flow` 构建器并在其中明确定义内容: +为了解决这个问题,你可以使用 `flow` 构建器并在其中明确定义内容: ![](https://cdn-images-1.medium.com/max/2000/1*JSkHKLjh9X-YDL1Olkl5hQ.png) @@ -515,7 +515,7 @@ Kotlin Flow 真的不错,可以跟 RxJava Observable 相媲美。它们的使 在处理线程的方式上,Kotlin Flow 和 RxJava 是相反的。在 RxJava 中,我们认为是从上到下,而在 Kotlin Flow 中则是从下到上。但无论如何,如果有必要,在不破坏大部分功能的前提下,将代码进行反向迁移也是可行的。 -希望您喜欢这篇文章,但愿它对您有所帮助 +希望你喜欢这篇文章,但愿它对你有所帮助 祝你编程快乐! diff --git a/article/2021/how-a-cache-stampede-caused-one-of-facebooks-biggest-outages.md b/article/2021/how-a-cache-stampede-caused-one-of-facebooks-biggest-outages.md index 6ae6fa1016e..8b7023ee0c1 100644 --- a/article/2021/how-a-cache-stampede-caused-one-of-facebooks-biggest-outages.md +++ b/article/2021/how-a-cache-stampede-caused-one-of-facebooks-biggest-outages.md @@ -9,19 +9,19 @@ ![由 [Susan Yin](https://unsplash.com/@syinq) 上传至 [Unsplash](https://unsplash.com)(https://unsplash.com)](https://cdn-images-1.medium.com/max/10368/0*FGGy038B4etUbHdm) -2010 年 9 月 23 日,Facebook 发生了迄今为止最严重的宕机事件之一。在这次事件中,Facebook 关闭了四个小时。情况如此严重,以至于工程师不得不让 Facebook 下线才能恢复。 +2010 年 9 月 23 日,Facebook 发生了迄今为止最严重的宕机事件之一。在这次事件中,Facebook 关闭了四个小时。情况如此严重,以至于工程师不得不让 Facebook 下线才得以恢复系统。 -虽然 Facebook 那时候还没有现在那么庞大,但它仍然拥有超过 10 亿的用户,并且它的宕机并没有被忽视。人们只是在 Twitter 上抱怨或开玩笑这件事。 +虽然 Facebook 那时候还没有现在那么庞大,但它当时仍然拥有超过 10 亿的用户,并且它的宕机并没有被忽视。人们只是在 Twitter 上或抱怨或开这件事的玩笑。 ![图片来源:[https://www.businessinsider.com/how-we-weathered-the-great-facebook-outage-of-2010-2010-9#the-outage-had-far-reaching-consequences-7](https://www.businessinsider.com/how-we-weathered-the-great-facebook-outage-of-2010-2010-9#the-outage-had-far-reaching-consequences-7)](https://cdn-images-1.medium.com/max/2000/0*_-dYSO7eL_K9Qypi) 那么,究竟是什么导致了 Facebook 的停运呢?[根据事件发生后的官方分析](https://www.facebook.com/notes/facebook-engineering/more-details-on-todays-outage/431441338919): -> 今天我们误改了一个配置。这意味着每个客户端都能看到这个错误配置并尝试修复它。由于修复操作涉及对数据库集群进行查询,因此该集群很快就被每秒数十万个查询所淹没。 +> 今天我们误改了一个配置。这意味着每个客户端都能收到这个错误配置并尝试修复它。由于修复操作涉及对数据库集群进行查询,因此该集群很快就被每秒数十万个查询所淹没。 --- -错误的配置更改导致大量请求被传送到他们的数据库。这种请求踩踏被恰当地称为 [**缓存踩踏**](https://en.wikipedia.org/wiki/Cache_stampede)。这是困扰科技行业的一个普遍问题,它已经导致许多公司出现故障,例如 2016 年的 [Internet Archive](https://archive.org/index.php)。许多大型应用程序每天都在与它作斗争,如 Instagram 和 DoorDash 。 +错误的配置更改导致大量请求被传送到他们的数据库。这种请求踩踏被恰当地称为 [**缓存踩踏**](https://en.wikipedia.org/wiki/Cache_stampede)。这是困扰科技行业的一个普遍问题,它已经导致许多系统发生故障,例如 2016 年的 [Internet Archive](https://archive.org/index.php)。许多大型应用程序每天都在为此防范着,如 Instagram 和 DoorDash 。 ## 什么是缓存踩踏? @@ -34,7 +34,7 @@ 3. 收到超时,所有线程重试他们的请求 —— 导致另一次踩踏。 4. 循环往复。 -并不需要拥有 Facebook 那样规模的用户,你一样会遭其折磨。缓存踩踏与用户规模无关,因此它同时困扰着初创公司和科技巨头。 +你不需要拥有 Facebook 那样规模的用户也一样会遭其折磨。缓存踩踏与用户规模无关,因此它同时困扰着初创公司和科技巨头。 --- @@ -62,7 +62,7 @@ 这对于防止频繁访问数据时发生踩踏事件特别有用。即使第 2 层缓存上的键过期,一些第 1 层缓存可能仍存储该值。这将限制需要重新计算缓存值的线程数。 -但是,这种方法有一些方面需要注意权衡。如果您不小心,在应用程序服务器上缓存内存中的数据可能会导致 [内存不足](https://en.wikipedia.org/wiki/Out_of_memory) 问题,尤其是在缓存大量数据时。 +但是,这种方法有一些方面需要注意权衡。如果你不小心,在应用程序服务器上缓存内存中的数据可能会导致 [内存不足](https://en.wikipedia.org/wiki/Out_of_memory) 问题,尤其是在缓存大量数据时。 此外,这种缓存策略仍然容易受到我所说的追随者踩踏的影响。 @@ -74,13 +74,13 @@ 那么,对于追随者踩踏事件,我们能做些什么呢? -## 锁 和 Promise +## Lock 和 Promise 从本质上讲,缓存踩踏是一种竞争状态 —— 多个线程争夺共享资源。在这种情况下,共享的资源是缓存。 ![图片来源:[https://instagram-engineering.com/thundering-herds-promises-82191c8af57d](https://instagram-engineering.com/thundering-herds-promises-82191c8af57d)](https://cdn-images-1.medium.com/max/2000/0*KThIA3rqDvhQLXHp) -在高并发系统中很常见,一种防止共享资源竞争条件的方法是使用**锁**。虽然锁通常用于同一台机器上的线程,但也有一些方法可以使用 [分布式锁](https://redis.io/topics/distlock) 用于远程缓存。 +在高并发系统中很常见,一种防止共享资源竞争条件的方法是使用**锁**。虽然锁通常用于同一台机器上的线程,但也有一些方法可以使用[分布式锁](https://redis.io/topics/distlock) 用于远程缓存。 通过在缓存键上加锁,限制一次只有一个调用者能够访问缓存。如果缓存键丢失或过期,调用者就可以生成并缓存数据,同时持有锁。任何其他尝试从同一个缓存键读取的进程都必须等到锁空闲。 @@ -88,11 +88,11 @@ 使用锁解决了竞争状态问题,但它会产生另一个问题。你如何处理所有等待锁释放的线程? -不知道你是否使用过 [自旋锁](https://en.wikipedia.org/wiki/Spinlock) 模式并让线程不断轮询锁?这将创建一个 [忙等待](https://en.wikipedia.org/wiki/Busy_waiting) 场景。 +不知道你是否使用过[自旋锁](https://en.wikipedia.org/wiki/Spinlock) 模式并让线程不断轮询锁?这将创建一个[忙等待](https://en.wikipedia.org/wiki/Busy_waiting) 场景。 -在检查锁是否空闲之前,你是否让线程休眠了任意时间?这样你就会遇到 [惊群效应问题](https://en.wikipedia.org/wiki/Thundering_herd_problem)。 +在检查锁是否空闲之前,你是否让线程休眠了任意时间?这样你就会遇到[惊群效应问题](https://en.wikipedia.org/wiki/Thundering_herd_problem)。 -你是否引入了 [退避和抖动机制](https://www.baeldung.com/resilience4j-backoff-jitter) 以防止惊群效应问题?这可能有效,但还有一个更普遍的问题。拥有锁的线程必须在释放锁之前重新计算值并更新缓存键。 +你是否引入了[退避和抖动机制](https://www.baeldung.com/resilience4j-backoff-jitter)以防止惊群效应问题?这可能有效,但还有一个更普遍的问题。拥有锁的线程必须在释放锁之前重新计算值并更新缓存键。 这个过程可能需要一段时间。特别是如果该值的计算成本很高或存在网络问题。如果缓存耗尽其可用连接池并且用户请求被丢弃,这仍然可能导致宕机。 @@ -116,7 +116,7 @@ --- -虽然这种情况不一定算作中断,但它会影响尾部延迟和整体用户体验。如果保持较低的尾部延迟对您的应用程序很重要,那么还有另一种策略需要考虑。 +虽然这种情况不一定算作中断,但它会影响尾部延迟和整体用户体验。如果保持较低的尾部延迟对你的应用程序很重要,那么还有另一种策略需要考虑。 ## 提前重新计算 @@ -130,7 +130,7 @@ #### 概率早期重新计算 -2015 年,一组研究人员发表了名为 「Optimal Probabilistic Cache Stampede Prevention」 的 [白皮书](https://cseweb.ucsd.edu/~avattani/papers/cache_stampede.pdf)。在其中,他们描述了一种算法,用于优化预测何时在缓存过期之前重新计算缓存值。 +2015 年,一组研究人员发表了名为 「Optimal Probabilistic Cache Stampede Prevention」 的[白皮书](https://cseweb.ucsd.edu/~avattani/papers/cache_stampede.pdf)。在其中,他们描述了一种算法,用于优化预测何时在缓存过期之前重新计算缓存值。 研究论文中有很多数学理论,但算法归结为: @@ -158,11 +158,11 @@ currentTime - ( timeToCompute * beta * log(rand()) ) > expiry Facebook 的缓存踩踏事件如此具有破坏性的原因之一是,即使工程师找到了解决方案,他们也无法部署它,因为踩踏事件仍在继续。 -来自 [事后分析](https://www.facebook.com/notes/facebook-engineering/more-details-on-todays-outage/431441338919): +根据[事后分析](https://www.facebook.com/notes/facebook-engineering/more-details-on-todays-outage/431441338919): > 更糟糕的是,每次客户端在尝试查询其中一个数据库时出错,它都会将其解释为无效值,并删除相应的缓存键。这意味着即使在解决了原始问题之后,查询流仍在继续。只要数据库无法为某些请求提供服务,它们就会对自己造成更多请求。我们进入了一个不允许数据库恢复的反馈循环。 -现实情况是,无法保证预防永远有效 —— 我们还需要缓解。[防御性编程](https://en.wikipedia.org/wiki/Defensive_programming) 规定应该制定一个计划,以防踩踏事件绕过我们设置的限制。 +现实情况是,无法保证预防永远有效 —— 我们还需要缓解。[防御性编程](https://en.wikipedia.org/wiki/Defensive_programming)规定应该制定一个计划,以防踩踏事件绕过我们设置的限制。 幸运的是,有一个已知的模式来处理这个问题。 @@ -170,7 +170,7 @@ Facebook 的缓存踩踏事件如此具有破坏性的原因之一是,即使 在编程中使用熔断器的想法并不新鲜。在 Michael Nygard 于 2007 年发表 [**Release It!**](https://www.amazon.com/gp/product/0978739213) 后,它开始流行。正如 Martin Fowler 在他的文章 **[CircuitBreaker](https://www.martinfowler.com/bliki/CircuitBreaker.html)** 所写的那样: -> 熔断器背后的基本思想非常简单。您将受保护的函数调用包装在熔断器对象中,该对象监视故障。一旦故障达到某个阈值,熔断器就会熔断,并且所有对熔断器的进一步调用都会返回错误,而不会调用到受到熔断器保护的地方。 +> 熔断器背后的基本思想非常简单。你将受保护的函数调用包装在熔断器对象中,该对象监视故障。一旦故障达到某个阈值,熔断器就会熔断,并且所有对熔断器的进一步调用都会返回错误,而不会调用到受到熔断器保护的地方。 ![图片来源:[https://www.martinfowler.com/bliki/CircuitBreaker.html](https://www.martinfowler.com/bliki/CircuitBreaker.html)](https://cdn-images-1.medium.com/max/2000/0*2Jn_bNJ6Vh2-Lwla.png) @@ -186,7 +186,7 @@ Facebook 的缓存踩踏事件如此具有破坏性的原因之一是,即使 他们从故障中吸取了哪些教训,他们采取了哪些保护措施来防止再次发生这种情况? -他们的工程帖子,[**幕后:向数百万人广播直播视频**](https://engineering.fb.com/2015/12/03/ios/under-the-hood-broadcasting-live-video-to-millions/),讨论他们对架构所做的改进。它讨论了我们已经讨论过的内容,例如缓存层次结构,但也包括一些新颖的方法,例如 HTTP 请求合并。这篇文章值得一读,但如果你时间不够,这个 [视频提供了一个全面的概述](https://www.facebook.com/Engineering/videos/10153675295382200/?t=0)。 +他们的工程帖子,[**幕后:向数百万人广播直播视频**](https://engineering.fb.com/2015/12/03/ios/under-the-hood-broadcasting-live-video-to-millions/),讨论他们对架构所做的改进。它讨论了我们已经讨论过的内容,例如缓存层次结构,但也包括一些新颖的方法,例如 HTTP 请求合并。这篇文章值得一读,但如果你时间不够,这个[视频提供了一个全面的概述](https://www.facebook.com/Engineering/videos/10153675295382200/?t=0)。 可以说 Facebook 从他们过去的错误中吸取了教训。