用 Linux、Python 和树莓派酿制啤酒

Summary: 容量提升到 10 加仑之后，我偶然看到了 StrangeBrew Elsinore ，我意识到我真正需要的是将整个酿酒过程转换成全电子化的，用树莓派来运行它。

>
> 怎样在家用 Python 和树莓派搭建一个家用便携的自制酿啤酒装置
>
>
>

![Brewing beer with Linux, Python, and Raspberry Pi](/data/attachment/album/201710/08/213019kf6388tfx16chbgf.png "Brewing beer with Linux, Python, and Raspberry Pi")

大约十年前我开始酿制自制啤酒，和许多自己酿酒的人一样，我开始在厨房制造提纯啤酒。这需要一些设备并且做出来后确实是好的啤酒，最终，我用一个放入了所有大麦的大贮藏罐作为我的麦芽浆桶。几年之后我一次酿制过 5 加仑啤酒，但是酿制 10 加仑时也会花费同样的时间和效用（只是容器比之前大些），之前我就是这么做的。容量提升到 10 加仑之后，我偶然看到了 [StrangeBrew Elsinore](http://dougedey.github.io/SB_Elsinore_Server/) ，我意识到我真正需要的是将整个酿酒过程转换成全电子化的，用树莓派来运行它。

建造自己的家用电动化酿酒系统需要大量这方面的技术信息，许多学习酿酒的人是在 [TheElectricBrewery.com](http://theelectricbrewery.com/) 这个网站起步的，只不过将那些控制版搭建在一起是十分复杂的，尽管最简单的办法在这个网站上总结的很好。当然你也能用[一个小成本的方法](http://www.instructables.com/id/Electric-Brewery-Control-Panel-on-the-Cheap/)并且依旧可以得到相同的结果 —— 用一个热水壶和热酒容器通过一个 PID 控制器来加热你的酿酒原料。但是我认为这有点太无聊（这也意味着你不能体验到完整的酿酒过程）。

### 需要用到的硬件

在我开始我的这个项目之前, 我决定开始买零件，我最基础的设计是一个可以将液体加热到 5500 瓦的热酒容器（HLT）和开水壶，加一个活底的麦芽浆桶，我通过一个 50 英尺的不锈钢线圈在热酒容器里让泵来再循环麦芽浆（["热量交换再循环麦芽浆系统, 也叫 HERMS](https://byo.com/hops/item/1325-rims-and-herms-brewing-advanced-homebrewing)）。同时我需要另一个泵来在热酒容器里循环水，并且把水传输到麦芽浆桶里，整个电子部件全部是用树莓派来控制的。

建立我的电子酿酒系统并且尽可能的自动化意味着我需要以下的组件：

* 一个 5500 瓦的电子加热酒精容器（HLT）
* 能够放入加热酒精容器里的 50 英尺（0.5 英寸）的不锈钢线圈（热量交换再循环麦芽浆系统）
* 一个 5500 瓦的电子加热水壶
* 多个固态继电器加热开关
* 2 个高温食品级泵
* 泵的开关用继电器
* 可拆除装置和一个硅管
* 不锈钢球阀
* 一个测量温度的探针
* 很多线
* 一个来容纳这些配件的电路盒子

![Brew system](/data/attachment/album/201710/08/213024gubeee9zap3w9tfp.png "Brew system")

*酿酒系统 (photo by Christopher Aedo. [CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)*

建立酿酒系统的电气化方面的细节 [The Electric Brewery](http://theelectricbrewery.com/) 这个网站概括的很好，这里我不再重复，当你计划用树莓派代替这个 PID 控制器的话，你可以读以下的建议。

一个重要的事情需要注意，固态继电器（SSR）信号电压，许多教程建议使用一个 12 伏的固态继电器来关闭电路，树莓派的 GPIO 针插口只支持 3 伏输出电压，然而，必须购买继电器将电压变为 3 伏。

![Inkbird SSR](/data/attachment/album/201710/08/213027lvwmxhsk9emebmsx.png "Inkbird SSR")

*Inkbird SSR (photo by Christopher Aedo. [CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)*

要运行酿酒系统，你的树莓派必须做两个关键事情：测量来自几个不同位置的温度，用继电器开关来控制加热元件，树莓派很容易来处理这些任务。

这里有一些不同的方法来将温度传感器连到树莓派上，但是我找到了最方便的方法用[单总线](https://en.wikipedia.org/wiki/1-Wire)。这就可以让多个传感器分享相同的线路（实际上是三根线），这三根线可以使酿酒系统的多个设备更方便的工作，如果你要从网上找一个防水的 DS18B20 温度传感器，你可以会找到很多选择。我用的是[日立 DS18B20 防水温度传感器](https://smile.amazon.com/gp/product/B018KFX5X0/)。

要控制加热元件，树莓派包括了几个用来软件寻址的总线扩展器（GPIO），它会通过在某个文件写入 0 或者 1 让你发送3.3v 的电压到一个继电器，在我第一次了解树莓派是怎样工作的时候，这个[用 GPIO 驱动继电器的树莓派教程](http://www.susa.net/wordpress/2012/06/raspberry-pi-relay-using-gpio/)对我来说是最有帮助的，总线扩展器控制着多个固态继电器，通过酿酒软件来直接控制加热元件的开关。

我首先将所有部件放到这个电路盒子，因为这将成为一个滚动的小车，我要让它便于移动，而不是固定不动的，如果我有一个店（比如说在车库、工具房、或者地下室），我需要要用一个装在墙上的更大的电路盒，而现在我找到一个大小正好的[防水工程盒子](http://amzn.to/2hupFCr)，能放进每件东西，最后它成为小巧紧凑工具盒，并且能够工作。在左下角是和树莓派连接的为总线扩展器到单总线温度探针和[固态继电器](http://amzn.to/2hL8JDS)的扩展板。

要保持 240v 的固态继电器温度不高，我在盒子上切了个洞，在盒子的外面用 CPU 降温凝胶把[铜片散热片](http://amzn.to/2i4DYwy)安装到盒子外面的热槽之间。它工作的很好，盒子里没有温度上的问题了，在盒子盖上我放了两个开关为 120v 的插座，加两个240v 的 led 来显示加热元件是否通电。我用干燥器的插座和插头，所以可以很容易的断开电热水壶的连接。首次尝试每件事情都工作正常。（第一次绘制电路图必有回报）

这个照片来自“概念”版，最终生产系统应该有两个以上的固态继电器，以便 240v 的电路两个针脚能够切换，另外我将通过软件来切换泵的开关。现在通过盒子前面的物理开关控制它们，但是也很容易用继电器控制它们。

![Control box](/data/attachment/album/201710/08/213029kwtzw45viv1wuuu7.png "Control box")

*控制盒子 (photo by Christopher Aedo. [CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)*

唯一剩下有点棘手的事情是温度探针的压合接头，这个探针安装在加热酒精容器和麦芽浆桶球形的最底部阀门前的 T 字型接头上。当液体流过温度传感器，温度可以准确显示。我考虑加一个套管到热水壶里，但是对于我的酿造工艺没有什么用。最后，我买到了[四分之一英寸的压合接头](https://www.brewershardware.com/CF1412.html)，它们工作完美。

### 软件

一旦硬件整理好，我就有时间来处理软件了，我在树莓派上跑了最新的 [Raspbian 发行版](https://www.raspberrypi.org/downloads/raspbian/)，操作系统方面没有什么特别的。

我开始使用 [Strangebrew Elsinore](https://github.com/DougEdey/SB_Elsinore_Server) 酿酒软件，当我的朋友问我是否我听说过 [Hosehead](https://brewtronix.com/)（一个基于树莓派的酿酒控制器），我找到了 [Strangebrew Elsinore](https://github.com/DougEdey/SB_Elsinore_Server) 。我认为 [Hosehead](https://brewtronix.com/) 很棒，但我并不是要买一个酿酒控制器，而是要挑战自己，搭建一个自己的。

设置 [Strangebrew Elsinore](https://github.com/DougEdey/SB_Elsinore_Server) 很简单，其[文档](http://dougedey.github.io/SB_Elsinore_Server/)直白，没有遇到任何的问题。尽管 Strangebrew Elsinore 工作的很好，但在我的一代树莓派上运行 java 有时是费力的，不止崩溃一次。我看到这个软件开发停顿也很伤心，似乎他们也没有更多贡献者的大型社区（尽管有很多人还在用它）。

#### CraftBeerPi

之后我偶然遇到了一个用 Python 写的 [CraftbeerPI](http://www.craftbeerpi.com/)，它有活跃的贡献者支持的开发社区。原作者（也是当前维护者） Manuel Fritsch 在贡献和反馈问题处理方面做的很好。克隆[这个仓库](https://github.com/manuel83/craftbeerpi)然后开始只用了我一点时间。其 README 文档也是一个连接 DS1820 温度传感器的好例子，同时也有关于硬件接口到树莓派或者[芯片电脑](https://www.nextthing.co/pages/chip) 的注意事项。

在启动的时候，CraftbeerPI 引导用户通过一个设置过程来发现温度探针是否可用，并且让你指定哪个 GPIO 总线扩展器指针来管理树莓派上哪个配件。

![CraftBeerPi](/data/attachment/album/201710/08/213030exw3k977r67qqdjk.png "CraftBeerPi")

*CraftBeerPi (photo by Christopher Aedo. [CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)*

用这个系统进行自制酿酒是容易的，我能够依靠它掌握可靠的温度，我能输入多个温度段来控制麦芽浆温度，用CraftbeerPi 酿酒的日子有一点点累，但是我很高兴用传统的手工管理丙烷燃烧器的“兴奋”来换取这个系统的有效性和持续性。

CraftBeerPI 的用户友好性鼓舞我设置了另一个控制器来运行“发酵室”。就我来说，那是一个二手冰箱，我用了 50 美元加上放在里面的 25 美元的加热器。CraftBeerPI 很容易控制电器元件的冷热，你也能够设置多个温度阶段。举个例子，这个图表显示我最近做的 IPA 进程的发酵温度。发酵室发酵麦芽汁在 67F° 的温度下需要 4 天，然后每 12 小时上升一度直到温度到达 72F°。剩下两天温度保持不变是为了双乙酰生成。之后 5 天温度降到 65F°，这段时间是让啤酒变“干”，最后啤酒发酵温度直接降到 38F°。CraftBeerPI 可以加入各个阶段，让软件管理发酵更加容易。

![SIPA fermentation profile](/data/attachment/album/201710/08/213030gwnumn7t2hg2tn7w.png "SIPA fermentation profile")

*SIPA 发酵设置 (photo by Christopher Aedo. [CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)*

我也试验过用[液体比重计](https://tilthydrometer.com/)来对酵啤酒的比重进行监测，通过蓝牙连接的浮动传感器可以达到。有一个整合的计划能让 CraftbeerPi 很好工作，现在它记录这些比重数据到谷歌的电子表格里。一旦这个液体比重计能连接到发酵控制器，设置的自动发酵设置会基于酵母的活动性直接运行且更加容易，而不是在 4 天内完成主要发酵，可以在比重稳定 24 小时后设定温度。

像这样的一些项目，构想并计划改进和增加组件是很容易，不过，我很高兴今天经历过的事情。我用这种装置酿造了很多啤酒，每次都能达到预期的麦芽汁比率，而且啤酒一直都很美味。我的最重要的消费者 —— 就是我！很高兴我可以随时饮用。

![Homebrew on tap](/data/attachment/album/201710/08/213031fqwahexxs88ejw98.png "Homebrew on tap")

*随时饮用 (photo by Christopher Aedo. [CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)*

这篇文章基于 Christopher 的开放的西部的讲话《用Linux、Python 和树莓派酿制啤酒》。

（题图：[Quinn Dombrowski](https://www.flickr.com/photos/quinndombrowski/). Modified by Opensource.com. [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)）

---

作者简介：

Christopher Aedo 从他的学生时代就从事并且贡献于开源软件事业。最近他在 IBM 领导一个极棒的上游开发者团队，同时他也是开发者拥护者。当他不再工作或者实在会议室演讲的时候，他可能在波特兰市俄勒冈州用树莓派酿制和发酵一杯美味的啤酒。

---

via: <https://opensource.com/article/17/7/brewing-beer-python-and-raspberry-pi>

作者：[Christopher Aedo](https://opensource.com/users/docaedo) 译者：[hwlife](https://github.com/hwlife) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


开发一个 Linux 调试器（十）：高级主题

![](/data/attachment/album/201710/08/223932vag1ajxuusf9zjfo.jpg)

我们终于来到这个系列的最后一篇文章！这一次，我将对调试中的一些更高级的概念进行高层的概述：远程调试、共享库支持、表达式计算和多线程支持。这些想法实现起来比较复杂，所以我不会详细说明如何做，但是如果你有问题的话，我很乐意回答有关这些概念的问题。

### 系列索引

1. [准备环境](/article-8626-1.html)
2. [断点](/article-8645-1.html)
3. [寄存器和内存](/article-8663-1.html)
4. [Elves 和 dwarves](/article-8719-1.html)
5. [源码和信号](/article-8812-1.html)
6. [源码层逐步执行](/article-8813-1.html)
7. [源码层断点](/article-8890-1.html)
8. [调用栈](/article-8930-1.html)
9. [处理变量](/article-8936-1.html)
10. [高级主题](https://blog.tartanllama.xyz/writing-a-linux-debugger-advanced-topics/)

### 远程调试

远程调试对于嵌入式系统或对不同环境进行调试非常有用。它还在高级调试器操作和与操作系统和硬件的交互之间设置了一个很好的分界线。事实上，像 GDB 和 LLDB 这样的调试器即使在调试本地程序时也可以作为远程调试器运行。一般架构是这样的：

![debugarch](/data/attachment/album/201710/08/224018oehth6yzpnap2fyp.png)

调试器是我们通过命令行交互的组件。也许如果你使用的是 IDE，那么在其上有另一个层可以通过*机器接口*与调试器进行通信。在目标机器上（可能与本机一样）有一个<ruby> 调试存根 <rt>  debug stub </rt></ruby> ，理论上它是一个非常小的操作系统调试库的包装程序，它执行所有的低级调试任务，如在地址上设置断点。我说“在理论上”，因为如今调试存根变得越来越大。例如，我机器上的 LLDB 调试存根大小是 7.6MB。调试存根通过使用一些特定于操作系统的功能（在我们的例子中是 `ptrace`）和被调试进程以及通过远程协议的调试器通信。

最常见的远程调试协议是 GDB 远程协议。这是一种基于文本的数据包格式，用于在调试器和调试存根之间传递命令和信息。我不会详细介绍它，但你可以在[这里](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html)进一步阅读。如果你启动 LLDB 并执行命令 `log enable gdb-remote packets`，那么你将获得通过远程协议发送的所有数据包的跟踪信息。在 GDB 上，你可以用 `set remotelogfile <file>` 做同样的事情。

作为一个简单的例子，这是设置断点的数据包：

```
$Z0,400570,1#43

```

`$` 标记数据包的开始。`Z0` 是插入内存断点的命令。`400570` 和 `1` 是参数，其中前者是设置断点的地址，后者是特定目标的断点类型说明符。最后，`#43` 是校验值，以确保数据没有损坏。

GDB 远程协议非常易于扩展自定义数据包，这对于实现平台或语言特定的功能非常有用。

### 共享库和动态加载支持

调试器需要知道被调试程序加载了哪些共享库，以便它可以设置断点、获取源代码级别的信息和符号等。除查找被动态链接的库之外，调试器还必须跟踪在运行时通过 `dlopen` 加载的库。为了达到这个目的，动态链接器维护一个 *交汇结构体*。该结构体维护共享库描述符的链表，以及一个指向每当更新链表时调用的函数的指针。这个结构存储在 ELF 文件的 `.dynamic` 段中，在程序执行之前被初始化。

一个简单的跟踪算法：

* 追踪程序在 ELF 头中查找程序的入口（或者可以使用存储在 `/proc/<pid>/aux` 中的辅助向量）。
* 追踪程序在程序的入口处设置一个断点，并开始执行。
* 当到达断点时，通过在 ELF 文件中查找 `.dynamic` 的加载地址找到交汇结构体的地址。
* 检查交汇结构体以获取当前加载的库的列表。
* 链接器更新函数上设置断点。
* 每当到达断点时，列表都会更新。
* 追踪程序无限循环，继续执行程序并等待信号，直到追踪程序信号退出。

我给这些概念写了一个小例子，你可以在[这里](https://github.com/TartanLlama/dltrace)找到。如果有人有兴趣，我可以将来写得更详细一点。

### 表达式计算

表达式计算是程序的一项功能，允许用户在调试程序时对原始源语言中的表达式进行计算。例如，在 LLDB 或 GDB 中，可以执行 `print foo()` 来调用 `foo` 函数并打印结果。

根据表达式的复杂程度，有几种不同的计算方法。如果表达式只是一个简单的标识符，那么调试器可以查看调试信息，找到该变量并打印出该值，就像我们在本系列最后一部分中所做的那样。如果表达式有点复杂，则可能将代码编译成中间表达式 （IR） 并解释来获得结果。例如，对于某些表达式，LLDB 将使用 Clang 将表达式编译为 LLVM IR 并将其解释。如果表达式更复杂，或者需要调用某些函数，那么代码可能需要 JIT 到目标并在被调试者的地址空间中执行。这涉及到调用 `mmap` 来分配一些可执行内存，然后将编译的代码复制到该块并执行。LLDB 通过使用 LLVM 的 JIT 功能来实现。

如果你想更多地了解 JIT 编译，我强烈推荐 [Eli Bendersky 关于这个主题的文章](http://eli.thegreenplace.net/tag/code-generation)。

### 多线程调试支持

本系列展示的调试器仅支持单线程应用程序，但是为了调试大多数真实程序，多线程支持是非常需要的。支持这一点的最简单的方法是跟踪线程的创建，并解析 procfs 以获取所需的信息。

Linux 线程库称为 `pthreads`。当调用 `pthread_create` 时，库会使用 `clone` 系统调用来创建一个新的线程，我们可以用 `ptrace` 跟踪这个系统调用（假设你的内核早于 2.5.46）。为此，你需要在连接到调试器之后设置一些 `ptrace` 选项：

```
ptrace(PTRACE_SETOPTIONS, m_pid, nullptr, PTRACE_O_TRACECLONE);

```

现在当 `clone` 被调用时，该进程将收到我们的老朋友 `SIGTRAP` 信号。对于本系列中的调试器，你可以将一个例子添加到 `handle_sigtrap` 来处理新线程的创建：

```
case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)):
    //get the new thread ID
    unsigned long event_message = 0;
    ptrace(PTRACE_GETEVENTMSG, pid, nullptr, message);

    //handle creation
    //...

```

一旦收到了，你可以看看 `/proc/<pid>/task/` 并查看内存映射之类来获得所需的所有信息。

GDB 使用 `libthread_db`，它提供了一堆帮助函数，这样你就不需要自己解析和处理。设置这个库很奇怪，我不会在这展示它如何工作，但如果你想使用它，你可以去阅读[这个教程](http://timetobleed.com/notes-about-an-odd-esoteric-yet-incredibly-useful-library-libthread_db/)。

多线程支持中最复杂的部分是调试器中线程状态的建模，特别是如果你希望支持[不间断模式](https://sourceware.org/gdb/onlinedocs/gdb/Non_002dStop-Mode.html)或当你计算中涉及不止一个 CPU 的某种异构调试。

### 最后！

呼！这个系列花了很长时间才写完，但是我在这个过程中学到了很多东西，我希望它是有帮助的。如果你有关于调试或本系列中的任何问题，请在 Twitter [@TartanLlama](https://twitter.com/TartanLlama)或评论区联系我。如果你有想看到的其他任何调试主题，让我知道我或许会再发其他的文章。

---

via: <https://blog.tartanllama.xyz/writing-a-linux-debugger-advanced-topics/>

作者：[Simon Brand](https://www.twitter.com/TartanLlama) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


进入 Linux 桌面之窗

>
> “它能做什么 Windows 不能做的吗？”
>
>
>

这是许多人在考虑使用 Linux 桌面时的第一个问题。虽然支撑 Linux 的开源哲学对于某些人来说就是一个很好的理由，但是有些人想知道它在外观、感受和功能上有多么不同。在某种程度上，这取决于你是否选择桌面环境或窗口管理器。

如果你想要的是闪电般快速的桌面体验且向高效妥协, 那么经典桌面中的窗口管理器可能适合你。

![](/data/attachment/album/201710/09/104530d2hwkpzr5hcipvie.jpg)

### 事实之真相

“<ruby> 桌面环境 <rt>  Desktop Environment </rt></ruby>（DE）”是一个技术术语，指典型的、全功能桌面，即你的操作系统的完整图形化布局。除了显示你的程序，桌面环境还包括应用程序启动器，菜单面板和小部件等组成部分。

在 Microsoft Windows 中，桌面环境包括开始菜单、显示打开的程序的任务栏和通知中心，还有与操作系统捆绑在一起的所有 Windows 程序，以及围绕这打开的程序的框架（包括右上角的最小按钮、最大按钮和关闭按钮）。

Linux 中有很多相似之处。

例如，Linux [Gnome](http://en.wikipedia.org/wiki/GNOME) 桌面环境的设计略有不同，但它共享了所有的 Microsoft Windows 的基本元素 - 从应用程序菜单到显示打开的应用程序的面板、通知栏、窗框式程序。

窗口程序框架依赖于一个组件来绘制它们，并允许你移动并调整大小：它被称为“<ruby> 窗口管理器 <rt>  Window Manager </rt></ruby>（WM）”。因为它们都有窗口，所以每个桌面环境都包含一个窗口管理器。

然而，并不是每个窗口管理器都是桌面环境的一部分。你可以只运行窗口管理器，并且完全有这么做的需要。

### 离开你的环境

对本专栏而言，所谓的“窗口管理器”指的是可以那种独立进行的。如果在现有的 Linux 系统上安装了一个窗口管理器，你可以在不关闭系统的情况下注销，在登录屏幕上选择新的窗口管理器，然后重新登录。

不过, 在研究你的窗口管理器之前，你可能不想这么做，因为你将会看到一个空白屏幕和稀疏的状态栏，而且它或许能、或许不能点击。

通常情况下，可以直接在窗口管理器中直接启动终端，因为这是你编辑其配置文件的方式。在那里你会发现用来启动程序的按键和鼠标组合，你实际上也可以使用你的新设置。

例如，在流行的 i3 窗口管理器中，你可以通过按下 `Super` 键（即 `Windows` 键）加 `Enter` 键来启动终端，或者按 `Super + D` 启动<ruby> 应用程序启动器 <rt>  app launcher </rt></ruby>。你可以在其中输入应用程序名称，然后按 `Enter` 键将其打开。所有已有的应用程序都可以通过这种方式找到，一旦选择后，它们将会全屏打开。

[![i3 window manager](/data/attachment/album/201710/09/104549iq6zf69zb7g9sgud.jpg)](http://www.linuxinsider.com/article_images/2017/84473_1200x750.jpg)

i3 还是一个平铺式窗口管理器，这意味着它可以确保所有的窗口均匀地扩展到屏幕，既不重叠也不浪费空间。当弹出新窗口时，它会减少现有的窗口，将它们推到一边腾出空间。用户可以以垂直或水平相邻的方式打开下一个窗口。

### 功能亦敌亦友

当然，桌面环境有其优点。首先，它们提供功能丰富、可识别的界面。每个都有其特征鲜明的风格，但总体而言，它们提供了普适的默认设置，这使得桌面环境从一开始就可以使用。

另一个优点是桌面环境带有一组程序和媒体编解码器，允许用户立即完成简单的任务。此外，它们还包括一些方便的功能，如电池监视器、无线小部件和系统通知。

与桌面环境的完善相应的，是这种大型软件库和用户体验理念独一无二，这就意味着它们所能做的都是有限度的。这也意味着它们并不总是非常可配置。桌面环境强调的是漂亮的外表，很多时候是金玉其外的。

许多桌面环境对系统资源的渴求是众所周知的，所以它们不太喜欢低端硬件。因为在其上运行的视觉效果，还有更多的东西可能会出错。我曾经尝试调整与我正在运行的桌面环境无关的网络设置，然后整个崩溃了。而当我打开一个窗口管理器，我就可以改变设置。

那些优先考虑安全性的人可能希望不要桌面环境，因为更多的程序意味着更大的攻击面 —— 也就是坏人可以突破的入口点。

然而，如果你想尝试一下桌面环境，XFCE 是一个很好的起点，因为它的较小的软件库消除了一些臃肿，如果你不往里面塞东西，垃圾就会更少。

乍一看，它不是最漂亮的，但在下载了一些 GTK 主题包（每个桌面环境都可以提供这些主题或 Qt 主题，而 XFCE 在 GTK 阵营之中），并且在“外观”部分的设置中，你可以轻松地修改。你甚至可以在这个[集中式画廊](http://www.xfce-look.org/)中找到你最喜欢的主题。

### 时间就是生命

如果你想了解桌面环境之外可以做什么，你会发现窗口管理器给了你足够的回旋余地。

无论如何，窗口管理器都是与定制有关的。事实上，它们的可定制性已经催生了无数的画廊，承载着一个充满活力的社区用户，他们手中的调色板就是窗口管理器。

窗口管理器的少量资源需求使它们成为较低规格硬件的理想选择，并且由于大多数窗口管理器不附带任何程序，因此允许喜欢模块化的用户只添加所需的程序。

可能与桌面环境最为显著的区别是，窗口管理器通常通过鼠标移动和键盘热键来打开程序或启动器来聚焦效率。

键盘驱动的窗口管理器特别流畅，你可以启动新的窗口、输入文本或更多的键盘命令、移动它们，并再次关闭它们，这一切无需将手从<ruby> 键盘中间 <rt>  home row </rt></ruby>移开。一旦你适应了其设计逻辑，你会惊讶于你能够如此快速地完成任务。

尽管它们提供了自由，窗口管理器也有其缺点。最显著的是，它们是赤裸裸的开箱即用。在你可以使用其中一个之前，你必须花时间阅读窗口管理器的文档以获取配置语法，可能还需要更多的时间来找到该语法的窍门。

如果你从桌面环境（这是最可能的情况）切换过来，尽管你会有一些用户程序，你也会缺少一些熟悉的东西，如电池指示器和网络小部件，并且需要一些时间来设置新的。

如果你想深入窗口管理器，i3 有[完整的文档](https://i3wm.org/docs/)和简明直白的配置语法。配置文件不使用任何编程语言 - 它只是每行定义一个变量值对。创建热键只要输入 `bindsym`、键盘绑定以及该组合启动的动作即可。

虽然窗口管理器不适合每个人，但它们提供了独特的计算体验，而 Linux 是少数允许使用它们的操作系统之一。无论你最终采用哪种模式，我希望这个概观能够给你足够的信息，以便对你所做的选择感到自信 —— 或者有足够的信心跨出您熟悉的区域来看看还有什么可用的。

---

作者简介：

Jonathan Terrasi - 自 2017 年以来一直是 ECT 新闻网专栏作家。他的主要兴趣是计算机安全（特别是 Linux 桌面）、加密和分析政治和时事。他是全职自由作家和音乐家。他的背景包括在芝加哥委员会发表的关于维护人权法案的文章中提供技术评论和分析。

---

via: <http://www.linuxinsider.com/story/84473.html>

作者：[Jonathan Terrasi](%5B1%5D:http://www.linuxinsider.com/story/84473.html?rss=1#) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


通过 SSH 实现 TCP / IP 隧道（端口转发）：使用 OpenSSH 可能的 8 种场景

请问场景4和场景8的vncviewer命令是不是写错了？

    interno2 $ internal vncviewer1::7900

好像应该是

    interno2 $ vncviewer interno1::7900

![](/data/attachment/album/201710/09/233056nkp8dbzv0qflqfbk.png)

对于 [Secure Shell (SSH)](http://en.wikipedia.org/wiki/Secure_Shell) 这样的网络协议来说，其主要职责就是在终端模式下访问一个远程系统。因为 SSH 协议对传输数据进行了加密，所以通过它在远端系统执行命令是安全的。此外，我们还可以在这种加密后的连接上通过创建隧道（端口转发）的方式，来实现两个不同终端间的互联。凭借这种方式，只要我们能通过 SSH 创建连接，就可以绕开防火墙或者端口禁用的限制。

这个话题在网络领域有大量的应用和讨论：

* [Wikipedia: SSH Tunneling](http://en.wikipedia.org/wiki/Tunneling_protocol#SSH_tunneling)
* [O’Reilly: Using SSH Tunneling](http://www.oreillynet.com/pub/a/wireless/2001/02/23/wep.html)
* [Ssh.com: Tunneling Explained](http://www.ssh.com/support/documentation/online/ssh/winhelp/32/Tunneling_Explained.html)
* [Ssh.com: Port Forwarding](http://www.ssh.com/support/documentation/online/ssh/adminguide/32/Port_Forwarding.html)
* [SecurityFocus: SSH Port Forwarding](http://www.securityfocus.com/infocus/1816)
* [Red Hat Magazine: SSH Port Forwarding](http://magazine.redhat.com/2007/11/06/ssh-port-forwarding/)

我们在接下来的内容中并不讨论端口转发的细节，而是准备介绍一个如何使用 [OpenSSH](http://www.openssh.com/) 来完成 TCP 端口转发的速查表，其中包含了八种常见的场景。有些 SSH 客户端，比如 [PuTTY](http://www.chiark.greenend.org.uk/%7Esgtatham/putty/)，也允许通过界面配置的方式来实现端口转发。而我们着重关注的是通过 OpenSSH 来实现的的方式。

在下面的例子当中，我们假设环境中的网络划分为外部网络（network1）和内部网络（network2）两部分，并且这两个网络之间，只能在 externo1 与 interno1 之间通过 SSH 连接的方式来互相访问。外部网络的节点之间和内部网络的节点之间是完全联通的。

![SSH tunnels: no tunnel](/data/attachment/album/201710/09/233340d14kprv7l7rb8b6t.png)

### 场景 1

>
> 在 externo1 节点访问由 interno1 节点提供的 TCP 服务（本地端口转发 / 绑定地址 = localhost / 主机 = localhost ）
>
>
>

externo1 节点可以通过 OpenSSH 连接到 interno1 节点，之后我们想通过其访问运行在 5900 端口上的 VNC 服务。

![SSH Tunnels: Scenario 1](/data/attachment/album/201710/09/233218dcae6pcq9aqeqdiy.png)

我们可以通过下面的命令来实现：

```
externo1 $ ssh -L 7900:localhost:5900 user@interno1

```

现在，我们可以在 externo1 节点上确认下 7900 端口是否处于监听状态中：

```
externo1 $ netstat -ltn
Active Internet connections  (only servers)
Proto Recv-Q  Send-Q  Local Address Foreign Address State
...
Tcp  0  0  127.0.0.1:7900  0.0.0.0:*  LISTEN
...

```

我们只需要在 externo1 节点上执行如下命令即可访问 internal 节点的 VNC 服务：

```
externo1 $ vncviewer localhost::7900

```

注意：在 [vncviewer 的 man 手册](http://www.realvnc.com/products/free/4.1/man/vncviewer.html)中并未提及这种修改端口号的方式。在 [About VNCViewer configuration of the output TCP port](http://www.realvnc.com/pipermail/vnc-list/2006-April/054551.html) 中可以看到。这也是 [the TightVNC vncviewer](http://www.tightvnc.com/vncviewer.1.html) 所介绍的的。

### 场景 2

>
> 在 externo2 节点上访问由 interno1 节点提供的 TCP 服务（本地端口转发 / 绑定地址 = 0.0.0.0 / 主机 = localhost）
>
>
>

这次的场景跟方案 1 的场景的类似，但是我们这次想从 externo2 节点来连接到 interno1 上的 VNC 服务：

![SSH Tunnels: Scenario 2](/data/attachment/album/201710/09/233125kvymsdsrzvm4yvp4.png)

正确的命令如下：

```
externo1 $ ssh -L 0.0.0.0:7900:localhost:5900 user@interno1

```

看起来跟方案 1 中的命令类似，但是让我们看看 `netstat` 命令的输出上的区别。7900 端口被绑定到了本地（`127.0.0.1`），所以只有本地进程可以访问。这次我们将端口关联到了 `0.0.0.0`，所以系统允许任何 IP 地址的机器访问 7900 这个端口。

```
externo1 $ netstat -ltn
Active Internet connections  (only servers)
Proto Recv-Q  Send-Q  Local Address Foreign Address State
...
Tcp  0  0  0.0.0.0:7900  0.0.0.0:*  LISTEN
...

```

所以现在在 externo2 节点上，我们可以执行：

```
externo2 $ vncviewer externo1::7900

```

来连接到 interno1 节点上的 VNC 服务。

除了将 IP 指定为 `0.0.0.0` 之外，我们还可以使用参数 `-g`（允许远程机器使用本地端口转发），完整命令如下：

```
externo1 $ ssh -g -L 7900:localhost:5900 user@interno1

```

这条命令与前面的命令能实现相同效果：

```
externo1 $ ssh -L 0.0.0.0:7900:localhost:5900 user@interno1

```

换句话说，如果我们想限制只能连接到系统上的某个 IP，可以像下面这样定义：

```
externo1 $ ssh -L 192.168.24.80:7900:localhost:5900 user@interno1

externo1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
...
Tcp 0 0 192.168.24.80:7900 0.0.0.0:* LISTEN
...

```

### 场景 3

>
> 在 interno1 上访问由 externo1 提供的 TCP 服务（远程端口转发 / 绑定地址 = localhost / 主机 = localhost）
>
>
>

在场景 1 中 SSH 服务器与 TCP 服务（VNC）提供者在同一个节点上。现在我们想在 SSH 客户端所在的节点上，提供一个 TCP 服务（VNC）供 SSH 服务端来访问：

![SSH Tunnels: Scenario 3](/data/attachment/album/201710/09/233130dwrt2atr0hgciig9.png)

将方案 1 中的命令参数由 `-L` 替换为 `-R`。

完整命令如下：

```
externo1 $ ssh -R 7900:localhost:5900 user@interno1

```

然后我们就能看到 interno1 节点上对 7900 端口正在监听：

```
interno1 $ netstat -lnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
...
Tcp 0 0 127.0.0.1:7900 0.0.0.0:* LISTEN
...

```

现在在 interno1 节点上，我们可以使用如下命令来访问 externo1 上的 VNC 服务：

```
interno1 $ vncviewer localhost::7900

```

### 场景 4

>
> interno2 使用 externo1 上提供的 TCP 服务（远端端口转发 / 绑定地址 = 0.0.0.0 / 主机 = localhost）
>
>
>

与场景 3 类似，但是现在我们尝试指定允许访问转发端口的 IP（就像场景 2 中做的一样）为 `0.0.0.0`，这样其他节点也可以访问 VNC 服务：

![SSH Tunnels: Scenario 4](/data/attachment/album/201710/09/233133g0r0500yiyyrsrvx.png)

正确的命令是：

```
externo1 $ ssh -R 0.0.0.0:7900:localhost:5900 user@interno1

```

但是这里有个重点需要了解，出于安全的原因，如果我们直接执行该命令的话可能不会生效，因为我们需要修改 SSH 服务端的一个参数值 `GatewayPorts`，它的默认值是：`no`。

>
> GatewayPorts
>
>
> 该参数指定了远程主机是否允许客户端访问转发端口。默认情况下，sshd(8) 只允许本机进程访问转发端口。这是为了阻止其他主机连接到该转发端口。GatewayPorts 参数可用于让 sshd 允许远程转发端口绑定到非回环地址上，从而可以让远程主机访问。当参数值设置为 “no” 的时候只有本机可以访问转发端口；“yes” 则表示允许远程转发端口绑定到通配地址上；或者设置为 “clientspecified” 则表示由客户端来选择哪些主机地址允许访问转发端口。默认值是 “no”。
>
>
>

如果我们没有修改服务器配置的权限，我们将不能使用该方案来进行端口转发。这是因为如果没有其他的限制，用户可以开启一个端口（> 1024）来监听来自外部的请求并转发到 `localhost:7900`。

参照这个案例：[netcat](http://en.wikipedia.org/wiki/Netcat) ( [Debian # 310431: sshd\_config should warn about the GatewayPorts workaround.](http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=310431) )

所以我们修改 `/etc/ssh/sshd_config`，添加如下内容：

```
GatewayPorts clientspecified

```

然后，我们使用如下命令来重载修改后的配置文件（在 Debian 和 Ubuntu 上）。

```
sudo  /etc/init.d/ssh reload

```

我们确认一下现在 interno1 节点上存在 7900 端口的监听程序，监听来自不同 IP 的请求：

```
interno1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
...
Tcp 0 0 0.0.0.0:7900 0.0.0.0:* LISTEN
...

```

然后我们就可以在 interno2 节点上使用 VNC 服务了：

```
interno2 $ internal vncviewer1::7900

```

### 场景 5

>
> 在 externo1 上使用由 interno2 提供的 TCP 服务（本地端口转发 / 绑定地址 localhost / 主机 = interno2 ）
>
>
>

![SSH Tunnels: Scenario 5](/data/attachment/album/201710/09/233137cpxvbbisvddvvlbb.png)

在这种场景下我们使用如下命令：

```
externo1 $ ssh -L 7900:interno2:5900 user@interno1

```

然后我们就能在 externo1 节点上，通过执行如下命令来使用 VNC 服务了：

```
externo1 $ vncviewer localhost::7900

```

### 场景 6

>
> 在 interno1 上使用由 externo2 提供的 TCP 服务（远程端口转发 / 绑定地址 = localhost / host = externo2）
>
>
>

![SSH Tunnels: Scenario 6](/data/attachment/album/201710/09/233143kyqncci54q1zjc1d.png)

在这种场景下，我们使用如下命令：

```
externo1 $ ssh -R 7900:externo2:5900 user@interno1

```

然后我们可以在 interno1 上通过执行如下命令来访问 VNC 服务：

```
interno1 $ vncviewer localhost::7900

```

### 场景 7

>
> 在 externo2 上使用由 interno2 提供的 TCP 服务（本地端口转发 / 绑定地址 = 0.0.0.0 / 主机 = interno2）
>
>
>

![SSH Tunnels: Scenario 7](/data/attachment/album/201710/09/233145y9ogqzfnw5o85g8h.png)

本场景下，我们使用如下命令：

```
externo1 $ ssh -L 0.0.0.0:7900:interno2:5900 user@interno1

```

或者：

```
externo1 $ ssh -g -L 7900:interno2:5900 user@interno1

```

然后我们就可以在 externo2 上执行如下命令来访问 vnc 服务：

```
externo2 $ vncviewer externo1::7900

```

### 场景 8

>
> 在 interno2 上使用由 externo2 提供的 TCP 服务（远程端口转发 / 绑定地址 = 0.0.0.0 / 主机 = externo2）
>
>
>

![SSH Tunnels: Scenario 8](/data/attachment/album/201710/09/233148cnbbykh9dyhb8dwe.png)

本场景下我们使用如下命令：

```
externo1 $ ssh -R 0.0.0.0:7900:externo2:5900 user@interno1

```

SSH 服务器需要配置为：

```
GatewayPorts clientspecified

```

就像我们在场景 4 中讲过的那样。

然后我们可以在 interno2 节点上执行如下命令来访问 VNC 服务：

```
interno2 $ internal vncviewer1::7900

```

如果我们需要一次性的创建多个隧道，使用配置文件的方式替代一个可能很长的命令是一个更好的选择。假设我们只能通过 SSH 的方式访问某个特定网络，同时又需要创建多个隧道来访问该网络内不同服务器上的服务，比如 VNC 或者 [远程桌面](http://en.wikipedia.org/wiki/Remote_Desktop_Services)。此时只需要创建一个如下的配置文件 `$HOME/redirects` 即可（在 SOCKS 服务器 上）。

```
# SOCKS server
DynamicForward 1080

# SSH redirects
LocalForward 2221 serverlinux1: 22
LocalForward 2222 serverlinux2: 22
LocalForward 2223 172.16.23.45:22
LocalForward 2224 172.16.23.48:22

# RDP redirects for Windows systems
LocalForward 3391 serverwindows1: 3389
LocalForward 3392 serverwindows2: 3389

# VNC redirects for systems with "vncserver"
LocalForward 5902 serverlinux1: 5901
LocalForward 5903 172.16.23.45:5901

```

然后我们只需要执行如下命令：

```
externo1 $ ssh -F $HOME/redirects user@interno1

```

---

via: <https://wesharethis.com/2017/07/creating-tcp-ip-port-forwarding-tunnels-ssh-8-possible-scenarios-using-openssh/>

作者：[Ahmad](https://wesharethis.com/author/ahmad/) 译者：[toutoudnf](https://github.com/toutoudnf) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


12 件可以用 GitHub 完成的很酷的事情

Summary: 我希望这里至少有三件事是你此前并不知道的……

我希望这里至少有三件事是你此前并不知道的……

![](/data/attachment/album/201710/10/170748tur07mwv03qd23ow.jpg)

我不能为我的人生想出一个引子来，所以……

### #1 在 GitHub.com 上编辑代码

我想我要开始介绍的第一件事是多数人都已经知道的（尽管我一周之前还不知道）。

当你登录到 GitHub ，查看一个文件时（任何文本文件，任何版本库），右上方会有一只小铅笔。点击它，你就可以编辑文件了。 当你编辑完成后，GitHub 会给出文件变更的建议，然后为你<ruby> 复刻 <rt>  fork </rt></ruby>该仓库并创建一个<ruby> 拉取请求 <rt>  pull request </rt></ruby>（PR）。

是不是很疯狂？它为你创建了一个复刻！

你不需要自己去复刻、拉取，然后本地修改，再推送，然后创建一个 PR。

![](/data/attachment/album/201710/10/170816t5s777ae77bzccsv.png)

*不是一个真正的 PR*

这对于修改错误拼写以及编辑代码时的一些糟糕的想法是很有用的。

### #2 粘贴图像

在评论和<ruby> 工单 <rt>  issue </rt></ruby>的描述中并不仅限于使用文字。你知道你可以直接从剪切板粘贴图像吗？ 在你粘贴的时候，你会看到图片被上传 (到云端，这毫无疑问)，并转换成 markdown 显示的图片格式。

棒极了。

### #3 格式化代码

如果你想写一个代码块的话，你可以用三个反引号（```）作为开始 —— 就像你在浏览 [精通 Markdown](https://guides.github.com/features/mastering-markdown/) 时所学到的一样 —— 而且 GitHub 会尝试去推测你所写下的编程语言。

但如果你粘贴的像是 Vue、Typescript 或 JSX 这样的代码，你就需要明确指出才能获得高亮显示。

在首行注明 ````jsx`：

![](/data/attachment/album/201710/10/170816uwk5nuck1ur3wu3w.png)

…这意味着代码段已经正确的呈现：

![](/data/attachment/album/201710/10/170816wkmduoz66lu6pmdx.png)

（顺便说一下，这些用法也可以用到 gist。 如果你给一个 gist 用上 `.jsx` 扩展名，你的 JSX 语法就会高亮显示。）

这里是[所有被支持的语法](https://github.com/github/linguist/blob/fc1404985abb95d5bc33a0eba518724f1c3c252e/vendor/README.md)的清单。

### #4 用 PR 中的魔法词来关闭工单

比方说你已经创建了一个用来修复 `#234` 工单的拉取请求。那么你就可以把 `fixes #234` 这段文字放在你的 PR 的描述中（或者是在 PR 的评论的任何位置）。

接下来，在合并 PR 时会自动关闭与之对应的工单。这是不是很酷？

这里是[更详细的学习帮助](https://help.github.com/articles/closing-issues-using-keywords/)。

### #5 链接到评论

是否你曾经想要链接到一个特定的评论但却无从着手？这是因为你不知道如何去做到这些。不过那都过去了，我的朋友，我告诉你啊，点击紧挨着名字的日期或时间，这就是如何链接到一个评论的方法。

![](/data/attachment/album/201710/10/170816p9zzs4rlbr4zza9e.png)

*嘿，这里有 gaearon 的照片！*

### #6 链接到代码

那么你想要链接到代码的特定行么。我了解了。

试试这个：在查看文件的时候，点击挨着代码的行号。

哇哦，你看到了么？URL 更新了，加上了行号！如果你按下 `Shift` 键并点击其他的行号，格里格里巴巴变！URL 再一次更新并且现在出现了行范围的高亮。

分享这个 URL 将会链接到这个文件的那些行。但等一下，链接所指向的是当前分支。如果文件发生变更了怎么办？也许一个文件当前状态的<ruby> 永久链接 <rt>  permalink </rt></ruby>就是你以后需要的。

我比较懒，所以我已经在一张截图中做完了上面所有的步骤：

![](/data/attachment/album/201710/10/170817f4tdfu34hezwfpwu.png)

*说起 URL…*

### #7 像命令行一样使用 GitHub URL

使用 UI 来浏览 GitHub 有着很好的体验。但有些时候最快到达你想去的地方的方法就是在地址栏输入。举个例子，如果我想要跳转到一个我正在工作的分支，然后查看与 master 分支的差异，我就可以在我的仓库名称的后边输入 `/compare/branch-name` 。

这样就会访问到指定分支的 diff 页面。

![](/data/attachment/album/201710/10/170817tudqm6l63d6lgh41.png)

然而这就是与 master 分支的 diff，如果我要与 develoment 分支比较，我可以输入 `/compare/development...my-branch`。

![](/data/attachment/album/201710/10/170817k99duuzq4ryx549x.png)

对于你这种键盘快枪手来说，`ctrl`+`L` 或 `cmd`+`L` 将会向上跳转光标进入 URL 那里（至少在 Chrome 中是这样）。这（再加上你的浏览器会自动补全）能够成为一种在分支间跳转的便捷方式。

专家技巧：使用方向键在 Chrome 的自动完成建议中移动同时按 `shift`+`delete` 来删除历史条目（例如，一旦分支被合并后）。

（我真的好奇如果我把快捷键写成 `shift + delete` 这样的话，是不是读起来会更加容易。但严格来说 ‘+’ 并不是快捷键的一部分，所以我并不觉得这很舒服。这一点纠结让 *我* 整晚难以入睡，Rhonda。）

### #8 在工单中创建列表

你想要在你的<ruby> 工单 <rt>  issue </rt></ruby>中看到一个复选框列表吗？

![](/data/attachment/album/201710/10/170818su4kvue47ji6wbo8.png)

你想要在工单列表中显示为一个漂亮的 “2 of 5” 进度条吗?

![](/data/attachment/album/201710/10/170818bx2hcwhqh8em5mjo.png)

很好！你可以使用这些的语法创建交互式的复选框：

```
 - [ ] Screen width (integer)
 - [x] Service worker support
 - [x] Fetch support
 - [ ] CSS flexbox support
 - [ ] Custom elements

```

它的表示方法是空格、破折号、再空格、左括号、填入空格（或者一个 `x` ），然后封闭括号，接着空格，最后是一些话。

然后你可以实际选中或取消选中这些框！出于一些原因这些对我来说看上去就像是技术魔法。你可以*选中*这些框! 同时底层的文本会进行更新。

他们接下来会想到什么魔法？

噢，如果你在一个<ruby> 项目面板 <rt>  project board </rt></ruby>上有这些工单的话，它也会在这里显示进度：

![](/data/attachment/album/201710/10/170818edbbr5d214gd1b1k.png)

如果在我提到“在一个项目面板上”时你不知道我在说些什么，那么你会在本页下面进一步了解。

比如，在本页面下 2 厘米的地方。

### #9 GitHub 上的项目面板

我常常在大项目中使用 Jira 。而对于个人项目我总是会使用 Trello 。我很喜欢它们两个。

当我学会 GitHub 的几周后，它也有了自己的项目产品，就在我的仓库上的 Project 标签，我想我会照搬一套我已经在 Trello 上进行的任务。

![](/data/attachment/album/201710/10/170818ir6199o9rr56p88o.png)

*没有一个是有趣的任务*

这里是在 GitHub 项目上相同的内容：

![](/data/attachment/album/201710/10/170819zfsbf7jswvaiasi9.png)

*你的眼睛最终会适应这种没有对比的显示*

出于速度的缘故，我把上面所有的都添加为 “<ruby> 备注 <rt>  note </rt></ruby>” —— 意思是它们不是真正的 GitHub 工单。

但在 GitHub 上，管理任务的能力被集成在版本库的其他地方 —— 所以你可能想要从仓库添加已有的工单到面板上。

你可以点击右上角的<ruby> 添加卡片 <rt>  Add Cards </rt></ruby>，然后找你想要添加的东西。在这里，特殊的[搜索语法](https://help.github.com/articles/searching-issues-and-pull-requests/)就派上用场了，举个例子，输入 `is:pr is:open` 然后现在你可以拖动任何开启的 PR 到项目面板上，或者要是你想清理一些 bug 的话就输入 `label:bug`。

![](/data/attachment/album/201710/10/170819bjsizfyrw2yo33to.png)

亦或者你可以将现有的备注转换为工单。

![](/data/attachment/album/201710/10/170819sn6wx62j655rp8s6.png)

再或者，从一个现有工单的屏幕上，把它添加到右边面板的项目上。

![](/data/attachment/album/201710/10/170819ybpj4k03jpwmdbkj.png)

它们将会进入那个项目面板的分类列表，这样你就能决定放到哪一类。

在实现那些任务的同一个仓库下放置任务的内容有一个巨大（超大）的好处。这意味着今后的几年你能够在一行代码上做一个 `git blame`，可以让你找出最初在这个任务背后写下那些代码的根据，而不需要在 Jira、Trello 或其它地方寻找蛛丝马迹。

#### 缺点

在过去的三周我已经对所有的任务使用 GitHub 取代 Jira 进行了测试（在有点看板风格的较小规模的项目上) ，到目前为止我都很喜欢。

但是我无法想象在 scrum（LCTT 译注：迭代式增量软件开发过程）项目上使用它，我想要在那里完成正确的工期估算、开发速度的测算以及所有的好东西怕是不行。

好消息是，GitHub 项目只有很少一些“功能”，并不会让你花很长时间去评估它是否值得让你去切换。因此要不要试试，你自己看着办。

无论如何，我*听说过* [ZenHub](https://www.zenhub.com/) 并且在 10 分钟前第一次打开了它。它是对 GitHub 高效的延伸，可以让你估计你的工单并创建 epic 和 dependency。它也有 velocity 和<ruby> 燃尽图 <rt>  burndown chart </rt></ruby>功能；这看起来*可能是*世界上最棒的东西了。

延伸阅读： [GitHub help on Projects](https://help.github.com/articles/tracking-the-progress-of-your-work-with-project-boards/)。

### #10 GitHub 维基

对于一堆非结构化页面（就像维基百科一样）， GitHub <ruby> 维基 <rt>  wiki </rt></ruby>提供的（下文我会称之为 Gwiki）就很优秀。

结构化的页面集合并没那么多，比如说你的文档。这里没办法说“这个页面是那个页面的子页”，或者有像‘下一节’和‘上一节’这样的按钮。Hansel 和 Gretel 将会完蛋，因为这里没有面包屑导航（LCTT 译注：引自童话故事《糖果屋》）。

（边注，你有*读过*那个故事吗? 这是个残酷的故事。两个混蛋小子将饥肠辘辘的老巫婆烧死在*她自己的火炉*里。毫无疑问她是留下来收拾残局的。我想这就是为什么如今的年轻人是如此的敏感 —— 今天的睡前故事太不暴力了。)

继续 —— 把 Gwiki 拿出来接着讲，我输入一些 NodeJS 文档中的内容作为维基页面，然后创建一个侧边栏以模拟一些真实结构。这个侧边栏会一直存在，尽管它无法高亮显示你当前所在的页面。

其中的链接必须手动维护，但总的来说，我认为这已经很好了。如果你觉得有需要的话可以[看一下](https://github.com/davidgilbertson/about-github/wiki)。

![](/data/attachment/album/201710/10/170820luqs3ws1qsrzkvwq.png)

它将不会与像 GitBook（它使用了 [Redux 文档](http://redux.js.org/)）或定制的网站这样的东西相比较。但它八成够用了，而且它就在你的仓库里。

我是它的一个粉丝。

我的建议：如果你已经拥有不止一个 `README.md` 文件，并且想要一些不同的页面作为用户指南或是更详细的文档，那么下一步你就需要停止使用 Gwiki 了。

如果你开始觉得缺少的结构或导航非常有必要的话，去切换到其他的产品吧。

### #11 GitHub 页面（带有 Jekyll）

你可能已经知道了可以使用 GitHub <ruby> 页面 <rt>  Pages </rt></ruby> 来托管静态站点。如果你不知道的话现在就可以去试试。不过这一节确切的说是关于使用 Jekyll 来构建一个站点。

最简单的来说， GitHub 页面 + Jekyll 会将你的 `README.md` 呈现在一个漂亮的主题中。举个例子，看看我的 [关于 github](https://github.com/davidgilbertson/about-github) 中的 readme 页面:

![](/data/attachment/album/201710/10/170820s7kcckccccv7u7yc.png)

点击 GitHub 上我的站点的<ruby> 设置 <rt>  settings </rt></ruby>标签，开启 GitHub 页面功能，然后挑选一个 Jekyll 主题……

![](/data/attachment/album/201710/10/170820ynsoovz4vr46oar3.png)

我就会得到一个 [Jekyll 主题的页面](https://davidgilbertson.github.io/about-github/)：

![](/data/attachment/album/201710/10/170821ms6brwglzfgrflr4.png)

由此我可以构建一个主要基于易于编辑的 markdown 文件的静态站点，其本质上是把 GitHub 变成一个 CMS（LCTT 译注：内容管理系统）。

我还没有真正的使用过它，但这就是 React 和 Bootstrap 网站构建的过程，所以并不可怕。

注意，在本地运行它需要 Ruby （Windows 用户会彼此交换一下眼神，然后转头看向其它的方向。macOS 用户会发出这样这样的声音 “出什么问题了，你要去哪里？Ruby 可是一个通用平台！GEMS 万岁！”）。

（这里也有必要加上，“暴力或威胁的内容或活动” 在 GitHub 页面上是不允许的，因此你不能去部署你的 Hansel 和 Gretel 重启之旅了。）

#### 我的意见

为了这篇文章，我对 GitHub 页面 + Jekyll 研究越多，就越觉得这件事情有点奇怪。

“拥有你自己的网站，让所有的复杂性远离”这样的想法是很棒的。但是你仍然需要在本地生成配置。而且可怕的是需要为这样“简单”的东西使用很多 CLI（LCTT 译注：命令行界面）命令。

我只是略读了[入门部分](https://jekyllrb.com/docs/home/)的七页，给我的感觉像是*我才是*那个小白。此前我甚至从来没有学习过所谓简单的 “Front Matter” 的语法或者所谓简单的 “Liquid 模板引擎” 的来龙去脉。

我宁愿去手工编写一个网站。

老实说我有点惊讶 Facebook 使用它来写 React 文档，因为他们能够用 React 来构建他们的帮助文档，并且在一天之内[预渲染到静态的 HTML 文件](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#pre-rendering-into-static-html-files)。

他们所需要做的就是利用已有的 Markdown 文件，就像跟使用 CMS 一样。

我想是这样……

### #12 使用 GitHub 作为 CMS

比如说你有一个带有一些文本的网站，但是你并不想在 HTML 的标记中储存那些文本。

取而代之，你想要把这堆文本存放到某个地方，以便非开发者也可以很容易地编辑。也许要使用某种形式的版本控制。甚至还可能需要一个审查过程。

这里是我的建议：在你的版本库中使用 markdown 文件存储文本。然后在你的前端使用插件来获取这些文本块并在页面呈现。

我是 React 的支持者，因此这里有一个 `<Markdown>` 插件的示例，给出一些 markdown 的路径，它就会被获取、解析，并以 HTML 的形式呈现。

（我正在使用 [marked](https://www.npmjs.com/package/marked) npm 包来将 markdown 解析为 HTML。）

这里是我的示例仓库 [/text-snippets](https://github.com/davidgilbertson/about-github/tree/master/text-snippets)，里边有一些 markdown 文件 。

（你也可以使用 GitHub API 来[获取内容](https://developer.github.com/v3/repos/contents/#get-contents) —— 但我不确定你是否能搞定。）

你可以像这样使用插件：

如此，GitHub 就是你的 CMS 了，可以说，不管有多少文本块都可以放进去。

上边的示例只是在浏览器上安装好插件后获取 markdown 。如果你想要一个静态站点那么你需要服务器端渲染。

有个好消息！没有什么能阻止你从服务器中获取所有的 markdown 文件 (并配上各种为你服务的缓存策略)。如果你沿着这条路继续走下去的话，你可能会想要去试试使用 GitHub API 去获取目录中的所有 markdown 文件的列表。

### 奖励环节 —— GitHub 工具！

我曾经使用过一段时间的 [Chrome 的扩展 Octotree](https://chrome.google.com/webstore/detail/octotree/bkhaagjahfmjljalopjnoealnfndnagc?hl=en-US)，而且现在我推荐它。虽然不是吐血推荐，但不管怎样我还是推荐它。

它会在左侧提供一个带有树状视图的面板以显示当前你所查看的仓库。

![](/data/attachment/album/201710/10/170821nb0brbg7etobk7zb.png)

从[这个视频](https://www.youtube.com/watch?v=NhlzMcSyQek&index=2&list=PLNYkxOF6rcIB3ci6nwNyLYNU6RDOU3YyL)中我了解到了 [octobox](https://octobox.io/) ，到目前为止看起来还不错。它是一个 GitHub 工单的收件箱。这一句介绍就够了。

说到颜色，在上面所有的截图中我都使用了亮色主题，所以希望不要闪瞎你的双眼。不过说真的，我看到的其他东西都是黑色的主题，为什么我非要忍受 GitHub 这个苍白的主题呐？

![](/data/attachment/album/201710/10/170822des44p8qcmscs2y3.png)

这是由 Chrome 扩展 [Stylish](https://chrome.google.com/webstore/detail/stylish-custom-themes-for/fjnbnpbmkenffdnngjfgmeleoegfcffe/related?hl=en)（它可以在任何网站使用主题）和 [GitHub Dark](https://userstyles.org/styles/37035/github-dark) 风格的一个组合。要完全黑化，那黑色主题的 Chrome 开发者工具（这是内建的，在设置中打开） 以及 [Atom One Dark for Chrome 主题](https://chrome.google.com/webstore/detail/atom-one-dark-theme/obfjhhknlilnfgfakanjeimidgocmkim?hl=en)你肯定也需要。

### Bitbucket

这些内容不适合放在这篇文章的任何地方，但是如果我不称赞 Bitbucket 的话，那就不对了。

两年前我开始了一个项目并花了大半天时间评估哪一个 git 托管服务更适合，最终 Bitbucket 赢得了相当不错的成绩。他们的代码审查流程遥遥领先（这甚至比 GitHub 拥有的指派审阅者的概念要早很长时间）。

GitHub 后来在这次审查竞赛中追了上来，干的不错。不幸的是在过去的一年里我没有机会再使用 Bitbucket —— 也许他们依然在某些方面领先。所以，我会力劝每一个选择 git 托管服务的人考虑一下 Bitbucket 。

### 结尾

就是这样！我希望这里至少有三件事是你此前并不知道的，祝好。

修订：在评论中有更多的技巧；请尽管留下你自己喜欢的技巧。真的，真心祝好。

（题图：orig08.deviantart.net）

---

via: <https://hackernoon.com/12-cool-things-you-can-do-with-github-f3e0424cf2f0>

作者：[David Gilbertson](https://hackernoon.com/@david.gilbertson) 译者：[softpaopao](https://github.com/softpaopao) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


动态端口转发：安装带有 SSH 的 SOCKS 服务器

Summary: 当我们谈论使用 SSH 进行动态端口转发时，我们说的是将 SSH 服务器转换为 SOCKS 服务器。

![](/data/attachment/album/201710/10/222722j7eum8e7kfbfk44m.jpg)

在上一篇文章（[通过 SSH 实现 TCP / IP 隧道（端口转发）：使用 OpenSSH 可能的 8 种场景](/article-8945-1.html)）中，我们看到了处理端口转发的所有可能情况，不过那只是静态端口转发。也就是说，我们只介绍了通过 SSH 连接来访问另一个系统的端口的情况。

在那篇文章中，我们未涉及动态端口转发，此外一些读者没看过该文章，本篇文章中将尝试补充完整。

当我们谈论使用 SSH 进行动态端口转发时，我们说的是将 SSH 服务器转换为 [SOCKS](https://wesharethis.com/goto/http://en.wikipedia.org/wiki/SOCKS) 服务器。那么什么是 SOCKS 服务器？

你知道 [Web 代理](https://wesharethis.com/goto/http://en.wikipedia.org/wiki/Proxy_server)是用来做什么的吗？答案可能是肯定的，因为很多公司都在使用它。它是一个直接连接到互联网的系统，允许没有互联网访问的[内部网](https://wesharethis.com/goto/http://en.wikipedia.org/wiki/Intranet)客户端让其浏览器通过代理来（尽管也有[透明代理](https://wesharethis.com/goto/http://en.wikipedia.org/wiki/Proxy_server#Transparent_and_non-transparent_proxy_server)）浏览网页。Web 代理除了允许输出到 Internet 之外，还可以缓存页面、图像等。已经由某客户端下载的资源，另一个客户端不必再下载它们。此外，它还可以过滤内容并监视用户的活动。当然了，它的基本功能是转发 HTTP 和 HTTPS 流量。

一个 SOCKS 服务器提供的服务类似于公司内部网络提供的代理服务器服务，但不限于 HTTP/HTTPS，它还允许转发任何 TCP/IP 流量（SOCKS 5 也支持 UDP）。

例如，假设我们希望在一个没有直接连接到互联网的内部网上通过 Thunderbird 使用 POP3 、 ICMP 和 SMTP 的邮件服务。如果我们只有一个 web 代理可以用，我们可以使用的唯一的简单方式是使用某个 webmail（也可以使用 [Thunderbird 的 Webmail 扩展](https://wesharethis.com/goto/http://webmail.mozdev.org/)）。我们还可以通过 [HTTP 隧道](https://wesharethis.com/goto/http://en.wikipedia.org/wiki/HTTP_tunnel_(software))来起到代理的用途。但最简单的方式是在网络中设置一个 SOCKS 服务器，它可以让我们使用 POP3、ICMP 和 SMTP，而不会造成任何的不便。

虽然有很多软件可以配置非常专业的 SOCKS 服务器，但用 OpenSSH 设置一个只需要简单的一条命令：

```
Clientessh $ ssh -D 1080 user@servidorssh

```

或者我们可以改进一下：

```
Clientessh $ ssh -fN -D 0.0.0.0:1080 user@servidorssh

```

其中：

* 选项 `-D` 类似于选项为 `-L` 和 `-R` 的静态端口转发。像那些一样，我们可以让客户端只监听本地请求或从其他节点到达的请求，具体取决于我们将请求关联到哪个地址：

```
-D [bind_address:] port

```

在静态端口转发中可以看到，我们使用选项 `-R` 进行反向端口转发，而动态转发是不可能的。我们只能在 SSH 客户端创建 SOCKS 服务器，而不能在 SSH 服务器端创建。
* 1080 是 SOCKS 服务器的典型端口，正如 8080 是 Web 代理服务器的典型端口一样。
* 选项 `-N` 防止实际启动远程 shell 交互式会话。当我们只用 `ssh` 来建立隧道时很有用。
* 选项 `-f` 会使 `ssh` 停留在后台并将其与当前 shell 分离，以便使该进程成为守护进程。如果没有选项 `-N`（或不指定命令），则不起作用，否则交互式 shell 将与后台进程不兼容。

使用 [PuTTY](https://wesharethis.com/goto/http://www.chiark.greenend.org.uk/%7Esgtatham/putty/download.html) 也可以非常简单地进行端口重定向。与 `ssh -D 0.0.0.0:1080` 相当的配置如下：

![PuTTY SOCKS](/data/attachment/album/201710/10/222742d1cmxmmxffczomnh.png)

对于通过 SOCKS 服务器访问另一个网络的应用程序，如果应用程序提供了对 SOCKS 服务器的特别支持，就会非常方便（虽然不是必需的），就像浏览器支持使用代理服务器一样。作为一个例子，如 Firefox 或 Internet Explorer 这样的浏览器使用 SOCKS 服务器访问另一个网络的应用程序：

![Firefox SOCKS](/data/attachment/album/201710/10/222745h60cyydqcm011qym.png)

![Internet Explorer SOCKS](/data/attachment/album/201710/10/222747rsdpooyololqbxbx.png)

注意：上述截图来自 [IE for Linux](https://wesharethis.com/goto/http://www.tatanka.com.br/ies4linux/page/Main_Page) ：如果您需要在 Linux 上使用 Internet Explorer，强烈推荐！

然而，最常见的浏览器并不要求 SOCKS 服务器，因为它们通常与代理服务器配合得更好。

不过，Thunderbird 也支持 SOCKS，而且很有用：

![Thunderbird SOCKS](/data/attachment/album/201710/10/222752o9aiuauj0me1iuk9.png)

另一个例子：[Spotify](https://wesharethis.com/goto/https://www.spotify.com/int/download/linux/) 客户端同样支持 SOCKS：

![Spotify SOCKS](/data/attachment/album/201710/10/222753d3qvipg99pgi1ppg.png)

需要关注一下名称解析。有时我们会发现，在目前的网络中，我们无法解析 SOCKS 服务器另一端所要访问的系统的名称。SOCKS 5 还允许我们通过隧道传播 DNS 请求（ 因为 SOCKS 5 允许我们使用 UDP）并将它们发送到另一端：可以指定是本地还是远程解析（或者也可以两者都试试）。支持此功能的应用程序也必须考虑到这一点。例如，Firefox 具有参数 `network.proxy.socks_remote_dns`（在 `about:config` 中），允许我们指定远程解析。而默认情况下，它在本地解析。

Thunderbird 也支持参数 `network.proxy.socks_remote_dns`，但由于没有地址栏来放置 `about:config`，我们需要改变它，就像在 [MozillaZine:about:config](https://wesharethis.com/goto/http://kb.mozillazine.org/About:config) 中读到的，依次点击 工具 → 选项 → 高级 → 常规 → 配置编辑器（按钮）。

没有对 SOCKS 特别支持的应用程序可以被 <ruby> sock 化 <rt>  socksified </rt></ruby>。这对于使用 TCP/IP 的许多应用程序都没有问题，但并不是全部。“sock 化” 需要加载一个额外的库，它可以检测对 TCP/IP 堆栈的请求，并修改请求，以通过 SOCKS 服务器重定向，从而不需要特别编程来支持 SOCKS 便可以正常通信。

在 Windows 和 [Linux](https://wesharethis.com/2017/07/10/linux-swap-partition/) 上都有 “Sock 化工具”。

对于 Windows，我们举个例子，SocksCap 是一种闭源，但对非商业使用免费的产品，我使用了很长时间都十分满意。SocksCap 由一家名为 Permeo 的公司开发，该公司是创建 SOCKS 参考技术的公司。Permeo 被 [Blue Coat](https://wesharethis.com/goto/http://www.bluecoat.com/) 买下后，它[停止了 SocksCap 项目](https://wesharethis.com/goto/http://www.bluecoat.com/products/sockscap)。现在你仍然可以在互联网上找到 `sc32r240.exe` 文件。[FreeCap](https://wesharethis.com/goto/http://www.freecap.ru/eng/) 也是面向 Windows 的免费代码项目，外观和使用都非常类似于 SocksCap。然而，它工作起来更加糟糕，多年来一直没有缺失维护。看起来，它的作者倾向于推出需要付款的新产品 [WideCap](https://wesharethis.com/goto/http://widecap.ru/en/support/)。

这是 SocksCap 的一个界面，可以看到我们 “sock 化” 了的几个应用程序。当我们从这里启动它们时，这些应用程序将通过 SOCKS 服务器访问网络：

![SocksCap](/data/attachment/album/201710/10/222753sgxhbmcn8x1mu1hh.png)

在配置对话框中可以看到，如果选择了协议 SOCKS 5，我们可以选择在本地或远程解析名称：

![SocksCap settings](/data/attachment/album/201710/10/222755lrzoov4joto3otor.png)

在 Linux 上，如同往常一样，对某个远程命令我们都有许多替代方案。在 Debian/Ubuntu 中，命令行：

```
$ Apt-cache search socks

```

的输出会告诉我们很多。

最著名的是 [tsocks](https://wesharethis.com/goto/http://tsocks.sourceforge.net/) 和 [proxychains](https://wesharethis.com/goto/http://proxychains.sourceforge.net/)。它们的工作方式大致相同：只需用它们启动我们想要 “sock 化” 的应用程序就行。使用 `proxychains` 的 `wget` 的例子：

```
$ Proxychains wget http://www.google.com
ProxyChains-3.1 (http://proxychains.sf.net)
--19: 13: 20-- http://www.google.com/
Resolving www.google.com ...
DNS-request | Www.google.com
| S-chain | - <- - 10.23.37.3:1080-<><>-4.2.2.2:53-<><>-OK
| DNS-response | Www.google.com is 72.14.221.147
72.14.221.147
Connecting to www.google.com | 72.14.221.147 |: 80 ...
| S-chain | - <- - 10.23.37.3:1080-<><>-72.14.221.147:80-<><>-OK
Connected.
HTTP request sent, awaiting response ... 200 OK
Length: unspecified [text / html]
Saving to: `index.html '

    [<=>] 6,016 24.0K / s in 0.2s

19:13:21 (24.0 KB / s) - `index.html 'saved [6016]

```

要让它可以工作，我们必须在 `/etc/proxychains.conf` 中指定要使用的代理服务器：

```
[ProxyList]
Socks5 clientessh 1080

```

我们也设置远程进行 DNS 请求：

```
# Proxy DNS requests - no leak for DNS data
Proxy_dns

```

另外，在前面的输出中，我们已经看到了同一个 `proxychains` 的几条信息性的消息， 非 `wget` 的行是标有字符串 `|DNS-request|`、`|S-chain|` 或 `|DNS-response|` 的。如果我们不想看到它们，也可以在配置中进行调整：

```
# Quiet mode (no output from library)
Quiet_mode

```

---

via: <https://wesharethis.com/2017/07/15/dynamic-port-forwarding-mount-socks-server-ssh/>

作者：[Ahmad](https://wesharethis.com/author/ahmad/) 译者：[firmianay](https://github.com/firmianay) 校对：[jasminepeng](https://github.com/jasminepeng)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


为什么 DevOps 如我们所知道的那样，是安全的终结

![](/data/attachment/album/201710/10/232115x6vxixjd3dyx16d4.jpg)

安全难以推行。在企业管理者迫使开发团队尽快发布程序的大环境下，很难说服他们花费有限的时间来修补安全漏洞。但是鉴于所有网络攻击中有 84％ 发生在应用层，作为一个组织是无法承担其开发团队不包括安全性带来的后果。

DevOps 的崛起为许多安全负责人带来了困境。Sonatype 的前 CTO [Josh Corman](https://twitter.com/joshcorman) 说：“这是对安全的威胁，但这也是让安全变得更好的机会。” Corman 是一个坚定的[将安全和 DevOps 实践整合起来创建 “坚固的 DevOps”](https://techbeacon.com/want-rugged-devops-team-your-release-security-engineers)的倡导者。Business Insights 与 Corman 谈论了安全和 DevOps 共同的价值，以及这些共同价值如何帮助组织更少地受到中断和攻击的影响。

### 安全和 DevOps 实践如何互惠互利？

**Josh Corman：** 一个主要的例子是 DevOps 团队对所有可测量的东西进行检测的倾向。安全性一直在寻找更多的情报和遥测。你可以获取许多 DevOps 团队正在测量的信息，并将这些信息输入到你的日志管理或 SIEM （安全信息和事件管理系统）。

一个 OODA 循环（<ruby> 观察 <rt>  observe </rt></ruby>、<ruby> 定向 <rt>  orient </rt></ruby>、<ruby> 决定 <rt>  decide </rt></ruby>、<ruby> 行为 <rt>  act </rt></ruby>）的前提是有足够普遍的眼睛和耳朵，以注意到窃窃私语和回声。DevOps 为你提供无处不在的仪器。

### 他们有分享其他文化观点吗？

**JC：** “严肃对待你的代码”是一个共同的价值观。例如，由 Netflix 编写的软件工具 Chaos Monkey 是 DevOps 团队的分水岭。它是为了测试亚马逊网络服务的弹性和可恢复性而创建的，Chaos Monkey 使得 Netflix 团队更加强大，更容易为中断做好准备。

所以现在有个想法是我们的系统需要测试，因此，James Wickett 和我及其他人决定做一个邪恶的、带有攻击性的 Chaos Monkey，这就是 GAUNTLT 项目的来由。它基本上是一堆安全测试, 可以在 DevOps 周期和 DevOps 工具链中使用。它也有非常适合 DevOps 的API。

### 企业安全和 DevOps 价值在哪里相交？

**JC：** 这两个团队都认为复杂性是一切事情的敌人。例如，[安全人员和 Rugged DevOps 人员](https://techbeacon.com/rugged-devops-rsa-6-takeaways-security-ops-pros)实际上可以说：“看，我们在我们的项目中使用了 11 个日志框架 - 也许我们不需要那么多，也许攻击面和复杂性可能会让我们受到伤害或者损害产品的质量或可用性。”

复杂性往往是许多事情的敌人。通常情况下，你不会很难说服 DevOps 团队在架构层面使用更好的建筑材料：使用最新的、最不易受攻击的版本，并使用较少的组件。

### “更好的建筑材料”是什么意思？

**JC：** 我是世界上最大的开源仓库的保管人，所以我能看到他们在使用哪些版本，里面有哪些漏洞，何时他们没有修复漏洞，以及等了多久。例如，某些日志记录框架从不会修复任何错误。其中一些会在 90 天内修复了大部分的安全漏洞。人们越来越多地遭到攻击，因为他们使用了一个毫无安全的框架。

除此之外，即使你不知道日志框架的质量，拥有 11 个不同的框架会变得非常笨重、出现 bug，还有额外的工作和复杂性。你暴露在漏洞中的风险是非常大的。你想把时间花在修复大量的缺陷上，还是在制造下一个大的破坏性的事情上？

[Rugged DevOps 的关键是软件供应链管理](https://techbeacon.com/josh-corman-security-devops-how-shared-team-values-can-reduce-threats)，其中包含三个原则：使用更少和更好的供应商、使用这些供应商的最高质量的部分、并跟踪这些部分，以便在发生错误时，你可以有一个及时和敏捷的响应。

### 所以变更管理也很重要。

**JC：** 是的，这是另一个共同的价值。我发现，当一家公司想要执行诸如异常检测或净流量分析等安全测试时，他们需要知道“正常”的样子。让人们失误的许多基本事情与仓库和补丁管理有关。

我在 *Verizon 数据泄露调查报告*中看到，追踪去年被成功利用的漏洞后，其中 97％ 归结为 10 个 CVE（常见漏洞和风险），而这 10 个已经被修复了十多年。所以，我们羞于谈论高级间谍活动。我们没有做基本的补丁工作。现在，我不是说如果你修复这 10 个CVE，那么你就没有被利用，而是这占据了人们实际失误的最大份额。

[DevOps 自动化工具](https://techbeacon.com/devops-automation-best-practices-how-much-too-much)的好处是它们已经成为一个意外的变更管理数据库。其真实反应了谁在哪里什么时候做了变更。这是一个巨大的胜利，因为我们经常对安全性有最大影响的因素无法控制。你承受了 CIO 和 CTO 做出的选择的后果。随着 IT 通过自动化变得更加严格和可重复，你可以减少人为错误的机会，并且哪里发生了变化更加可追溯。

### 你认为什么是最重要的共同价值？

**JC：** DevOps 涉及到过程和工具链，但我认为定义这种属性的是文化，特别是同感。 DevOps 有用是因为开发人员和运维团队能够更好地了解彼此，并做出更明智的决策。不是在解决孤岛中的问题，而是为了活动流程和目标解决。如果你向 DevOps 的团队展示安全如何能使他们变得更好，那么作为回馈他们往往会问：“那么，我们是否有任何选择让你的生活更轻松？”因为他们通常不知道他们做的 X、Y 或 Z 的选择使它无法包含安全性。

对于安全团队，驱动价值的方法之一是在寻求帮助之前变得更有所帮助，在我们告诉 DevOps 团队要做什么之前提供定性和定量的价值。你必须获得 DevOps 团队的信任，并获得发挥的权利，然后才能得到回报。它通常比你想象的快很多。

---

via: <https://techbeacon.com/why-devops-end-security-we-know-it>

作者：[Mike Barton](https://twitter.com/intent/follow?original_referer=https%3A%2F%2Ftechbeacon.com%2Fwhy-devops-end-security-we-know-it%3Fimm_mid%3D0ee8c5%26cmp%3Dem-webops-na-na-newsltr_20170310&ref_src=twsrc%5Etfw&region=follow_link&screen_name=mikebarton&tw_p=followbutton) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


关于 HTML5 你需要了解的基础知识

![](/data/attachment/album/201710/12/090409dpfqygzrpzuhpnqd.jpg)

>
> HTML5 是第五个且是当前的 HTML 版本，它是用于在万维网上构建和呈现内容的标记语言。本文将帮助读者了解它。
>
>
>

HTML5 通过 W3C 和<ruby> Web 超文本应用技术工作组 <rt>  Web Hypertext Application Technology Working Group </rt></ruby>之间的合作发展起来。它是一个更高版本的 HTML，它的许多新元素可以使你的页面更加语义化和动态。它是为所有人提供更好的 Web 体验而开发的。HTML5 提供了很多的功能，使 Web 更加动态和交互。

HTML5 的新功能是：

* 新标签，如 `<header>` 和 `<section>`
* 用于 2D 绘图的 `<canvas>` 元素
* 本地存储
* 新的表单控件，如日历、日期和时间
* 新媒体功能
* 地理位置

HTML5 还不是正式标准（LCTT 译注：HTML5 已于 2014 年成为“推荐标准”），因此，并不是所有的浏览器都支持它或其中一些功能。开发 HTML5 背后最重要的原因之一是防止用户下载并安装像 Silverlight 和 Flash 这样的多个插件。

### 新标签和元素

* **语义化元素：** 图 1 展示了一些有用的语义化元素。
* **表单元素：** HTML5 中的表单元素如图 2 所示。
* **图形元素：** HTML5 中的图形元素如图 3 所示。
* **媒体元素：** HTML5 中的新媒体元素如图 4 所示。

[![](/data/attachment/album/201710/12/090413iaagqebiezzkgeq3.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/Figure-1-7.jpg)

*图 1：语义化元素*

[![](/data/attachment/album/201710/12/090415ngx5znw9xnwgn686.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/Figure-2-5.jpg)

*图 2：表单元素*

[![](/data/attachment/album/201710/12/090416f87iokiit9vc2nv7.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/Figure-3-2.jpg)

*图 3：图形元素*

[![](/data/attachment/album/201710/12/090416x72j7i8p2ajzdpbd.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/Figure-4-2.jpg)

*图 4：媒体元素*

### HTML5 的高级功能

#### 地理位置

这是一个 HTML5 API，用于获取网站用户的地理位置，用户必须首先允许网站获取他或她的位置。这通常通过按钮和/或浏览器弹出窗口来实现。所有最新版本的 Chrome、Firefox、IE、Safari 和 Opera 都可以使用 HTML5 的地理位置功能。

地理位置的一些用途是：

* 公共交通网站
* 出租车及其他运输网站
* 电子商务网站计算运费
* 旅行社网站
* 房地产网站
* 在附近播放的电影的电影院网站
* 在线游戏
* 网站首页提供本地标题和天气
* 工作职位可以自动计算通勤时间

**工作原理：** 地理位置通过扫描位置信息的常见源进行工作，其中包括以下：

* 全球定位系统（GPS）是最准确的
* 网络信号 - IP地址、RFID、Wi-Fi 和蓝牙 MAC地址
* GSM/CDMA 蜂窝 ID
* 用户输入

该 API 提供了非常方便的函数来检测浏览器中的地理位置支持：

```
if (navigator.geolocation) {
// do stuff
}

```

`getCurrentPosition` API 是使用地理位置的主要方法。它检索用户设备的当前地理位置。该位置被描述为一组地理坐标以及航向和速度。位置信息作为位置对象返回。

语法是：

```
getCurrentPosition(showLocation, ErrorHandler, options);

```

* `showLocation`：定义了检索位置信息的回调方法。
* `ErrorHandler`（可选）：定义了在处理异步调用时发生错误时调用的回调方法。
* `options` （可选）： 定义了一组用于检索位置信息的选项。

我们可以通过两种方式向用户提供位置信息：测地和民用。

1. 描述位置的测地方式直接指向纬度和经度。
2. 位置信息的民用表示法是人类可读的且容易理解。

如下表 1 所示，每个属性/参数都具有测地和民用表示。

[![](/data/attachment/album/201710/12/090417q0a4teffzx4y08ta.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/table-1.jpg)

图 5 包含了一个位置对象返回的属性集。

[![](/data/attachment/album/201710/12/090418pesuejuuemojk1gu.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/Figure5-1.jpg)

*图5：位置对象属性*

#### 网络存储

在 HTML 中，为了在本机存储用户数据，我们需要使用 JavaScript cookie。为了避免这种情况，HTML5 已经引入了 Web 存储，网站利用它在本机上存储用户数据。

与 Cookie 相比，Web 存储的优点是：

* 更安全
* 更快
* 存储更多的数据
* 存储的数据不会随每个服务器请求一起发送。只有在被要求时才包括在内。这是 HTML5 Web 存储超过 Cookie 的一大优势。

有两种类型的 Web 存储对象：

1. 本地 - 存储没有到期日期的数据。
2. 会话 - 仅存储一个会话的数据。

**如何工作：** `localStorage` 和 `sessionStorage` 对象创建一个 `key=value` 对。比如： `key="Name"`， `value="Palak"`。

这些存储为字符串，但如果需要，可以使用 JavaScript 函数（如 `parseInt()` 和 `parseFloat()`）进行转换。

下面给出了使用 Web 存储对象的语法：

* 存储一个值：
	+ `localStorage.setItem("key1", "value1");`
	+ `localStorage["key1"] = "value1";`
* 得到一个值：
	+ `alert(localStorage.getItem("key1"));`
	+ `alert(localStorage["key1"]);`
* 删除一个值： -`removeItem("key1");`
* 删除所有值：
	+ `localStorage.clear();`

#### 应用缓存（AppCache）

使用 HTML5 AppCache，我们可以使 Web 应用程序在没有 Internet 连接的情况下脱机工作。除 IE 之外，所有浏览器都可以使用 AppCache（截止至此时）。

应用缓存的优点是：

* 网页浏览可以脱机
* 页面加载速度更快
* 服务器负载更小

`cache manifest` 是一个简单的文本文件，其中列出了浏览器应缓存的资源以进行脱机访问。 `manifest` 属性可以包含在文档的 HTML 标签中，如下所示：

```
<html manifest="test.appcache">
...
</html>

```

它应该在你要缓存的所有页面上。

缓存的应用程序页面将一直保留，除非：

1. 用户清除它们
2. `manifest` 被修改
3. 缓存更新

#### 视频

在 HTML5 发布之前，没有统一的标准来显示网页上的视频。大多数视频都是通过 Flash 等不同的插件显示的。但 HTML5 规定了使用 video 元素在网页上显示视频的标准方式。

目前，video 元素支持三种视频格式，如表 2 所示。

[![](/data/attachment/album/201710/12/090419fak6wz77bjja345t.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/table-2.jpg)

下面的例子展示了 video 元素的使用：

```
<! DOCTYPE HTML>
<html>
<body>

<video src=" vdeo.ogg" width="320" height="240" controls="controls">

This browser does not support the video element.

</video>

</body>
</html>

```

例子使用了 Ogg 文件，并且可以在 Firefox、Opera 和 Chrome 中使用。要使视频在 Safari 和未来版本的 Chrome 中工作，我们必须添加一个 MPEG4 和 WebM 文件。

`video` 元素允许多个 `source` 元素。`source` 元素可以链接到不同的视频文件。浏览器将使用第一个识别的格式，如下所示：

```
<video width="320" height="240" controls="controls">
<source src="vdeo.ogg" type="video/ogg" />
<source src=" vdeo.mp4" type="video/mp4" />
<source src=" vdeo.webm" type="video/webm" />
This browser does not support the video element.
</video>

```

[![](/data/attachment/album/201710/12/090419sx3zui9gxf5mv0au.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/Figure6-1.jpg)

*图6：Canvas 的输出*

#### 音频

对于音频，情况类似于视频。在 HTML5 发布之前，在网页上播放音频没有统一的标准。大多数音频也通过 Flash 等不同的插件播放。但 HTML5 规定了通过使用音频元素在网页上播放音频的标准方式。音频元素用于播放声音文件和音频流。

目前，HTML5 `audio` 元素支持三种音频格式，如表 3 所示。

[![](/data/attachment/album/201710/12/090420ep02f7zpdchh5ocn.jpg)](http://opensourceforu.com/wp-content/uploads/2017/05/table-3.jpg)

`audio` 元素的使用如下所示：

```
<! DOCTYPE HTML>
<html>
<body>

<audio src=" song.ogg" controls="controls">

This browser does not support the audio element.

</video>

</body>
</html>

```

此例使用 Ogg 文件，并且可以在 Firefox、Opera 和 Chrome 中使用。要在 Safari 和 Chrome 的未来版本中使 audio 工作，我们必须添加一个 MP3 和 Wav 文件。

`audio` 元素允许多个 `source` 元素，它可以链接到不同的音频文件。浏览器将使用第一个识别的格式，如下所示：

```
<audio controls="controls">
<source src="song.ogg" type="audio/ogg" />
<source src="song.mp3" type="audio/mpeg" />

This browser does not support the audio element.

</audio>

```

#### 画布（Canvas）

要在网页上创建图形，HTML5 使用 画布 API。我们可以用它绘制任何东西，并且它使用 JavaScript。它通过避免从网络下载图像而提高网站性能。使用画布，我们可以绘制形状和线条、弧线和文本、渐变和图案。此外，画布可以让我们操作图像中甚至视频中的像素。你可以将 `canvas` 元素添加到 HTML 页面，如下所示：

```
<canvas id="myCanvas" width="200" height="100"></canvas>

```

画布元素不具有绘制元素的功能。我们可以通过使用 JavaScript 来实现绘制。所有绘画应在 JavaScript 中。

```
<script type="text/javascript">
var c=document.getElementById("myCanvas");
var cxt=c.getContext("2d");
cxt.fillStyle="blue";
cxt.storkeStyle = "red";
cxt.fillRect(10,10,100,100);
cxt.storkeRect(10,10,100,100);
</script>

```

以上脚本的输出如图 6 所示。

你可以绘制许多对象，如弧、圆、线/垂直梯度等。

### HTML5 工具

为了有效操作，所有熟练的或业余的 Web 开发人员/设计人员都应该使用 HTML5 工具，当需要设置工作流/网站或执行重复任务时，这些工具非常有帮助。它们提高了网页设计的可用性。

以下是一些帮助创建很棒的网站的必要工具。

* **HTML5 Maker：** 用来在 HTML、JavaScript 和 CSS 的帮助下与网站内容交互。非常容易使用。它还允许我们开发幻灯片、滑块、HTML5 动画等。
* **Liveweave：** 用来测试代码。它减少了保存代码并将其加载到屏幕上所花费的时间。在编辑器中粘贴代码即可得到结果。它非常易于使用，并为一些代码提供自动完成功能，这使得开发和测试更快更容易。
* **Font dragr：** 在浏览器中预览定制的 Web 字体。它会直接载入该字体，以便你可以知道看起来是否正确。也提供了拖放界面，允许你拖动字形、Web 开放字体和矢量图形来马上测试。
* **HTML5 Please：** 可以让我们找到与 HTML5 相关的任何内容。如果你想知道如何使用任何一个功能，你可以在 HTML Please 中搜索。它提供了支持的浏览器和设备的有用资源的列表，语法，以及如何使用元素的一般建议等。
* **Modernizr：** 这是一个开源工具，用于给访问者浏览器提供最佳体验。使用此工具，你可以检测访问者的浏览器是否支持 HTML5 功能，并加载相应的脚本。
* **Adobe Edge Animate：** 这是必须处理交互式 HTML 动画的 HTML5 开发人员的有用工具。它用于数字出版、网络和广告领域。此工具允许用户创建无瑕疵的动画，可以跨多个设备运行。
* **Video.js：** 这是一款基于 JavaScript 的 HTML5 视频播放器。如果要将视频添加到你的网站，你应该使用此工具。它使视频看起来不错，并且是网站的一部分。
* **The W3 Validator：** W3 验证工具测试 HTML、XHTML、SMIL、MathML 等中的网站标记的有效性。要测试任何网站的标记有效性，你必须选择文档类型为 HTML5 并输入你网页的 URL。这样做之后，你的代码将被检查，并将提供所有错误和警告。
* **HTML5 Reset：** 此工具允许开发人员在 HTML5 中重写旧网站的代码。你可以使用这些工具为你网站的访问者提供一个良好的网络体验。

---

Palak Shah

作者是高级软件工程师。她喜欢探索新技术，学习创新概念。她也喜欢哲学。你可以通过 [palak311@gmail.com](mailto:palak311@gmail.com) 联系她。

---

via: <http://opensourceforu.com/2017/06/introduction-to-html5/>

作者：[Palak Shah](http://opensourceforu.com/author/palak-shah/) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


用 C 语言对 Gtk+ 应用进行功能测试

Summary: 这个简单教程教你如何测试你应用的功能。

这个简单教程教你如何测试你应用的功能。

>
> 这个简单教程教你如何测试你应用的功能。
>
>
>

![Functional testing Gtk+ applications in C](/data/attachment/album/201710/12/132540ni4mi77xmr2m27x2.png "Functional testing Gtk+ applications in C ")

自动化测试用来保证你程序的质量以及让它以预想的运行。单元测试只是检测你算法的某一部分，而并不注重各组件间的适应性。这就是为什么会有功能测试，它有时也称为集成测试。

功能测试简单地与你的用户界面进行交互，无论它是网站还是桌面应用。为了展示功能测试如何工作，我们以测试一个 Gtk+ 应用为例。为了简单起见，这个教程里，我们使用 Gtk+ 2.0 教程的示例。

### 基础设置

对于每一个功能测试，你通常需要定义一些全局变量，比如 “用户交互时延” 或者 “失败的超时时间”（也就是说，如果在指定的时间内一个事件没有发生，程序就要中断）。

```
#define TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(f) ((TttFunctionalTestUtilIdleCondition)(f))
#define TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME (125000)
#define TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG (500000)
typedef gboolean (*TttFunctionalTestUtilIdleCondition)(gpointer data);
struct timespec ttt_functional_test_util_default_timeout = {
  20,
  0,
};

```

现在我们可以实现我们自己的超时函数。这里，为了能够得到期望的延迟，我们采用 `usleep` 函数。

```
void
ttt_functional_test_util_reaction_time()
{
  usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME);
}

void
ttt_functional_test_util_reaction_time_long()
{
  usleep(TTT_FUNCTIONAL_TEST_UTIL_REACTION_TIME_LONG);
}

```

直到获得控制状态，超时函数才会推迟执行。这对于一个异步执行的动作很有帮助，这也是为什么采用这么长的时延。

```
void
ttt_functional_test_util_idle_condition_and_timeout(
     TttFunctionalTestUtilIdleCondition idle_condition,
     struct timespec *timeout,
     pointer data)
{
  struct timespec start_time, current_time;

  clock_gettime(CLOCK_MONOTONIC,
                &start_time);

  while(TTT_FUNCTIONAL_TEST_UTIL_IDLE_CONDITION(idle_condition)(data)){
    ttt_functional_test_util_reaction_time();

    clock_gettime(CLOCK_MONOTONIC,
                  &current_time);

    if(start_time.tv_sec + timeout->tv_sec < current_time.tv_sec){
      break;
    }
  }

  ttt_functional_test_util_reaction_time();
}

```

### 与图形化用户界面交互

为了模拟用户交互的操作， [Gdk 库](https://developer.gnome.org/gdk3/stable/) 为我们提供了一些需要的函数。要完成我们的工作，我们只需要如下 3 个函数：

* `gdk_display_warp_pointer()`
* `gdk_test_simulate_button()`
* `gdk_test_simulate_key()`

举个例子，为了测试按钮点击，我们可以这么做：

```
gboolean
ttt_functional_test_util_button_click(GtkButton *button)
{
  GtkWidget *widget;

  GdkWindow *window;

  gint x, y;
  gint origin_x, origin_y;

  if(button == NULL ||
     !GTK_IS_BUTTON(button)){
    return(FALSE);
  }

  widget = button;

  if(!GTK_WIDGET_REALIZED(widget)){
    ttt_functional_test_util_reaction_time_long();
  }

  /* retrieve window and pointer position */
  gdk_threads_enter();

  window = gtk_widget_get_window(widget);

  x = widget->allocation.x + widget->allocation.width / 2.0;
  y = widget->allocation.y + widget->allocation.height / 2.0;

  gdk_window_get_origin(window, &origin_x, &origin_y);

  gdk_display_warp_pointer(gtk_widget_get_display(widget),
                           gtk_widget_get_screen(widget),
                           origin_x + x, origin_y + y);

  gdk_threads_leave();

  /* click the button */
  ttt_functional_test_util_reaction_time();

  gdk_test_simulate_button(window,
                           x,
                           y,
                           1,
                           GDK_BUTTON1_MASK,
                           GDK_BUTTON_PRESS);

  ttt_functional_test_util_reaction_time();

  gdk_test_simulate_button(window,
                           x,
                           y,
                           1,
                           GDK_BUTTON1_MASK,
                           GDK_BUTTON_RELEASE);

  ttt_functional_test_util_reaction_time();

  ttt_functional_test_util_reaction_time_long();

  return(TRUE);
}

```

我们想要保证按钮处于激活状态，因此我们提供一个空闲条件函数：

```
gboolean
ttt_functional_test_util_idle_test_toggle_active(
     GtkToggleButton **toggle_button)
{
  gboolean do_idle;

  do_idle = TRUE;

  gdk_threads_enter();

  if(*toggle_button != NULL &&
     GTK_IS_TOGGLE_BUTTON(*toggle_button) &&
     gtk_toggle_button_get_active(*toggle_button)){
    do_idle = FALSE;
  }

  gdk_threads_leave();

  return(do_idle);
}

```

### 测试场景

因为这个 Tictactoe 程序非常简单，我们只需要确保点击了一个 [**GtkToggleButton**](https://developer.gnome.org/gtk3/stable/GtkToggleButton.html) 按钮即可。一旦该按钮肯定进入了激活状态，功能测试就可以执行。为了点击按钮，我们使用上面提到的很方便的 `util` 函数。

如图所示，我们假设，填满第一行，玩家 A 就赢，因为玩家 B 没有注意，只填充了第二行。

```
GtkWindow *window;
Tictactoe *ttt;

void*
ttt_functional_test_gtk_main(void *)
{
  gtk_main();

  pthread_exit(NULL);
}

void
ttt_functional_test_dumb_player_b()
{
  GtkButton *buttons[3][3];

  guint i;

  /* to avoid race-conditions copy the buttons */
  gdk_threads_enter();

  memcpy(buttons, ttt->buttons, 9 * sizeof(GtkButton *));

  gdk_threads_leave();

  /* TEST 1 - the dumb player B */
  for(i = 0; i < 3; i++){
    /* assert player A clicks the button successfully */
    if(!ttt_functional_test_util_button_click(buttons[0][i])){
      exit(-1);
    }

    functional_test_util_idle_condition_and_timeout(
         ttt_functional_test_util_idle_test_toggle_active,
         ttt_functional_test_util_default_timeout,
         &buttons[0][i]);

    /* assert player B clicks the button successfully */
    if(!ttt_functional_test_util_button_click(buttons[1][i])){
      exit(-1);
    }

    functional_test_util_idle_condition_and_timeout(
         ttt_functional_test_util_idle_test_toggle_active,
         ttt_functional_test_util_default_timeout,
         &buttons[1][i]);
  }
}

int
main(int argc, char **argv)
{
  pthread_t thread;

  gtk_init(&argc, &argv);

  /* start the tictactoe application */
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  ttt = tictactoe_new();
  gtk_container_add(window, ttt);

  gtk_widget_show_all(window);

  /* start the Gtk+ dispatcher */
  pthread_create(&thread, NULL,
                 ttt_functional_test_gtk_main, NULL);

  /* launch test routines */
  ttt_functional_test_dumb_player_b();

  /* terminate the application */
  gdk_threads_enter();

  gtk_main_quit();

  gdk_threads_leave();

  return(0);
}

```

（题图：opensource.com）

---

作者简介：

Joël Krähemann - 精通 C 语言编程的自由软件爱好者。不管代码多复杂，它也是一点点写成的。作为高级的 Gtk+ 程序开发者，我知道多线程编程有多大的挑战性，有了多线程编程，我们就有了未来需求的良好基础。

摘自: <https://opensource.com/article/17/7/functional-testing>

作者：[Joël Krähemann](https://opensource.com/users/joel2001k) 译者：[sugarfillet](https://github.com/sugarfillet) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


当你只想将事情搞定时，为什么开放式工作这么难？

Summary: 学习使用开放式决策框架来写一本书

学习使用开放式决策框架来写一本书

>
> 学习使用开放式决策框架来写一本书
>
>
>

![Why working openly is hard when you just want to get stuff done](/data/attachment/album/201710/13/064330gp5x5mjzml29klpp.png "Why working openly is hard when you just want to get stuff done")

GSD（get stuff done 的缩写，即搞定）指导着我的工作方式。数年来，我将各种方法论融入我日常工作的习惯中，包括精益方法的反馈循环，和敏捷开发的迭代优化，以此来更好地 GSD（如果把 GSD 当作动词的话）。这意味着我必须非常有效地利用我的时间：列出清晰、各自独立的目标；标记已完成的项目；用迭代的方式地持续推进项目进度。但是当我们以开放为基础时仍然能够 GSD 吗？又或者 GSD 的方法完全行不通呢？大多数人都认为这会导致糟糕的状况，但我发现事实并不一定这样。

在开放的环境中工作，遵循<ruby> <a href="https://opensource.com/open-organization/resources/open-decision-framework">  开放式决策框架 </a> <rt>  Open Decision Framework </rt></ruby>中的指导，会让项目起步变慢。但是在最近的一个项目中，我们作出了一个决定，一个从开始就正确的决定：以开放的方式工作，并与我们的社群一起合作。

这是我们能做的最好的决定。

我们来看看这次经历带来的意想不到的结果，再看看我们如何将 GSD 思想融入开放式组织框架。

### 建立社区

2014 年 10 月，我接手了一个新的项目：当时红帽的 CEO Jim Whitehurst 即将推出一本新书<ruby> 《开放式组织》 <rt>  The Open Organization </rt></ruby>，我要根据书中提出的概念，建立一个社区。“太棒了，这听起来是一个挑战，我加入了！”我这样想。但不久，[冒牌者综合征](https://opensource.com/open-organization/17/5/team-impostor-syndrome)便出现了，我又开始想：“我们究竟要做什么呢？怎样才算成功呢？”

让我剧透一下，在这本书的结尾处，Jim 鼓励读者访问 Opensource.com，继续探讨 21 世纪的开放和管理。所以，在 2015 年 5 月，我们的团队在网站上建立了一个新的板块来讨论这些想法。我们计划讲一些故事，就像我们在 Opensource.com 上常做的那样，只不过这次围绕着书中的观点与概念。之后，我们每周都发布新的文章，在 Twitter 上举办了一个在线的读书俱乐部，还将《开放式组织》打造成了系列书籍。

我们内部独自完成了该系列书籍的前三期，每隔六个月发布一期。每完成一期，我们就向社区发布。然后我们继续完成下一期的工作，如此循环下去。

这种工作方式，让我们看到了很大的成功。近 3000 人订阅了[该系列的新书](https://opensource.com/open-organization/resources/leaders-manual)：《开放式组织领袖手册》。我们用 6 个月的周期来完成这个项目，这样新书的发行日正好是前书的两周年纪念日。

在这样的背景下，我们完成这本书的方式是简单直接的：针对开放工作这个主题，我们收集了最好的故事，并将它们组织起来形成文章，招募作者填补一些内容上的空白，使用开源工具调整字体样式，与设计师一起完成封面，最终发布这本书。这样的工作方式使得我们能按照自己的时间线（GSD）全速前进。到[第三本书](https://opensource.com/open-organization/resources/leaders-manual)时，我们的工作流已经基本完善了。

然而这一切在我们计划开始《开放式组织》的最后一本书时改变了，这本书将重点放在开放式组织和 IT 文化的交融上。我提议使用开放式决策框架来完成这本书，因为我想通过这本书证明开放式的工作方法能得到更好的结果，尽管我知道这可能会完全改变我们的工作方式。时间非常紧张（只有两个半月），但我们还是决定试一试。

### 用开放式决策框架来完成一本书

开放式决策框架列出了组成开放决策制定过程的 4 个阶段。下面是我们在每个阶段中的工作情况（以及开放是如何帮助完成工作的）。

#### 1、 构思

我们首先写了一份草稿，罗列了对项目设想的愿景。我们需要拿出东西来和潜在的“顾客”分享（在这个例子中，“顾客”指潜在的利益相关者和作者）。然后我们约了一些领域专家面谈，这些专家能够给我们直接的诚实的意见。这些专家表现出的热情与他们提供的指导验证了我们的想法，同时提出了反馈意见使我们能继续向前。如果我们没有得到这些验证，我们会退回到我们最初的想法，再决定从哪里重新开始。

#### 2、 计划与研究

经过几次面谈，我们准备在 [Opensource.com 上公布这个项目](https://opensource.com/open-organization/17/3/announcing-it-culture-book)。同时，我们在 [Github 上也启动了这个项目](https://github.com/open-organization-ambassadors/open-org-it-culture)，提供了项目描述、预计的时间线，并阐明了我们所受的约束。这次公布得到了很好的效果，我们最初计划的目录中欠缺了一些内容，在项目公布之后的 72 小时内就被补充完整了。另外（也是更重要的），读者针对一些章节，提出了本不在我们计划中的想法，但是读者觉得这些想法能够补充我们最初设想的版本。

回顾过去，我觉得在项目的第一和第二个阶段，开放项目并不会影响我们搞定项目的能力。事实上，这样工作有一个很大的好处：发现并填补内容的空缺。我们不只是填补了空缺，我们是迅速地填补了空缺，并且还是用我们自己从未考虑过的点子。这并不一定要求我们做更多的工作，只是改变了我们的工作方式。我们动用有限的人脉，邀请别人来写作，再组织收到的内容，设置上下文，将人们导向正确的方向。

#### 3、 设计，开发和测试

项目的这个阶段完全围绕项目管理，管理一些像猫一样特立独行的人，并处理项目的预期。我们有明确的截止时间，我们提前沟通，频繁沟通。我们还使用了一个战略：列出了贡献者和利益相关者，在项目的整个过程中向他们告知项目的进度，尤其是我们在 Github 上标出的里程碑。

最后，我们的书需要一个名字。我们收集了许多反馈，指出书名应该是什么，更重要的是反馈指出了书名不应该是什么。我们通过 [Github 上的工单](https://github.com/open-organization-ambassadors/open-org-it-culture/issues/20)收集反馈意见，并公开表示我们的团队将作最后的决定。当我们准备宣布最后的书名时，我的同事 Bryan Behrenshausen 做了很好的工作，[分享了我们作出决定的过程](https://github.com/open-organization-ambassadors/open-org-it-culture/issues/20#issuecomment-297970303)。人们似乎对此感到高兴——即使他们不同意我们最后的书名。

书的“测试”阶段需要大量的[校对](https://github.com/open-organization-ambassadors/open-org-it-culture/issues/29)。社区成员真的参与到回答这个“求助”贴中来。我们在 GitHub 工单上收到了大约 80 条意见，汇报校对工作的进度（更不用说通过电子邮件和其他反馈渠道获得的许多额外的反馈）。

关于搞定任务：在这个阶段，我们亲身体会了 [Linus 法则](https://en.wikipedia.org/wiki/Linus%27s_Law)：“<ruby> 众目之下， <strong>  笔误 </strong> 无所遁形。 <rt>  With more eyes, all  <strong>   typos  </strong>  are shallow. </rt></ruby>” 如果我们像前三本书一样自己独立完成，那么整个校对的负担就会落在我们的肩上（就像这些书一样）！相反，社区成员慷慨地帮我们承担了校对的重担，我们的工作从自己校对（尽管我们仍然做了很多工作）转向管理所有的 change requests。对我们团队来说，这是一个受大家欢迎的改变；对社区来说，这是一个参与的机会。如果我们自己做的话，我们肯定能更快地完成校对，但是在开放的情况下，我们在截止日期之前发现了更多的错误，这一点毋庸置疑。

#### 4、 发布

好了，我们现在推出了这本书的最终版本。（或者只是第一版？）

我们把发布分为两个阶段。首先，根据我们的公开的项目时间表，在最终日期之前的几天，我们安静地推出了这本书，以便让我们的社区贡献者帮助我们测试[下载表格](https://opensource.com/open-organization/resources/culture-change)。第二阶段也就是现在，这本书的[通用版](https://opensource.com/open-organization/resources/culture-change)的正式公布。当然，我们在发布后的仍然接受反馈，开源方式也正是如此。

### 成就解锁

遵循开放式决策框架是<ruby> 《IT 文化变革指南》 <rt>  Guide to IT Culture Change </rt></ruby>成功的关键。通过与客户和利益相关者的合作，分享我们的制约因素，工作透明化，我们甚至超出了自己对图书项目的期望。

我对整个项目中的合作，反馈和活动感到非常满意。虽然有一段时间内没有像我想要的那样快速完成任务，这让我有一种焦虑感，但我很快就意识到，开放这个过程实际上让我们能完成更多的事情。基于上面我的概述这一点显而易见。

所以也许我应该重新考虑我的 GSD 心态，并将其扩展到 GMD：Get **More** Done，搞定**更多**工作，并且就这个例子说，取得更好的结果。

（题图：opensource.com）

---

作者简介：

Jason Hibbets - Jason Hibbets 是 Red Hat 企业营销中的高级社区传播者，也是 Opensource.com 的社区经理。 他自2003 年以来一直在 Red Hat，并且是开源城市基金会的创立者。之前的职位包括高级营销专员、项目经理、Red Hat 知识库维护人员和支持工程师。

---

via: <https://opensource.com/open-organization/17/6/working-open-and-gsd>

作者：[Jason Hibbets](https://opensource.com/users/jhibbets) 译者：[explosic4](https://github.com/explosic4) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


LEDE 和 OpenWrt 分裂之争

Summary: （2016 年）五月初，当一群 OpenWrt 核心开发者 宣布 他们将开始着手 OpenWrt 的一个副产品 （或者，可能算一个分支）叫 Linux 嵌入开发环境 （LEDE）时，OpenWrt 用户社区陷入一片巨大的混乱中。

![](/data/attachment/album/201710/13/151519npfoxfeh5xfrgwnk.jpg)

对于家用 WiFi 路由器和接入点来说，[OpenWrt](https://openwrt.org/) 项目可能是最广为人知的 Linux 发行版；在 12 年以前，它产自现在有名的 Linksys WRT54G 路由器的源代码。（2016 年）五月初，当一群 OpenWrt 核心开发者 [宣布](https://lwn.net/Articles/686180/) 他们将开始着手 OpenWrt 的一个副产品 （或者，可能算一个分支）叫 [Linux 嵌入开发环境](https://www.lede-project.org/) （LEDE）时，OpenWrt 用户社区陷入一片巨大的混乱中。为什么产生分裂对公众来说并不明朗，而且 LEDE 宣言惊到了一些其他 OpenWrt 开发者也暗示这团队的内部矛盾。

LEDE 宣言被 Jo-Philipp Wich 于五月三日发往所有 OpenWrt 开发者列表和新 LEDE 开发者列表。它将 LEDE 描述为“OpenWrt 社区的一次重启” 和 “OpenWrt 项目的一个副产品” ，希望产生一个 “注重透明性、合作和权利分散”的 Linux 嵌入式开发社区。

给出的重启的原因是 OpenWrt 遭受着长期以来存在且不能从内部解决的问题 —— 换句话说，关于内部处理方式和政策。例如，宣言称，开发者的数目在不断减少，却没有接纳新开发者的方式（而且貌似没有授权委托访问给新开发者的方法）。宣言说到，项目的基础设施不可靠（例如，去年服务器挂掉在这个项目中也引发了相当多的矛盾），但是内部不合和单点错误阻止了修复它。内部和从这个项目到外面世界也存在着“交流、透明度和合作”的普遍缺失。最后，一些技术缺陷被引述：不充分的测试、缺乏常规维护，以及窘迫的稳固性与文档。

该宣言继续描述 LEDE 重启将怎样解决这些问题。所有交流频道都会打开供公众使用，决策将在项目范围内的投票决出，合并政策将放宽等等。更详细的说明可以在 LEDE 站点的[规则](https://www.lede-project.org/rules.html)页找到。其他细节中，它说贡献者将只有一个阶级（也就是，没有“核心开发者”这样拥有额外权利的群体），简单的少数服从多数投票作出决定，并且任何被这个项目管理的基础设施必须有三个以上管理员账户。在 LEDE 邮件列表， Hauke Mehrtens [补充](http://lists.infradead.org/pipermail/lede-dev/2016-May/000080.html)到，该项目将会努力把补丁投递到上游项目 —— 这是过去 OpenWrt 被批判的一点，尤其是对 Linux 内核。

除了 Wich，这个宣言被 OpenWrt 贡献者 John Crispin、 Daniel Golle、 Felix Fietkau、 Mehrtens、 Matthias Schiffer 和 Steven Barth 共同签署，并以给其他有兴趣参与的人访问 LEDE 站点的邀请作为了宣言结尾。

### 回应和问题

有人可能会猜想 LEDE 组织者预期他们的宣言会有或积极或消极的反响。毕竟，细读宣言中批判 OpenWrt 项目暗示了 LEDE 阵营发现有一些 OpenWrt 项目成员难以共事（例如，“单点错误” 或 “内部不和”阻止了基础设施的修复）。

并且，确实，有很多消极回应。OpenWrt 创立者之一 Mike Baker [回应](https://lwn.net/Articles/686988/) 了一些警告，反驳所有 LEDE 宣言中的结论并称“像‘重启’这样的词语都是含糊不清的，且具有误导性的，而且 LEDE 项目未能揭晓其真实本质。”与此同时，有人关闭了那些在 LEDE 宣言上署名的开发者的 @openwrt.org 邮件入口；当 Fietkau [提出反对](https://lwn.net/Articles/686989/)， Baker [回复](https://lwn.net/Articles/686990/)账户“暂时停用”是因为“还不确定 LEDE 能不能代表 OpenWrt。” 另一个 OpenWrt 核心成员 Imre Kaloz [写](https://lwn.net/Articles/686991/)到，他们现在所抱怨的 OpenWrt 的“大多数[破]事就是 LEDE 团队弄出来的”。

但是大多数 OpenWrt 列表的回应对该宣言表示困惑。邮件列表成员不明确 LEDE 团队是否将对 OpenWrt [继续贡献](https://lwn.net/Articles/686995/)，或导致了这次分裂的架构和内部问题的[确切本质](https://lwn.net/Articles/686996/)是什么。 Baker 的第一反应是对宣言中引述的那些问题缺乏公开讨论表示难过：“我们意识到当前的 OpenWrt 项目遭受着许多的问题，”但“我们希望有机会去讨论并尝试着解决”它们。 Baker 作出结论：

>
> 我们想强调，我们确实希望能够公开的讨论，并解决掉手头事情。我们的目标是与所有能够且希望对 OpenWrt 作出贡献的参与者共事，包括 LEDE 团队。
>
>
>

除了有关新项目的初心的问题之外，一些邮件列表订阅者提出了 LEDE 是否与 OpenWrt 有相同的使用场景定位，给新项目取一个听起来更一般的名字的疑惑。此外，许多人，像 Roman Yeryomin，对为什么这些问题需要 LEDE 团队的离开（来解决）[表示了疑惑](https://lwn.net/Articles/686992/)，特别是，与此同时，LEDE 团队由大部分活跃核心 OpenWrt 开发者构成。一些列表订阅者，像 Michael Richardson，甚至不清楚[谁还会继续开发](https://lwn.net/Articles/686993/) OpenWrt。

### 澄清

LEDE 团队尝试着深入阐释他们的境况。在 Fietkau 给 Baker 的回复中，他说在 OpenWrt 内部关于有目的地改变的讨论会很快变得“有毒，”因此导致没有进展。而且：

>
> 这些讨论的要点在于那些掌握着基础设施关键部分的人精力有限却拒绝他人的加入和帮助，甚至是面对无法及时解决的重要问题时也是这样。
>
>
> 这种像单点错误一样的事已经持续了很多年了，没有任何有意义的进展来解决它。
>
>
>

Wich 和 Fietkau 都没有明显指出具体的人，虽然在列表的其他人可能会想到这个基础设施和 OpenWrt 的内部决策问题要归咎于某些人。 Daniel Dickinson [陈述](https://lwn.net/Articles/686998/)到：

>
> 我的印象是 Kaloz (至少) 以基础设施为胁来保持控制，并且根本性的问题是 OpenWrt 是*不*民主的，而且忽视那些真正在 OpenWrt 工作的人想要的是什么，无视他们的愿望，因为他/他们把控着要害。
>
>
>

另一方面， Luka Perkov [指出](https://lwn.net/Articles/687001/) 很多 OpemWrt 开发者想从 Subversion 转移到 Git，但 Fietkau 却阻止这种变化。

看起来是 OpenWrt 的管理结构并非如预期般发挥作用，其结果导致个人冲突爆发，而且由于没有完好定义的流程，某些人能够简单的忽视或阻止提议的变化。明显，这不是一个能长期持续的模式。

五月六日，Crispin 在一个新的帖子中[写给](https://lwn.net/Articles/687003/) OpenWrt 列表，尝试着重构 LEDE 项目宣言。他说，这并不是意味着“敌对或分裂”行为，只是与结构失衡的 OpenWrt 做个清晰的划分并以新的方式开始。问题在于“不要归咎于一次单独的事件、一个人或者一次口水战”，他说，“我们想与过去自己造成的错误和多次作出的错误管理决定分开”。 Crispin 也承认宣言没有把握好，说 LEDE 团队 “弄糟了发起纲领。”

Crispin 的邮件似乎没能使 Kaloz 满意，她[坚持认为](https://lwn.net/Articles/687004/) Crispin（作为发行经理）和 Fietkau（作为领头开发者）可以轻易地在 OpenWrt 内部作出想要的改变。但是讨论的下文后来变得沉寂；之后 LEDE 或者 OpenWrt 哪边会发生什么还有待观察。

### 目的

对于那些想要探究 LEDE 所认为有问题的事情的更多细节的 OpenWrt 成员来说，有更多的信息来源可以为这个问题提供线索。在公众宣言之前，LEDE 组织花了几周谈论他们的计划，会议的 IRC 日志现已[发布](http://meetings.lede-project.org/lede-adm/2016/?C=M;O=A)。特别有趣的是，三月三十日的[会议](http://meetings.lede-project.org/lede-adm/2016/lede-adm.2016-03-30-11.05.log.html)包含了这个项目目标的细节讨论。

其中包括一些针对 OpenWrt 的基础设施的抱怨，像项目的 Trac 工单追踪器的缺点。它充斥着不完整的漏洞报告和“我也是”的评论，Wich 说，结果几乎没有贡献者使用它。此外，他们也在 Github 上追踪 bug，人们对这件事感到困惑，这使得工单应该在哪里讨论不明了。

这些 IRC 讨论也定下了开发流程本身。LEDE 团队想作出些改变，以使用会合并到主干的阶段开发分支为开端，与 OpenWrt 所使用的“直接提交到主干”方式不同。该项目也将提供基于时间的发行版，并通过只发行已被成功测试的二进制模块来鼓励用户测试，由社区而不是核心开发者在实际的硬件上进行测试。

最后，这些 IRC 讨论也确定了 LEDE 团队的目的不是用它的宣言吓唬 OpenWrt。Crispin 提到 LEDE 首先是“半公开的”并渐渐做得更公开。 Wich 解释说他希望 LEDE 是“中立的、专业的，并打开大门欢迎 OpenWrt 以便将来的合并”。不幸的是，前期发起工作并不是做得很好。

在一封邮件中， Fietkau 补充到 OpenWrt 核心开发者确实在任务中遇到瓶颈，像补丁复审和基础设施维护这些事情让他们完成不了其他工作，比如配置下载镜像和改良构建系统。在 LEDE 宣言之后短短几天内，他说，团队成功解决了镜像和构建系统任务，而这些已被搁置多年。

>
> 我们在 LEDE 所做的事情很多是基于转移到 Github 的去中心化软件包开发经验，并放弃了软件包应如何被维护的许多控制。这样最终有效减少了我们的工作量，而且我们有了很多更活跃的开发者。
>
>
> 我们真的希望为核心开发做一些类似的事，但是基于我们想作出更大改变的经验，我们觉得在 OpenWrt 项目内做不到。
>
>
>

修复基础设施也将收获其他好处，他说，就比如改进了用于管理签署发布版本的密码的系统。团队正在考虑在某些情况下非上游补丁的规则，像需要补丁的描述和为什么没有发送到上游的解释。他也提到很多留下的 OpenWrt 开发者表示有兴趣加入 LEDE，相关当事人正试图弄清楚他们是否会重新合并该项目。

有人希望 LEDE 更为扁平的管理模式和更为透明的分工会在困扰 OpenWrt 的方面取得成功。解决最初的宣言中被诟病的沟通方面的问题会是最大的障碍。如果那个过程处理得好，那么，未来 LEDE 和 OpenWrt 可能能够求同存异并协作。否则，之后两个团队可能一起被迫发展到比以前拥有更少资源的方向，这也许不是开发者或用户想看到的。

---

via: <https://lwn.net/Articles/686767/>

作者：[Nathan Willis](https://lwn.net/Articles/686767/) 译者：[XYenChi](https://github.com/XYenChi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在一个 U 盘上安装多个 Linux 发行版

Summary: 本教程介绍如何在一个 U 盘上安装多个 Linux 发行版。这样，你可以在单个 U 盘上享受多个现场版Linux 发行版了。

>
> 本教程介绍如何在一个 U 盘上安装多个 Linux 发行版。这样，你可以在单个 U 盘上享受多个<ruby> 现场版 <rt>  live </rt></ruby> Linux 发行版了。
>
>
>

我喜欢通过 U 盘尝试不同的 Linux 发行版。它让我可以在真实的硬件上测试操作系统，而不是虚拟化的环境中。此外，我可以将 USB 插入任何系统（比如 Windows 系统），做任何我想要的事情，以及享受相同的 Linux 体验。而且，如果我的系统出现问题，我可以使用 U 盘恢复！

创建单个[可启动的现场版 Linux USB](https://itsfoss.com/create-live-usb-of-ubuntu-in-windows/) 很简单，你只需下载一个 ISO 文件并将其刻录到 U 盘。但是，如果你想尝试多个 Linux 发行版呢？你可以使用多个 U 盘，也可以覆盖同一个 U 盘以尝试其他 Linux 发行版。但这两种方法都不是很方便。

那么，有没有在单个 U 盘上安装多个 Linux 发行版的方式呢？我们将在本教程中看到如何做到这一点。

### 如何创建有多个 Linux 发行版的可启动 USB

![How to install multiple linux distributions on a single USB](/data/attachment/album/201710/13/235401s37mueawuwd8z7z7.jpg)

我们有一个工具正好可以做到*在单个 U 盘上保留多个 Linux 发行版*。你所需要做的只是选择要安装的发行版。在本教程中，我们将介绍*如何在 U 盘中安装多个 Linux 发行版*用于<ruby> 现场会话 <rt>  live session </rt></ruby>。

要确保你有一个足够大的 U 盘，以便在它上面安装多个 Linux 发行版，一个 8 GB 的 U 盘应该足够用于三四个 Linux 发行版。

#### 步骤 1

[MultiBootUSB](http://multibootusb.org/) 是一个自由、开源的跨平台应用程序，允许你创建具有多个 Linux 发行版的 U 盘。它还支持在任何时候卸载任何发行版，以便你回收驱动器上的空间用于另一个发行版。

下载 .deb 包并双击安装。

[下载 MultiBootUSB](https://github.com/mbusb/multibootusb/releases/download/v8.8.0/python3-multibootusb_8.8.0-1_all.deb)

#### 步骤 2

推荐的文件系统是 FAT32，因此在创建多引导 U 盘之前，请确保格式化 U 盘。

#### 步骤 3

下载要安装的 Linux 发行版的 ISO 镜像。

#### 步骤 4

完成这些后，启动 MultiBootUSB。

![MultiBootUSB](/data/attachment/album/201710/13/235401q6gpcvo5rdgx8gb6.png)

主屏幕要求你选择 U 盘和你打算放到 U 盘上的 Linux 发行版镜像文件。

MultiBootUSB 支持 Ubuntu、Fedora 和 Debian 发行版的持久化，这意味着对 Linux 发行版的现场版本所做的更改将保存到 USB 上。

你可以通过拖动 MultiBootUSB 选项卡下的滑块来选择持久化大小。持久化为你提供了在运行时将更改保存到 U 盘的选项。

![MultiBootUSB persistence storage](/data/attachment/album/201710/13/235402hme1x542pf01kp4z.png)

#### 步骤 5

单击“安装发行版”选项并继续安装。在显示成功的安装消息之前，需要一些时间才能完成。

你现在可以在已安装部分中看到发行版了。对于另外的操作系统，重复该过程。这是我安装 Ubuntu 16.10 和 Fedora 24 后的样子。

![MultiBootSystem with Ubuntu and Fedora](/data/attachment/album/201710/13/235402mny2gwa9414wq9hf.png)

#### 步骤 6

下次通过 USB 启动时，我可以选择任何一个发行版。

![Boot Menu](/data/attachment/album/201710/13/235402uphyxjnksnjynk6n.png)

只要你的 U 盘允许，你可以添加任意数量的发行版。要删除发行版，请从列表中选择它，然后单击卸载发行版。

### 最后的话

MultiBootUSB 真的很便于在 U 盘上安装多个 Linux 发行版。只需点击几下，我就有两个我最喜欢的操作系统的工作盘了，我可以在任何系统上启动它们。

如果你在安装或使用 MultiBootUSB 时遇到任何问题，请在评论中告诉我们。

---

via: <https://itsfoss.com/multiple-linux-one-usb/>

作者：[Ambarish Kumar](https://itsfoss.com/author/ambarish/) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


促使项目团队作出改变的五步计划

![](/data/attachment/album/201710/15/004535v6gsu6p1u2sbu2ud.jpeg)

目的是任何团队组建的首要之事。如果一个人足以实现那个目的，那么就没有必要组成团队。而且如果没有重要目标，你根本不需要一个团队。但只要任务需要的专业知识比一个人所拥有的更多，我们就会遇到集体参与的问题——如果处理不当，会使你脱离正轨。

想象一群人困在洞穴中。没有一个人具备如何出去的全部知识，所以每个人要协作，心路常开，在想要做的事情上尽力配合。当（且仅当）组建了适当的工作团队之后，才能为实现团队的共同目标创造出合适的环境。

但确实有人觉得待在洞穴中很舒适而且只想待在那里。在组织里，领导者们如何掌控那些实际上抵触改善、待在洞穴中觉得舒适的人？同时该如何找到拥有共同目标但是不在自己组织的人？

我从事指导国际销售培训，刚开始甚至很少有人认为我的工作有价值。所以，我想出一套使他们信服的战术。那个战术非常成功以至于我决定深入研究它并与各位[分享](http://www.slideshare.net/RonMcFarland1/creating-change-58994683)。

### 获得支持

为了建立公司强大的企业文化，有人会反对改变，并且从幕后打压任何改变的提议。他们希望每个人都待在那个舒适的洞穴里。例如，当我第一次接触到海外销售培训，我受到了一些关键人物的严厉阻挠。他们迫使其他人相信某个东京人做不了销售培训——只要基本的产品培训就行了。

尽管我最终解决了这个问题，但我那时候真的不知道该怎么办。所以，我开始研究顾问们在改变公司里抗拒改变的人的想法这个问题上该如何给出建议。从学者 [Laurence Haughton](http://www.laurencehaughton.com/) 的研究中，我发现一般对于改变的提议，组织中 83% 的人最开始不会支持你。大约 17% *会*从一开始就支持你，但是只要看到一个实验案例成功之后，他们觉得这个主意安全可行了，60% 的人会支持你。最后，有部分人会反对任何改变，无论它有多棒。

我研究的步骤：

* 从试验项目开始
* 开导洞穴人
* 快速跟进
* 开导洞穴首领
* 全局展开

### 1、 从试验项目开始

找到高价值且成功率较高的项目——而不是大的、成本高的、周期长的、全局的行动。然后，找到能看到项目价值、理解它的价值并能为之奋斗的关键人物。这些人不应该只是“老好人”或者“朋友”；他们必须相信项目的目标而且拥有推进项目的能力或经验。不要急于求成。只要足够支持你研究并保持进度即可。

个人而言，我在新加坡的一个小型车辆代理商那里举办了自己的第一场销售研讨会。虽然并不是特别成功，但足以让人们开始讨论销售训练会达到怎样的效果。那时候的我困在洞穴里（那是一份我不想做的工作）。这个试验销售训练是我走出困境的蓝图。

### 2、 开导洞穴人

洞穴（CAVE）实际上是我从 Laurence Haughton 那里听来的缩略词。它代表着 Citizens Against Virtually Everything。（LCTT 译注，此处一语双关前文提及的洞穴。）

你得辨别这些人，因为他们会暗地里阻挠项目的进展，特别是早期脆弱的时候。他们容易黑化：总是消极。他们频繁使用“但是”、“如果”和“为什么”，只是想推脱你。他们询问轻易不可得的细节信息。他们花费过多的时间在问题上，而不是寻找解决方案。他们认为每个失败都是一个趋势。他们总是对人而不是对事。他们作出反对建议的陈述却又不能简单确认。

避开洞穴人；不要让他们太早加入项目的讨论。他们固守成见，因为他们看不到改变所具有的价值。他们安居于洞穴，所以试着让他们去做些其他事。你应该找出我上面提到那 17% 的人群中的关键人物，那些想要改变的人，并且跟他们开一个非常隐秘的准备会。

我在五十铃汽车（股东之一是通用汽车公司）的时候，销售训练项目开始于一个销往世界上其他小国家的合资分销商，主要是非洲、南亚、拉丁美洲和中东。我的个人团队由通用汽车公司雪佛兰的人、五十铃产品经理和分公司的销售计划员工组成。隔绝其他任何人于这个圈子之外。

### 3、 快速跟进

洞穴人总是慢吞吞的，那么你就迅速行动起来。如果你在他们参与之前就有了小成就的经历，他们对你团队产生消极影响的能力将大大减弱——你要在他们提出之前就解决他们必然反对的问题。再一次，选择一个成功率高的试验项目，很快能出结果的。然后宣传成功，就像广告上的加粗标题。

当我在新加坡研讨会上所言开始流传时，其他地区开始意识到销售训练的好处。仅在新加坡研讨会之后，我就被派到马来西亚开展了四次以上。

### 4、 开导洞穴首领

只要你取得了第一个小项目的成功，就针对能影响洞穴首领的关键人物推荐项目。让团队继续该项目以告诉关键人物成功的经历。一线人员甚至顾客也能提供有力的证明。 洞穴管理者往往只着眼于销量和收益，那么就宣扬项目在降低开支、减少浪费和增加销量方面的价值。

自新加坡的第一次研讨会及之后，我向直接掌握了五十铃销售渠道的前线销售部门员工和通用汽车真正想看到进展的人极力宣传他们的成功。当他们接受了之后，他们会向上级提出培训请求并让其看到分公司销量的提升。

### 5、 全局展开

一旦一把手站在了自己这边，立马向整个组织宣告成功的试验项目。讨论项目的扩展。

用上面的方法，在 21 年的职业生涯中，我在世界各地超过 60 个国家举办了研讨会。我确实走出了洞穴——并且真的看到了广阔的世界。

---

作者简介：

Ron McFarland - Ron McFarland 已在日本工作 40 年，从事国际销售、销售管理和在世界范围内扩展销售业务 30 载有余。他曾去过或就职于 80 多个国家。在过去的 14 年里， Ron 为总部位于东京的日本硬件切割厂在美国和欧洲各地建立分销商。

---

via: <https://opensource.com/open-organization/17/1/escape-the-cave>

作者：[Ron McFarland](https://opensource.com/users/ron-mcfarland) 译者：[XYenChi](https://github.com/XYenChi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


阿里巴巴 CTO 行癫：开源有意思

2017云栖大会，阿里巴巴集团 CTO 兼阿里云 CTO 行癫就开源谈了一番他的看法。

行癫在阿里历经了从技术到商业，又从商业到技术的过程，十多年的阿里生涯，让他对开源、技术和商业有了深刻了解。

![](/data/attachment/album/201710/15/170526zm9ncv2urzpprwhm.jpeg)

### 开源的核心是连接，社区的根本是连接

行癫说，阿里巴巴的平台将“消费者和商家连接在了一起，这个平台不仅是个渠道，也从消费者获得了非常多一些反馈，能够快速的根据消费者的需求，来做出满足消费者要求的一些产品。我们回过头来想一下，开源社区，非常像这个模式。”

这个商业模式，其实就是将相关的人、物、关系连接到了一起，与开源的道理是一致的。

行癫认为“开源要做好，它最重要、最核心的一点，是把相关的一些开发者、用户，通过软件、工具和平台连接在一起了。”

纵观那些发展的比较好的开源软件，都是通过开源软件、通过开源的模式和开源的平台，将最优秀的开发者联系起来，将最有价值的软件用户连接起来。

互联网的本质是连接。没有互联网之前，所有的行为、所有的商业都是单向的；有互联网之后，非常非常多的连接就产生了。所以对于开源，行癫认为它“是根植于互联网的，有了互联网技术平台之后，开源能够做得更好。”

开源是生长于社区土壤中的，而社区就是一种将参与者连接起来的机制。首先通过将人连接起来，然后才能逐步考虑将来的发展，考虑如何发展和进行商业化发展。

一个开源软件在诞生之初，有可能只是表达一下对技术的理解和看法，也有可能只是解决某个痛点——大多数情况下是自己的痛点，还有可能只是好玩。至于将来能走多远，能否得到社区的迎合，能否发展出一个生态，甚至成为商业新动力，在最初往往并没有很远的计划和远景。

但是，在建立连接后，有了一个社区的土壤之后，就有了成长为一棵大树、一片森林的可能。“把人连接在一起，然后后面才是讨论核心问题，和怎么样进行商业化”。

而现在的云栖大会“也是一个连接”，通过网上直播，中国大概会有一千万左右的开发者会来参加这个云栖大会，所以“云栖大会把中国最具有活力的一群开发者，全部连接在一起了。今天这种形式的大会，本身就是一个对开发者来说，一个最重要的纽带。”

### 阿里为何拥抱开源

阿里巴巴最初是采用商用软件做解决方案，基于小型机、企业级的基础设施。阿里巴巴的平台三个比较大的特点，“互联网级的规模、金融级的稳定性、企业级的复杂程度。”在这种情况下，一方面，如果继续用 IOE 基础设施，随着业务规模的扩大，将来根本无法覆盖剧增的成本。另外一方面，商用软件的支持情况也难以满足业务的增长带来的各种需求。

因此，阿里巴巴发起了去 IOE 行动，全面投向开源解决方案，用开源软件构建了满足其体量和需求的基础设施。在这个过程中，阿里巴巴一方面大量采用开源软件替代传统的 IOE 基础设施，另外一方面也要面临一些前所未有的需求。

“阿里巴巴应该是最早把这么复杂的一个应用系统，全部放到开源社区的应用上的。”因此，在规模扩大了到开源软件原来很少涉及的数量级时，就会发现很多之前隐藏的场景问题。在这其中解决了无数的问题，因为面临的环境跟别人不一样，面临的要求也跟别人不一样。阿里做了非常多的工作，把他们的互联网的架构中现在社区不具备的一些功能，都纷纷补上去。自己开发了很多的中间件去满足这些功能需求。

### 积极回馈开源

在全面投入开源的怀抱后，阿里也积极回馈开源社区，真正使自己成为开源社区的一份子。这可以从近年来阿里加大对开源社区的赞助、代码的贡献、开源社区的扶持，以及鼓励技术人员走出去等举措上可以看出来。

在本次云栖大会上，阿里巴巴宣布了正式发布了 OpenMessaging 和 ApsaraCache 两个开源项目。此前，阿里巴巴捐赠的开源的 RocketMQ 已被 Apache 基金会接纳为全球顶级项目。

“开源和阿里巴巴都根植于互联网，有了互联网技术平台之后，开源和商业将在未来相当长的时间内保持平衡的发展。”行癫表示。

据悉， OpenMessaging 项目是由阿里巴巴发起，与雅虎、滴滴出行、 Streamlio 公司共同参与创立的分布式消息中间件、流处理领域的应用开发标准，目前已正式入驻 Linux 基金会，这也是国内首个在全球范围内发起的分布式消息领域的国际标准。

该标准可以不受编程语言限制，能满足企业对扩展性、伸缩性、隔离和安全的要求，可提供大规模的工业级支持，支持标准参照点的添加与标准化测试，开放接口便于对其他不同标准的接入，适用于金融、电商、物联网、工业互联网等行业。

“OpenMessaging 希望成为全球化、无国界、无公司边界、面向云和大数据、多行业领域的一站式方案标准，这也是阿里巴巴第一次在国际社区进行的主导和探索。” 项目负责人蒋江伟表示。

同时，在云栖大会现场，阿里云数据库负责人余锋与 Redis 创始人 Salvatore 共同宣布 ApsaraCache 在 Github 上正式开放下载。ApsaraCache 是阿里云数据库 Redis 版的分支，适用于更大的数据规模和更多的应用场景。

“ApsaraCache 项目开源是一件非常好的事情，将能够吸引全世界更多 Redis 核心专家参与，进一步提升产品的稳定性和可用性。” Salvatore 表示。

Mysql 之父、 MariaDB 创始人 Michael Widenius 已经连续三年参加云栖大会，年过 50 的他依然奋斗在代码第一线，Widenius 表示：“很多 MariaDB 的运用源自我们的开发者，维基百科用的就是 MariaDB，我们也从阿里巴巴中获得了很多开源的支持和贡献，确保能给大家提供功能丰富的数据库产品。”

![](/data/attachment/album/201710/15/170545maq8vzyw2yrv272w.jpeg)

*图为 Mysql 之父、 MariaDB 创始人 Michael Widenius*

近年来，阿里巴巴在技术领域投入不断加强，拥抱开源也由来已久，积极加入了包括自由软件基金会、Apache 软件基金会和 Linux 基金会在内的多家国际知名开源组织。目前，阿里巴巴开源和维护的开源项目超过 150 个，涵盖中间件、开发框架、数据库和各种工具类软件。在开源中国公布的“2016 年度最受欢迎中国开源软件评选 TOP20”榜单中，阿里巴巴独占 4 席。其中 Weex、Ant Design、Dubbo、Fastjson 在 GitHub 上的星标数已经破万，“Alibaba”组织在 GitHub 上星标数超过 170,000，组织排名进入前十。

### 开源之路

行癫认为，“开源我觉得有几个层次”，刚开始可能只是做了一个工具，这个工具做得非常好，可以解决一个非常确定性的问题。逐渐地，这个工具可能会变成一个产品、变成一个系统，慢慢延伸出一堆工具。“开源要成功，第一步要做好一个工具，第二步会变成全链的产品，我觉得最成功的就是变成新的一个生态。” 开源软件组成了一个生态，无数人为这个生态贡献了新的智慧、新的工具。融入这个生态的人，或许只用非常少的代价，就能够找到跟他的工作场景、业务场景相匹配的模式。到这个程度，“这个社区就发展得比较成熟了。这个可能是大多数开源软件必须要去走的一些路径。”

“今天要开源的其实不仅是软件，还有很多硬件”，行癫说。“今天的开源比以前的更复杂，有可能是端跟云端的结合。……互联网第一阶段的开源，是基于互联网的端建成的；互联网的第二个阶段是 IoT，我们希望所有的设备能够串起来。所以我认为接下去开源软件会与硬件结合，这就是从单纯的互联网向 IoT 时代发展非常重要的一个过程。”

### 结语

在近来几届云栖大会上，开源已经成为了永恒的主题，除了开源专场之外，在各个会场和论坛，充斥着各种热烈的开源气息，无数建筑于开源之上的产品、服务源源不断的开发出来，无数的技术人员和开源爱好者投身于开源世界。让我们期待云栖大会成为开源的大会，成为中国开源界和世界开源接轨的枢纽。


介绍 Flashback，一个互联网模拟工具

>
> Flashback 用于测试目的来模拟 HTTP 和 HTTPS 资源，如 Web 服务和 REST API。
>
>
>

![](/data/attachment/album/201710/15/215549qf6816w11zwjpkwz.jpeg)

在 LinkedIn，我们经常开发需要与第三方网站交互的 Web 应用程序。我们还采用自动测试，以确保我们的软件在发布到生产环境之前的质量。然而，测试只是在它可靠时才有用。

考虑到这一点，有外部依赖关系的测试是有很大的问题的，例如在第三方网站上。这些外部网站可能会没有通知地发生改变、遭受停机，或者由于互联网的不可靠性暂时无法访问。

如果我们的一个测试依赖于能够与第三方网站通信，那么任何故障的原因都很难确定。失败可能是因为 LinkedIn 的内部变更、第三方网站的维护人员进行的外部变更，或网络基础设施的问题。你可以想像，与第三方网站的交互可能会有很多失败的原因，因此你可能想要知道，我将如何处理这个问题？

好消息是有许多互联网模拟工具可以帮助。其中一个是 [Betamax](https://github.com/betamaxteam/betamax)。它通过拦截 Web 应用程序发起的 HTTP 连接，之后进行重放的方式来工作。对于测试，Betamax 可以用以前记录的响应替换 HTTP 上的任何交互，它可以非常可靠地提供这个服务。

最初，我们选择在 LinkedIn 的自动化测试中使用 Betamax。它工作得很好，但我们遇到了一些问题：

* 出于安全考虑，我们的测试环境没有接入互联网。然而，与大多数代理一样，Betamax 需要 Internet 连接才能正常运行。
* 我们有许多需要使用身份验证协议的情况，例如 OAuth 和 OpenId。其中一些协议需要通过 HTTP 进行复杂的交互。为了模拟它们，我们需要一个复杂的模型来捕获和重放请求。

为了应对这些挑战，我们决定基于 Betamax 的思路，构建我们自己的互联网模拟工具，名为 Flashback。我们也很自豪地宣布 Flashback 现在是开源的。

### 什么是 Flashback?

Flashback 用于测试目的来模拟 HTTP 和 HTTPS 资源，如 Web 服务和 [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) API。它记录 HTTP/HTTPS 请求并重放以前记录的 HTTP 事务 - 我们称之为“<ruby> 场景 <rt>  scene </rt></ruby>”，这样就不需要连接到 Internet 才能完成测试。

Flashback 也可以根据请求的部分匹配重放场景。它使用的是“匹配规则”。匹配规则将传入请求与先前记录的请求相关联，然后将其用于生成响应。例如，以下代码片段实现了一个基本匹配规则，其中测试方法“匹配”[此 URL](https://gist.github.com/anonymous/91637854364287b38897c0970aad7451)的传入请求。

HTTP 请求通常包含 URL、方法、标头和正文。Flashback 允许为这些组件的任意组合定义匹配规则。Flashback 还允许用户向 URL 查询参数，标头和正文添加白名单或黑名单标签。

例如，在 OAuth 授权流程中，请求查询参数可能如下所示：

```
oauth_consumer_key="jskdjfljsdklfjlsjdfs",
oauth_nonce="ajskldfjalksjdflkajsdlfjasldfja;lsdkj",
oauth_signature="asdfjaklsdjflasjdflkajsdklf",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318622958",
oauth_token="asdjfkasjdlfajsdklfjalsdjfalksdjflajsdlfa",
oauth_version="1.0"

```

这些值许多将随着每个请求而改变，因为 OAuth 要求客户端每次为 `oauth_nonce` 生成一个新值。在我们的测试中，我们需要验证 `oauth_consumer_key`、`oauth_signature_method` 和 `oauth_version` 的值，同时确保 `oauth_nonce`、`oauth_signature`、`oauth_timestamp` 和 `oauth_token` 存在于请求中。Flashback 使我们有能力创建我们自己的匹配规则来实现这一目标。此功能允许我们测试随时间变化的数据、签名、令牌等的请求，而客户端没有任何更改。

这种灵活的匹配和在不连接互联网的情况下运行的功能是 Flashback 与其他模拟解决方案不同的特性。其他一些显著特点包括：

* Flashback 是一种跨平台和跨语言解决方案，能够测试 JVM（Java虚拟机）和非 JVM（C++、Python 等）应用程序。
* Flashback 可以随时生成 SSL/TLS 证书，以模拟 HTTPS 请求的安全通道。

### 如何记录 HTTP 事务

使用 Flashback 记录 HTTP 事务以便稍后重放是一个比较简单的过程。在我们深入了解流程之前，我们首先列出一些术语：

* `Scene` ：场景存储以前记录的 HTTP 事务 (以 JSON 格式)，它可以在以后重放。例如，这里是一个[Flashback 场景](https://gist.github.com/anonymous/17d226050d8a9b79746a78eda9292382)示例。
* `Root Path` ：根路径是包含 Flashback 场景数据的目录的文件路径。
* `Scene Name` ：场景名称是给定场景的名称。
* `Scene Mode` ：场景模式是使用场景的模式, 即“录制”或“重放”。
* `Match Rule` ：匹配规则确定传入的客户端请求是否与给定场景的内容匹配的规则。
* `Flashback Proxy` ：Flashback 代理是一个 HTTP 代理，共有录制和重放两种操作模式。
* `Host` 和 `Port` ：代理主机和端口。

为了录制场景，你必须向目的地址发出真实的外部请求，然后 HTTPS 请求和响应将使用你指定的匹配规则存储在场景中。在录制时，Flashback 的行为与典型的 MITM（中间人）代理完全相同 - 只有在重放模式下，连接流和数据流仅限于客户端和代理之间。

要实际看下 Flashback，让我们创建一个场景，通过执行以下操作捕获与 example.org 的交互：

1、 取回 Flashback 的源码：

```
git clone https://github.com/linkedin/flashback.git

```

2、 启动 Flashback 管理服务器：

```
./startAdminServer.sh -port 1234

```

3、 注意上面的 Flashback 将在本地端口 5555 上启动录制模式。匹配规则需要完全匹配（匹配 HTTP 正文、标题和 URL）。场景将存储在 `/tmp/test1` 下。

4、 Flashback 现在可以记录了，所以用它来代理对 example.org 的请求：

```
curl http://www.example.org -x localhost:5555 -X GET

```

5、 Flashback 可以（可选）在一个记录中记录多个请求。要完成录制，[关闭 Flashback](https://gist.github.com/anonymous/f899ebe7c4246904bc764b4e1b93c783)。

6、 要验证已记录的内容，我们可以在输出目录（`/tmp/test1`）中查看场景的内容。它应该[包含以下内容](https://gist.github.com/sf1152/c91d6d62518fe62cc87157c9ce0e60cf)。

[在 Java 代码中使用 Flashback](https://gist.github.com/anonymous/fdd972f1dfc7363f4f683a825879ce19)也很容易。

### 如何重放 HTTP 事务

要重放先前存储的场景，请使用与录制时使用的相同的基本设置。唯一的区别是[将“场景模式”设置为上述步骤 3 中的“播放”](https://gist.github.com/anonymous/ae1c519a974c3bc7de2a925254b6550e)。

验证响应来自场景而不是外部源的一种方法，是在你执行步骤 1 到 6 时临时禁用 Internet 连接。另一种方法是修改场景文件，看看响应是否与文件中的相同。

这是 [Java 中的一个例子](https://gist.github.com/anonymous/edcc1d60847d51b159c8fd8a8d0a5f8b)。

### 如何记录并重播 HTTPS 事务

使用 Flashback 记录并重放 HTTPS 事务的过程非常类似于 HTTP 事务的过程。但是，需要特别注意用于 HTTPS SSL 组件的安全证书。为了使 Flashback 作为 MITM 代理，必须创建证书颁发机构（CA）证书。在客户端和 Flashback 之间创建安全通道时将使用此证书，并允许 Flashback 检查其代理的 HTTPS 请求中的数据。然后将此证书存储为受信任的源，以便客户端在进行调用时能够对 Flashback 进行身份验证。有关如何创建证书的说明，有很多[类似这样](https://jamielinux.com/docs/openssl-certificate-authority/introduction.html)的资源是非常有帮助的。大多数公司都有自己的管理和获取证书的内部策略 - 请务必用你们自己的方法。

这里值得一提的是，Flashback 仅用于测试目的。你可以随时随地将 Flashback 与你的服务集成在一起，但需要注意的是，Flashback 的记录功能将需要存储所有的数据，然后在重放模式下使用它。我们建议你特别注意确保不会无意中记录或存储敏感成员数据。任何可能违反贵公司数据保护或隐私政策的行为都是你的责任。

一旦涉及安全证书，HTTP 和 HTTPS 之间在记录设置方面的唯一区别是添加了一些其他参数。

* `RootCertificateInputStream`： 表示 CA 证书文件路径或流。
* `RootCertificatePassphrase`： 为 CA 证书创建的密码。
* `CertificateAuthority`： CA 证书的属性

[查看 Flashback 中用于记录 HTTPS 事务的代码](https://gist.github.com/anonymous/091d13179377c765f63d7bf4275acc11)，它包括上述条目。

用 Flashback 重放 HTTPS 事务的过程与录制相同。唯一的区别是场景模式设置为“播放”。这在[此代码](https://gist.github.com/anonymous/ec6a0fd07aab63b7369bf8fde69c1f16)中演示。

### 支持动态修改

为了测试灵活性，Flashback 允许你动态地更改场景和匹配规则。动态更改场景允许使用不同的响应（如 `success`、`time_out`、`rate_limit` 等）测试相同的请求。[场景更改](https://gist.github.com/anonymous/1f1660280acb41277fbe2c257bab2217)仅适用于我们已经 POST 更新外部资源的场景。以下图为例。

![Scenarios where we have POSTed data to update the external resource.](/data/attachment/album/201710/15/215603k11skaj8jee8fjf1.jpg "Scenarios where we have POSTed data to update the external resource.")

能够动态[更改匹配规则](https://gist.github.com/anonymous/0683c43f31bd916b76aff348ff87f51b)可以使我们测试复杂的场景。例如，我们有一个使用情况，要求我们测试 Twitter 的公共和私有资源的 HTTP 调用。对于公共资源，HTTP 请求是不变的，所以我们可以使用 “MatchAll” 规则。然而，对于私人资源，我们需要使用 OAuth 消费者密码和 OAuth 访问令牌来签名请求。这些请求包含大量具有不可预测值的参数，因此静态 MatchAll 规则将无法正常工作。

### 使用案例

在 LinkedIn，Flashback 主要用于在集成测试中模拟不同的互联网提供商，如下图所示。第一张图展示了 LinkedIn 生产数据中心内的一个内部服务，通过代理层，与互联网提供商（如 Google）进行交互。我们想在测试环境中测试这个内部服务。

![Testing this internal service in a testing environment.](/data/attachment/album/201710/15/215603h9nbfc6b6z72b1fm.jpg "Testing this internal service in a testing environment.")

第二和第三张图表展示了我们如何在不同的环境中录制和重放场景。记录发生在我们的开发环境中，用户在代理启动的同一端口上启动 Flashback。从内部服务到提供商的所有外部请求将通过 Flashback 而不是我们的代理层。在必要场景得到记录后，我们可以将其部署到我们的测试环境中。

![After the necessary scenes get recorded, we can deploy them to our test environment.](/data/attachment/album/201710/15/215604yr3fwqwqbazvzgwv.jpg "After the necessary scenes get recorded, we can deploy them to our test environment.")

在测试环境（隔离并且没有 Internet 访问）中，Flashback 在与开发环境相同的端口上启动。所有 HTTP 请求仍然来自内部服务，但响应将来自 Flashback 而不是 Internet 提供商。

![Responses will come from Flashback instead of the Internet providers.](/data/attachment/album/201710/15/215604d83nijiy3t3y03wt.jpg "Responses will come from Flashback instead of the Internet providers.")

### 未来方向

我们希望将来可以支持非 HTTP 协议（如 FTP 或 JDBC），甚至可以让用户使用 MITM 代理框架来自行注入自己的定制协议。我们将继续改进 Flashback 设置 API，使其更容易支持非 Java 语言。

### 现在为一个开源项目

我们很幸运能够在 GTAC 2015 上发布 Flashback。在展会上，有几名观众询问是否将 Flashback 作为开源项目发布，以便他们可以将其用于自己的测试工作。

### Google TechTalks：GATC 2015 - 模拟互联网

我们很高兴地宣布，Flashback 现在以 BSD 两句版许可证开源。要开始使用，请访问 [Flashback GitHub 仓库](https://github.com/linkedin/flashback)。

*该文原始发表在[LinkedIn 工程博客上](https://engineering.linkedin.com/blog/2017/03/flashback-mocking-tool)。获得转载许可*

### 致谢

Flashback 由 [Shangshang Feng](https://www.linkedin.com/in/shangshangfeng)、[Yabin Kang](https://www.linkedin.com/in/benykang) 和 [Dan Vinegrad](https://www.linkedin.com/in/danvinegrad/) 创建，并受到 [Betamax](https://github.com/betamaxteam/betamax) 启发。特别感谢 [Hwansoo Lee](https://www.linkedin.com/in/hwansoo/)、[Eran Leshem](https://www.linkedin.com/in/eranl/)、[Kunal Kandekar](https://www.linkedin.com/in/kunalkandekar/)、[Keith Dsouza](https://www.linkedin.com/in/dsouzakeith/) 和 [Kang Wang](https://www.linkedin.com/in/kang-wang-44960b4/) 帮助审阅代码。同样感谢我们的管理层 - [Byron Ma](https://www.linkedin.com/in/byronma/)、[Yaz Shimizu](https://www.linkedin.com/in/yazshimizu/)、[Yuliya Averbukh](https://www.linkedin.com/in/yuliya-averbukh-818a41/)、[Christopher Hazlett](https://www.linkedin.com/in/chazlett/) 和 [Brandon Duncan](https://www.linkedin.com/in/dudcat/) - 感谢他们在开发和开源 Flashback 中的支持。

---

作者简介：

Shangshang Feng - Shangshang 是 LinkedIn 纽约市办公室的高级软件工程师。在 LinkedIn 他从事了三年半的网关平台工作。在加入 LinkedIn 之前，他曾在 Thomson Reuters 和 ViewTrade 证券的基础设施团队工作。

---

via: <https://opensource.com/article/17/4/flashback-internet-mocking-tool>

作者： [Shangshang Feng](https://opensource.com/users/shangshangfeng) 译者：[geekpi](https://github.com/geekpi) 校对：[jasminepeng](https://github.com/jasminepeng)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


见多识广的人工智能比你认识更多的 XXX 明星

![](/data/attachment/album/201710/15/225517vmzpzkf3efpuifsz.jpeg)

你有没有想过，之所以能够根据自己不同兴趣的组合搜索到需要的视频，是因为有那些每日浏览无数视频内容且对它们进行分类和标记的可怜人存在，然而这些看不见的英雄们却在人工智能面前变得英雄无用武之地。

世界上最大的 XXX 电影分享网站 Pornhub 宣布，它将推出新的 AI 模型，利用计算机视觉技术自动检测和识别 XXX 明星的名字。

根据 X-rated 网站的消息，目前该算法经过训练后已经通过简单的扫描和对镜头的理解，可以识别超过 1 万名 XXX 明星。Pornhub 说，通过向此 AI 模型输入数千个视频和 XXX 明星的正式照片，以让它学习如何返回准确的名字。

为了减小错误，这个成人网站将向用户求证由 AI 提供的标签和分类是否合适。用户可以根据结果的准确度，提出支持或是反对。这将会让算法变得更加智能。

![](/data/attachment/album/201710/15/225531zh2tf2mc3m54fian.jpeg)

“现在，用户可以根据自身喜好寻找指定的 XXX 明星，我们也能够返回给用户尽可能精确的搜索结果，” PornHub 副总裁 Corey Price 说。“毫无疑问，我们的模型也将在未来的发展中扮演关键角色，尤其是考虑到每天有超过 1 万个的视频添加到网站上。”

“事实上，在过去的一个月里，我们测试了这个模型的测试版本，它（每天）可以扫描 5 万段视频，并且向视频添加或者移除标签。”

除了识别表演者，该算法还能区分不同类别的内容：比如在 “Public” 类别下的是户外拍摄的视频，以及 “Blonde” 类别下的视频应该至少有名金发女郎。

XXX 公司计划明年在 AI 模型的帮助下，对全部 500 万个视频编目，希望能让用户更容易找到与他们的期望最接近的视频片段。

早先就有研究人员借助计算机视觉算法对 XXX 电影进行描述。之前就有一名开发者使用微软的人工智能技术来构建这个机器人，它可以整天[观察和解读](https://thenextweb.com/shareables/2017/03/03/porn-bot-microsoft-ai-pornhub/)各种内容。

Pornhub 似乎让这一想法更进一步，这些那些遍布全球的视频审看员的噩梦。

虽然人工智能被发展到这个方面可能会让你感觉有些不可思议，但 XXX 业因其对搜索引擎优化技术的狂热追求而[闻名](https://moz.com/ugc/yes-dear-there-is-porn-seo-and-we-can-learn-a-lot-from-it)。

事实上，成人内容服务一直以来都有广泛市场，且受众不分年龄，这也是这些公司盈利的重要组成部分。

但是，这些每日阅片无数、兢兢业业为其分类的人们可能很快就会成为自动化威胁的牺牲品。但从好的一面看，他们终于有机会坐下来[让自动化为他们工作](https://thenextweb.com/gear/2017/07/07/fleshlight-launch-review-masturbation/)。

---

via: <https://thenextweb.com/artificial-intelligence/2017/10/11/pornhub-ai-watch-tag/>

作者：[MIX](https://thenextweb.com/author/dimitarmihov/) 译者：[东风唯笑](https://github.com/dongfengweixiao) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


混合云的变化

Summary: 围绕云计算的概念和术语仍然很新，但是也在不断的改进。

围绕云计算的概念和术语仍然很新，但是也在不断的改进。

>
> 围绕云计算的概念和术语仍然很新，但是也在不断的改进。
>
>
>

![](/data/attachment/album/201710/16/220451s9fc07mrjq9sf39s.jpeg)

不管怎么看，云计算也只有十多年的发展时间。一些我们习以为常的云计算的概念和术语仍然很新。美国国家标准与技术研究所（NIST）文档显示，一些已经被熟悉的术语定义在 2011 年才被发布，例如基础设施即服务（IaaS），而在此之前它就以草案的形式广泛流传。

在该文档中其它定义中，有一个叫做<ruby> 混合云 <rt>  hybrid cloud </rt></ruby>。让我们回溯一下该术语在这段期间的变化是很有启发性的。云基础设施已经超越了相对简单的分类。此外，它还强调了开源软件的使用者所熟悉的优先级，例如灵活性、可移植性、选择性，已经被运用到了混合云上。

NIST 对混合云最初的定义主要集中于<ruby> 云爆发 <rt>  cloud bursting </rt></ruby>，你能使用内部的基础设施去处理一个基本的计算负荷，但是如果你的负荷量暴涨，可以将多出来的转为使用公有云。与之密切联系的是加强私有云与公有云之间 API 的兼容性，甚至是创造一个现货市场来提供最便宜的容量。

Nick Carr 在 [The Big Switch](http://www.nicholascarr.com/?page_id=21) 一书中提出一个概念，云是一种计算单元，其与输电网类似。这个故事不错，但是即使在早期，[这种类比的局限性也变得很明显](https://www.cnet.com/news/there-is-no-big-switch-for-cloud-computing/)。计算不是以电流方式呈现的一种物品。需要关注的是，公有云提供商以及 OpenStack 一类的开源云软件激增的新功能，可见许多用户并不仅仅是寻找最便宜的通用计算能力。

云爆发的概念基本上忽略了计算是与数据相联系的现实，你不可能只移动洪水般突如其来的数据而不承担巨大的带宽费用，以及不用为转移需要花费的时间而操作。Dave McCrory 发明了 “<ruby> 数据引力 <rt>  data gravity </rt></ruby>”一词去描述这个限制。

那么既然混合云有如此负面的情况，为什么我们现在还要再讨论混合云？

正如我说的，混合云的最初的构想是在云爆发的背景下诞生的。云爆发强调的是快速甚至是即时的将工作环境从一个云转移到另一个云上；然而，混合云也意味着应用和数据的移植性。确实，如之前 [2011 年我在 CNET 的文章](https://www.cnet.com/news/cloudbursting-or-just-portable-clouds/)中写到：“我认为过度关注于全自动的工作转换给我们自己造成了困扰，我们真正应该关心的是，如果供应商不能满意我们的需求或者尝试将我们锁定在其平台上时，我们是否有将数据从一个地方到另一个地方的迁移能力。”

从那以后，探索云之间的移植性有了进一步的进展。

Linux 是云移植性的关键，因为它能运行在各种地方，无论是从裸机到内部虚拟基础设施，还是从私有云到公有云。Linux 提供了一个完整、可靠的平台，其具有稳定的 API 接口，且可以依靠这些接口编写程序。

被广泛采纳的容器进一步加强了 Linux 提供应用在云之间移植的能力。通过提供一个包含了应用的基础配置环境的镜像，应用在开发、测试和最终运行环境之间移动时容器提供了可移植性和兼容性。

Linux 容器被应用到要求可移植性、可配置性以及独立性的许多方面上。不管是预置的云，还是公有云，以及混合云都是如此。

容器使用的是基于镜像的部署模式，这让在不同环境中分享一个应用或者具有全部基础环境的服务集变得容易了。

在 OCI 支持下开发的规范定义了容器镜像的内容及其所依赖、环境、参数和一些镜像正确运行所必须的要求。在标准化的作用下，OCI 为许多其它工具提供了一个机会，它们现在可以依靠稳定的运行环境和镜像规范了。

同时，通过 Gluster 和 Ceph 这类的开源技术，分布式存储能提供数据在云上的可移植性。 物理约束限制了如何快速简单地把数据从一个地方移动到另一个地方；然而，随着组织部署和使用不同类型的基础架构，他们越来越渴望一个不受物理、虚拟和云资源限制的开放的软件定义储存平台。

尤其是在数据存储需求飞速增长的情况下，由于预测分析，物联网和实时监控的趋势。[2016 年的一项研究表明](https://www.redhat.com/en/technologies/storage/vansonbourne)，98% 的 IT 决策者认为一个更敏捷的存储解决方案对他们的组织是有利的。在同一个研究中，他们列举出不恰当的存储基础设施是最令他们组织受挫的事情之一。

混合云表现出的是提供在不同计算能力和资源之间合适的移植性和兼容性。其不仅仅是将私有云和公有云同时运用在一个应用中。它是一套多种类型的服务，其中的一部分可能是你们 IT 部门建立和操作的，而另一部分可能来源于外部。

它们可能是软件即服务（SaaS）应用的混合，例如邮件和客户关系管理（CRM）。被 Kubernetes 这类开源软件协调在一起的容器平台越来越受新开发应用的欢迎。你的组织可能正在运用某一家大型云服务提供商来做一些事情。同时你也能在私有云或更加传统的内部基础设施上操作一些你自己的基础设施。

这就是现在混合云的现状，它能被归纳为两个选择，选择最合适的基础设施和服务，以及选择把应用和数据从一个地方移动到另一个你想的地方。

**相关阅读: [多重云和混合云有什么不同？](https://enterprisersproject.com/article/2017/7/multi-cloud-vs-hybrid-cloud-whats-difference)**

---

作者简介：

Gordon Haff 是红帽云的布道者，常受到业内和客户的高度赞赏，帮助红帽云组合方案的发展。他是《Computing Next: How the Cloud Opens the Future》的作者，除此之外他还有许多出版物。在红帽之前，Gordon 写了大量的研究简报，经常被纽约时报等出版物在 IT 类话题上引用，在产品和市场策略上给予客户建议。他职业生涯的早期，在 Data General 他负责将各种不同的计算机系统，从微型计算机到大型的 UNIX 服务器，引入市场。他有麻省理工学院和达特茅斯学院的工程学位，还是康奈尔大学约翰逊商学院的工商管理学硕士。

---

via: <https://opensource.com/article/17/7/hybrid-cloud>

作者：[Gordon Haff (Red Hat)](https://opensource.com/users/ghaff) 译者：[ZH1122](https://github.com/ZH1122) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何让网站不下线而从 Redis 2 迁移到 Redis 3

我们在 Sky Betting＆Gaming 中使用 [Redis](https://redis.io/) 作为共享内存缓存，用于那些需要跨 API 服务器或者 Web 服务器鉴别令牌之类的操作。在 Core Tribe 内，它用来帮助处理日益庞大的登录数量，特别是在繁忙的时候，我们在一分钟内登录数量会超过 20,000 人。这在很大程度上适用于数据存放在大量服务器的情况下（在 SSO 令牌用于 70 台 Apache HTTPD 服务器的情况下）。我们最近着手升级 Redis 服务器，此升级旨在使用 Redis 3.2 提供的原生集群功能。这篇博客希望解释为什么我们要使用集群、我们遇到的问题以及我们的解决方案。

![](/data/attachment/album/201710/17/124529kghyhkcyhalsvhd3.jpg)

### 在开始阶段（或至少在升级之前）

我们的传统缓存中每个缓存都包括一对 Redis 服务器，使用 keepalive 确保始终有一个主节点监听<ruby> 浮动 IP <rt>  floating IP </rt></ruby>地址。当出现问题时，这些服务器对需要很大的精力来进行管理，而故障模式有时是非常各种各样的。有时，只允许读取它所持有的数据，而不允许写入的从属节点却会得到浮动 IP 地址，这种问题是相对容易诊断的，但会让无论哪个程序试图使用该缓存时都很麻烦。

### 新的应用程序

因此，这种情况下，我们需要构建一个新的应用程序，一个使用<ruby> 共享内存缓存 <rt>  shared in-memory cache </rt></ruby>的应用程序，但是我们不希望对该缓存进行迂回的故障切换过程。因此，我们的要求是共享的内存缓存，没有单点故障，可以使用尽可能少的人为干预来应对多种不同的故障模式，并且在事件恢复之后也能够在很少的人为干预下恢复，一个额外的要求是提高缓存的安全性，以减少数据泄露的范围（稍后再说）。当时 Redis Sentinel 看起来很有希望，并且有许多程序支持代理 Redis 连接，比如 [twemproxy](https://github.com/twitter/twemproxy)。这会导致还要安装其它很多组件，它应该有效，并且人际交互最少，但它复杂而需要运行大量的服务器和服务，并且相互通信。

![](/data/attachment/album/201710/17/124116w64g340irk6g2i8i.jpg)

将会有大量的应用服务器与 twemproxy 进行通信，这会将它们的调用路由到合适的 Redis 主节点，twemproxy 将从 sentinal 集群获取主节点的信息，它将控制哪台 Redis 实例是主，哪台是从。这个设置是复杂的，而且仍有单点故障，它依赖于 twemproxy 来处理分片，来连接到正确的 Redis 实例。它具有对应用程序透明的优点，所以我们可以在理论上做到将现有的应用程序转移到这个 Redis 配置，而不用改变应用程序。但是我们要从头开始构建一个应用程序，所以迁移应用程序不是一个必需条件。

幸运的是，这个时候，Redis 3.2 出来了，而且内置了原生集群，消除了对单一 sentinel 集群需要。

![](/data/attachment/album/201710/17/124155qm707j5bbuzww7u0.jpg)

它有一个更简单的设置，但 twemproxy 不支持 Redis 集群分片，它能为你分片数据，但是如果尝试在与分片不一致的集群中这样做会导致问题。有参考的指南可以使其匹配，但是集群可以自动改变形式，并改变分片的设置方式。它仍然有单点故障。正是在这一点上，我将永远感谢我的一位同事发现了一个 Node.js 的 Redis 的集群发现驱动程序，让我们完全放弃了 twemproxy。

![](/data/attachment/album/201710/17/124206z4hzb4bxux7lllmh.jpg)

因此，我们能够自动分片数据，故障转移和故障恢复基本上是自动的。应用程序知道哪些节点存在，并且在写入数据时，如果写入错误的节点，集群将自动重定向该写入。这是被选的配置，这让我们共享的内存缓存相当健壮，可以没有干预地应付基本的故障模式。在测试期间，我们的确发现了一些缺陷。复制是在一个接一个节点的基础上进行的，因此如果我们丢失了一个主节点，那么它的从节点会成为一个单点故障，直到死去的节点恢复服务，也只有主节点对集群健康投票，所以如果我们一下失去太多主节点，那么集群无法自我恢复。但这比我们过去的好。

### 向前进

随着使用集群 Redis 配置的新程序，我们对于老式 Redis 实例的状态变得越来越不适应，但是新程序与现有程序的规模并不相同（超过 30GB 的内存专用于我们最大的老式 Redis 实例数据库）。因此，随着 Redis 集群在底层得到了证实，我们决定迁移老式的 Redis 实例到新的 Redis 集群中。

由于我们有一个原生支持 Redis 集群的 Node.js Redis 驱动程序，因此我们开始将 Node.js 程序迁移到 Redis 集群。但是，如何将数十亿字节的数据从一个地方移动到另一个地方，而不会造成重大问题？特别是考虑到这些数据是认证令牌，所以如果它们错了，我们的终端用户将会被登出。一个选择是要求网站完全下线，将所有内容都指向新的 Redis 群集，并将数据迁移到其中，以希望获得最佳效果。另一个选择是切换到新集群，并强制所有用户再次登录。由于显而易见的原因，这些都不是非常合适的。我们决定采取的替代方法是将数据同时写入老式 Redis 实例和正在替换它的集群，同时随着时间的推移，我们将逐渐更多地向该集群读取。由于数据的有效期有限（令牌在几个小时后到期），这种方法可以导致零停机，并且不会有数据丢失的风险。所以我们这么做了。迁移是成功的。

剩下的就是服务于我们的 PHP 代码（其中还有一个项目是有用的，其它的最终是没必要的）的 Redis 的实例了，我们在这过程中遇到了一个困难，实际上是两个。首先，也是最紧迫的是找到在 PHP 中使用的 Redis 集群发现驱动程序，还要是我们正在使用的 PHP 版本。这被证明是可行的，因为我们升级到了最新版本的 PHP。我们选择的驱动程序不喜欢使用 Redis 的授权方式，因此我们决定使用 Redis 集群作为一个额外的安全步骤 （我告诉你，这将有更多的安全性）。当我们用 Redis 集群替换每个老式 Redis 实例时，修复似乎很直接，将 Redis 授权关闭，这样它将会响应所有的请求。然而，这并不是真的，由于某些原因，Redis 集群不会接受来自 Web 服务器的连接。 Redis 在版本 3 中引入的称为“保护模式”的新安全功能将在 Redis 绑定到任何接口时将停止监听来自外部 IP 地址的连接，并无需配置 Redis 授权密码。这被证明相当容易修复，但让我们保持警惕。

### 现在？

这就是我们现在的情况。我们已经迁移了我们的一些老式 Redis 实例，并且正在迁移其余的。我们通过这样做解决了我们的一些技术债务，并提高了我们的平台的稳定性。使用 Redis 集群，我们还可以扩展内存数据库并扩展它们。 Redis 是单线程的，所以只要在单个实例中留出更多的内存就会可以得到这么多的增长，而且我们已经紧跟在这个限制后面。我们期待着从新的集群中获得改进的性能，同时也为我们提供了扩展和负载均衡的更多选择。

### 未来怎么样？

我们解决了一些技术性债务，这使我们的服务更容易支持，更加稳定。但这并不意味着这项工作完成了，Redis 4 似乎有一些我们可能想要研究的功能。而且 Redis 并不是我们使用的唯一软件。我们将继续努力改进平台，缩短处理技术债务的时间，但随着客户群体的扩大，我们力求提供更丰富的服务，我们总是会遇到需要改进的事情。下一个挑战可能与每分钟超过 20,000次 登录到超过 40,000 次甚至更高的扩展有关。

---

via: <http://engineering.skybettingandgaming.com/2017/09/25/redis-2-to-redis-3/>

作者：[Craig Stewart](http://engineering.skybettingandgaming.com/authors#craig_stewart) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出