├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE ├── README.md ├── book.toml ├── src ├── SUMMARY.md ├── lang │ └── rust │ │ ├── 01-用Rust学习解析器组合器.md │ │ ├── 02-用Rust编写LLVM的玩具前端.md │ │ ├── 03-使用nom解析url.md │ │ ├── 04-Rust中异步编程实用介绍.md │ │ ├── 05-tokio内幕-自底向上理解Rust的异步IO框架.md │ │ ├── 06-Rust中的Arenas.md │ │ ├── 07-Rust异步IO:从mio到coroutine.md │ │ ├── 08-图解Rust所有权与生命周期.md │ │ ├── 09-Rust异步执行器.md │ │ ├── 10-Rust标准库特征指南.md │ │ ├── 11-Rust中的宏:带有示例的教程.md │ │ ├── 12-libp2p教程:使用Rust构建p2p应用.md │ │ ├── 13-Rust的Pin与Unpin.md │ │ ├── 14-使用GDB调试Rust应用.md │ │ ├── 15-解释Rust中的原子性.md │ │ ├── 16-Rust和TUI:在Rust中构建命令行界面.md │ │ ├── 17-在Android中运行Rust.md │ │ ├── 18-Rust中常见的有关生命周期的误解.md │ │ ├── 19-生命周期型变示例.md │ │ ├── 20-rust如何实现线程安全.md │ │ ├── 22-无需fork_Clippy就可以编写Rust_lints.md │ │ ├── 23-使用Mio编写底层TCP服务器.md │ │ ├── 24-UnsafeRust的取舍.md │ │ ├── 25-Rust中的类型强转.md │ │ ├── 26-为什么Rust需要Pin和Unpin.md │ │ ├── code │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── async-io-example │ │ │ ├── Cargo.toml │ │ │ ├── examples │ │ │ │ ├── tcp.rs │ │ │ │ └── tcp1.rs │ │ │ └── src │ │ │ │ └── lib.rs │ │ ├── rust-gdb-example │ │ │ ├── Cargo.toml │ │ │ ├── examples │ │ │ │ └── basic.rs │ │ │ └── src │ │ │ │ └── lib.rs │ │ ├── rust-p2p-example │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── main.rs │ │ └── telnet-chat │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ ├── accept.rs │ │ │ ├── client.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── main_loop.rs │ │ │ └── telnet.rs │ │ ├── img │ │ ├── GDB-Layout-Split-Visual.avif │ │ ├── Layout-GDB-SRC-Command-Line-Interface.avif │ │ ├── Make-Struct-Public-with-Keyword.webp │ │ ├── Parsing-Struct-Name-Field-Diagram.webp │ │ ├── Screen-Shot-2021-02-09-at-9.24.46-AM.webp │ │ ├── asynchronous_multi.svg │ │ ├── asynchronous_single.svg │ │ ├── bilde.png │ │ ├── dylint.jpeg │ │ ├── dylint1.png │ │ ├── dylint2.png │ │ ├── futures-join.svg │ │ ├── futures-rs.png │ │ ├── futures-spawn.svg │ │ ├── macro_in_rust.webp │ │ ├── parsing-rust-nom.png │ │ ├── recv-sequence-1.svg │ │ ├── recv-sequence-2.svg │ │ ├── rust-tui-example-command-line-interface-finished.webp │ │ ├── rust-tui-example-command-line-interface.webp │ │ ├── rust_ownership_1.jpg │ │ ├── rust_ownership_2.jpg │ │ ├── rust_ownership_3.png │ │ ├── rust_ownership_4.jpg │ │ ├── rust_ownership_5.jpg │ │ ├── rust_ownership_6.jpg │ │ ├── rust_ownership_7.jpg │ │ ├── rust_ownership_8.jpg │ │ ├── rust_ownership_9.jpg │ │ ├── swap_problem.jpeg │ │ ├── synchronous.svg │ │ ├── synchronous_multios.svg │ │ ├── tokio-event-loop.svg │ │ ├── tokio-stack.svg │ │ ├── trpl04-04.svg │ │ ├── two-launcher-activities.png │ │ ├── udpsocket.svg │ │ └── welcome_to_the_futures.jpeg │ │ ├── std │ │ ├── 20-std-pin.md │ │ └── 21-std-condvar.md │ │ └── summary.md ├── oci │ ├── 01-oci-spec-overview.md │ ├── img │ │ ├── oci1.png │ │ ├── oci2.png │ │ ├── oci3.png │ │ ├── oci4.png │ │ ├── oci5.png │ │ └── oci6.png │ └── summary.md ├── 架构 │ ├── ServiceMesh │ │ ├── 01-ServiceMesh.md │ │ ├── 02-xDS与gRPC协议.md │ │ ├── 03-gRPC使用xDS实现负载均衡.md │ │ └── summary.md │ ├── img │ │ ├── Pattern_ Service Mesh_files │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5-a.png │ │ │ ├── 5.png │ │ │ ├── 6-a.png │ │ │ ├── 6-b.png │ │ │ ├── 6.png │ │ │ ├── analytics.js │ │ │ ├── main.css │ │ │ ├── mesh1.png │ │ │ ├── mesh2.png │ │ │ └── mesh3.png │ │ ├── ads.svg │ │ ├── cds-eds-resources.svg │ │ ├── eds-distinct-stream.svg │ │ ├── eds-same-stream.svg │ │ ├── error-detail-nack.svg │ │ ├── incremental-reconnect.svg │ │ ├── incremental.svg │ │ ├── later-ack.svg │ │ ├── mesh1.png │ │ ├── mesh2.png │ │ ├── mesh3.png │ │ ├── pattern_service_mesh5-a.png │ │ ├── pattern_service_mesh6-a.png │ │ ├── pattern_service_mesh6-b.png │ │ ├── pattern_service_mesh_1.png │ │ ├── pattern_service_mesh_2.png │ │ ├── pattern_service_mesh_3.png │ │ ├── pattern_service_mesh_4.png │ │ ├── pattern_service_mesh_5.png │ │ ├── pattern_service_mesh_6.png │ │ ├── simple-ack.svg │ │ ├── simple-nack.svg │ │ ├── stale-requests.svg │ │ ├── tcc │ │ │ ├── 2pc-xa-tansaction-model.png │ │ │ ├── communication-in-three-phase-commit-protocol.png │ │ │ ├── communication-in-two-phase-commit-protocol.png │ │ │ ├── coordinator-participants.png │ │ │ ├── distributed-dead-locking.png │ │ │ ├── flat-and-nested-transactions.png │ │ │ └── properties-of-soft-transaction.png │ │ └── update-race.svg │ └── 分布式 │ │ ├── summary.md │ │ └── 事务 │ │ ├── 01-分布式事务_系统之底层原理揭秘.md │ │ └── summary.md └── 网络协议 │ ├── img │ ├── kcp_1.svg │ ├── kcp_2.svg │ ├── kcp_3.svg │ ├── kcp_4.svg │ ├── kcp_5.svg │ ├── kcp_6.svg │ ├── kcp_7.svg │ ├── kcp_8.svg │ └── kcp_9.svg │ ├── kcp │ └── 详解KCP协议的原理和实现.md │ └── summary.md └── theme ├── 2018-edition.css ├── ferris.css └── ferris.js /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-18.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | - uses: actions-rs/cargo@v1 18 | with: 19 | command: install 20 | args: mdbook-katex 21 | - name: Setup mdBook 22 | uses: peaceiris/actions-mdbook@v1 23 | with: 24 | # mdbook-version: '0.4.5' 25 | mdbook-version: 'latest' 26 | - run: mdbook build 27 | - name: Deploy 28 | uses: peaceiris/actions-gh-pages@v3 29 | with: 30 | github_token: ${{ secrets.GITHUB_TOKEN }} 31 | publish_dir: ./book -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | .DS_Store 3 | target 4 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 fucking-translation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 清单 2 | 3 | ## rust 4 | 5 | |翻译|转载| 6 | |---|---| 7 | |[用 Rust 学习解析器组合器](./src/lang/rust/01-用Rust学习解析器组合器.md)| [图解 Rust 所有权与生命周期](./src/lang/rust/08-图解Rust所有权与生命周期.md)| 8 | | [用 Rust 编写 LLVM 的玩具前端](./src/lang/rust/02-用Rust编写LLVM的玩具前端.md)| [Rust 中常见的有关生命周期的误解](./src/lang/rust/18-Rust中常见的有关生命周期的误解.md)| 9 | | [使用 nom 解析 url](./src/lang/rust/03-使用nom解析url.md)| [Rust 中的 Arenas](./src/lang/rust/06-Rust中的Arenas.md)| 10 | | [Rust 中异步编程实用介绍](./src/lang/rust/04-Rust中异步编程实用介绍.md)| [Rust 标准库特征指南](./src/lang/rust/10-Rust标准库特征指南.md)| 11 | | [Tokio 内幕-自底向上理解 Rust 的 IO 框架](./src/lang/rust/05-tokio内幕-自底向上理解Rust的异步IO框架.md)| [从 mio 到 coroutine](https://hexilee.me/2018/12/17/rust-async-io/)| 12 | | [Rust 异步执行器](./src/lang/rust/09-Rust异步执行器.md)| [Rust mio 库源码情景分析](https://blog.zongwu233.com/rust-mio-source-scenario-analysis/)| 13 | | [Rust 中的宏:带有示例的教程](./src/lang/rust/11-Rust中的宏:带有示例的教程.md)| [Rust的 Pin 与 Unpin](https://folyd.com/blog/rust-pin-unpin/)| 14 | | [libp2p 教程:使用 Rust 构建 p2p 应用](./src/lang/rust/12-libp2p教程:使用Rust构建p2p应用.md)| [Unsafe Rust 的取舍](https://github.com/RustMagazine/rust_magazine_2021/blob/main/src/chapter_3/Unsafe_Rust_How_and_when_not_to_use_it.md)| 15 | | [使用 GDB 调试 Rust 中的应用](./src/lang/rust/14-使用GDB调试Rust应用.md)|| 16 | | [解释 Rust 中的原子性](./src/lang/rust/15-解释Rust中的原子性.md)|| 17 | | [Rust 和 TUI:在 Rust 中构建命令行界面](./src/lang/rust/16-Rust和TUI:在Rust中构建命令行界面.md)|| 18 | | [在 Android 中运行 Rust](./src/lang/rust/17-在Android中运行Rust.md)|| 19 | | [生命周期型变示例](./src/lang/rust/19-生命周期型变示例.md)|| 20 | | [Rust 如何实现线程安全](./src/lang/rust/20-rust如何实现线程安全.md)|| 21 | | [无需 fork Clippy 就可以编写 Rust lint](./src/lang/rust/22-无需fork_Clippy就可以编写Rust_lints.md) || 22 | | [使用 Mio 编写底层 TCP 服务器](./src/lang/rust/23-使用Mio编写底层TCP服务器.md)|| 23 | | [Rust 中的类型强制转换](./src/lang/rust/25-Rust中的类型强转.md)|| 24 | 25 | ## service mesh 26 | 27 | |翻译|转载| 28 | |---|---| 29 | | [模式:Service Mesh](./src/架构/ServiceMesh/01-ServiceMesh.md)|| 30 | | [xDS 与 gRPC 协议](./src/架构/ServiceMesh/02-xDS与gRPC协议.md)|| 31 | 32 | ## OCI 33 | 34 | |翻译|转载| 35 | |---|---| 36 | |[OCI 规范概述](./src/oci/01-oci-spec-overview.md)|| 37 | 38 | ## 网络协议 39 | 40 | |翻译|转载| 41 | |---|---| 42 | ||[「转」详解 KCP 协议的原理和实现](./src/网络协议/kcp/详解KCP协议的原理和实现.md)| 43 | 44 | ## 分布式事务 45 | 46 | |翻译|转载| 47 | |---|---| 48 | ||[「转」分布式事务/系统之底层原理揭秘](./src/架构/分布式/事务/01-分布式事务_系统之底层原理揭秘.md)| 49 | 50 | 51 | ## 推荐阅读 52 | 53 | - [潘少的技术博客](https://strikefreedom.top/) 54 | - [⭐️ Read Rust](https://readrust.net/) 55 | - [爆米花胡了](https://blog.ideawand.com/) 56 | - [从易到难 Rust 问题解答](https://dtolnay.github.io/rust-quiz) 57 | - [dapr 源码详解](https://github.com/1046102779/daprdocs) 58 | - [Kafka 读书笔记](https://www.cnblogs.com/jixp/category/1308441.html) 59 | - [Kubernetes 内幕](https://github.com/fucking-translation/kubernetes-internals) 60 | - [⭐️ Rust GameDev](https://gamedev.rs/) -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["iamazy"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Blog Translation" 7 | 8 | [output.html] 9 | additional-css = ["theme/ferris.css", "theme/2018-edition.css"] 10 | additional-js = ["theme/ferris.js"] 11 | git-repository-url = "https://github.com/fucking-translation/blog" 12 | 13 | [output.html.playground] 14 | editable = true 15 | copy-js = true 16 | 17 | [output.html.fold] 18 | enable = true 19 | level = 0 20 | 21 | [preprocessor.katex] -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Rust](./lang/rust/summary.md) 4 | - [用 Rust 学习解析器组合器](./lang/rust/01-用Rust学习解析器组合器.md) 5 | - [用 Rust 编写 LLVM 的玩具前端](./lang/rust/02-用Rust编写LLVM的玩具前端.md) 6 | - [使用 nom 解析 url](./lang/rust/03-使用nom解析url.md) 7 | - [Rust 中异步编程实用介绍](./lang/rust/04-Rust中异步编程实用介绍.md) 8 | - [tokio 内幕-自底向上理解 Rust 的异步 IO 框架](./lang/rust/05-tokio内幕-自底向上理解Rust的异步IO框架.md) 9 | - [「转」Rust 中的 Arenas](./lang/rust/06-Rust中的Arenas.md) 10 | - [「转」Rust 异步 IO: 从 mio 到 coroutine](./lang/rust/07-Rust异步IO:从mio到coroutine.md) 11 | - [「转」图解 Rust 所有权与生命周期](./lang/rust/08-图解Rust所有权与生命周期.md) 12 | - [Rust 异步执行器](./lang/rust/09-Rust异步执行器.md) 13 | - [「转」Rust 标准库特征指南](./lang/rust/10-Rust标准库特征指南.md) 14 | - [Rust中的宏: 带有示例的教程](./lang/rust/11-Rust中的宏:带有示例的教程.md) 15 | - [libp2p 教程: 使用 Rust 构建 p2p 应用](./lang/rust/12-libp2p教程:使用Rust构建p2p应用.md) 16 | - [「转」Rust 的 Pin 与 Unpin](./lang/rust/13-Rust的Pin与Unpin.md) 17 | - [使用 GDB 调试 Rust 中的应用](./lang/rust/14-使用GDB调试Rust应用.md) 18 | - [解释 Rust 中的原子性](./lang/rust/15-解释Rust中的原子性.md) 19 | - [Rust 和 TUI:在 Rust 中构建命令行界面](./lang/rust/16-Rust和TUI:在Rust中构建命令行界面.md) 20 | - [在 Android 中运行 Rust](./lang/rust/17-在Android中运行Rust.md) 21 | - [「转」Rust 中常见的有关生命周期的误解](./lang/rust/18-Rust中常见的有关生命周期的误解.md) 22 | - [生命周期型变示例](./lang/rust/19-生命周期型变示例.md) 23 | - [Rust 如何实现线程安全](./lang/rust/20-rust如何实现线程安全.md) 24 | - [无需 fork Clippy 就可以编写 Rust lint](./lang/rust/22-无需fork_Clippy就可以编写Rust_lints.md) 25 | - [使用 Mio 编写底层 TCP 服务器](./lang/rust/23-使用Mio编写底层TCP服务器.md) 26 | - [「转」Unsafe Rust 的取舍](./lang/rust/24-UnsafeRust的取舍.md) 27 | - [Rust 中的类型强制转换](./lang/rust/25-Rust中的类型强转.md) 28 | 29 | 30 | - [标准库](./lang/rust/std/20-std-pin.md) 31 | - [std::pin](./lang/rust/std/20-std-pin.md) 32 | - [std::sync::Condvar](./lang/rust/std/21-std-condvar.md) 33 | 34 | - [Service Mesh](./架构/ServiceMesh/summary.md) 35 | - [模式:Service Mesh](./架构/ServiceMesh/01-ServiceMesh.md) 36 | - [xDS 与 gRPC 协议](./架构/ServiceMesh/02-xDS与gRPC协议.md) 37 | 38 | - [Open Container Initiative](./oci/summary.md) 39 | - [OCI 规范概述](./oci/01-oci-spec-overview.md) 40 | 41 | - [网络协议](./网络协议/summary.md) 42 | - [「转」详解 KCP 协议的原理和实现](./网络协议/kcp/详解KCP协议的原理和实现.md) 43 | 44 | - [分布式](./架构/分布式/summary.md) 45 | - [分布式事务](./架构/分布式/事务/summary.md) 46 | - [分布式事务/系统之底层原理揭秘](./架构/分布式/事务/01-分布式事务_系统之底层原理揭秘.md) 47 | -------------------------------------------------------------------------------- /src/lang/rust/20-rust如何实现线程安全.md: -------------------------------------------------------------------------------- 1 | # Rust 如何实现线程安全 2 | 3 | [原文](https://manishearth.github.io/blog/2015/05/30/how-rust-achieves-thread-safety/) 4 | 5 | 在我迄今为止的每一次演讲中,都会遇到 “Rust 是如何实现线程安全”的提问,我通常只是概述一下,而本文为感兴趣的人提供了更全面的解释。 6 | 7 | 你也可以参阅:[Huon 关于此主题的博客](http://huonw.github.io/blog/2015/02/some-notes-on-send-and-sync/) 8 | 9 | 在[之前的文章](http://manishearth.github.io/blog/2015/05/27/wrapper-types-in-rust-choosing-your-guarantees/)中,我稍微谈到了 [Copy] 特征。标准库中还有其他这样的“标记”特征,本文中与之相关的是 [Send] 和 [Sync] 特征。如果你对像 [RefCell] 和 [Rc] 这样的包装器类型不熟悉,我建议你阅读那篇文章,因为我将在本文中将它们作为示例;但是这里解释的概念在很大程度上是独立的。 10 | 11 | 处于本文的目的,我将线程安全限制为没有数据竞争或跨线程悬垂指针。Rust 的目的不是为了解决竞争条件。然而,有些项目利用类型系统来提供某种形式的额外安全,如 [rust-session](https://github.com/Munksgaard/rust-sessions) 尝试使用会话类型提供协议安全。 12 | 13 | 这些特征是使用被称为“可选的内置特征”自动实现的。举个例子,如果`struct Foo`仅包含 [Sync] 字段,则它也将是 [Sync],除非我们使用`impl !Sync for Foo {}`明确说明不实现此特征。类似的,如果`struct Foo`包含至少一种非 [Sync] 类型,则它也不会是 [Sync],除非它显式指定`unsafe impl Sync for Foo {}`。 14 | 15 | 这意味着,[Send] 类型的 [Sender] 本身就是 [Send],但非 [Send] 类型的 [Sender] 将不是 [Send]。这种模式非常强大。它允许在单线程上下文中使用具有非线程安全数据的通道,而无需单独的“单线程”通道抽象。 16 | 17 | 同时,像 [Rc] 和 [RefCell] 这样包含 [Send]/[Sync] 字段的结构已经明确指出退出其中的一个或多个,因为它们依赖的不变量 (invariants) 在多线程的情况下不成立。 18 | 19 | 实际上可以在编译器之外设计你自己的具有类似线程安全保证的库 - 虽然这些标记特征由编译器特别处理,但它们的工作不需要特殊处理。这里可以使用任意两个可选 (opt-in) 的内置特性。 20 | 21 | [Send] 和 [Sync] 的含义略有不同,但是功能有交织 (intertwined) 的部分。 22 | 23 | [Send] 类型可以在线程之间移动而不会有问题。它回答了“如果某个变量被移动到另一个线程,仍然可以使用吗”的问题。大多数完全拥有其包含数据的对象都符合此要求。值得注意的是,[Rc] 没有(因为它是共享所有权)。另一个例外是 [LocalKey],它确实拥有自己的数据,但对其他线程无效。借用的数据确实有资格被发送,但在大多数情况下,由于稍后会涉及的约束,它不能跨线程发送。 24 | 25 | 即使像 [RefCell] 这样的类型使用非原子引用计数,它也可以在线程之间安全的发送,因为这是所有权的转移 (move)。将 [RefCell] 发送到另一个线程将是一个 move,并且将无法在原来的线程中使用。 26 | 27 | 另一方面,[Sync] 与同步访问有关。它回答了“如果多个线程都试图访问这些数据,它会安全吗?”。[Mutex] 等类型和其他基于`lock/atomic`的类型以及原始类型都实现了这一点。包含指针的结构通常不是 [Sync]。 28 | 29 | [Sync] 有点像 [Send] 的拐杖 (crutch)。它有助于在涉及共享时让其他类型具有 [Send] 特征。例如,`&T`和 [Arc] 仅在内部数据为 [Sync] 时才具有 [Send] 特征(在 [Arc] 的情况下有一个额外的 [Send] 边界)。换句话说,如果`共享/借用 (shared/borrowed)`的数据是同步安全 (synchronous-safe) 的,则可以将具有`共享/借用`所有权的内容发送到另一个线程。 30 | 31 | 由于非原子引用计数,[RefCell] 是 [Send] 但不是 [Sync]。 32 | 33 | 把它们放在一起,所有这些的看门人 (gatekeeper) 是 [thread::spawn()]。它有签名: 34 | 35 | ```rust 36 | pub fn spawn(f: F) -> JoinHandle 37 | where 38 | F: FnOnce() -> T, 39 | F: Send + 'static, 40 | T: Send + 'static 41 | ``` 42 | 43 | 诚然,这令人感到困惑,部分原因是它允许返回一个值,还返回了一个句柄,我们可以从中阻塞线程连接 (thread join)。不过,我们可以为我们的需要创造一个更简单的`spawn`API: 44 | 45 | ```rust 46 | pub fn spawn(f: F) 47 | where 48 | F: FnOnce(), 49 | F: Send + 'static 50 | ``` 51 | 52 | 可以这样调用: 53 | 54 | ```rust 55 | let mut x = vec![1,2,3,4]; 56 | 57 | // `move` instructs the closure to move out of its environment 58 | thread::spawn(move || { 59 | x.push(1); 60 | 61 | }); 62 | 63 | // x is not accessible here since it was moved 64 | ``` 65 | 66 | `spawn()` 将接受一个将被调用一次的可调用对象(通常是一个闭包),并包含 [Send] 和`'static`的数据。这里,`'static`只是意味着闭包中不包含借用的数据。这是前面提到的阻止跨线程共享借用数据的约束。如果没有它,我们将能够将借用的指针发送到一个线程,该线程很容易超过借用时间,从而导致安全问题。 67 | 68 | 这里有一个关于闭包的细微差别 - 闭包可以捕获外部变量,但默认情况下它们是通过引用进行的(因此有`move`关键字)。它们根据捕获子句自动实现 [Send] 和 [Sync]。有关它们内部的更多信息,请参阅 [huon 的博客](http://huonw.github.io/blog/2015/05/finding-closure-in-rust/)。在这种情况下,`x`将被捕获;即作为 [`Vec`] (而不是类似于`&Vec`或其他东西),所以闭包本身可以是 [Send]。如果没有`move`关键字,闭包就不会是`'static`,因为它包含借用的内容。 69 | 70 | 由于闭包继承了其捕获数据的`Send/Sync/'static`,捕获正确类型数据的闭包将满足`F: Send + 'static`边界。 71 | 72 | 此函数允许和不允许的一些示例(对于`x`类型): 73 | 74 | - [`Vec`],[`Box`] 是允许的,因为它们是 [Send] 和`'static`(当内部类型是相同类型时)。 75 | - `&T`是不允许的,因为它不是`'static`的。这很棒,因为借用应该有一个静态已知的生命周期。将借用的指针发送到其他线程可能会导致释放后使用,或者以其他方式破坏别名规则 (aliasing rules)。 76 | - [`Rc`] 不是 [Send],所以是不允许的。我们可能会有其他一些 [`Rc`] 闲置,并最终导致引用计数上的数据竞争。 77 | - `Arc>`是允许的(如果内部类型 [`Vec`] 是 [Send] 和 [Sync]);我们不能在这里造成安全违规。迭代器失效需要可变性,而 [`Arc`]默认不提供。 78 | - `Arc>`是不允许的。`Cell`提供基于复制的内部可变性,并且不是`Sync`(因此`Arc>`不是 [Send])。如果允许这样做,我们可能会遇到较大的结构同时从不同的线程写入的情况,从而导致两者随机混杂,即数据竞争。 79 | - `Arc>`或`Arc>`是允许的(对于`Send T`)。内部类型使用线程安全锁并提供基于锁的内部可变性。它们可以保证在任何时间点只有一个线程正在写入。因此,互斥体 (mutex) 是 [Sync],只要其内部 T 是 [Send] 即可,[Sync] 类型可以与 [Arc] 等包装器安全的共享。从内部类型的角度来看,它一次只能被一个线程访问 ([RwLock] 的情况稍微复杂一点),因此不需要知道所涉及的线程。当涉及这些 [Sync] 类型时,就不会出现数据竞争。 80 | 81 | 如上所述,你实际上可以创建一对非 [Send] 对象的`Sender/Receiver`。这听起来有点违反直觉 (counterintuitive) - 我们不是应该只发送`Send`的值吗?但是 [`Sender`] 仅当`T`为 [Send] 时才为 [Send];所以即使我们可以使用非 [Send] 类型的 [Sender],我们也不能将它发送到另一个线程,因此它不能用于破坏线程安全。 82 | 83 | 还有一种方法可以讲`&T`的 [Send] 用于某些`Sync T`,即 [thread::scoped]。这个函数没有`'static`边界,但它有一个`RAII`保护,它在借用结束之前强制 join。这使得不需要互斥体 (Mutex) 就可以轻松的实现 fork-join 并行性。可悲的是,当这与 [Rc] 循环交互时会出现问题,因此该 API 目前不稳定,将会重新设计。这不是语言设计或 [Send]/[Sync] 设计的问题,而是库中小设计的不一致导致的完美风暴。 84 | 85 | [Copy]: http://doc.rust-lang.org/std/marker/trait.Copy.html 86 | [Send]: http://doc.rust-lang.org/std/marker/trait.Send.html 87 | [Sync]: http://doc.rust-lang.org/std/marker/trait.Sync.html 88 | [Rc]: https://doc.rust-lang.org/std/rc/struct.Rc.html 89 | [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html 90 | [RefCell]: https://doc.rust-lang.org/std/cell/struct.RefCell.html 91 | [Sender]: http://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html 92 | [`Sender`]: http://doc.rust-lang.org/std/sync/mpsc/struct.Sender.html 93 | [LocalKey]: https://doc.rust-lang.org/nightly/std/thread/struct.LocalKey.html 94 | [Mutex]: http://doc.rust-lang.org/std/sync/struct.Mutex.html 95 | [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html 96 | [Arc]: https://doc.rust-lang.org/std/sync/struct.Arc.html 97 | [thread::spawn()]: http://doc.rust-lang.org/std/thread/fn.spawn.html 98 | [`Box`]: http://doc.rust-lang.org/std/boxed/struct.Box.html 99 | [RwLock]: http://doc.rust-lang.org/std/sync/struct.RwLock.html 100 | [Receiver]:http://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html 101 | [thread::scoped]: http://doc.rust-lang.org/std/thread/fn.scoped.html 102 | [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html -------------------------------------------------------------------------------- /src/lang/rust/22-无需fork_Clippy就可以编写Rust_lints.md: -------------------------------------------------------------------------------- 1 | # 无需 fork Clippy 就可以编写 Rust lints 2 | 3 | ![dylint](./img/dylint.jpeg) 4 | 5 | [原文](https://www.trailofbits.com/post/write-rust-lints-without-forking-clippy) 6 | 7 | 本文主要介绍 [Dylint](https://github.com/trailofbits/dylint),它是一个可以从动态库中加载 Rust lints 规则的工具。Dylint 可以让开发人员轻松维护自己的个人 lint 集合。 8 | 9 | 在此之前,编写一个新的 Rust lint 的最简单的方式就是 fork [Clippy](https://github.com/rust-lang/rust-clippy),它是 Rust 事实上的 (de facto) lint 工具。但是这种方式在运行或维护新 lint 时存在缺陷 (drawback)。Dylint 最大程度的减少了这方面的干扰 (distraction),让开发者可以专注于编写 lint。 10 | 11 | 首先,我们将回顾 (go over) Rust linting 的当前状态以及 Clippy 的工作原理。然后,我们将解释 Dylint 是如何改善现状 (quo) 并提供一些关于如何开始使用它的提示。如果你想直接编写 lint,请跳到最后一节。 12 | 13 | ### Rust linting 和 Clippy 14 | 15 | Clippy 等工具利用了 Rust 编译器对 linting 的专用支持。Rust linter 的核心组件(即“驱动程序”)可以链接到对应的库 (rustc_driver) 中。通过这种方式,驱动程序本质上是对 Rust 编译器做了封装。 16 | 17 | 为了运行 linter,环境变量 [RUSTC_WORKSPACE_WRAPPER](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads) 需要指向驱动程序并运行`cargo check`。Cargo 会注意到该环境变量已被赋值并调用该驱动程序,而不是调用 **rustc**。当驱动程序被调用时,它在 Rust 编译器中的 [Config](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html) 结构体中设置了一个 [callback](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints)。该 [callback](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints) 注册了一些 lint,它们将会与 Rust [内置的 lint](https://doc.rust-lang.org/rustc/lints/listing/index.html) 一起运行。 18 | 19 | Clippy 执行一些[检查](https://github.com/rust-lang/rust-clippy/blob/12fce557669a0de230399cf8e6eee4f5307bf87b/src/driver.rs#L329-L338)以确保它已被启用,否则将以上述方式进行工作。(关于 Clippy 架构,请参阅图 1)。尽管它在安装之后人们对它的认识依旧不是那么的清晰,但是 Clippy 实际上有两个二进制文件:一个 Cargo 命令以及一个 **rustc** 驱动。你可以输入以下命令进行验证: 20 | 21 | ```console 22 | which cargo-clippy 23 | which clippy-driver 24 | ``` 25 | 26 |
dylint1
27 |
图 1:Clippy 架构
28 | 29 | 现在假设你想编写自己的 lint。你该怎么办?你需要一个驱动程序来运行它们,而 Clippy 有一个驱动程序,因此 fork Clippy 看起来是一个合理的步骤。但是这个解决方案有一些缺陷,即 (namely) 运行和维护你将开发的 lint。 30 | 31 | 首先,你的 fork 将拥有两个二进制文件的副本,确保它们可以被找到是一件很麻烦 (hassle) 的事情。你必须确保至少 cargo 命令在你的`PATH`中,并且你可能必须将二进制文件重命名,以保证它们不会干扰 Clippy。虽然这些问题不是难以克服 (insurmountable),但你可能会选择尽量避免它们。 32 | 33 | 其次,所有的 lint (包括 Clippy 的 lint) 都是在 [unstable](https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/index.html#note) 编译器 API 之上构建的。一起编译的 lint 必须使用相同版本的 API。为了理解为什么会出现这个问题,我们将参考 [clippy_utils](https://github.com/rust-lang/rust-clippy/tree/master/clippy_utils) - Clippy 作者慷慨地公开的一组实用程序。请注意,**clippy_utils** 使用与 lint 相同的编译器 API,并且同样不提供稳定性保证(参见下文)。 34 | 35 | 假设你已经 fork Clippy,然后你希望添加一个新的 lint。很显然,你希望新的 lint 使用最新版本 **clippy_utils**。但是假设 **clippy_utils** 使用的编译器版本是 B,而你 fork 的 Clippy 使用的编译器版本是 A。然后你将面临一个困境 (dilemma):你应该使用一个旧版本的 **clippy_utils** (使用的 A 版本的编译器)还是将 fork 中所有 lint 更新到 B 版本的编译器?两者都不是理想的选择。 36 | 37 | Dylint 同时解决了这两个问题。首先,它提供了一个 Cargo 命令,使你不必管理多个这样的命令。其次,对于 Dylint,lint 是在一起编译的以生成动态库。因此在上述情况下,你可以简单地将新的 lint 存储在使用B 版本的编译器的新动态库中。你可以根据需要将这个新库与现有库一起使用,如果你愿意的话,可以将现有库升级到更新的库的编译器版本。 38 | 39 | Dylint 提供了与重用中间编译结果相关的额外好处。要理解它,我们需要了解 Dylint 的工作原理。 40 | 41 | ### Dylint 的工作原理 42 | 43 | 和 Clippy 一样,Dylint 提供了一个 cargo 命令。可以指定用户想要加载 lint 的动态库。Dylint 以确保在将控制权移交给 Rust 编译器之前注册 lint 的方式运行`cargo check`。 44 | 45 | 然而,Dylint 的 lint 注册过程比 Clippy 更复杂。Clippy 的所有 lint 都使用相同的编译器版本,因此只需要一个驱动程序。但是 Dylint 用户可以选择从不同编译器版本的库中加载 lint。 46 | 47 | Dylint 按需即时 (on-the-fly) 构建驱动程序来处理此类情况。换句话说,如果用户想要 A 版本的编译器库中加载 lint,并且找不到 A 版本的驱动程序,Dylint 将构建一个新的 A 版本的驱动程序。驱动程序缓存在用户的主目录中,因此仅在必要时重建它们。 48 | 49 |
dylint1
50 |
图 2:Dylint 架构
51 | 52 | 这给我们带来了上面暗指 (alluded to) 的额外好处。Dylint 根据它们使用的编译器版本对库进行分组,使用相同编译器版本的库一起加载,并在它们的 lint 一起运行。这允许在 lint 之间共享中间编译结果(如:符号解析,类型检查,特征求解等)。 53 | 54 | 举个例子,在图 2 中,如果库 U 和 V 都使用了 A 版本的编译器,这两个库将被放到同一个分组中。A 版本编译器的驱动程序将只被调用一次。驱动程序在将控制权移交给 Rust 编译器之前会在库 U 和库 V 中注册 lint。 55 | 56 | 为了理解为什么这种方式更好,可以做如下思考。假设 lint 由编译器驱动程序(而不是动态库)直接存储,并回顾一下驱动程序本质上是 Rust 编译器的封装。因此,如果在使用相同编译器版本的两个编译器的驱动程序中有两个 lint,则在同一代码上运行这两个驱动程序将等同于该代码进行了两次编译。通过将 lint 存储在动态库中并按照编译器版本对它们进行分组,Dylint 避免了这些低效的操作。 57 | 58 | ### 应用:特定项目的 lint 59 | 60 | 你是否知道 Clippy 包含 lint,其唯一目的是对 Clippy 的代码进行 lint?[这是真的](https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/internal_lints.rs)。Clippy 包含用于检查的 lint,例如:每个 lint 都有一个关联的 **LintPass**,它使用某些 Clippy 封装函数而不是它们自己封装的函数,并且每个 lint 都有一个非默认的描述。将这些 lint 应用于 Clippy 以外的代码是没有意义的。但是没有规定所有 lint 都必须是通用的,Clippy 就利用了这一点。 61 | 62 | Dylint 包含 lint 的主要目的是对 Dylint 的代码进行 lint。例如:在开发 Dylint 时,我们发现自己编写了如下代码: 63 | 64 | ```rust 65 | let rustup_toolchain = std::env::var("RUSTUP_TOOLCHAIN")?; 66 | ... 67 | std::env::remove_var("RUSTUP_TOOLCHAIN"); 68 | ``` 69 | 70 | 这么做不是很好。为什么?因为我们对字符串字面量进行 fat-fingered 只是时间问题。 71 | 72 | ```rust 73 | std::env::remove_var("RUSTUP_TOOLCHIAN"); // Oops 74 | ``` 75 | 76 | 更好的方法是使用常量而不是字符串字面量,就如下代码所示: 77 | 78 | ```rust 79 | const RUSTUP_TOOLCHAIN: &str = "RUSTUP_TOOLCHAIN"; 80 | ... 81 | std::env::remove_var(RUSTUP_TOOLCHAIN); 82 | ``` 83 | 84 | 因此当使用 Dylint 时,我们编写了一个 lint 来检查这种不适当的做法并提出适当的建议。我们将该 lint 应用到 Dylint 源码。lint 称其为 [env_literal](https://github.com/trailofbits/dylint/tree/master/examples/env_literal),其当前的核心实现如下: 85 | 86 | ```rust 87 | impl<'tcx> LateLintPass<'tcx> for EnvLiteral { 88 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { 89 | if_chain! { 90 | if let ExprKind::Call(callee, args) = expr.kind; 91 | if is_expr_path_def_path(cx, callee, &REMOVE_VAR) 92 | || is_expr_path_def_path(cx, callee, &SET_VAR) 93 | || is_expr_path_def_path(cx, callee, &VAR); 94 | if !args.is_empty(); 95 | if let ExprKind::Lit(lit) = &args[0].kind; 96 | if let LitKind::Str(symbol, _) = lit.node; 97 | let ident = symbol.to_ident_string(); 98 | if is_upper_snake_case(&ident); 99 | then { 100 | span_lint_and_help( 101 | cx, 102 | ENV_LITERAL, 103 | args[0].span, 104 | "referring to an environment variable with a string literal is error prone", 105 | None, 106 | &format!("define a constant `{}` and use that instead", ident), 107 | ); 108 | } 109 | } 110 | } 111 | } 112 | ``` 113 | 114 | 以下是它可以产生的警告示例: 115 | 116 | ```console 117 | warning: referring to an environment variable with a string literal is error prone 118 | --> src/main.rs:2:27 119 | | 120 | 2 | let _ = std::env::var("RUSTFLAGS"); 121 | | ^^^^^^^^^^^ 122 | | 123 | = note: `#[warn(env_literal)]` on by default 124 | = help: define a constant `RUSTFLAGS` and use that instead 125 | ``` 126 | 127 | 回顾之前所说的,编译器以及 **clippy_utils** 都没有为它的 API 提供稳定性保证,因此 **env_literal** 的未来版本可能看起来有点不同。(实际上,当本文还在撰写的过程中,**clippy_utils** 某个 API 的变更就已经导致 **env_literal** 某个实现发生改变!)。当前版本的 **env_literal** 总是可以在 Dylint 仓库中的 [examples](https://github.com/trailofbits/dylint/tree/master/examples) 目录下找到。 128 | 129 | 但是 Clippy “自我 lint” 的方式与 Dylint 略有不同。Clippy 的内部 lint 被编译成启用了特定功能的 Clippy 版本。但是对于 Dylint,**env_literal** lint 被编译成了一个动态库。因此,**env_literal** 不是 Dylint 的一部分。它本质上是输入。 130 | 131 | 为什么这很重要?因为你可以为你的项目编写自定义 lint 并使用 Dylint 来运行它们,就像 Dylint 运行自己的 lint 一样。在 Dylint 仓库中 Dylint 运行的 lint 来源没有任何重要意义。Dylint 可以很轻易的在你的仓库中运行该仓库的 lint。 132 | 133 | 最重要的是 (The bottom line is this):如果你发现不喜欢自己编写的代码,并且可以使用 lint 检测该代码,Dylint 可以帮助你清除该代码并防止重新引入。 134 | 135 | ### 开始 linting 136 | 137 | 使用以下命令安装 Dylint: 138 | 139 | ```console 140 | cargo install cargo-dylint 141 | ``` 142 | 143 | 我们还推荐安装 [dylint-link](https://github.com/trailofbits/dylint/tree/master/dylint-link) 来处理超链接: 144 | 145 | ```console 146 | cargo install dylint-link 147 | ``` 148 | 149 | 编写 Dylint 库的最简单的方式是 fork [dylint-template](https://github.com/trailofbits/dylint-template) 仓库。该仓库直接生成了一个可加载的库。你可以按如下方法进行验证: 150 | 151 | ```console 152 | git clone https://github.com/trailofbits/dylint-template 153 | cd dylint-template 154 | cargo build 155 | cargo dylint fill_me_in --list 156 | ``` 157 | 158 | 你只需实现 [LateLintPass](https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html) 特征并容纳要求填写的符号即可。 159 | 160 | 以下资源对你编写 lint 将很有帮助: 161 | 162 | - [添加一个新的 lint](https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md) (针对 Clippy 但依然很有用) 163 | - [编写 lint 的常用工具](https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md) 164 | - [rustc_hir 文档](https://doc.rust-lang.org/stable/nightly-rustc/rustc_hir/index.html) 165 | 166 | 也可以考虑使用上面提到的 [clippy_utils](https://github.com/rust-lang/rust-clippy/tree/master/clippy_utils)。它包含许多底层任务的功能,如查找符号和打印诊断信息,可以让编写 lint 变得更加容易。 167 | 168 | 我们十分感谢 Clippy 作者将 **clippy_utils** 开放在 Rust 169 | 社区。我们也十分感谢 [Philipp Krones](https://github.com/flip1995) 在本文的早期版本中提供了有用的建议。 -------------------------------------------------------------------------------- /src/lang/rust/23-使用Mio编写底层TCP服务器.md: -------------------------------------------------------------------------------- 1 | # 使用 Mio 编写底层 TCP 服务器 2 | 3 | [原文](https://sergey-melnychuk.github.io/2019/08/01/rust-mio-tcp-server/) 4 | 5 | 是时候认识 (acquainted) 一下 [Metal IO](https://github.com/tokio-rs/mio) 了,它是在 epoll/kqueue 之上用 Rust 编写的跨平台抽象。 6 | 7 | 在本文中,我们将会展示并解释如何编写一个单线程异步 TCP 服务器,用它模拟 HTTP 协议,然后使用`ab/wrk`对其进行 benchmark。结果将会令人印象深刻。 8 | 9 | ## Getting started 10 | 11 | 我使用的是`mio = "0.6"`。 12 | 13 | 首先,需要 TCP listener。 14 | 15 | ```rust 16 | let address = "0.0.0.0:8080"; 17 | let listener = TcpListener::bind(&address.parse().unwrap()).unwrap(); 18 | ``` 19 | 20 | 然后创建`Poll`对象并将 listener 注册到`Token(0)`中用于可读事件 (readable events),由 edge (而不是 level) 激活。更多内容请参阅 [edge vs level](https://en.wikipedia.org/wiki/Epoll#Triggering_modes)。 21 | 22 | ```rust 23 | let poll = Poll::new().unwrap(); 24 | poll.register( 25 | &listener, 26 | Token(0), 27 | Ready::readable(), 28 | PollOpt::edge()).unwrap(); 29 | ``` 30 | 31 | 下一步我们要做的就是根据给定的容量创建`Events`对象以及主循环(本例中是无限循环)。在循环中,事件被一一轮询并处理。 32 | 33 | ```rust 34 | let mut events = Events::with_capacity(1024); 35 | loop { 36 | poll.poll(&mut events, None).unwrap(); 37 | for event in &events { 38 | // handle the event 39 | } 40 | } 41 | ``` 42 | 43 | ## Accepting connections (and dropping them) 44 | 45 | 事件可以是以下其中一种: 46 | 47 | - listener 上的可读事件意味着有要准备接入的连接。 48 | - 已连接的 socket 上的事件 49 | - readable - socket 有数据可以读取 50 | - writable - socket 已经写数据就绪 51 | 52 | listener 以及 socket 事件可以被 token 区分,对于 listener token 它总是 0,因为它已在`Poll`中注册。 53 | 54 | 以下代码是最简单的事件处理方式,在循环中接受所有的传入连接,并且对于每个连接 - 只需删除 socket。它将会关闭连接。在你的服务中[抛弃协议](https://en.wikipedia.org/wiki/Discard_Protocol)。 55 | 56 | ```rust 57 | // handle the event 58 | match event.token() { 59 | Token(0) => { 60 | loop { 61 | match listener.accept() { 62 | Ok((socket, address)) => { 63 | // What to do with the connection? 64 | // One option is to simply drop it! 65 | println!("Got connection from {}", address); 66 | }, 67 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => 68 | // No more connections ready to be accepted 69 | break, 70 | Err(e) => 71 | panic!("Unexpected error: {}", e) 72 | } 73 | } 74 | }, 75 | _ => () // Ignore all other tokens 76 | } 77 | ``` 78 | 79 | listener 的`.accept()`方法返回`std::io::Result<(TcpStream, SocketAddr)>`(见 [accept](https://docs.rs/mio/0.5.1/mio/tcp/struct.TcpListener.html#method.accept)),因此我需要匹配并处理成功的响应或者错误。这里有一个特定的错误类型 [io::ErrorKind::WouldBlock](https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.WouldBlock),它表示“我将等待(阻塞)以取得任何进展”。这是非阻塞 (non-blocking) 行为的本质 - 关键是不要阻塞(而是返回相应的错误)!遇到此类错误时,意味着此时没有更多的传入连接等待接入,因此循环中断,并处理下一个事件。 80 | 81 | 现在如果我运行服务器并尝试和它建立连接,我可以看到正在抛弃协议!是不是很神奇? 82 | 83 | ```console 84 | $ nc 127.0.0.1 8080 85 | $ 86 | ``` 87 | 88 | ## Registering connections for events 89 | 90 | 接着说下一个事件。为了发生下一个事件,首先必须使用`Poll`注册 token-socket 对。在底层 (under the hook),`Poll`将会跟踪哪一个 token 对应哪一个 socket,但是客户端代码只能访问 token。这意味着如果服务器打算与客户端进行实际通信(我很确信大多数服务器都这样做),就必须以某种方式存储 token-socket 对。在本例中,我使用了简单的`HashMap`,但是使用 [slab](https://docs.rs/slab/0.4.2/slab/) 可能会更加高效。 91 | 92 | token 只是`usize`的一个封装器,因此简单的计数器就足以提供递增的 token 序列。一旦使用相应的 token 注册了 socket,它就会被插入到`HashMap`中。 93 | 94 | ```rust 95 | let mut counter: usize = 0; 96 | let mut sockets: HashMap = HashMap::new(); 97 | 98 | // handle the event 99 | match event.token() { 100 | Token(0) => { 101 | loop { 102 | match listener.accept() { 103 | Ok((socket, _)) => { 104 | counter += 1; 105 | let token = Token(counter); 106 | 107 | // Register for readable events 108 | poll.register(&socket, token 109 | Ready::readable(), 110 | PollOpt::edge()).unwrap(); 111 | 112 | sockets.insert(token, socket); 113 | }, 114 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => 115 | // No more connections ready to be accepted 116 | break, 117 | Err(e) => 118 | panic!("Unexpected error: {}", e) 119 | } 120 | } 121 | }, 122 | token if event.readiness().is_readable() => { 123 | // Socket associated with token is ready for reading data from it 124 | } 125 | } 126 | ``` 127 | 128 | ## Reading data from client 129 | 130 | 当给定 token 发生可读事件时,意味着数据在相应的 socket 中读就绪。我将只使用字节数组作为读取数据的缓冲区。 131 | 132 | 在循环中执行读取操作,直到返回已知的`WouldBlock`错误。每次调用`read`将返回(如果成功的话)实际读取的字节数,当读取的字节数为 0 时 - [意味着](https://doc.rust-lang.org/nightly/std/io/trait.Read.html#tymethod.read)客户端已经断开连接,此后保持 socket (或继续循环读取)没有意义。 133 | 134 | ```rust 135 | // Fixed size buffer for reading/writing to/from sockets 136 | let mut buffer = [0 as u8; 1024]; 137 | ... 138 | token if event.readiness().is_readable() => { 139 | loop { 140 | let read = sockets.get_mut(token).unwrap().read(&mut buffer); 141 | match read { 142 | Ok(0) => { 143 | // Successful read of zero bytes means connection is closed 144 | sockets.remove(token); 145 | break; 146 | }, 147 | Ok(len) => { 148 | // Now do something with &buffer[0..len] 149 | println!("Read {} bytes for token {}", len, token.0); 150 | }, 151 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break, 152 | Err(e) => panic!("Unexpected error: {}", e) 153 | } 154 | } 155 | } 156 | ... 157 | ``` 158 | 159 | ## Writing data to the client 160 | 161 | 对于接收可写事件的 token,它必须先在`Poll`中注册。`oneshot`选项对于安排可写事件可能很有用,该选项确保感兴趣的 (interest) 事件只被触发一次。 162 | 163 | ```rust 164 | poll.register(&socket, token 165 | Ready::writable(), 166 | PollOpt::edge() | PollOpt::oneshot()).unwrap(); 167 | ``` 168 | 169 | 向客户端 socket 写入数据与之类似,也是通过缓冲区完成的,但是不需要显式循环,因为已经有一个[方法](https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all)在执行循环:`write_all()`。 170 | 171 | 如果我想让协议返回接收到的字节数,我将需要写入的实际字节数(`HashMap`将会做这件事),在发生可读事件时计算字节数,然后安排一次可写事件,以及何时发生可写事件 - 然后发送响应并断开连接。 172 | 173 | ```rust 174 | let mut response: HashMap = HashMap::new(); 175 | ... 176 | token if event.readiness().is_readable() => { 177 | let mut bytes_read: usize = 0; 178 | loop { 179 | ... // sum up number of bytes received 180 | } 181 | response.insert(token, bytes_read); 182 | // re-register for one-shot writable event 183 | } 184 | ... 185 | token if event.readiness().is_writable() => { 186 | let n_bytes = response[&token]; 187 | let message = format!("Received {} bytes\n", n_bytes); 188 | sockets.get_mut(&token).unwrap().write_all(message.as_bytes()).unwrap(); 189 | response.remove(&token); 190 | sockets.remove(&token); // Drop the connection 191 | }, 192 | ``` 193 | 194 | ## What happens between reading and writing data? 195 | 196 | 此时我已经从 socket 上读取了数据,并且将数据写入 socket 中。但是写入事件永远也不会发生,因为没有为可写事件注册 token! 197 | 198 | 我应该什么时候为可写事件注册 token?好吧,当它有东西要写入的时候(进行注册)!听起来很简单,不是吗?在实践中,这意味着要真正实现一些协议了。 199 | 200 | ## How do I implement a protocol? 201 | 202 | 我只想发回文本(或 JSON),而 [TCP](https://ru.wikipedia.org/wiki/Transmission_Control_Protocol) 是一种[协议](https://en.wikipedia.org/wiki/Communication_protocol),一种传输级的传输控制协议。TCP 关心接收方以发送方发送的确切顺序来接收确切数量的字节!所以在传输级别,我必须处理两个字节流:一个从客户端到服务端,另一个直接返回。 203 | 204 | 与服务器打交道时应用层协议会很有用(如 HTTP)。应用层协议可以定义实体,如`request` - 服务器从客户端接收,以及`response` - 客户端从服务器接收回来。 205 | 206 | 值得一提的是,正确实现 HTTP 并不像听起来那么容易。但是已经有现成的 HTTP 库可供使用(如 [hyper](https://github.com/hyperium/hyper))。在这里,我不会为如何实现 HTTP 而烦恼,我要做的是让我的服务器表现的好像它真的理解 GET 请求,但总会用包含 6 个字节的响应来应答这样的请求:`b"hello \n"`。 207 | 208 | ## Mocking HTTP 209 | 210 | 对于本文而言,mock HTTP 已经绰绰有余。我将把 HTTP 请求头与请求体(如果有的话)用 4 个字节`b"\r\n\r\n"`进行分割。因此,如果我跟踪当前客户端发送的内容,并且在任何时候那里都有 4 个字节,我就可以使用预定义的 HTTP 响应进行应答: 211 | 212 | ```plain 213 | HTTP/1.1 200 OK 214 | Content-Type: text/html 215 | Connection: keep-alive 216 | Content-Length: 6 217 | 218 | hello 219 | ``` 220 | 221 | `HashMap`就已经足够用于跟踪所有接收到的字节。 222 | 223 | ```rust 224 | let mut requests: HashMap> = HashMap::new(); 225 | ``` 226 | 227 | 一旦读取结束,就需要检查请求是否已就绪: 228 | 229 | ```rust 230 | fn is_double_crnl(window: &[u8]) -> bool { /* trivial */ } 231 | 232 | let ready = requests.get(&token).unwrap() 233 | .windows(4) 234 | .find(|window| is_double_crnl(*window)) 235 | .is_some(); 236 | ``` 237 | 238 | 如果已就绪,则可以安排一些数据写入! 239 | 240 | ```rust 241 | if ready { 242 | let socket = sockets.get(&token).unwrap(); 243 | poll.reregister( 244 | socket, 245 | token, 246 | Ready::writable(), 247 | PollOpt::edge() | PollOpt::oneshot()).unwrap(); 248 | } 249 | ``` 250 | 251 | 写入完成之后,重要的是要保持连接打开,并重新注册 socket 以再次读取。 252 | 253 | ```rust 254 | poll.reregister( 255 | sockets.get(&token).unwrap(), 256 | token, 257 | Ready::readable(), 258 | PollOpt::edge()).unwrap(); 259 | ``` 260 | 261 | 服务器已就绪! 262 | 263 | ```console 264 | $ curl localhost:8080 265 | hello 266 | ``` 267 | 268 | 好戏开始了 - 让我们看看这个单线程服务器表现如何。我将会使用常用的工具:`ab`和`wrk`。 269 | 270 | - `ab`需要使用`-k`选项以使用`keep-alive`并重用已有连接。 271 | - `wrk2`实际与`wrk`用法相同,因此需要`--rate`参数。 272 | - `ab/wrk`运行在不同的 VM 上而不是在服务器上(但是在相同的 region 中)。 273 | 274 | 以下是我在某个云提供商的实例`n1-standard-8 (8 vCPUs, 30 GB memory)`上尝试对服务器进行 benchmark 时得到的数字: 275 | 276 | ```console 277 | $ ab -n 1000000 -c 128 -k http://instance-1:8080/ 278 | 279 | Requests per second: 105838.76 [#/sec] (mean) 280 | Transfer rate: 9095.52 [Kbytes/sec] received 281 | ``` 282 | 283 | ```console 284 | $ wrk -d 60s -t 8 -c 128 --rate 150k http://instance-1:8080/ 285 | 286 | Requests/sec: 120596.75 287 | Transfer/sec: 10.12MB 288 | ``` 289 | 290 | 对于单线程来说,105k 与 120k 的 rps 不算太差。 291 | 292 | 当然,这次可以当作是作弊,但只要涉及真实网络(即使在同一区域内),这就是负载下的真实服务器,这可能(或多或少)是使用单线程完成此网络速度的重要底线。 293 | 294 | 完成可运行的代码地址是:[github](https://github.com/sergey-melnychuk/mio-tcp-server),每一个 pull-request 由一个逻辑章节组成: 295 | 296 | - 初始化项目:[PR#1](https://github.com/sergey-melnychuk/mio-tcp-server/pull/1) 297 | - accept & discard: [PR#2](https://github.com/sergey-melnychuk/mio-tcp-server/pull/2) 298 | - read from socket:[PR#3](https://github.com/sergey-melnychuk/mio-tcp-server/pull/3) 299 | - writing to socket:[PR#4](https://github.com/sergey-melnychuk/mio-tcp-server/pull/4) 300 | - mocking HTTP:[PR#5](https://github.com/sergey-melnychuk/mio-tcp-server/pull/5) 301 | 302 | ## Where to go from here 303 | 304 | 扩展到多线程:从[这里](https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/)开始。 -------------------------------------------------------------------------------- /src/lang/rust/26-为什么Rust需要Pin和Unpin.md: -------------------------------------------------------------------------------- 1 | # 为什么 Rust 需要 Pin 和 Unpin 2 | 3 | [原文](https://blog.adamchalmers.com/pin-unpin/) 4 | 5 | 使用 Rust 异步框架通常很简单。只需要编写正常的 Rust 代码,并添加`async`或`.await`即可。但是如果要编写你自己的异步框架可能会比较困难。当我第一次尝试时,被神秘 (arcane) 又深奥 (esoteric) 的语法弄的十分迷惑(如`T: ?Unpin`和`Pin<&mut Self>`)。我在之前从没见过这些类型,我也不知道它们起了什么作用。在本文中,我们将重点介绍关于这方面的内容,我们将会讨论: 6 | 7 | - 什么是 Future 8 | - 什么是自引用 (self-referential) 类型 9 | - 为什么它们是 unsafe 的 10 | - `Pin/Unpin`是如何让它们变成 safe 的 11 | - 使用`Pin/Unpin`编写棘手的嵌套 future 12 | 13 | ## 什么是 Future? 14 | 15 | 几年前,我需要编写一些代码以收集某些函数运行指标(如执行时间)。我想要编写一个如下所示的`TimedWrapper`类型: 16 | 17 | ```rust 18 | // Some async function, e.g. polling a URL with [https://docs.rs/reqwest] 19 | // Remember, Rust functions do nothing until you .await them, so this isn't 20 | // actually making a HTTP request yet. 21 | let async_fn = reqwest::get("http://adamchalmers.com"); 22 | 23 | // Wrap the async function in my hypothetical wrapper. 24 | let timed_async_fn = TimedWrapper::new(async_fn); 25 | 26 | // Call the async function, which will send a HTTP request and time it. 27 | let (resp, time) = timed_async_fn.await; 28 | println!("Got a HTTP {} in {}ms", resp.unwrap().status(), time.as_millis()) 29 | ``` 30 | 31 | 这个接口十分简单且便于团队中其他成员使用。让我们试着实现它吧!Rust 中的异步函数其实就是返回 [Future](https://doc.rust-lang.org/stable/std/future/trait.Future.html) 的函数。`Future`特征十分简单。它表示: 32 | 33 | - 可以被轮询 34 | - 当它被轮训时,它可能返回`Pending`或`Ready` 35 | - 当它返回`Pending`时,应该在之后再次对其轮训 36 | - 当它返回`Ready`时,它会携带一个值。 37 | 38 | 下面是一个`Future`的简单实现。 39 | 40 | ```rust 41 | use std::{future::Future, pin::Pin, task::Context} 42 | 43 | /// A future which returns a random number when it resolves. 44 | #[derive(Default)] 45 | struct RandFuture; 46 | 47 | impl Future for RandFuture { 48 | // Every future has to specify what type of value it returns when it resolves. 49 | // This particular future will return a u16. 50 | type Output = u16; 51 | 52 | // The `Future` trait has only one method, named "poll". 53 | fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll { 54 | Poll::ready(rand::random()) 55 | } 56 | } 57 | ``` 58 | 59 | 不是很难!我觉得我们已经准备好实现`TimedWrapper`了。 60 | 61 | ## 尝试并使用嵌套 Future 失败 62 | 63 | 定义`TimedWrapper`类型 64 | 65 | ```rust 66 | pub struct TimedWrapper { 67 | start: Option, 68 | future: Fut, 69 | } 70 | ``` -------------------------------------------------------------------------------- /src/lang/rust/code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "rust-p2p-example", 4 | "async-io-example", 5 | "rust-gdb-example", 6 | "telnet-chat" 7 | ] 8 | -------------------------------------------------------------------------------- /src/lang/rust/code/async-io-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-io-example" 3 | version = "0.1.0" 4 | authors = ["iamazy <1448588084@qq.com>"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | mio = { version = "0.7", features = ["net", "os-poll"]} 11 | failure = "0.1" 12 | 13 | [dev-dependencies] 14 | env_logger = "0.8" -------------------------------------------------------------------------------- /src/lang/rust/code/async-io-example/examples/tcp.rs: -------------------------------------------------------------------------------- 1 | use mio::{Token, Poll, Events, Interest, Registry}; 2 | use mio::net::{TcpListener, TcpStream}; 3 | use std::collections::HashMap; 4 | use mio::event::Event; 5 | use std::io::{Write, Read}; 6 | use std::str::from_utf8; 7 | 8 | const SERVER: Token = Token(0); 9 | const DATA: &[u8] = b"Hello World!\n"; 10 | 11 | fn main() -> std::io::Result<()> { 12 | 13 | env_logger::init(); 14 | 15 | // Create a poll instance 16 | let mut poll = Poll::new()?; 17 | 18 | let mut events = Events::with_capacity(128); 19 | 20 | // Setup the TCP server socket 21 | let addr = "127.0.0.1:9000".parse().unwrap(); 22 | let mut server = TcpListener::bind(addr)?; 23 | 24 | // Register the server with poll we can receive events for it 25 | poll.registry() 26 | .register(&mut server, SERVER, Interest::READABLE)?; 27 | 28 | // Map of `Token` -> `TcpStream` 29 | let mut connections = HashMap::new(); 30 | 31 | // Unique token for each incoming connection 32 | let mut unique_token = Token(SERVER.0 + 1); 33 | 34 | println!("You can connect to the server using `nc`:"); 35 | println!("$ nc 127.0.0.1 9000"); 36 | println!("You'll see our welcome message and anything you type we'll be printed here."); 37 | 38 | loop { 39 | poll.poll(&mut events, None)?; 40 | 41 | for event in events.iter() { 42 | match event.token() { 43 | SERVER => loop { 44 | // Receive an event for the TCP server socket, which indicates we can 45 | // accept an connection 46 | let (mut connection, address) = match server.accept() { 47 | Ok((connection, address)) => (connection, address), 48 | // If we got a `WouldBlock` error we know our listener has no more incoming 49 | // connections queued, so we can return to polling and wait for some more. 50 | Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { 51 | break; 52 | } 53 | Err(e) => { 54 | // If it was any other kind of error, something went wrong and we terminate 55 | // with an error 56 | return Err(e); 57 | } 58 | }; 59 | 60 | println!("Accepted connection from: {}", address); 61 | 62 | let token = next(&mut unique_token); 63 | poll.registry().register( 64 | &mut connection, 65 | token, 66 | Interest::READABLE.add(Interest::WRITABLE), 67 | )?; 68 | 69 | connections.insert(token, connection); 70 | }, 71 | token => { 72 | // Maybe received an event for a TCP connection 73 | let done = if let Some(connection) = connections.get_mut(&token) { 74 | handle_connection_event(poll.registry(), connection, event)? 75 | } else { 76 | // Sporadic events happen, we can safely ignore them. 77 | false 78 | }; 79 | 80 | if done { 81 | connections.remove(&token); 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | } 89 | 90 | fn next(current: &mut Token) -> Token { 91 | let next = current.0; 92 | current.0 += 1; 93 | Token(next) 94 | } 95 | 96 | fn handle_connection_event( 97 | registry: &Registry, 98 | connection: &mut TcpStream, 99 | event: &Event 100 | ) -> std::io::Result { 101 | if event.is_writable() { 102 | // We can (maybe) write to the connection 103 | match connection.write(DATA) { 104 | // We want to write the entire `DATA` buffer in a single go. If we write 105 | // less we'll return a short write error (same as `std::io::Write::write_all` does.) 106 | Ok(n) if n < DATA.len() => return Err(std::io::ErrorKind::WriteZero.into()), 107 | Ok(_) => { 108 | // After we've written something we'll reregister the connection 109 | // to only respond to readable events 110 | registry.reregister(connection, event.token(), Interest::READABLE)? 111 | } 112 | // Would block "errors" are the OS's way of saying that the connection 113 | // is not actually ready to perform this I/O operation. 114 | Err(ref err) if would_block(err) => {} 115 | // Got interrupted (how rude!), we'll try again. 116 | Err(ref err) if interrupted(err) => { 117 | return handle_connection_event(registry, connection, event) 118 | } 119 | // Other errors we'll consider fatal 120 | Err(err) => return Err(err) 121 | } 122 | } 123 | 124 | if event.is_readable() { 125 | let mut connection_closed = false; 126 | let mut received_data = vec![0; 4096]; 127 | let mut bytes_read = 0; 128 | // We can (maybe) read from the connection 129 | loop { 130 | match connection.read(&mut received_data[bytes_read..]) { 131 | Ok(0) => { 132 | // Reading 0 bytes means the other side has closed the connection 133 | // or is done writing, the so are we. 134 | connection_closed = true; 135 | break; 136 | } 137 | Ok(n) => { 138 | bytes_read += n; 139 | if bytes_read == received_data.len() { 140 | received_data.resize(received_data.len() + 1024, 0); 141 | } 142 | } 143 | // Would block "errors" are the OS's way of saying that the 144 | // connection is not actually ready to perform this I/O operation 145 | Err(ref err) if would_block(err) => break, 146 | Err(ref err) if interrupted(err) => continue, 147 | // Other errors we'll consider fatal 148 | Err(err) => return Err(err) 149 | } 150 | } 151 | 152 | if bytes_read != 0 { 153 | let received_data = &received_data[..bytes_read]; 154 | if let Ok(str_buf) = from_utf8(received_data) { 155 | println!("Received data: {}", str_buf.trim_end()); 156 | } else { 157 | println!("Received (none UTF-8) data: {:?}", received_data); 158 | } 159 | } 160 | 161 | if connection_closed { 162 | println!("Connection closed"); 163 | return Ok(true); 164 | } 165 | } 166 | Ok(false) 167 | } 168 | 169 | 170 | fn would_block(err: &std::io::Error) -> bool { 171 | err.kind() == std::io::ErrorKind::WouldBlock 172 | } 173 | 174 | fn interrupted(err: &std::io::Error) -> bool { 175 | err.kind() == std::io::ErrorKind::Interrupted 176 | } -------------------------------------------------------------------------------- /src/lang/rust/code/async-io-example/examples/tcp1.rs: -------------------------------------------------------------------------------- 1 | use mio::{Token, Poll, Interest, Events}; 2 | use failure::Error; 3 | use mio::net::{TcpListener, TcpStream}; 4 | use std::time::{Instant, Duration}; 5 | use std::io::{Write, Read}; 6 | 7 | const SERVER_ACCEPT: Token = Token(0); 8 | const SERVER: Token = Token(1); 9 | const CLIENT: Token = Token(2); 10 | const SERVER_HELLO: &[u8] = b"PING"; 11 | const CLIENT_HELLO: &[u8] = b"PONG"; 12 | 13 | fn main() -> Result<(), Error> { 14 | let addr = "127.0.0.1:9000".parse().unwrap(); 15 | 16 | // Setup the server socket 17 | let mut server = TcpListener::bind(addr)?; 18 | 19 | // Create a poll instance 20 | let mut poll = Poll::new()?; 21 | 22 | // Start listening for incoming connections 23 | poll.registry() 24 | .register(&mut server, SERVER_ACCEPT, Interest::READABLE)?; 25 | 26 | // Setup the client socket 27 | let mut client = TcpStream::connect(addr)?; 28 | let mut server_handler = None; 29 | 30 | poll.registry() 31 | .register(&mut client, CLIENT, Interest::READABLE.add(Interest::WRITABLE))?; 32 | 33 | let mut events = Events::with_capacity(1024); 34 | 35 | let start = Instant::now(); 36 | let timeout = Duration::from_millis(10); 37 | 'top: loop { 38 | poll.poll(&mut events, None)?; 39 | for event in events.iter() { 40 | if start.elapsed() >= timeout { 41 | break 'top 42 | } 43 | 44 | match event.token() { 45 | SERVER_ACCEPT => { 46 | let (mut handler, addr) = server.accept()?; 47 | println!("accept from addr: {}", &addr); 48 | poll.registry() 49 | .register(&mut handler, SERVER, Interest::READABLE.add(Interest::WRITABLE))?; 50 | server_handler = Some(handler); 51 | } 52 | SERVER => { 53 | if event.is_writable() { 54 | if let Some(ref mut handler) = &mut server_handler { 55 | match handler.write(SERVER_HELLO) { 56 | Ok(_) => { 57 | println!("server wrote"); 58 | } 59 | Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => continue, 60 | err => { 61 | err?; 62 | } 63 | } 64 | } 65 | } 66 | if event.is_readable() { 67 | let mut hello = [0; 4]; 68 | if let Some(ref mut handler) = &mut server_handler { 69 | match handler.read_exact(&mut hello) { 70 | Ok(_) => { 71 | assert_eq!(CLIENT_HELLO, &hello); 72 | println!("server received"); 73 | } 74 | Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => continue, 75 | err => { 76 | err?; 77 | } 78 | } 79 | } 80 | } 81 | }, 82 | CLIENT => { 83 | if event.is_writable() { 84 | match client.write(CLIENT_HELLO) { 85 | Ok(_) => { 86 | println!("client wrote"); 87 | } 88 | Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => continue, 89 | err => { 90 | err?; 91 | }, 92 | } 93 | } 94 | if event.is_readable() { 95 | let mut hello = [0; 4]; 96 | match client.read_exact(&mut hello) { 97 | Ok(_) => { 98 | assert_eq!(SERVER_HELLO, &hello); 99 | println!("client received"); 100 | } 101 | Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => continue, 102 | err => { 103 | err?; 104 | } 105 | } 106 | } 107 | }, 108 | _ => unreachable!() 109 | } 110 | } 111 | }; 112 | Ok(()) 113 | } -------------------------------------------------------------------------------- /src/lang/rust/code/async-io-example/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | #[test] 4 | fn it_works() { 5 | assert_eq!(2 + 2, 4); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/lang/rust/code/rust-gdb-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-gdb-example" 3 | version = "0.1.0" 4 | authors = ["iamazy <1448588084@qq.com>"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | tokio = { version = "1.1", features=["full"] } -------------------------------------------------------------------------------- /src/lang/rust/code/rust-gdb-example/examples/basic.rs: -------------------------------------------------------------------------------- 1 | use rust_gdb_example::{Animal, AnimalType}; 2 | 3 | fn main() { 4 | 5 | let animals = vec![ 6 | Animal { 7 | kind: AnimalType::Cat, 8 | name: "Chip".to_string(), 9 | age: 4, 10 | }, 11 | Animal { 12 | kind: AnimalType::Cat, 13 | name: "Nacho".to_string(), 14 | age: 6, 15 | }, 16 | Animal { 17 | kind: AnimalType::Dog, 18 | name: "Taco".to_string(), 19 | age: 2 20 | } 21 | ]; 22 | get_chip(&animals); 23 | } 24 | 25 | fn get_chip(animals: &Vec) { 26 | let chip = animals.get(0); 27 | println!("chip: {:?}", chip); 28 | } -------------------------------------------------------------------------------- /src/lang/rust/code/rust-gdb-example/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | #[derive(Clone, Debug)] 3 | pub enum AnimalType { 4 | Cat, 5 | Dog 6 | } 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct Animal { 10 | pub kind: AnimalType, 11 | pub name: String, 12 | pub age: usize, 13 | } 14 | 15 | #[derive(Clone, Debug)] 16 | pub struct Person { 17 | pub name: String, 18 | pub pets: Vec, 19 | pub age: usize 20 | } -------------------------------------------------------------------------------- /src/lang/rust/code/rust-p2p-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-p2p-example" 3 | version = "0.1.0" 4 | authors = ["iamazy <1448588084@qq.com>"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libp2p = { version = "0.31", features = ["tcp-tokio", "mdns-tokio"] } 11 | tokio = { version = "0.3", features = ["io-util", "io-std", "stream", "macros", "rt", "rt-multi-thread", "fs", "time", "sync"] } 12 | serde = {version = "=1.0", features = ["derive"] } 13 | serde_json = "1.0" 14 | once_cell = "1.5" 15 | log = "0.4" 16 | pretty_env_logger = "0.4" -------------------------------------------------------------------------------- /src/lang/rust/code/telnet-chat/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "telnet-chat" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"]} 10 | tokio-util = { version = "0.6", features = ["codec"]} 11 | futures = "0.3" 12 | bytes = "1" -------------------------------------------------------------------------------- /src/lang/rust/code/telnet-chat/src/accept.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | use std::io; 3 | 4 | use crate::main_loop::{ServerHandle, ToServer}; 5 | use crate::client::{spawn_client, ClientInfo}; 6 | 7 | use tokio::net::TcpListener; 8 | 9 | pub async fn start_accept(bind: SocketAddr, mut handle: ServerHandle) { 10 | let res = accept_loop(bind, handle.clone()).await; 11 | match res { 12 | Ok(()) => {}, 13 | Err(err) => { 14 | handle.send(ToServer::FatalError(err)).await; 15 | }, 16 | } 17 | } 18 | 19 | pub async fn accept_loop( 20 | bind: SocketAddr, 21 | handle: ServerHandle 22 | ) -> Result<(), io::Error> { 23 | 24 | let listen = TcpListener::bind(bind).await?; 25 | 26 | loop { 27 | let (tcp, ip) = listen.accept().await?; 28 | 29 | let id = handle.next_id(); 30 | 31 | let data = ClientInfo { 32 | ip, 33 | id, 34 | tcp, 35 | handle: handle.clone(), 36 | }; 37 | 38 | spawn_client(data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/lang/rust/code/telnet-chat/src/client.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::net::SocketAddr; 3 | 4 | use futures::stream::StreamExt; 5 | use tokio::io::AsyncWriteExt; 6 | use tokio::net::{TcpStream, tcp::{ReadHalf, WriteHalf}}; 7 | use tokio::sync::mpsc::{unbounded_channel, UnboundedSender, UnboundedReceiver}; 8 | use tokio::sync::mpsc::{channel, Sender, Receiver}; 9 | use tokio::sync::oneshot; 10 | use tokio::{try_join, select}; 11 | use tokio::task::JoinHandle; 12 | use tokio_util::codec::FramedRead; 13 | 14 | use crate::ClientId; 15 | use crate::main_loop::{ServerHandle, ToServer}; 16 | use crate::telnet::{TelnetCodec, Item}; 17 | 18 | /// Messages received from the main loop. 19 | pub enum FromServer { 20 | Message(Vec), 21 | } 22 | 23 | /// A handle to this actor, used by the server. 24 | #[derive(Debug)] 25 | pub struct ClientHandle { 26 | pub id: ClientId, 27 | ip: SocketAddr, 28 | chan: Sender, 29 | kill: JoinHandle<()>, 30 | } 31 | 32 | impl ClientHandle { 33 | /// Send a message to this client actor. Will emit an error if sending does 34 | /// not succeed immediately, as this means that forwarding messages to the 35 | /// tcp connection cannot keep up. 36 | pub fn send(&mut self, msg: FromServer) -> Result<(), io::Error> { 37 | if self.chan.try_send(msg).is_err() { 38 | Err(io::Error::new(io::ErrorKind::BrokenPipe, "Can't keep up or dead")) 39 | } else { 40 | Ok(()) 41 | } 42 | } 43 | 44 | /// Kill the actor. 45 | pub fn kill(self) { 46 | // run the destructor 47 | drop(self); 48 | } 49 | } 50 | 51 | impl Drop for ClientHandle { 52 | fn drop(&mut self) { 53 | self.kill.abort() 54 | } 55 | } 56 | 57 | /// This struct is constructed by the accept loop and used as the argument to 58 | /// `spawn_client`. 59 | pub struct ClientInfo { 60 | pub ip: SocketAddr, 61 | pub id: ClientId, 62 | pub handle: ServerHandle, 63 | pub tcp: TcpStream, 64 | } 65 | 66 | /// This struct stores the information used internally by this client actor. 67 | struct ClientData { 68 | id: ClientId, 69 | handle: ServerHandle, 70 | recv: Receiver, 71 | tcp: TcpStream, 72 | } 73 | 74 | /// Spawn a new client actor. 75 | pub fn spawn_client(info: ClientInfo) { 76 | let (send, recv) = channel(64); 77 | 78 | let data = ClientData { 79 | id: info.id, 80 | handle: info.handle.clone(), 81 | tcp: info.tcp, 82 | recv, 83 | }; 84 | 85 | // This spawns the new task. 86 | let (my_send, my_recv) = oneshot::channel(); 87 | let kill = tokio::spawn(start_client(my_recv, data)); 88 | 89 | // Then we create a ClientHandle to this new task, and use the oneshot 90 | // channel to send it to the task. 91 | let handle = ClientHandle { 92 | id: info.id, 93 | ip: info.ip, 94 | chan: send, 95 | kill, 96 | }; 97 | 98 | // Ignore send errors here. Should only happen if the server is shutting 99 | // down. 100 | let _ = my_send.send(handle); 101 | } 102 | 103 | async fn start_client(my_handle: oneshot::Receiver, mut data: ClientData) { 104 | // Wait for `spawn_client` to send us the `ClientHandle` so we can forward 105 | // it to the main loop. We need the oneshot channel because we cannot 106 | // otherwise get the `JoinHandle` returned by `tokio::spawn`. We forward it 107 | // from here instead of in `spawn_client` because we want the server to see 108 | // the NewClient message before this actor starts sending other messages. 109 | let my_handle = match my_handle.await { 110 | Ok(my_handle) => my_handle, 111 | Err(_) => return, 112 | }; 113 | data.handle.send(ToServer::NewClient(my_handle)).await; 114 | 115 | // We sent the client handle to the main loop. Start talking to the tcp 116 | // connection. 117 | let res = client_loop(data).await; 118 | match res { 119 | Ok(()) => {}, 120 | Err(err) => { 121 | eprintln!("Something went wrong: {}.", err); 122 | }, 123 | } 124 | } 125 | 126 | /// This method performs the actual job of running the client actor. 127 | async fn client_loop(mut data: ClientData) -> Result<(), io::Error> { 128 | let (read, write) = data.tcp.split(); 129 | 130 | // communication between tcp_read and tcp_write 131 | let (send, recv) = unbounded_channel(); 132 | 133 | let ((), ()) = try_join! { 134 | tcp_read(data.id, read, data.handle, send), 135 | tcp_write(write, data.recv, recv), 136 | }?; 137 | 138 | let _ = data.tcp.shutdown().await; 139 | 140 | Ok(()) 141 | } 142 | 143 | #[derive(Debug)] 144 | enum InternalMsg { 145 | GotAreYouThere, 146 | SendDont(u8), 147 | SendWont(u8), 148 | SendDo(u8), 149 | } 150 | 151 | async fn tcp_read( 152 | id: ClientId, 153 | read: ReadHalf<'_>, 154 | mut handle: ServerHandle, 155 | to_tcp_write: UnboundedSender, 156 | ) -> Result<(), io::Error> { 157 | let mut telnet = FramedRead::new(read, TelnetCodec::new()); 158 | 159 | while let Some(item) = telnet.next().await { 160 | match item? { 161 | Item::Line(line) => { 162 | handle.send(ToServer::Message(id, line)).await; 163 | }, 164 | Item::AreYouThere => { 165 | to_tcp_write.send(InternalMsg::GotAreYouThere) 166 | .expect("Should not be closed."); 167 | }, 168 | Item::GoAhead => { /* ignore */ }, 169 | Item::InterruptProcess => return Ok(()), 170 | Item::Will(3) => { // suppress go-ahead 171 | to_tcp_write.send(InternalMsg::SendDo(3)) 172 | .expect("Should not be closed."); 173 | }, 174 | Item::Will(i) => { 175 | to_tcp_write.send(InternalMsg::SendDont(i)) 176 | .expect("Should not be closed."); 177 | }, 178 | Item::Do(i) => { 179 | to_tcp_write.send(InternalMsg::SendWont(i)) 180 | .expect("Should not be closed."); 181 | }, 182 | item => { 183 | return Err(io::Error::new( 184 | io::ErrorKind::Other, 185 | format!("Unable to handle {:?}", item), 186 | )); 187 | }, 188 | } 189 | } 190 | 191 | // disconnected 192 | 193 | Ok(()) 194 | } 195 | 196 | async fn tcp_write( 197 | mut write: WriteHalf<'_>, 198 | mut recv: Receiver, 199 | mut from_tcp_read: UnboundedReceiver, 200 | ) -> Result<(), io::Error> { 201 | loop { 202 | select! { 203 | msg = recv.recv() => match msg { 204 | Some(FromServer::Message(msg)) => { 205 | write.write_all(&msg).await?; 206 | write.write_all(&[13, 10]).await?; 207 | }, 208 | None => { 209 | break; 210 | }, 211 | }, 212 | msg = from_tcp_read.recv() => match msg { 213 | Some(InternalMsg::GotAreYouThere) => { 214 | write.write_all(b"Yes.\r\n").await?; 215 | }, 216 | Some(InternalMsg::SendDont(i)) => { 217 | write.write_all(&[0xff, 254, i]).await?; 218 | }, 219 | Some(InternalMsg::SendWont(i)) => { 220 | write.write_all(&[0xff, 252, i]).await?; 221 | }, 222 | Some(InternalMsg::SendDo(i)) => { 223 | write.write_all(&[0xff, 253, i]).await?; 224 | }, 225 | None => { 226 | break; 227 | }, 228 | }, 229 | }; 230 | } 231 | 232 | Ok(()) 233 | } 234 | -------------------------------------------------------------------------------- /src/lang/rust/code/telnet-chat/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod accept; 2 | pub mod client; 3 | pub mod telnet; 4 | pub mod main_loop; 5 | 6 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] 7 | pub struct ClientId(usize); 8 | 9 | -------------------------------------------------------------------------------- /src/lang/rust/code/telnet-chat/src/main.rs: -------------------------------------------------------------------------------- 1 | #[tokio::main] 2 | async fn main() { 3 | let (handle, join) = telnet_chat::main_loop::spawn_main_loop(); 4 | 5 | tokio::spawn(async move { 6 | let bind = ([0, 0, 0, 0], 3456).into(); 7 | telnet_chat::accept::start_accept(bind, handle).await; 8 | }); 9 | 10 | println!("Starting on port 3456"); 11 | 12 | join.await.unwrap(); 13 | } 14 | -------------------------------------------------------------------------------- /src/lang/rust/code/telnet-chat/src/main_loop.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::collections::HashMap; 3 | use std::sync::{Arc, atomic::{AtomicUsize, Ordering}}; 4 | use tokio::sync::mpsc::Sender; 5 | 6 | use tokio::sync::mpsc::{channel, Receiver}; 7 | use tokio::task::JoinHandle; 8 | 9 | use crate::ClientId; 10 | use crate::client::{ClientHandle, FromServer}; 11 | 12 | /// This struct is used by client actors to send messages to the main loop. The 13 | /// message type is `ToServer`. 14 | #[derive(Clone, Debug)] 15 | pub struct ServerHandle { 16 | chan: Sender, 17 | next_id: Arc, 18 | } 19 | impl ServerHandle { 20 | pub async fn send(&mut self, msg: ToServer) { 21 | if self.chan.send(msg).await.is_err() { 22 | panic!("Main loop has shut down."); 23 | } 24 | } 25 | pub fn next_id(&self) -> ClientId { 26 | let id = self.next_id.fetch_add(1, Ordering::Relaxed); 27 | ClientId(id) 28 | } 29 | } 30 | 31 | /// The message type used when a client actor sends messages to the main loop. 32 | pub enum ToServer { 33 | NewClient(ClientHandle), 34 | Message(ClientId, Vec), 35 | FatalError(io::Error), 36 | } 37 | 38 | pub fn spawn_main_loop() -> (ServerHandle, JoinHandle<()>) { 39 | let (send, recv) = channel(64); 40 | 41 | let handle = ServerHandle { 42 | chan: send, 43 | next_id: Default::default(), 44 | }; 45 | 46 | let join = tokio::spawn(async move { 47 | let res = main_loop(recv).await; 48 | match res { 49 | Ok(()) => {}, 50 | Err(err) => { 51 | eprintln!("Oops {}.", err); 52 | }, 53 | } 54 | }); 55 | 56 | (handle, join) 57 | } 58 | 59 | #[derive(Default, Debug)] 60 | struct Data { 61 | clients: HashMap, 62 | } 63 | 64 | async fn main_loop( 65 | mut recv: Receiver, 66 | ) -> Result<(), io::Error> { 67 | let mut data = Data::default(); 68 | 69 | while let Some(msg) = recv.recv().await { 70 | match msg { 71 | ToServer::NewClient(handle) => { 72 | data.clients.insert(handle.id, handle); 73 | }, 74 | ToServer::Message(from_id, msg) => { 75 | // If we fail to send messages to any actor, we need to remove 76 | // it, but we can't do so while iterating. 77 | let mut to_remove = Vec::new(); 78 | 79 | // Iterate through clients so we can send the message. 80 | for (id, handle) in data.clients.iter_mut() { 81 | let id = *id; 82 | 83 | // Don't send it to the client who sent it to us. 84 | if id == from_id { continue; } 85 | 86 | let msg = FromServer::Message(msg.clone()); 87 | 88 | if handle.send(msg).is_err() { 89 | // Remove this client. 90 | to_remove.push(id); 91 | } 92 | } 93 | 94 | // Remove those clients. 95 | for id in to_remove { 96 | // The destructor of ClientHandle will kill the actor when 97 | // we remove it from the HashMap. 98 | data.clients.remove(&id); 99 | } 100 | }, 101 | // This message comes only from the accept loop. 102 | ToServer::FatalError(err) => return Err(err), 103 | } 104 | } 105 | 106 | Ok(()) 107 | } 108 | -------------------------------------------------------------------------------- /src/lang/rust/code/telnet-chat/src/telnet.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use tokio_util::codec::Decoder; 3 | 4 | use bytes::{Buf, BytesMut}; 5 | 6 | pub struct TelnetCodec { 7 | current_line: Vec, 8 | } 9 | 10 | impl TelnetCodec { 11 | pub fn new() -> Self { 12 | TelnetCodec { 13 | current_line: Vec::with_capacity(1024), 14 | } 15 | } 16 | } 17 | 18 | #[derive(Debug)] 19 | pub enum Item { 20 | Line(Vec), 21 | SE, 22 | DataMark, 23 | Break, 24 | InterruptProcess, 25 | AbortOutput, 26 | AreYouThere, 27 | GoAhead, 28 | SB, 29 | Will(u8), 30 | Wont(u8), 31 | Do(u8), 32 | Dont(u8), 33 | } 34 | 35 | impl Decoder for TelnetCodec { 36 | type Item = Item; 37 | type Error = io::Error; 38 | 39 | fn decode( 40 | &mut self, 41 | src: &mut BytesMut 42 | ) -> Result, Self::Error> { 43 | loop { 44 | if src.is_empty() { 45 | return Ok(None); 46 | } 47 | 48 | if src[0] == 0xff { 49 | let (res, consume) = try_parse_iac(src.chunk()); 50 | src.advance(consume); 51 | 52 | match res { 53 | ParseIacResult::Invalid(err) => { 54 | return Err(io::Error::new( 55 | io::ErrorKind::InvalidData, 56 | err, 57 | )); 58 | }, 59 | ParseIacResult::NeedMore => return Ok(None), 60 | ParseIacResult::Item(item) => return Ok(Some(item)), 61 | ParseIacResult::NOP => { /* go around loop */ }, 62 | ParseIacResult::EraseCharacter => { 63 | self.current_line.pop(); 64 | }, 65 | ParseIacResult::EraseLine => { 66 | self.current_line.clear(); 67 | }, 68 | ParseIacResult::Escaped => { 69 | self.current_line.push(0xff); 70 | }, 71 | } 72 | } else { 73 | let byte = src.get_u8(); 74 | 75 | match byte { 76 | 10 => { 77 | let line = self.current_line.to_vec(); 78 | self.current_line.clear(); 79 | 80 | return Ok(Some(Item::Line(line))); 81 | }, 82 | 0 ..= 31 => { 83 | // ignore 84 | }, 85 | _ => self.current_line.push(byte), 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | enum ParseIacResult { 93 | Invalid(String), 94 | NeedMore, 95 | Item(Item), 96 | NOP, 97 | EraseCharacter, 98 | EraseLine, 99 | Escaped, 100 | } 101 | 102 | /// Returns the parsed result of the first few bytes, as well as how many bytes 103 | /// to consume. 104 | fn try_parse_iac(bytes: &[u8]) -> (ParseIacResult, usize) { 105 | if bytes.len() < 2 { 106 | return (ParseIacResult::NeedMore, 0); 107 | } 108 | if bytes[0] != 0xff { 109 | unreachable!(); 110 | } 111 | if is_three_byte_iac(bytes[1]) && bytes.len() < 3 { 112 | return (ParseIacResult::NeedMore, 0); 113 | } 114 | 115 | match bytes[1] { 116 | 240 => (ParseIacResult::Item(Item::SE), 2), 117 | 241 => (ParseIacResult::NOP, 2), 118 | 242 => (ParseIacResult::Item(Item::DataMark), 2), 119 | 243 => (ParseIacResult::Item(Item::Break), 2), 120 | 244 => (ParseIacResult::Item(Item::InterruptProcess), 2), 121 | 245 => (ParseIacResult::Item(Item::AbortOutput), 2), 122 | 246 => (ParseIacResult::Item(Item::AreYouThere), 2), 123 | 247 => (ParseIacResult::EraseCharacter, 2), 124 | 248 => (ParseIacResult::EraseLine, 2), 125 | 249 => (ParseIacResult::Item(Item::GoAhead), 2), 126 | 250 => (ParseIacResult::Item(Item::SB), 2), 127 | 251 => (ParseIacResult::Item(Item::Will(bytes[2])), 3), 128 | 252 => (ParseIacResult::Item(Item::Wont(bytes[2])), 3), 129 | 253 => (ParseIacResult::Item(Item::Do(bytes[2])), 3), 130 | 254 => (ParseIacResult::Item(Item::Dont(bytes[2])), 3), 131 | 255 => (ParseIacResult::Escaped, 2), 132 | cmd => (ParseIacResult::Invalid(format!("Unknown IAC command {}.", cmd)), 0), 133 | } 134 | } 135 | 136 | fn is_three_byte_iac(byte: u8) -> bool { 137 | match byte { 138 | 251 ..= 254 => true, 139 | _ => false, 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/lang/rust/img/GDB-Layout-Split-Visual.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/GDB-Layout-Split-Visual.avif -------------------------------------------------------------------------------- /src/lang/rust/img/Layout-GDB-SRC-Command-Line-Interface.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/Layout-GDB-SRC-Command-Line-Interface.avif -------------------------------------------------------------------------------- /src/lang/rust/img/Make-Struct-Public-with-Keyword.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/Make-Struct-Public-with-Keyword.webp -------------------------------------------------------------------------------- /src/lang/rust/img/Parsing-Struct-Name-Field-Diagram.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/Parsing-Struct-Name-Field-Diagram.webp -------------------------------------------------------------------------------- /src/lang/rust/img/Screen-Shot-2021-02-09-at-9.24.46-AM.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/Screen-Shot-2021-02-09-at-9.24.46-AM.webp -------------------------------------------------------------------------------- /src/lang/rust/img/bilde.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/bilde.png -------------------------------------------------------------------------------- /src/lang/rust/img/dylint.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/dylint.jpeg -------------------------------------------------------------------------------- /src/lang/rust/img/dylint1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/dylint1.png -------------------------------------------------------------------------------- /src/lang/rust/img/dylint2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/dylint2.png -------------------------------------------------------------------------------- /src/lang/rust/img/futures-join.svg: -------------------------------------------------------------------------------- 1 | 2 |
Tokio core event loop
Tokio core event loop
Main task
Main task
futures::future::FromAll
futures::future::FromAll
Future #1
Future #1
Future #2
Future #2
Future #3
Future #3
Future #4
Future #4
Future #n
Future #n
Future #5
Future #5
...
[Not supported by viewer]
-------------------------------------------------------------------------------- /src/lang/rust/img/futures-rs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/futures-rs.png -------------------------------------------------------------------------------- /src/lang/rust/img/macro_in_rust.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/macro_in_rust.webp -------------------------------------------------------------------------------- /src/lang/rust/img/parsing-rust-nom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/parsing-rust-nom.png -------------------------------------------------------------------------------- /src/lang/rust/img/rust-tui-example-command-line-interface-finished.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust-tui-example-command-line-interface-finished.webp -------------------------------------------------------------------------------- /src/lang/rust/img/rust-tui-example-command-line-interface.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust-tui-example-command-line-interface.webp -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_1.jpg -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_2.jpg -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_3.png -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_4.jpg -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_5.jpg -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_6.jpg -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_7.jpg -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_8.jpg -------------------------------------------------------------------------------- /src/lang/rust/img/rust_ownership_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/rust_ownership_9.jpg -------------------------------------------------------------------------------- /src/lang/rust/img/swap_problem.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/swap_problem.jpeg -------------------------------------------------------------------------------- /src/lang/rust/img/tokio-stack.svg: -------------------------------------------------------------------------------- 1 | 2 |
Your program
[Not supported by viewer]
Mio
[Not supported by viewer]
System selector
(epoll/kqueue()/IOCP/etc.)
[Not supported by viewer]
Tokio
[Not supported by viewer]
Futures
[Not supported by viewer]
-------------------------------------------------------------------------------- /src/lang/rust/img/trpl04-04.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | table0 15 | 16 | 17 | s1 18 | 19 | name 20 | 21 | value 22 | 23 | ptr 24 | 25 | 26 | len 27 | 28 | 5 29 | 30 | capacity 31 | 32 | 5 33 | 34 | 35 | 36 | table1 37 | 38 | index 39 | 40 | value 41 | 42 | 0 43 | 44 | h 45 | 46 | 1 47 | 48 | e 49 | 50 | 2 51 | 52 | l 53 | 54 | 3 55 | 56 | l 57 | 58 | 4 59 | 60 | o 61 | 62 | 63 | 64 | table0:c->table1:pointee 65 | 66 | 67 | 68 | 69 | 70 | table3 71 | 72 | s2 73 | 74 | name 75 | 76 | value 77 | 78 | ptr 79 | 80 | 81 | len 82 | 83 | 5 84 | 85 | capacity 86 | 87 | 5 88 | 89 | 90 | 91 | table3:c->table1:pointee 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/lang/rust/img/two-launcher-activities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/two-launcher-activities.png -------------------------------------------------------------------------------- /src/lang/rust/img/udpsocket.svg: -------------------------------------------------------------------------------- 1 | 2 |
tokio_core::net::UdpSocket
tokio_core::net::UdpSocket
tokio_core::reactor::PollEvented<mio::net::UdpSocket>
tokio_core::reactor::PollEvented&lt;mio::net::UdpSocket&gt;
mio::net::UdpSocket (impl Evented)
<div><span>mio::net::UdpSocket (impl Evented)</span><br></div>
mio::sys::UdpSocket
<div><span>mio::sys::UdpSocket</span><br></div>
std::net::UdpSocket
[Not supported by viewer]
c_int file descriptor (Linux)
<div>c_int file descriptor (Linux)</div>
-------------------------------------------------------------------------------- /src/lang/rust/img/welcome_to_the_futures.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/lang/rust/img/welcome_to_the_futures.jpeg -------------------------------------------------------------------------------- /src/lang/rust/std/20-std-pin.md: -------------------------------------------------------------------------------- 1 | # std::pin 2 | 3 | [原文](https://doc.rust-lang.org/std/pin/index.html) 4 | 5 | 定义:一种将数据固定在内存中的类型 6 | 7 | ## 描述 8 | 9 | 从对象在内存中位置不变的意义上来说,保证对象不移动有时很有用。这种情况的一个主要示例就是构建`自引用`结构,因为使用指向自身的指针移动对象会使他们无效,这可能导致未定义的行为。 10 | 11 | 从较高的层次来说,`Pin

`可以确保任何类型的指针`P`在内存中都有固定的位置,这意味着它不能被移动到其他地方,并且在删除之前不能释放其内存。当讨论将固定数据和非固定数据结合在一起组成类型时,事情将变得更加微妙。 12 | 13 | 默认情况下,Rust中所有类型都是可移动的。Rust允许按值传递类型。普通的智能指针类型(例如Box或者&mut T)允许替换和移动他们包含的值:可以将其移出`Box`或者使用`mem::swap`。`Pin

`封装了一个指针类型P,因此`Pin>`功能和常规类型`Box`非常相似。当`Pin>`被销毁时,其包含的内容也将被销毁,并且其占用的内存也被释放。同样的,`Pin<&mut T>`和`&mut T`非常像。但是`Pin

`不会让使用者真的去包含`Box`或者`&mut T`来固定数据,这意味着不能使用`mem::swap`之类的操作。 14 | 15 | ```rust 16 | use std::pin::Pin; 17 | fn swap_pins(x: Pin<&mut T>, y: Pin<&mut T>) { 18 | // `mem::swap` needs `&mut T`, but we cannot get it. 19 | // We are stuck, we cannot swap the contents of these references. 20 | // We could use `Pin::get_unchecked_mut`, but that is unsafe for a reason: 21 | // we are not allowed to use it for moving things out of the `Pin`. 22 | } 23 | ``` 24 | 25 | 值得重申的是,`Pin

`不会改变Rust编译器允许所有类型都是可移动的事实。`mem::swap`依然可以调用任意类型`T`。取而代之的是,`Pin

`使调用需要`&mut T`的方法变得不可能来阻止一些(由`Pin

`封装的指针所指向的)值被移动(如`mem::swap`)。 26 | 27 | `Pin

`可用于包装任何类型的指针P,因此他可以与`Deref`和`DerefMut`交互。`Pin

where P: Deref`应该被视为固定`P::Target`的P型指针。因此,`Pin>`是一个固定T的指针,`Pin>`是固定T的指针计数器。为了确保正确性,`Pin

`依赖`Deref`和`DerefMut`的实现使其不能移出其自身参数,并且总是只返回一个指向固定数据的Pin指针。 28 | 29 | 30 | ## Unpin 31 | 即使不固定,许多类型也始终可以自由移动,因为他们不依赖于具有稳定的地址。这包括所有基本类型(如`bool`,`i32`和引用类型)以及仅用这些类型组成的类型。不需要固定的类型将实现`Unpin`特征,从而取消了`Pin

`的影响。对于`T: Unpin`,`Pin>`和`Box`的功能相同,`Pin<&mut T>`和`&mut T`的作用相同。 32 | 33 | 值得注意的是,`Pin`和`Unpin`只影响指向类型`P::Target`,而不影响封装在`Pin

`中的P类型本身。例如,`Box`是否为`Unpin`对`Pin>`的行为没有影响 34 | 35 | ## 例子1: 自引用结构(self-referential struct) 36 | 37 | 在进一步解释`Pin`之前,先讨论如何使用它的一些示例。 38 | 39 | ```rust 40 | use std::pin::Pin; 41 | use std::marker::PhantomPinned; 42 | use std::ptr::NonNull; 43 | 44 | // This is a self-referential struct because the slice field points to the data field. 45 | // We cannot inform the compiler about that with a normal reference, 46 | // as this pattern cannot be described with the usual borrowing rules. 47 | // Instead we use a raw pointer, though one which is known not to be null, 48 | // as we know it's pointing at the string. 49 | struct Unmovable { 50 | data: String, 51 | slice: NonNull, 52 | _pin: PhantomPinned, 53 | } 54 | 55 | impl Unmovable { 56 | // To ensure the data doesn't move when the function returns, 57 | // we place it in the heap where it will stay for the lifetime of the object, 58 | // and the only way to access it would be through a pointer to it. 59 | fn new(data: String) -> Pin> { 60 | let res = Unmovable { 61 | data, 62 | // we only create the pointer once the data is in place 63 | // otherwise it will have already moved before we even started 64 | slice: NonNull::dangling(), 65 | _pin: PhantomPinned, 66 | }; 67 | let mut boxed = Box::pin(res); 68 | 69 | let slice = NonNull::from(&boxed.data); 70 | // we know this is safe because modifying a field doesn't move the whole struct 71 | unsafe { 72 | let mut_ref: Pin<&mut Self> = Pin::as_mut(&mut boxed); 73 | Pin::get_unchecked_mut(mut_ref).slice = slice; 74 | } 75 | boxed 76 | } 77 | } 78 | 79 | let unmoved = Unmovable::new("hello".to_string()); 80 | // The pointer should point to the correct location, 81 | // so long as the struct hasn't moved. 82 | // Meanwhile, we are free to move the pointer around. 83 | let mut still_unmoved = unmoved; 84 | assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data)); 85 | 86 | // Since our type doesn't implement Unpin, this will fail to compile: 87 | // let mut new_unmoved = Unmovable::new("world".to_string()); 88 | // std::mem::swap(&mut *still_unmoved, &mut *new_unmoved); 89 | ``` 90 | 91 | ## 例子2: 侵入式双向链表(intrusive doubly-linked list) 92 | 93 | 在侵入式双向链表中,不会真的为表中的元素分配内存空间,如何分配由使用者控制,并且元素可以分配在(比链表存活时间更短暂的)栈帧上。 94 | 双向链表中每个元素都有指针分别指向它的前驱和后继元素。只有当链表中的元素位置在内存中都是固定的才可以添加元素,因为移动一个元素可能会使指针失效。并且,链表中的元素实现`Drop`会在它被移除出链表时,修复指向它前驱和后继元素的指针。 95 | 至关重要的是,如果一个元素在不调用`Drop::drop`的情况下被释放或者以其他方式失效,则其相邻元素指向该元素的指针将变为无效,这将破坏数据结构。 96 | 因此,`Pin`也许附带`Drop`相关的保证。 97 | 98 | ## Drop保证 99 | 100 | `Pin`的目的是为了能够依赖内存中数据的位置。因此不仅限制了移动数据,还限制了用于存储数据的内存的重新分配。具体来说,对于固定的数据,从固定到`drop`被调用的那一刻之前,它的内存都不会失效或者被重新利用,只有当`drop`返回或者`panic`,该内存才可以重用。 101 | 内存可以因为被释放而“失效”,也可以用`None`来代替`Some(v)`,或者调用`Vec::set_len`来`消灭`数组中的元素。可以通过`ptr::write`覆写来重新利用它,而无需事先调用析构函数。未经调用`drop`的固定数据均不允许使用这些方法。 102 | 注意这个保证并不意味着不会产生内存泄漏!你依然可以不去调用固定元素的`drop`方法(如:你可以对`Pin>`使用`mem::forget`方法),在双向列表的示例中,元素将只保存在列表中,但是如果不调用`drop`方法,将无法释放或重新使用内存。 103 | 104 | ## Drop实现 105 | 106 | 如果你的数据类型使用`Pin`,在实现`Drop`时需要格外注意,`drop`函数携带`&mut self`参数,即使你的类型先前已经固定,也将调用该函数,好像编译器会自动调用`Pin::get_unchecked_mut`。 107 | 这永远不会在安全代码中引起问题,因为实现需要固定的类型需要`unsafe`的代码。请注意,如果在你的类型中使用`Pin`(如在`Pin<&Self>`或`Pin<&mut Self>`)会对`Drop`实现产生影响:如果你的类型中的一个元素已经被固定,你必须将`Drop`视为隐式获取`Pin<&mut Self>`。 108 | 109 | 举个例子,你可以像如下代码一样实现`Drop`: 110 | 111 | ```rust 112 | impl Drop for Type { 113 | fn drop(&mut self) { 114 | // `new_unchecked` is okay because we know this value is never used 115 | // again after being dropped. 116 | inner_drop(unsafe { Pin::new_unchecked(self)}); 117 | fn inner_drop(this: Pin<&mut Type>) { 118 | // Actual drop code goes here. 119 | } 120 | } 121 | } 122 | ``` 123 | 124 | `inner_drop`函数具有`drop`应有的类型,所以这确保你不会偶发的在这种与`Pin`互相矛盾的方式中使用`self/this`。 125 | 126 | 而且,如果你的字段被`#[repr(packed)]`修饰,编译器将自动移除字段。它甚至可以对恰好对齐的字段执行此操作。所以你不能将`Pin`和`#[repr(packed)]`一起使用。 127 | 128 | ## 投影和结构固定 129 | 130 | 在处理固定结构时,会遇到一个问题,如何访问方法中结构(`Pin<&mut Struct>`)的字段。通常是写辅助方法(即投影)将`Pin<&mut Struct>`转化成字段中的一个引用。但是该引用应该具有什么类型?是`Pin<&mut Field>`还是`&mut Field`呢?枚举字段和容器字段(`Vec`)或者封装类型(`Box`或`RefCell`)也会出现相同的问题(这个问题适用于可变引用和共享引用,我们仅在此处使用可变引用的更常见情况进行说明)。 131 | 132 | 实际上,是由数据结构的作者决定是否为特殊的字段固定投影来将`Pin<&mut Struct>`转换为`Pin<&mut Field>`或者`&mut Field`。这里有一些限制,最大的限制就是`一致性`:每个字段都可以被投射到固定的引用上,或者作为投影的一部分去掉了`Pin`。如果同一个字段同时满足这两个条件将是有问题的。 133 | 134 | 作为数据结构的作者,你需要为每个字段决定是否需要将`Pin`(的影响)“传播”到这个字段上。传播中的`Pin`也被称为结构化,因为它遵循该类型的结构。在以下小结中,我们描述了两种选择都必须考虑的因素。 -------------------------------------------------------------------------------- /src/lang/rust/std/21-std-condvar.md: -------------------------------------------------------------------------------- 1 | # std::sync::Condvar 2 | 3 | [原文](https://doc.rust-lang.org/std/sync/struct.Condvar.html) 4 | 5 | 定义:一种条件变量 6 | 7 | ## 描述 8 | 9 | 条件变量代表阻塞线程的能力,以使线程在等待时间发生时不占用CPU时间。条件变量通常与布尔谓词(predicate)和互斥锁相关联,在验证线程必须阻塞之前,始终在互斥对象内部验证该谓词。 10 | 11 | 该模块中的函数将阻止当前执行的线程,并在可能的情况下绑定系统提供的条件变量。请注意,此模块对系统条件变量又一个附加限制:每一个`Condvar`在运行时可以与一个`Mutex`一起使用。任何在同一条件变量上使用多个互斥锁的尝试都将引起运行时`panic`。如果不希望这样,在`sys`中的`unsafe`原语将没有这种限制,但是可能会导致为定义的行为。 12 | 13 | ```rust 14 | use std::sync::{Arc, Mutex, Condvar}; 15 | use std::thread; 16 | 17 | let pair = Arc::new((Mutex::new(false), Condvar::new())); 18 | let pair2 = pair.clone(); 19 | 20 | // Inside of our lock, spawn a new thread, and then wait for it to start. 21 | thread::spawn(move|| { 22 | let (lock, cvar) = &*pair2; 23 | let mut started = lock.lock().unwrap(); 24 | *started = true; 25 | // We notify the condvar that the value has changed. 26 | println!("111"); 27 | cvar.notify_one(); 28 | }); 29 | 30 | // Wait for the thread to start up. 31 | let (lock, cvar) = &*pair; 32 | let mut started = lock.lock().unwrap(); 33 | println!("222"); 34 | while !*started { 35 | println!("333"); 36 | started = cvar.wait(started).unwrap(); 37 | println!("444"); 38 | } 39 | ``` 40 | 41 | 输出 42 | 43 | ``` 44 | 222 45 | 333 46 | 111 47 | 444 48 | ``` 49 | 50 | ## 实现 51 | 52 | ### impl Condvar 53 | 54 | > pub fn new() -> Condvar 55 | 56 | 创建一个新的条件变量,并随时等待并通知它。 57 | 58 | ```rust 59 | use std::sync::Condvar; 60 | let condvar = Condvar::new(); 61 | ``` 62 | 63 | > pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult> 64 | 65 | 阻塞当前线程,直到条件变量接收到一个通知。 66 | 67 | 这个函数将会自动解锁指定的`MutexGuard`并且阻塞当前线程。这意味着在互斥体(`mutex`)解锁后,任何逻辑上的调用`notify_one`和`notify_all`方法都是唤醒该线程的候选对象。当函数返回时,将重新获得指定的锁。 68 | 69 | 请注意,这个方法易受虚假唤醒的影响。条件变量通常使用布尔谓词和他们建立关联,并且该方法每次返回时都必须检查谓词以防止虚假唤醒。 70 | 71 | ❗️:如果正在等待的互斥锁在此线程重新获取锁时中毒,该方法将返回错误。想要了解更多信息,请参阅有关资料: 中毒的互斥体(`Mutex`)。 72 | 73 | 😱:如果同时使用多个互斥体将会触发`panic!`。每个条件变量都动态的绑定在一个互斥体上以确保跨平台行为定义。如果不需要此限制,则需提供`sys`中的`unsafe`原语。 74 | 75 | ```rust 76 | use std::sync::{Arc, Mutex, Condvar}; 77 | use std::thread; 78 | 79 | let pair = Arc::new((Mutex::new(false), Condvar::new())); 80 | let pair2 = pair.clone(); 81 | 82 | thread::spawn(move|| { 83 | let (lock, cvar) = &*pair2; 84 | let mut started = lock.lock().unwrap(); 85 | *started = true; 86 | // We notify the condvar that the value has changed. 87 | cvar.notify_one(); 88 | }); 89 | 90 | // Wait for the thread to start up. 91 | let (lock, cvar) = &*pair; 92 | let mut started = lock.lock().unwrap(); 93 | // As long as the value inside the `Mutex` is `false`, we wait. 94 | while !*started { 95 | started = cvar.wait(started).unwrap(); 96 | } 97 | ``` 98 | 99 | > pub fn wait_while<'a, T, F>(&self, guard: MutexGuard<'a, T>, condition: F) -> LockResult> where F: FnMut(&mut T) -> bool 100 | 101 | 阻塞当前线程直到条件变量收到一个通知并且所提供的条件为`false`为止。 102 | 103 | 这个函数将会自动解锁指定的`MutexGuard`并且阻塞当前线程。这意味着在互斥体(`mutex`)解锁后,任何逻辑上的调用`notify_one`和`notify_all`方法都是唤醒该线程的候选对象。当函数返回时,将重新获得指定的锁。 104 | 105 | ❗️:如果正在等待的互斥锁在此线程重新获取锁时中毒,该方法将返回错误。想要了解更多信息,请参阅有关资料: 中毒的互斥体(`Mutex`)。 106 | 107 | ```rust 108 | use std::sync::{Arc, Mutex, Condvar}; 109 | use std::thread; 110 | 111 | let pair = Arc::new((Mutex::new(true), Condvar::new())); 112 | let pair2 = pair.clone(); 113 | 114 | thread::spawn(move|| { 115 | let (lock, cvar) = &*pair2; 116 | let mut pending = lock.lock().unwrap(); 117 | *pending = false; 118 | // We notify the condvar that the value has changed. 119 | cvar.notify_one(); 120 | }); 121 | 122 | // Wait for the thread to start up. 123 | let (lock, cvar) = &*pair; 124 | // As long as the value inside the `Mutex` is `true`, we wait. 125 | let _guard = cvar.wait_while(lock.lock().unwrap(), |pending| { *pending }).unwrap(); 126 | ``` 127 | 128 | > pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, dur: Duration) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> 129 | 130 | 在此条件变量上等待通知,在指定的持续时间之后超时。 131 | 132 | 此函数的语义等同于`wait`(除了线程被阻塞的时间不超过`dur`),由于诸如抢占或平台差异之类的异常可能不会导致等待的最大时间精确地变短,因此该方法不应用于精确的计时。 133 | 134 | ⚠️:已尽最大的努力确保等待时间是由单调时间测量的,并且不会被系统时间修改所影响。这个函数容易受到虚假唤醒的影响。条件变量通常都会有一个布尔谓词 -------------------------------------------------------------------------------- /src/lang/rust/summary.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | - [用 Rust 学习解析器组合器](01-用Rust学习解析器组合器.md) 4 | - [用 Rust 编写 LLVM 的玩具前端](02-用Rust编写LLVM的玩具前端.md) 5 | - [使用 nom 解析 url](03-使用nom解析url.md) 6 | - [Rust 中异步编程实用介绍](04-Rust中异步编程实用介绍.md) 7 | - [tokio 内幕-自底向上理解 Rust 的异步 IO 框架](05-tokio内幕-自底向上理解Rust的异步IO框架.md) 8 | - [「转」Rust 中的 Arenas](06-Rust中的Arenas.md) 9 | - [「转」Rust 异步 IO: 从 mio 到 coroutine](07-Rust异步IO:从mio到coroutine.md) 10 | - [「转」图解 Rust 所有权与生命周期](08-图解Rust所有权与生命周期.md) 11 | - [Rust 异步执行器](09-Rust异步执行器.md) 12 | - [「转」Rust 标准库特征指南](10-Rust标准库特征指南.md) 13 | - [Rust中的宏: 带有示例的教程](11-Rust中的宏:带有示例的教程.md) 14 | - [libp2p 教程: 使用 Rust 构建 p2p 应用](12-libp2p教程:使用Rust构建p2p应用.md) 15 | - [「转」Rust 的 Pin 与 Unpin](13-Rust的Pin与Unpin.md) 16 | - [使用 GDB 调试 Rust 中的应用](14-使用GDB调试Rust应用.md) 17 | - [解释 Rust 中的原子性](15-解释Rust中的原子性.md) 18 | - [Rust 和 TUI:在 Rust 中构建命令行界面](16-Rust和TUI:在Rust中构建命令行界面.md) 19 | - [在 Android 中运行 Rust](17-在Android中运行Rust.md) 20 | - [「转」Rust 中常见的有关生命周期的误解](18-Rust中常见的有关生命周期的误解.md) 21 | - [生命周期型变示例](19-生命周期型变示例.md) 22 | - [Rust 如何实现线程安全](20-rust如何实现线程安全.md) 23 | - [无需 fork Clippy 就可以编写 Rust lint](22-无需fork_Clippy就可以编写Rust_lints.md) 24 | - [使用 Mio 编写底层 TCP 服务器](23-使用Mio编写底层TCP服务器.md) 25 | - [「转」Unsafe Rust 的取舍](24-UnsafeRust的取舍.md) 26 | - [Rust 中的类型强制转换](25-Rust中的类型强转.md) 27 | 28 | --- 29 | 30 | # 标准库文档翻译 31 | 32 | - [std::pin](./std/20-std-pin.md) 33 | - [std::sync::Condvar](./std/21-std-condvar.md) 34 | -------------------------------------------------------------------------------- /src/oci/01-oci-spec-overview.md: -------------------------------------------------------------------------------- 1 | # 开放容器倡议 (OCI) 规范概述 2 | 3 | [原文](https://alibaba-cloud.medium.com/open-container-initiative-oci-specifications-375b96658f55) 4 | 5 | OCI (Open Container Initiative) 是一个由行业合作提出的倡议,旨在定义有关容器镜像格式与运行时的开放[容器](https://www.alibabacloud.com/product/container-service)规范。就开源世界的合作与竞争而言,从最初的分歧到如何一步步到达今天地位上的历史是一个非常有趣的故事。 6 | 7 | 如今,容器生态系统的所有主要玩家都遵循 OCI 容器规范。对于任何想要了解容器工作原理的同学,这是一门你绝不想错过的技术。 8 | 9 | ## 概览 10 | 11 | OCI 有两项规范,[Image spec](https://github.com/opencontainers/image-spec) 和 [Runtime spec](https://github.com/opencontainers/runtime-spec)。 12 | 13 | 下图显示了它们涵盖的内容和交互的方式。 14 | 15 | ![oci1](./img/oci1.png) 16 | 17 | OCI 镜像可以从其他地方下载(如 Docker Hub) 并在 OCI 运行时文件系统包中解压。然后 OCI 运行时包将会运行在 OCI 运行时中。其中,OCI 运行时规范定义了如何运行一个”文件系统包“。 18 | 19 | ## 镜像规范 (image-spec) 20 | 21 | 镜像规范定义了 OCI 容器镜像的格式,其中包含 manifest,镜像索引 (image index),一组文件系统层以及一个配置文件。这个规范的目标是创建互操作工具,用于构建,传输,以及准备要运行的容器镜像。 22 | 23 | 在顶层视角,容器镜像只是个 tarball 压缩包,在解压之后,它拥有以下`布局`。 24 | 25 | ```console 26 | ├── blobs 27 | │ └── sha256 28 | │ ├── 4297f0* (image.manifest) 29 | │ └── 7ea049 (image.config) 30 | ├── index.json 31 | └── oci-layout 32 | ``` 33 | 34 | 如果没有说明这些东西都是些什么以及它们是如何关联的,该布局就没那么有用。 35 | 36 | 我们可以简单的忽略`oci-layout`文件。`index.json`是入口点,它包含了主要的`manifest`,其中列出了所有用于单个容器镜像的”资源“。 37 | 38 | `manifest`包含主要的`config`和`layers`。 39 | 40 | 将它使用图表表示: 41 | 42 | ![oci2](./img/oci2.png) 43 | 44 | [配置 (config)](https://github.com/opencontainers/image-spec/blob/master/config.md) 包含: 45 | - 镜像的配置,它将会转换成 OCI 运行时包的运行时配置文件。 46 | - layers,构成 OCI 运行时包的根文件系统 47 | - 一些与镜像历史相关的元数据 48 | 49 | [层级 (layers)](https://github.com/opencontainers/image-spec/blob/master/layer.md) 构成最终的`rootfs`。第一层级是基础,其他所有层级仅包含第一次层级的变更。在下一节中我们深入研究一下什么是 OCI 层级规范。 50 | 51 | ## 层级 52 | 53 | 规范中本质上关于层级定义了以下两点: 54 | 55 | 1. 如何表示一个层级 56 | 57 | - 对于基础层级, 压缩所有的内容 58 | - 对于非基础层级,压缩所有与其主层级比较后变更内容的集合 59 | - 因此,首先检测变更,形成一个`changeset`;然后压缩此变更集合以表示该层级 60 | 61 | 2. 如何联合所有层级 62 | 63 | 在主层级的顶层应用所有的变更集合。将会给你带来`rootfs`。 64 | 65 | ## 运行时规范 (runtime-spec) 66 | 67 | 一旦在磁盘文件系统中将镜像解压到 OCI 运行时包中,你可以将其运行起来。此时是 OCI 运行时规范在起作用。该规范指定了容器的配置,执行环境以及生命周期。 68 | 69 | 容器配置包含必要的元数据用来创建及运行此容器。其中包含要使用的运行线程,环境变量,资源限制以及沙盒功能等。一些配置与平台无关,可用于 Linux,Windows,Solaris 以及特定的虚拟机;但另一些配置与平台相关,可能仅适用于 Linux。 70 | 71 | OCI 运行时规范还定义了容器的生命周期,这是一系列从容器创建到停止时发生的事件。 72 | 73 | ## 容器生命周期 74 | 75 | 容器拥有生命周期,从本质上讲,它可以用以下状态图建模。 76 | 77 | 你可以添加一些其他的动作和状态,如`pause`和`paused`,但是这些都是最oci基础的。 78 | 79 | ![oci3](./img/oci3.png) 80 | 81 | 此状态图比较常规,但是这里有一个重要的事情需要提及一下 - `Hooks`。你可能有些惊讶,容器规范并没有定义如何设置网络,它实际上依赖 hook 来正确的设置网络,比如在容器启动之前创建网络并在容器停止之后删除它。 82 | 83 | ## 容器配置 84 | 85 | 我们之前提过容器的配置包含创建和运行容器的必要配置,我们将更仔细地查看一些配置,以了解容器的真正含义,并且我们将专注于 Linux 平台上的所有配置。 86 | 87 | 1. Root 88 | 89 | 它定义了容器的根文件系统。 90 | 91 | 2. Mounts 92 | 93 | 它指定了可以挂在到根文件系统上的附加文件系统。你在使用它挂载本地路径或者分布式路径(如 Ceph)。 94 | 95 | 3. Process 96 | 97 | 它指定了与你要在容器中运行的进程相关的所有内容。包括环境变量和进程参数。 98 | 99 | 对于 Linux 进程,你可以额外指定有关进程安全方面的内容,如 capabilities,rlimits 以及 selinux 标签。 100 | 101 | 1. Hooks 102 | 103 | 你可以在此处连接到容器的生命周期,并执行网络设置与清理操作。 104 | 105 | 2. Linux 命名空间 106 | 107 | Linux 平台的大量配置专用于命名空间配置。实际上,命名空间是容器技术的基础。换句话说,没有命名空间就没有容器。Linux 提供了 7 种命名空间,它们都得到了 OCI 运行时规范的支持。 108 | 109 | |名称|宏定义|隔离的资源| 110 | |---|---|---| 111 | |IPC|CLONE_NEWIPC|System V IPC (信号量,消息队列和共享内存),POSIX message queues| 112 | |Network|CLONE_NEWNET|Network devices,stacks,ports (网络设备,网络栈,端口等)| 113 | |Mount|CLONE_NEWNS|Mount points (文件系统挂载点)| 114 | |PID|CLONE_NEWPID|Process IDs (进程编号)| 115 | |User|CLONE_NEWUSER|User and group IDs (用户和用户组)| 116 | |UTS|CLONE_NEWUTS|Hostname and NIS domain name (主机名与 NIS 域名)| 117 | |Cgroup|CLONE_NEWCGROUP|Cgroup root directory (cgroup 的根目录)| 118 | 119 | 3. 注释 120 | 121 | 除了容器应该运行的内容和方式之外,还可以通过注释标记容器。基于某些属性标记和选择容器的能力是容器编排 (orchestration) 平台的基本要求。 122 | 123 | ## 镜像,容器和进程 124 | 125 | 容器通过(容器)镜像创建。你可以通过一个镜像创建多个容器,还可以重新打包容器(这些容器通常带有一些基础容器的变更内容)以创建新的镜像。 126 | 127 | 在你获取容器之后,你可以在容器内部运行进程,而无需容器的所有优点。最值得注意的是,一旦我们容器化一个应用程序,它就会称为自包含的,不会与宿主环境混淆,因此它可以”run everwhere“。 128 | 129 | 以下是各种概念,镜像,容器和进程之间的关系,将它们理清楚是至关重要的。 130 | 131 | ![oci4](./img/oci4.png) 132 | 133 | ## Docker 和 Kubernetes 134 | 135 | Docker 使容器称为行业趋势,很多人认为 Docker 是容器,容器是 Docker。Docker 在这里绝对值得称赞,但从技术角度来看,Docker 是应用最广泛的容器实现。Docker 实现的架构从一个版本到另一个版本的演变非常快。在撰写本文时,它如下所示: 136 | 137 | ![oci5](./img/oci5.png) 138 | 139 | 该图遵循`github]Org/project`的格式。大多数组件源自 Docker,但目前在不同的 Github 组织和项目下。最上面使我们日常使用的 Docker 命令工具,它是 Docker Inc. 的商业产品;Docker 工具以来于一个名为 moby 的开源项目,该项目又使用了 runc,它是 oci 运行时规范的参考实现。runc 严重以来 libcontainer,它也是 Docker Inc. 捐赠的。 140 | 141 | ## 容器编排 142 | 143 | 如果我们只需要 1 到 2 个容器,Docker 可能就够了。但是如果我们要运行成千上万的容器,我们就有很多的问题需要解决。仅举几例: 144 | 145 | 1. 调度:容器放在哪个主机上? 146 | 2. 更新:如何更新容器镜像? 147 | 3. 伸缩性:当需要扩容时如何添加更多的容器? 148 | 149 | 这是容器编排系统的工作。Kubernetes 是其中之一,但是截至目前,我认为它是最有前途。这里我们不会深入研究 Kubernetes,而是从容器运行时如何适应容器编排平台的角度简要介绍一下。 150 | 151 | 下图中展示了 Kubernetes 是如何与容器运行时进行交互的。 152 | 153 | ![oci6](./img/oci6.png) 154 | 155 | Kubernetes 使用容器运行时接口对运行时实现进行解耦。简单来说,CRI 定义了创建,启动,停止和删除容器的接口。它允许 Kubernetes 使用可插拔式容器运行时,你不必锁定到某一个特定的运行时。目前有几种实现方式,如`cri-containerd`和`cri-o`,两者最终都会使用`oci/runc`。 156 | 157 | ## 总结 158 | 159 | 本文是对 OCI 运行时镜像和运行时规范的概述。它涵盖了每个规范的责任以及它们如何相互协作,我们回顾了容器生命周期和运行时规范的主要配置。然后介绍了 Docker 和`runc`的关系,并简要介绍了容器编排以及容器运行时是如何融入其中的。 -------------------------------------------------------------------------------- /src/oci/img/oci1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/oci/img/oci1.png -------------------------------------------------------------------------------- /src/oci/img/oci2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/oci/img/oci2.png -------------------------------------------------------------------------------- /src/oci/img/oci3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/oci/img/oci3.png -------------------------------------------------------------------------------- /src/oci/img/oci4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/oci/img/oci4.png -------------------------------------------------------------------------------- /src/oci/img/oci5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/oci/img/oci5.png -------------------------------------------------------------------------------- /src/oci/img/oci6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/oci/img/oci6.png -------------------------------------------------------------------------------- /src/oci/summary.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | - [OCI 规范概述](01-oci-spec-overview.md) -------------------------------------------------------------------------------- /src/架构/ServiceMesh/01-ServiceMesh.md: -------------------------------------------------------------------------------- 1 | # 模式:Service Mesh 2 | 3 | [原文](https://philcalcado.com/2017/08/03/pattern_service_mesh.html) 4 | 5 | 自从数十年前首次推出以来,我们了解到分布式系统可以提供我们之前甚至无法想到的用例,但是它们也引入了很多新的问题。 6 | 7 | 当这些系统很少且很简单的时候,工程师们通过减少远程交互的数量来应对新增的复杂性。处理分发 (distribution) 最安全的方式就是尽可能避免它,即使这可能导致跨系统的逻辑与数据重复。 8 | 9 | 但是,我们的工业需求推动我们进一步从几台大型中央计算机发展到成千上万台小型服务器。在这个新的领域,我们必须勇往直前,应对新的挑战和问题。首先逐例使用点对点 (ad-hoc) 的解决方案,然后 (subsequently) 使用更加复杂的解决方案。随着我们发现有关问题领域更多的信息并设计出更好的解决方案,我们开始将一些最通用的需求凝结 (crystallise) 成模式,库,最终是平台。 10 | 11 | ## 当我们第一次启动网络计算机时发生了什么 12 | 13 | 自从人们第一次对两个或更多计算机之间相互通信进行思考,他们设想 (envision) 以下模型: 14 | 15 | ![pattern_service_mesh](../img/pattern_service_mesh_1.png) 16 | 17 | 两个服务进行对话以实现用户的某些目标。这显然是过于简化的视图,因为缺少了在代码层面操作的字节与通过电线发送和接收的电信号之间转换的许多层。但是对于我们的讨论而言,这种抽象已经足够。让我们通过将网络堆栈显示为不同的组件来添加更多的细节。 18 | 19 | ![pattern_service_mesh](../img/pattern_service_mesh_2.png) 20 | 21 | 上面模型的变体已经在 1950 年就投入使用。最初,计算机比较稀有且很昂贵,因此两个节点间的每个连接需要被精心设计 (craft) 以及维护。当计算机变得廉价且更加流行时,它们之间的连接数量以及通过它们地数据量会剧烈的 (drastically) 增长。随着人们越来越依赖网络系统,工程师需要确保它们构建的软件符合用户要求的服务质量。 22 | 23 | 而且,想要达到所需(服务)质量的水平,还需要回答许多问题。人们需要一种方式,让机器可以互相发现;可以在同一根电缆中同时处理多个连接;当机器没有直连时,也允许彼此之间的通信;可以在网络中路由数据包,加密流量等等。 24 | 25 | 其中,有一个被称为 *流控* 的东西,我们将用它来举个例子。流控是一种机制,它可以阻止服务器发送超过下游服务处理能力的数据包。这是很有必要的,因为在一个网络系统中,你至少有两台彼此独立的计算机,它们互相都不了解。计算机 A 使用指定的速率向 计算机 B 发送字节流,但是不能保证 B 能够以一致且足够快的速度处理接收的字节流。举个例子,B 可能在并行运行其他的任务,或者数据包可能会乱序到达,但是 B 会阻塞等待应该首先到达的数据包。这意味着不仅 A 不会获得 B 的预期性能,它可能还会使事情变得更糟,因为它可能会让 B 过载导致其必须将这些传入的数据包排队进行处理。 26 | 27 | 一段时间以来,人们期望构建的网络服务和应用程序可以处理上面代码中显示的挑战。在我们的流控示例中,意味着应用程序自身必须包含相应的逻辑以确保我们不会因(传入过多的)数据包而使服务过载。繁重的网络逻辑与你的业务逻辑并存。对应的抽象图表如下所示: 28 | 29 | ![pattern_service_mesh_3](../img/pattern_service_mesh_3.png) 30 | 31 | 幸运的是,科技迅速发展,并很快有了足够的标准(如 TCP/IP)将流控的解决方案以及网络堆栈中的其他许多问题集成 (incorporate) 起来。这意味着那段代码依然存在,但是它已经从你的应用程序中剥离出来,并放到操作系统提供的网络层中: 32 | 33 | ![pattern_service_mesh](../img/pattern_service_mesh_4.png) 34 | 35 | 这个模型取得了巨大的成功。很多组织仅使用商业操作系统携带的 TCP/IP 协议栈来驱动他们的业务发展,且它已经足以应对高性能和可靠性的要求。 36 | 37 | ## 当我们第一次启动微服务时发生了什么 38 | 39 | 几年之后,计算机变得更加便宜并随处可见,上面描述的网络栈已经证明自己是实现系统间可靠连接事实上的 (do-facto) 工具集。有了更多的节点和稳定的连接,行业中就玩起了各种各样的网络系统,从细粒度的 (fine-grained) 的分布式代理和对象到广泛使用的分布式组件组成的面向服务的体系结构。 40 | 41 | 这种极端的分布带来了许多有趣的高级用例和收益,但是它依然要面对很多挑战。其中的某些挑战是全新的,而其他则只是我们讨论的原始网络挑战的更高版本。 42 | 43 | 在上世纪 90 年代,Peter Deutsch 和他在 Sun Microsystems 公司的工程师同伴合编了[”分布式计算的 8 个谬论 (Fallacy)“](https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing),其中列出了人们在使用分布式系统时倾向做出的一些假设。Peter 的观点是,这些可能在原始网络架构或理论模型中是正确的,但是在现代界中却不成立: 44 | 45 | 1. 网络是可靠的 46 | 2. 没有网路延迟 47 | 3. 无限带宽 48 | 4. 网络是安全的 49 | 5. 拓扑结构 (Topology) 不会改变 50 | 6. 只有一个管理员 51 | 7. 传输是零成本开销 52 | 8. 网络是同质的(homogeneous),即各处的网络状态完全一样 53 | 54 | 将上述的列表抨击 (Denounce) 为”谬论“是因为工程师无法忽视这些问题,他们需要明确的处理它们。 55 | 56 | 为了使问题进一步复杂化,在我们经常称之为*微服务架构*的分布式系统中,在可操作性方面引入了新的需求。[我们在之前讨论过这些细节](https://philcalcado.com/2017/06/11/calcados_microservices_prerequisites.html),但是在这里,我们将其归纳成一个清单: 57 | 58 | 1. 快速配置 (provisioning) 计算机资源 59 | 2. 基础监控 60 | 3. 快速发布 61 | 4. 易于配置存储 62 | 5. 易于访问边界 63 | 6. 鉴权/授权 64 | 7. 标准化的 RPC 65 | 66 | 因此,尽管数十年前开发的 TCP/IP 协议栈和通用网络模型仍然是可以让计算机彼此通信的强大工具,但是更复杂的体系构又引入了另一层要求,必须由在这样的架构中工作的工程师来实现。 67 | 68 | 例如服务发现和服务熔断,这是用于解决上面列出的一些弹性 (resiliency) 和分布式挑战的技术。 69 | 70 | 正如历史会周期性的出现,基于微服务构建系统的第一批组织使用的策略与前几代联网计算机使用的策略十分相似。这意味着处理上面列出的需求的责任留给了编写服务的工程师。 71 | 72 | ![pattern_service_mesh5](../img/pattern_service_mesh_5.png) 73 | 74 | 服务发现是一个自动查找哪些服务实例满足给定查询的过程。例如,一个名为`Teams`的服务使用值为`production`的`environment`属性查询名为`Players`服务的实例。你将会调用一些服务发现的进程,这些进程将返回合适的服务节点的列表。对于更加整体 (monolithic) 架构,这是一个简单的任务,通常使用 DNS,负载均衡和一些关于端口的约定 (convention) 来实现(例如,所有服务将其 HTTP 服务绑定到 8080 端口)。在分布式环境中,这类任务将变得更加复杂,以前可以盲目的依靠 DNS 查找依赖关系的服务现在必须处理诸如客户端的负载均衡,多个不同环境(如:staging 和 production),地理位置分散的服务器等。如果你之前只需一行代码来解析主机名,那么现在你的服务就需要更多的样板代码来处理分布式引入的各种极端情况 (various corner cases)。 75 | 76 | 熔断器是 Michael Nygard 在他的 [Release It](http://amzn.to/2viDf23) 书中分类的 (catalogued) 一种模式。我喜欢 [Martin Fowler 对这个模式的总结](https://martinfowler.com/bliki/CircuitBreaker.html): 77 | 78 | > 熔断器的原理非常简单。你在用于检测故障的熔断器对象中封装了一个受保护的函数调用。一旦故障达到某个阈值,熔断器将会跳闸,并且所有对该熔断器进一步的调用都会返回错误,根本不会进行被保护的调用。通常如果熔断器跳闸,你还需要某种监视器告警。 79 | 80 | 这些都是简单出色的设备,可以为服务之间的交互增加更多的可靠性。然而,就像其他所有的内容一样,随着分布式水平的提高,它们往往会变得更加复杂。系统中出现问题的可能性随着分布式水平的提高呈现指数级增长,因此,即使诸如“熔断器跳闸时出现某种监视器告警”之类的简单事情也不一定变得直接了当。一个组件中的某个故障会在许多客户端之间造成级联影响,从而触发成千上万的线路同时跳闸。过去仅需几行代码,现在又需要大量的样板代码来处理仅在这个新的领域中存在的问题。 81 | 82 | 事实上,上面的两个例子很难正确的实现,以至于某些大型且复杂的库(如 [Twitter 的 Finagle](https://finagle.github.io/) 和 [Facebook 的 Proxygen](https://code.facebook.com/posts/1503205539947302)) 非常受欢迎,因为它避免在每个服务中重写相同的逻辑。 83 | 84 | ![pattern_service_mesh5-a](../img/pattern_service_mesh5-a.png) 85 | 86 | 上面描述的 (depict) 的模型被大多数开创了微服务架构的组织所采用,例如:Netflix,Twitter 以及 SoundCloud。随着系统中服务数量的增加,它们还偶然发现 (stumble upon) 了这类方法的各种缺点。 87 | 88 | 即使使用像 Finagle 这样的库,最大的挑战可能是组织仍需要花费其工程团队的时间来构建将库与其他生态系统联系起来的粘合剂。根据我在 SoundCloud 和 DigitalOcean 的经验,我估计在 100 到 250 人的工程师组织中采取这种策略后,需要让 1/10 的员工专门负责构建工具。有时,这笔费用是明确的,因为工程师被分配到了专门负责构建工具的团队中,但是价格标签通常是不可见的,因为它会随着你使用产品时间的增长而消失。 89 | 90 | 第二个问题是,以上设置限制了微服务的工具,运行时和语言。微服务的库通常是为特定平台编写的,无论是编程语言还是诸如 JVM 之类的运行时。如果组织使用库不支持的平台时,通常需要先将代码移植到新平台中。这浪费了宝贵的 (scarce) 工程时间。工程师不必再致力于核心业务和产品,而必须再次构建工具和基础架构。这就是为什么像 SoundCloud 和 DigitalOcean 之类的中型组织决定仅支持其内部服务的一个平台的原因,分别是 Scala 和 Go。 91 | 92 | 这个模型值得讨论的最后一个问题是治理 (governance)。库模型可以抽象化处理微服务体系结构中所需功能的实现,但是它本身仍然是需要维护的组件。确保成千上万的服务实例使用相同或至少兼容的库版本并非易事,并且每次更新都意味着所有服务的的集成,测试以及重新部署,即使该服务本身并未发生改动。 93 | 94 | ## 下一个合乎逻辑的步骤 95 | 96 | 与我们在网络堆栈中看到的类似,非常需要将大规模分布式服务所需的功能提取到基础平台中。 97 | 98 | 人们使用 HTTP 等高级协议编写非常复杂的应用程序和服务,而无需考虑 TCP 如何控制该网络上的数据包。这种场景正是微服务所需要的,在微服务中,从事服务的工程师可以专注于他们的业务逻辑,避免浪费时间编写自己的服务基础架构的代码或管理整个团队中的库和框架。 99 | 100 | 将这个想法整合到我们图表中,我们可能会得到如下所示的结果: 101 | 102 | ![pattern_service_mesh](../img/pattern_service_mesh_6.png) 103 | 104 | 不幸的是,修改网络堆栈以添加此层不是一项可行的 (feasible) 任务。许多从业人员使用的解决方案是将其作为一组代理来实现。这里的想法是,服务不会直接连接其下游依赖项,而是将所有流量通过一小段程序透明的添加所需的功能。 105 | 106 | 在这个领域中,第一次使用了边车 (sidecar) 的概念。边车是一个辅助 (auxiliary) 进程,运行在你的应用程序周围,并为它提供额外的功能。在 2013 年,[Airbnb 编写了 Synapse 和 Nerve](https://medium.com/airbnb-engineering/smartstack-service-discovery-in-the-cloud-4b8a080de619),这是边车的开源实现。一年后,[Netflix 推出了 Prana](https://medium.com/netflix-techblog/prana-a-sidecar-for-your-netflix-paas-based-applications-and-services-258a5790a015),它是一种边车,致力于允许非 JVM 应用从 [Netflix OSS 生态系统](http://netflix.github.io/)中受益。在 SoundCloud 中,我们构建了边车,使我们的传统 Ruby 程序可以使用[我们为 JVM 微服务构建的基础架构](https://www.youtube.com/watch?v=ttmuN8hPQLA)。 107 | 108 | ![pattern_service_mesh6](../img/pattern_service_mesh6-a.png) 109 | 110 | 尽管所许多此类开源代理的实现,但它们往往被设计成与特定的基础架构的组件一起使用。例如,在服务发现方面,Airbnb 的 Nerve & Synapse 假定服务注册在 Zookeeper 中,而对于 Prana,则应使用 Netflix 自己的 [Eureka](https://github.com/Netflix/eureka) 服务注册组件。 111 | 112 | 随着微服务架构的日益普及,最近我们看到了新一轮的代理浪潮,这些代理足够灵活,可以适应不同的基础架构组件和偏好设置。这个领域第一个广为人知的系统是 [Linkerd](https://buoyant.io/2016/02/18/linkerd-twitter-style-operability-for-microservices/),它是由 Buoyant 根据工程师在 Twitter 微服务平台上的先前工作创建的。很快,Lyft 工程团队发布了 [Envoy](https://eng.lyft.com/announcing-envoy-c-l7-proxy-and-communication-bus-92520b6c8191),它遵循类似的原则。 113 | 114 | ## Service Mesh 115 | 116 | 在这种模型中,你的每一个服务都将伴随一个代理边车 (sidecar),鉴于服务仅通过边车代理相互通信,因此最终我们得到了如下图所示的部署: 117 | 118 | ![mesh1](../img/mesh1.png) 119 | 120 | Buoyant 公司的 CEO [William Morgan](https://twitter.com/wm) 观察到,代理之间的互联形成了[网状网络](https://en.wikipedia.org/wiki/Mesh_networking)。[在 2017 年初,William 为该平台下了一个定义,并称其为 Service Mesh](https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/): 121 | 122 | > Service Mesh 是用于处理服务到服务之间通信的专用基础架构层。它负责通过构建现代云原生应用程序的复杂服务拓扑来可靠地传递请求。实际上,Service Mesh 通常被实现为轻量级网络代理的阵列,这些代理与应用程序的代码一起部署,但是不需要了解应用程序的具体逻辑。 123 | 124 | 他的定义中最有力的方面可能是,它摆脱了将代理视为独立组件的想法,并认识到它们形成的网络本身就是有价值的东西。 125 | 126 | ![mesh2](../img/mesh2.png) 127 | 128 | 随着组织将其微服务部署移至更复杂的运行时(如 k8s 和 Mesos),人们和组织已经开始使用那些平台提供的工具来正确的实现这个网状网络的思想。它们正在从一组隔离工作的独立代理转移到一个适当的,有点集中 (centralise) 的控制平面。 129 | 130 | ![pattern_service_mesh6-b](../img/pattern_service_mesh6-b.png) 131 | 132 | 纵观我们的鸟瞰图,我们看到实际的服务流量仍然直接地从代理流向代理,但是控制平面知道每一个代理实例。它使代理可以实现诸如访问控制和指标收集之类的事情,这需要协作: 133 | 134 | ![mesh3](../img/mesh3.png) 135 | 136 | 最近发布的 [Istio](https://istio.io/) 项目就是此类系统最突出 (prominent) 的示例。 137 | 138 | 全面了解 Service Mesh 在大规模系统中的影响还为时过早。这个方法的两个好处对我来说已经很明显 (evident) 了。首先,不必编写定制软件来处理微服务体系结构的最终商品代码,这将使许多较小的组织可以使用到以前仅大型企业才能使用的特性,从而创建各种有趣的用例。其次,这种体系结构可能让我们最终实现使用最佳工具/语言完成工作的梦想,而不用担心每个平台库和模式的可用性。 -------------------------------------------------------------------------------- /src/架构/ServiceMesh/summary.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | - [模式:Service Mesh](01-ServiceMesh.md) 4 | - [xDS 与 gRPC 协议](02-xDS与gRPC协议.md) -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/1.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/2.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/3.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/4.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/5-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/5-a.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/5.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/6-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/6-a.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/6-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/6-b.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/6.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/main.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Reset some basic elements 3 | */ 4 | body, h1, h2, h3, h4, h5, h6, 5 | p, blockquote, pre, hr, 6 | dl, dd, ol, ul, figure { 7 | margin: 0; 8 | padding: 0; } 9 | 10 | /** 11 | * Basic styling 12 | */ 13 | body { 14 | font-family: Helvetica, Arial, sans-serif; 15 | font-size: 16px; 16 | line-height: 1.5; 17 | font-weight: 300; 18 | color: #111; 19 | background-color: #fdfdfd; 20 | -webkit-text-size-adjust: 100%; } 21 | 22 | /** 23 | * Set `margin-bottom` to maintain vertical rhythm 24 | */ 25 | h1, h2, h3, h4, h5, h6, 26 | p, blockquote, pre, 27 | ul, ol, dl, figure, 28 | .highlight { 29 | margin-bottom: 15px; } 30 | 31 | /** 32 | * Images 33 | */ 34 | img { 35 | max-width: 100%; 36 | vertical-align: middle; } 37 | 38 | /** 39 | * Figures 40 | */ 41 | figure > img { 42 | display: block; } 43 | 44 | figcaption { 45 | font-size: 14px; } 46 | 47 | /** 48 | * Lists 49 | */ 50 | ul, ol { 51 | margin-left: 30px; } 52 | 53 | li > ul, 54 | li > ol { 55 | margin-bottom: 0; } 56 | 57 | /** 58 | * Headings 59 | */ 60 | h1, h2, h3, h4, h5, h6 { 61 | font-weight: 300; } 62 | 63 | /** 64 | * Links 65 | */ 66 | a { 67 | color: #2a7ae2; 68 | text-decoration: none; } 69 | a:visited { 70 | color: #1756a9; } 71 | a:hover { 72 | color: #111; 73 | text-decoration: underline; } 74 | 75 | /** 76 | * Blockquotes 77 | */ 78 | blockquote { 79 | color: #828282; 80 | border-left: 4px solid #e8e8e8; 81 | padding-left: 15px; 82 | font-size: 18px; 83 | letter-spacing: -1px; 84 | font-style: italic; } 85 | blockquote > :last-child { 86 | margin-bottom: 0; } 87 | 88 | /** 89 | * Code formatting 90 | */ 91 | pre, 92 | code { 93 | font-size: 15px; 94 | border: 1px solid #e8e8e8; 95 | border-radius: 3px; 96 | background-color: #eef; } 97 | 98 | code { 99 | padding: 1px 5px; } 100 | 101 | pre { 102 | padding: 8px 12px; 103 | overflow-x: scroll; } 104 | pre > code { 105 | border: 0; 106 | padding-right: 0; 107 | padding-left: 0; } 108 | 109 | /** 110 | * Wrapper 111 | */ 112 | .wrapper { 113 | max-width: -webkit-calc(800px - (30px * 2)); 114 | max-width: calc(800px - (30px * 2)); 115 | margin-right: auto; 116 | margin-left: auto; 117 | padding-right: 30px; 118 | padding-left: 30px; } 119 | @media screen and (max-width: 800px) { 120 | .wrapper { 121 | max-width: -webkit-calc(800px - (30px)); 122 | max-width: calc(800px - (30px)); 123 | padding-right: 15px; 124 | padding-left: 15px; } } 125 | 126 | /** 127 | * Clearfix 128 | */ 129 | .wrapper:after, .footer-col-wrapper:after { 130 | content: ""; 131 | display: table; 132 | clear: both; } 133 | 134 | /** 135 | * Icons 136 | */ 137 | .icon > svg { 138 | display: inline-block; 139 | width: 16px; 140 | height: 16px; 141 | vertical-align: middle; } 142 | .icon > svg path { 143 | fill: #828282; } 144 | 145 | /** 146 | * Site header 147 | */ 148 | .site-header { 149 | border-top: 5px solid #424242; 150 | border-bottom: 1px solid #e8e8e8; 151 | min-height: 56px; 152 | position: relative; } 153 | 154 | .site-title { 155 | font-size: 26px; 156 | line-height: 56px; 157 | letter-spacing: -1px; 158 | margin-bottom: 0; 159 | float: left; } 160 | .site-title, .site-title:visited { 161 | color: #424242; } 162 | 163 | .site-nav { 164 | float: right; 165 | line-height: 56px; } 166 | .site-nav .menu-icon { 167 | display: none; } 168 | .site-nav .page-link { 169 | color: #111; 170 | line-height: 1.5; } 171 | .site-nav .page-link:not(:first-child) { 172 | margin-left: 20px; } 173 | @media screen and (max-width: 600px) { 174 | .site-nav { 175 | position: absolute; 176 | top: 9px; 177 | right: 30px; 178 | background-color: #fdfdfd; 179 | border: 1px solid #e8e8e8; 180 | border-radius: 5px; 181 | text-align: right; } 182 | .site-nav .menu-icon { 183 | display: block; 184 | float: right; 185 | width: 36px; 186 | height: 26px; 187 | line-height: 0; 188 | padding-top: 10px; 189 | text-align: center; } 190 | .site-nav .menu-icon > svg { 191 | width: 18px; 192 | height: 15px; } 193 | .site-nav .menu-icon > svg path { 194 | fill: #424242; } 195 | .site-nav .trigger { 196 | clear: both; 197 | display: none; } 198 | .site-nav:hover .trigger { 199 | display: block; 200 | padding-bottom: 5px; } 201 | .site-nav .page-link { 202 | display: block; 203 | padding: 5px 10px; } } 204 | 205 | /** 206 | * Site footer 207 | */ 208 | .site-footer { 209 | border-top: 1px solid #e8e8e8; 210 | padding: 30px 0; } 211 | 212 | .footer-heading { 213 | font-size: 18px; 214 | margin-bottom: 15px; } 215 | 216 | .contact-list, 217 | .social-media-list { 218 | list-style: none; 219 | margin-left: 0; } 220 | 221 | .footer-col-wrapper { 222 | font-size: 15px; 223 | color: #828282; 224 | margin-left: -15px; } 225 | 226 | .footer-col { 227 | float: left; 228 | margin-bottom: 15px; 229 | padding-left: 15px; } 230 | 231 | .footer-col-1 { 232 | width: -webkit-calc(35% - (30px / 2)); 233 | width: calc(35% - (30px / 2)); } 234 | 235 | .footer-col-2 { 236 | width: -webkit-calc(20% - (30px / 2)); 237 | width: calc(20% - (30px / 2)); } 238 | 239 | .footer-col-3 { 240 | width: -webkit-calc(45% - (30px / 2)); 241 | width: calc(45% - (30px / 2)); } 242 | 243 | @media screen and (max-width: 800px) { 244 | .footer-col-1, 245 | .footer-col-2 { 246 | width: -webkit-calc(50% - (30px / 2)); 247 | width: calc(50% - (30px / 2)); } 248 | 249 | .footer-col-3 { 250 | width: -webkit-calc(100% - (30px / 2)); 251 | width: calc(100% - (30px / 2)); } } 252 | @media screen and (max-width: 600px) { 253 | .footer-col { 254 | float: none; 255 | width: -webkit-calc(100% - (30px / 2)); 256 | width: calc(100% - (30px / 2)); } } 257 | /** 258 | * Page content 259 | */ 260 | .page-content { 261 | padding: 30px 0; } 262 | 263 | .page-heading { 264 | font-size: 20px; } 265 | 266 | .post-list { 267 | margin-left: 0; 268 | list-style: none; } 269 | .post-list > li { 270 | margin-bottom: 30px; } 271 | 272 | .post-meta { 273 | font-size: 14px; 274 | color: #828282; } 275 | 276 | .post-link { 277 | display: block; 278 | font-size: 24px; } 279 | 280 | /** 281 | * Posts 282 | */ 283 | .post-header { 284 | margin-bottom: 30px; } 285 | 286 | .post-title { 287 | font-size: 42px; 288 | letter-spacing: -1px; 289 | line-height: 1; } 290 | @media screen and (max-width: 800px) { 291 | .post-title { 292 | font-size: 36px; } } 293 | 294 | .post-content { 295 | margin-bottom: 30px; } 296 | .post-content h2 { 297 | font-size: 32px; } 298 | @media screen and (max-width: 800px) { 299 | .post-content h2 { 300 | font-size: 28px; } } 301 | .post-content h3 { 302 | font-size: 26px; } 303 | @media screen and (max-width: 800px) { 304 | .post-content h3 { 305 | font-size: 22px; } } 306 | .post-content h4 { 307 | font-size: 20px; } 308 | @media screen and (max-width: 800px) { 309 | .post-content h4 { 310 | font-size: 18px; } } 311 | 312 | /** 313 | * Syntax highlighting styles 314 | */ 315 | .highlight { 316 | background: #fff; } 317 | .highlight .c { 318 | color: #998; 319 | font-style: italic; } 320 | .highlight .err { 321 | color: #a61717; 322 | background-color: #e3d2d2; } 323 | .highlight .k { 324 | font-weight: bold; } 325 | .highlight .o { 326 | font-weight: bold; } 327 | .highlight .cm { 328 | color: #998; 329 | font-style: italic; } 330 | .highlight .cp { 331 | color: #999; 332 | font-weight: bold; } 333 | .highlight .c1 { 334 | color: #998; 335 | font-style: italic; } 336 | .highlight .cs { 337 | color: #999; 338 | font-weight: bold; 339 | font-style: italic; } 340 | .highlight .gd { 341 | color: #000; 342 | background-color: #fdd; } 343 | .highlight .gd .x { 344 | color: #000; 345 | background-color: #faa; } 346 | .highlight .ge { 347 | font-style: italic; } 348 | .highlight .gr { 349 | color: #a00; } 350 | .highlight .gh { 351 | color: #999; } 352 | .highlight .gi { 353 | color: #000; 354 | background-color: #dfd; } 355 | .highlight .gi .x { 356 | color: #000; 357 | background-color: #afa; } 358 | .highlight .go { 359 | color: #888; } 360 | .highlight .gp { 361 | color: #555; } 362 | .highlight .gs { 363 | font-weight: bold; } 364 | .highlight .gu { 365 | color: #aaa; } 366 | .highlight .gt { 367 | color: #a00; } 368 | .highlight .kc { 369 | font-weight: bold; } 370 | .highlight .kd { 371 | font-weight: bold; } 372 | .highlight .kp { 373 | font-weight: bold; } 374 | .highlight .kr { 375 | font-weight: bold; } 376 | .highlight .kt { 377 | color: #458; 378 | font-weight: bold; } 379 | .highlight .m { 380 | color: #099; } 381 | .highlight .s { 382 | color: #d14; } 383 | .highlight .na { 384 | color: #008080; } 385 | .highlight .nb { 386 | color: #0086B3; } 387 | .highlight .nc { 388 | color: #458; 389 | font-weight: bold; } 390 | .highlight .no { 391 | color: #008080; } 392 | .highlight .ni { 393 | color: #800080; } 394 | .highlight .ne { 395 | color: #900; 396 | font-weight: bold; } 397 | .highlight .nf { 398 | color: #900; 399 | font-weight: bold; } 400 | .highlight .nn { 401 | color: #555; } 402 | .highlight .nt { 403 | color: #000080; } 404 | .highlight .nv { 405 | color: #008080; } 406 | .highlight .ow { 407 | font-weight: bold; } 408 | .highlight .w { 409 | color: #bbb; } 410 | .highlight .mf { 411 | color: #099; } 412 | .highlight .mh { 413 | color: #099; } 414 | .highlight .mi { 415 | color: #099; } 416 | .highlight .mo { 417 | color: #099; } 418 | .highlight .sb { 419 | color: #d14; } 420 | .highlight .sc { 421 | color: #d14; } 422 | .highlight .sd { 423 | color: #d14; } 424 | .highlight .s2 { 425 | color: #d14; } 426 | .highlight .se { 427 | color: #d14; } 428 | .highlight .sh { 429 | color: #d14; } 430 | .highlight .si { 431 | color: #d14; } 432 | .highlight .sx { 433 | color: #d14; } 434 | .highlight .sr { 435 | color: #009926; } 436 | .highlight .s1 { 437 | color: #d14; } 438 | .highlight .ss { 439 | color: #990073; } 440 | .highlight .bp { 441 | color: #999; } 442 | .highlight .vc { 443 | color: #008080; } 444 | .highlight .vg { 445 | color: #008080; } 446 | .highlight .vi { 447 | color: #008080; } 448 | .highlight .il { 449 | color: #099; } 450 | -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/mesh1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/mesh1.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/mesh2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/mesh2.png -------------------------------------------------------------------------------- /src/架构/img/Pattern_ Service Mesh_files/mesh3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/Pattern_ Service Mesh_files/mesh3.png -------------------------------------------------------------------------------- /src/架构/img/cds-eds-resources.svg: -------------------------------------------------------------------------------- 1 | participant Envoy as E [color="black"] 2 | participant Management Server 0 as M0 [color="black"] 3 | participant Management Server 1 as M1 [color="black"] 4 | 5 | E->M1: (V=..,R={},N=..,T=CDS) [color="green"] 6 | E->M0: (V=X,R={foo},N=A,T=EDS) [color="green"] 7 | M1->E: (V=M,R={foo:...,bar:...},N=D,T=CDS) [color="gray"] 8 | E->M0: (V=X,R={foo,bar},N=A,T=EDS [color="green"]Created with Raphaël 2.2.0EnvoyEnvoyManagement Server 0Management Server 0Management Server 1Management Server 1(V=..,R={},N=..,T=CDS)(V=X,R={foo},N=A,T=EDS)(V=M,R={foo:...,bar:...},N=D,T=CDS)(V=X,R={foo,bar},N=A,T=EDS -------------------------------------------------------------------------------- /src/架构/img/eds-same-stream.svg: -------------------------------------------------------------------------------- 1 | participant Envoy as E [color="black"] 2 | participant Management Server as M [color="black"] 3 | 4 | E->M: (V=X,R={foo,bar},N=A,T=EDS) [color="green"] 5 | M->E: (V=Y,R={foo:...,bar:...},N=B,T=EDS) [color="gray"] 6 | E->M: (V=Y,R={foo,bar},N=B,T=EDS) [color="green"]Created with Raphaël 2.2.0EnvoyEnvoyManagement ServerManagement Server(V=X,R={foo,bar},N=A,T=EDS)(V=Y,R={foo:...,bar:...},N=B,T=EDS)(V=Y,R={foo,bar},N=B,T=EDS) -------------------------------------------------------------------------------- /src/架构/img/incremental-reconnect.svg: -------------------------------------------------------------------------------- 1 | Created with Raphaël 2.2.0EnvoyEnvoyManagement ServerManagement Server{T=CDS}{R={(foo, v0), (bar, v0),nonce=n0, T=CDS}{response_nonce=n0, T=CDS} (ACK)Session is interrupted here. Reconnect.{initial_resource_versions={(foo, v0), (bar, v0)}, T=CDS} -------------------------------------------------------------------------------- /src/架构/img/later-ack.svg: -------------------------------------------------------------------------------- 1 | participant Envoy as E [color="black"] 2 | participant Management Server as M [color="black"] 3 | 4 | E->M: (V=,R={foo},N=,T=EDS) [color="green"] 5 | M->E: (V=X,R={foo:...},N=A,T=EDS) [color="gray"] 6 | E->M: (V=,R={foo},N=A,T=EDS) [color="red"] 7 | M->E: (V=Y,R={foo:...},N=B,T=EDS) [color="gray"] 8 | E->M: (V=Y,R={foo},N=B,T=EDS) [color="green"]Created with Raphaël 2.2.0EnvoyEnvoyManagement ServerManagement Server(V=,R={foo},N=,T=EDS)(V=X,R={foo:...},N=A,T=EDS)(V=,R={foo},N=A,T=EDS)(V=Y,R={foo:...},N=B,T=EDS)(V=Y,R={foo},N=B,T=EDS) -------------------------------------------------------------------------------- /src/架构/img/mesh1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/mesh1.png -------------------------------------------------------------------------------- /src/架构/img/mesh2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/mesh2.png -------------------------------------------------------------------------------- /src/架构/img/mesh3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/mesh3.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh5-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh5-a.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh6-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh6-a.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh6-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh6-b.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh_1.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh_2.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh_3.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh_4.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh_5.png -------------------------------------------------------------------------------- /src/架构/img/pattern_service_mesh_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/pattern_service_mesh_6.png -------------------------------------------------------------------------------- /src/架构/img/simple-ack.svg: -------------------------------------------------------------------------------- 1 | participant Envoy as E [color="black"] 2 | participant Management Server as M [color="black"] 3 | 4 | E->M: (V=,R={foo},N=,T=EDS) [color="green"] 5 | M->E: (V=X,R={foo:...},N=A,T=EDS) [color="gray"] 6 | E->M: (V=X,R={foo},N=A,T=EDS) [color="green"]Created with Raphaël 2.2.0EnvoyEnvoyManagement ServerManagement Server(V=,R={foo},N=,T=EDS)(V=X,R={foo:...},N=A,T=EDS)(V=X,R={foo},N=A,T=EDS) -------------------------------------------------------------------------------- /src/架构/img/simple-nack.svg: -------------------------------------------------------------------------------- 1 | participant Envoy as E [color="black"] 2 | participant Management Server as M [color="black"] 3 | 4 | E->M: (V=,R={foo},N=,T=EDS) [color="green"] 5 | M->E: (V=X,R={foo:...},N=A,T=EDS) [color="gray"] 6 | E->M: (V=,R={foo},N=A,T=EDS) [color="red"]Created with Raphaël 2.2.0EnvoyEnvoyManagement ServerManagement Server(V=,R={foo},N=,T=EDS)(V=X,R={foo:...},N=A,T=EDS)(V=,R={foo},N=A,T=EDS) -------------------------------------------------------------------------------- /src/架构/img/tcc/2pc-xa-tansaction-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/tcc/2pc-xa-tansaction-model.png -------------------------------------------------------------------------------- /src/架构/img/tcc/communication-in-three-phase-commit-protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/tcc/communication-in-three-phase-commit-protocol.png -------------------------------------------------------------------------------- /src/架构/img/tcc/communication-in-two-phase-commit-protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/tcc/communication-in-two-phase-commit-protocol.png -------------------------------------------------------------------------------- /src/架构/img/tcc/coordinator-participants.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/tcc/coordinator-participants.png -------------------------------------------------------------------------------- /src/架构/img/tcc/distributed-dead-locking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/tcc/distributed-dead-locking.png -------------------------------------------------------------------------------- /src/架构/img/tcc/flat-and-nested-transactions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/tcc/flat-and-nested-transactions.png -------------------------------------------------------------------------------- /src/架构/img/tcc/properties-of-soft-transaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fucking-translation/blog/058b4871f1e265d9032f2a7ee9314052defffd1b/src/架构/img/tcc/properties-of-soft-transaction.png -------------------------------------------------------------------------------- /src/架构/分布式/summary.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | - [分布式事务/系统之底层原理揭秘](事务/01-分布式事务_系统之底层原理揭秘.md) -------------------------------------------------------------------------------- /src/架构/分布式/事务/summary.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | - [分布式事务/系统之底层原理揭秘](01-分布式事务_系统之底层原理揭秘.md) -------------------------------------------------------------------------------- /src/网络协议/img/kcp_1.svg: -------------------------------------------------------------------------------- 1 |
KCP
KCP
ikcp_recv
ikcp_recv
ikcp_send
ikcp_send
ikcp_input
ikcp_input
output
output
Viewer does not support full SVG 1.1
2 | -------------------------------------------------------------------------------- /src/网络协议/img/kcp_4.svg: -------------------------------------------------------------------------------- 1 |
2
2
3
3
1
1
rcv_wnd
rcv_wnd
rcv_nxt
rcv_nxt
5
5
6
6
nrcv_que
nrcv_que
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /src/网络协议/summary.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | - [「转」详解 KCP 协议的原理和实现](./kcp/详解KCP协议的原理和实现.md) -------------------------------------------------------------------------------- /theme/2018-edition.css: -------------------------------------------------------------------------------- 1 | span.caption { 2 | font-size: .8em; 3 | font-weight: 600; 4 | } 5 | 6 | span.caption code { 7 | font-size: 0.875em; 8 | font-weight: 400; 9 | } -------------------------------------------------------------------------------- /theme/ferris.css: -------------------------------------------------------------------------------- 1 | body.light .does_not_compile, 2 | body.light .panics, 3 | body.light .not_desired_behavior, 4 | body.rust .does_not_compile, 5 | body.rust .panics, 6 | body.rust .not_desired_behavior { 7 | background: #fff1f1; 8 | } 9 | 10 | body.coal .does_not_compile, 11 | body.coal .panics, 12 | body.coal .not_desired_behavior, 13 | body.navy .does_not_compile, 14 | body.navy .panics, 15 | body.navy .not_desired_behavior, 16 | body.ayu .does_not_compile, 17 | body.ayu .panics, 18 | body.ayu .not_desired_behavior { 19 | background: #501f21; 20 | } 21 | 22 | .ferris { 23 | position: absolute; 24 | z-index: 99; 25 | right: 5px; 26 | top: 30px; 27 | width: 10%; 28 | height: auto; 29 | } 30 | 31 | .ferris-explain { 32 | width: 100px; 33 | } -------------------------------------------------------------------------------- /theme/ferris.js: -------------------------------------------------------------------------------- 1 | var ferrisTypes = [ 2 | { 3 | attr: 'does_not_compile', 4 | title: 'This code does not compile!' 5 | }, 6 | { 7 | attr: 'panics', 8 | title: 'This code panics!' 9 | }, 10 | { 11 | attr: 'unsafe', 12 | title: 'This code block contains unsafe code.' 13 | }, 14 | { 15 | attr: 'not_desired_behavior', 16 | title: 'This code does not produce the desired behavior.' 17 | } 18 | ] 19 | 20 | document.addEventListener('DOMContentLoaded', () => { 21 | for (var ferrisType of ferrisTypes) { 22 | attachFerrises(ferrisType) 23 | } 24 | }) 25 | 26 | function attachFerrises (type) { 27 | var elements = document.getElementsByClassName(type.attr) 28 | 29 | for (var codeBlock of elements) { 30 | var lines = codeBlock.textContent.split(/\r|\r\n|\n/).length - 1; 31 | 32 | if (lines >= 4) { 33 | attachFerris(codeBlock, type) 34 | } 35 | } 36 | } 37 | 38 | function attachFerris (element, type) { 39 | var a = document.createElement('a') 40 | a.setAttribute('href', 'ch00-00-introduction.html#ferris') 41 | a.setAttribute('target', '_blank') 42 | 43 | var img = document.createElement('img') 44 | img.setAttribute('src', 'img/ferris/' + type.attr + '.svg') 45 | img.setAttribute('title', type.title) 46 | img.className = 'ferris' 47 | 48 | a.appendChild(img) 49 | 50 | element.parentElement.insertBefore(a, element) 51 | } 52 | --------------------------------------------------------------------------------