mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-02-03 23:40:14 +08:00
commit
26be59e1e1
75
lctt2018.md
Normal file
75
lctt2018.md
Normal file
@ -0,0 +1,75 @@
|
||||
LCTT 2018:五周年纪念日
|
||||
======
|
||||
|
||||
我是老王,可能大家有不少人知道我,由于历史原因,我有好几个生日(;o),但是这些年来,我又多了一个生日,或者说纪念日——每过两年,我就要严肃认真地写一篇 [LCTT](https://linux.cn/lctt) 生日纪念文章。
|
||||
|
||||
喏,这一篇,就是今年的了,LCTT 如今已经五岁了!
|
||||
|
||||
或许如同小孩子过生日总是比较快乐,而随着年岁渐长,过生日往往有不少负担——比如说,每次写这篇纪念文章时,我就需要回忆、反思这两年的做了些什么,往往颇为汗颜。
|
||||
|
||||
不过不管怎么说,总要总结一下这两年我们做了什么,有什么不足,也发一些展望吧。
|
||||
|
||||
### 江山代有英豪出
|
||||
|
||||
LCTT,如同一般的开源贡献组织,总是有不断的新老传承。我们的翻译组,也有不少成员,由于工作学习的原因,慢慢淡出,但同时,也不断有新的成员加入并接过前辈手中的旗帜(就是没人接我的)。
|
||||
|
||||
> **加入方式**
|
||||
|
||||
> 请首先加入翻译组的 QQ 群,群号是:**198889102**,加群时请说明是“**志愿者**”。加入后记得修改您的群名片为您的 GitHub 的 ID。
|
||||
|
||||
> 加入的成员,请先阅读 [WIKI 如何开始](https://github.com/LCTT/TranslateProject/wiki/01-%E5%A6%82%E4%BD%95%E5%BC%80%E5%A7%8B)。
|
||||
|
||||
比如说,我们这两年来,oska874 承担了主要的选题工作,然后 lujun9972 适时的出现接过了不少选题工作;再比如说,qhwdw 出现后承担了大量繁难文章的翻译,pityonline 则专注于校对,甚至其校对的严谨程度让我都甘拜下风。还有 MjSeven 也同 qhwdw 一样,以极高的翻译频率从一星译者迅速登顶五星译者。当然,还有 Bestony、Locez、VizV 等人为 LCTT 提供了不少技术支持和开发工作。
|
||||
|
||||
### 硕果累累
|
||||
|
||||
我们并没有特别的招新渠道,但是总是时不时会有新的成员慕名而来,到目前为止,我们已经有 [331](https://linux.cn/lctt-list) 位做过贡献的成员,已经翻译发布了 3885 篇译文,合计字节达 33MB 之多!
|
||||
|
||||
这两年,我们不但翻译了很多技术、新闻和评论类文章,也新增了新的翻译类型:[漫画](https://linux.cn/talk/comic/),其中一些漫画得到了很多好评。
|
||||
|
||||
我们发布的文章有一些达到了 100000+ 的访问量,这对于我们这种技术垂直内容可不容易。
|
||||
|
||||
而同时,[Linux 中国](https://linux.cn/)也发布了近万篇文章,而这一篇,应该就是第 [9999](https://linux.cn/article-9999-1.html) 篇文章,我们将在明天,进入新的篇章。
|
||||
|
||||
### 贡献者主页和贡献者证书
|
||||
|
||||
为了彰显诸位贡献者的贡献,我们为每位贡献者创立的自己的专页,并据此建立了[排行榜](https://linux.cn/lctt-list)。
|
||||
|
||||
同时,我们还特意请 Bestony 和“一一”设计开发和”贡献者证书”,大家可以在 [LCTT 贡献平台](https://lctt.linux.cn/)中领取。
|
||||
|
||||
### 规则进化
|
||||
|
||||
LCTT 最初创立时,甚至都没有采用 PR 模式。但是随着贡献者的增多,我们也逐渐在改善我们的流程、方法。
|
||||
|
||||
之前采用了很粗糙的 PR 模式,对 PR 中的文件、提交乃至于信息都没有进行硬性约束。后来在 VizV 的帮助下,建立了对 PR 的合规性检查;又在 pityonline 的督促下,采用了更为严格的 PR 审查机制。
|
||||
|
||||
LCTT 创立几年来,我们的一些流程和规范,已经成为其它一些翻译组的参考范本,我们也希望我们的这些经验,可以进一步帮助到其它的开源社区。
|
||||
|
||||
### 仓库重建和版权问题
|
||||
|
||||
今年还发生一次严重的事故,由于对选题来源把控不严和对版权问题没有引起足够的重视,我们引用的一篇文章违背了原文的版权规定,结果被原文作者投诉到 GitHub。而我并没有及时看到 GitHub 给我发的 DMCA 处理邮件,因此错过了处理窗口期,从而被 GitHub 将整个库予以删除。
|
||||
|
||||
出现这样的重大失误之后,经过大家的帮助,我们历经周折才将仓库基本恢复。这要特别感谢 VizV 的辛苦工作。
|
||||
|
||||
在此之后,我们对译文选文的规则进行了梳理,并全面清查了文章版权。这个教训对我们来说弥足沉重。
|
||||
|
||||
### 通证时代
|
||||
|
||||
在 Linux 中国及 LCTT 发展过程中,我一直小心翼翼注意商业化的问题。严格来说,没有经济支持的开源组织如同无根之木,无源之水,是长久不了的。而商业化的技术社区又难免为了三斗米而折腰。所以往往很多技术社区要么渐渐凋零,要么就变成了商业机构。
|
||||
|
||||
从中国电信辞职后,我专职运营 Linux 中国这个开源社区已经近三年了,其间也有一些商业性收入,但是仅能勉强承担基本的运营费用。
|
||||
|
||||
这种尴尬的局面,使我,以及其它的开源社区同仁们纷纷寻求更好的发展之路。
|
||||
|
||||
去年参加中国开源年会时,在闭门会上,大家的讨论启发了我和诸位同仁,我们认为,开源社区结合通证经济,似乎是一条可行的开源社区发展之路。
|
||||
|
||||
今年 8 月 1 日,我们经过了半年的论证和实验,[发布了社区通证 LCCN](https://linux.cn/article-9886-1.html),并已经初步发放到了各位译者手中。我们还在继续建设通证生态各种工具,如合约、交易商城等。
|
||||
|
||||
我们希望能够通过通证为开源社区转入新的活力,也愿意将在探索道路上遇到的问题和解决的思路、工具链分享给更多的社区。
|
||||
|
||||
### 总结
|
||||
|
||||
从上一次总结以来,这又是七百多天,时光荏苒,而 LCTT 的创立也近两千天了。我希望,我们的翻译组以及更多的贡献者可以在通证经济的推动下,找到自洽、自治的发展道路;也希望能有更多的贡献者涌现出来接过我们的大旗,将开源发扬光大。
|
||||
|
||||
wxy
|
||||
2018/9/9 夜
|
@ -0,0 +1,143 @@
|
||||
Cloud Commander:一个有控制台和编辑器的 Web 文件管理器
|
||||
======
|
||||
|
||||
![](https://www.ostechnix.com/wp-content/uploads/2016/05/Cloud-Commander-A-Web-File-Manager-With-Console-And-Editor-720x340.png)
|
||||
|
||||
**Cloud Commander** 是一个基于 web 的文件管理程序,它允许你通过任何计算机、移动端或平板电脑的浏览器查看、访问或管理系统文件或文件夹。它有两个简单而又经典的面板,并且会像你设备的显示尺寸一样自动转换大小。它也拥有两款内置的叫做 **Dword** 和 **Edward** 的文本编辑器,它们支持语法高亮,并带有一个支持系统命令行的控制台。因此,您可以随时随地编辑文件。Cloud Commander 服务器是一款在 Linux、Windows、Mac OS X 运行的跨平台应用,而且该应用客户端可以在任何一款浏览器上运行。它是用 **JavaScript/Node.Js** 写的,并使用 **MIT** 许可证。
|
||||
|
||||
在这个简易教程中,让我们看一看如何在 Ubuntu 18.04 LTS 服务器上安装 Cloud Commander。
|
||||
|
||||
### 前提条件
|
||||
|
||||
像我之前提到的,是用 Node.js 写的。所以为了安装 Cloud Commander,我们需要首先安装 Node.js。要执行安装,参考下面的指南。
|
||||
|
||||
- [如何在 Linux 上安装 Node.js](https://www.ostechnix.com/install-node-js-linux/)
|
||||
|
||||
### 安装 Cloud Commander
|
||||
|
||||
在安装 Node.js 之后,运行下列命令安装 Cloud Commander:
|
||||
|
||||
```
|
||||
$ npm i cloudcmd -g
|
||||
```
|
||||
|
||||
祝贺!Cloud Commander 已经安装好了。让我们往下继续看看 Cloud Commander 的基本使用。
|
||||
|
||||
### 开始使用 Cloud Commander
|
||||
|
||||
运行以下命令启动 Cloud Commander:
|
||||
|
||||
```
|
||||
$ cloudcmd
|
||||
```
|
||||
|
||||
**输出示例:**
|
||||
|
||||
```
|
||||
url: http://localhost:8000
|
||||
```
|
||||
|
||||
现在,打开你的浏览器并转到链接:`http://localhost:8000` 或 `http://IP-address:8000`。
|
||||
|
||||
从现在开始,您可以直接在本地系统或远程系统或移动设备,平板电脑等Web浏览器中创建,删除,查看,管理文件或文件夹。
|
||||
|
||||
![][2]
|
||||
|
||||
如你所见上面的截图,Clouder Commander 有两个面板,十个热键 (`F1` 到 `F10`),还有控制台。
|
||||
|
||||
每个热键执行的都是一个任务。
|
||||
|
||||
* `F1` – 帮助
|
||||
* `F2` – 重命名文件/文件夹
|
||||
* `F3` – 查看文件/文件夹
|
||||
* `F4` – 编辑文件
|
||||
* `F5` – 复制文件/文件夹
|
||||
* `F6` – 移动文件/文件夹
|
||||
* `F7` – 创建新目录
|
||||
* `F8` – 删除文件/文件夹
|
||||
* `F9` – 打开菜单
|
||||
* `F10` – 打开设置
|
||||
|
||||
#### Cloud Commmander 控制台
|
||||
|
||||
点击控制台图标。这即将打开系统默认的命令行界面。
|
||||
|
||||
![][3]
|
||||
|
||||
在此控制台中,您可以执行各种管理任务,例如安装软件包、删除软件包、更新系统等。您甚至可以关闭或重新引导系统。 因此,Cloud Commander 不仅仅是一个文件管理器,还具有远程管理工具的功能。
|
||||
|
||||
#### 创建文件/文件夹
|
||||
|
||||
要创建新的文件或文件夹就右键单击任意空位置并找到 “New - >File or Directory”。
|
||||
|
||||
![][4]
|
||||
|
||||
#### 查看文件
|
||||
|
||||
你可以查看图片,查看音视频文件。
|
||||
|
||||
![][5]
|
||||
|
||||
#### 上传文件
|
||||
|
||||
另一个很酷的特性是我们可以从任何系统或设备简单地上传一个文件到 Cloud Commander 系统。
|
||||
|
||||
要上传文件,右键单击 Cloud Commander 面板的任意空白处,并且单击“Upload”选项。
|
||||
|
||||
![][6]
|
||||
|
||||
选择你想要上传的文件。
|
||||
|
||||
另外,你也可以上传来自像 Google 云盘、Dropbox、Amazon 云盘、Facebook、Twitter、Gmail、GitHub、Picasa、Instagram 还有很多的云服务上的文件。
|
||||
|
||||
要从云端上传文件, 右键单击面板的任意空白处,并且右键单击面板任意空白处并选择“Upload from Cloud”。
|
||||
|
||||
![][7]
|
||||
|
||||
选择任意一个你选择的网络服务,例如谷歌云盘。点击“Connect to Google drive”按钮。
|
||||
|
||||
![][8]
|
||||
|
||||
下一步,用 Cloud Commander 验证你的谷歌云端硬盘,从谷歌云端硬盘选择文件并点击“Upload”。
|
||||
|
||||
![][9]
|
||||
|
||||
#### 更新 Cloud Commander
|
||||
|
||||
要更新到最新的可用版本,执行下面的命令:
|
||||
|
||||
```
|
||||
$ npm update cloudcmd -g
|
||||
```
|
||||
|
||||
#### 总结
|
||||
|
||||
据我测试,它运行很好。在我的 Ubuntu 服务器测试期间,我没有遇到任何问题。此外,Cloud Commander 不仅是基于 Web 的文件管理器,还可以充当执行大多数 Linux 管理任务的远程管理工具。 您可以创建文件/文件夹、重命名、删除、编辑和查看它们。此外,您可以像在终端中在本地系统中那样安装、更新、升级和删除任何软件包。当然,您甚至可以从 Cloud Commander 控制台本身关闭或重启系统。 还有什么需要的吗? 尝试一下,你会发现它很有用。
|
||||
|
||||
目前为止就这样吧。 我将很快在这里发表另一篇有趣的文章。 在此之前,请继续关注我们。
|
||||
|
||||
祝贺!
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/cloud-commander-a-web-file-manager-with-console-and-editor/
|
||||
|
||||
作者:[SK][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[fuzheng1998](https://github.com/fuzheng1998)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.ostechnix.com/author/sk/
|
||||
[1]:data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[2]:http://www.ostechnix.com/wp-content/uploads/2016/05/Cloud-Commander-Google-Chrome_006-4.jpg
|
||||
[3]:http://www.ostechnix.com/wp-content/uploads/2016/05/Cloud-Commander-Google-Chrome_007-2.jpg
|
||||
[4]:http://www.ostechnix.com/wp-content/uploads/2016/05/Cloud-commander-file-folder-1.png
|
||||
[5]:http://www.ostechnix.com/wp-content/uploads/2016/05/Cloud-Commander-home-sk-Google-Chrome_008-1.jpg
|
||||
[6]:http://www.ostechnix.com/wp-content/uploads/2016/05/cloud-commander-upload-2.png
|
||||
[7]:http://www.ostechnix.com/wp-content/uploads/2016/05/upload-from-cloud-1.png
|
||||
[8]:http://www.ostechnix.com/wp-content/uploads/2016/05/Cloud-Commander-home-sk-Google-Chrome_009-2.jpg
|
||||
[9]:http://www.ostechnix.com/wp-content/uploads/2016/05/Cloud-Commander-home-sk-Google-Chrome_010-1.jpg
|
@ -1,57 +1,27 @@
|
||||
Docker 指南:Docker 化 Python Django 应用程序
|
||||
如何 Docker 化 Python Django 应用程序
|
||||
======
|
||||
|
||||
### 目录
|
||||
|
||||
1. [我们要做什么?][6]
|
||||
|
||||
2. [步骤 1 - 安装 Docker-ce][7]
|
||||
|
||||
3. [步骤 2 - 安装 Docker-compose][8]
|
||||
|
||||
4. [步骤 3 - 配置项目环境][9]
|
||||
1. [创建一个新的 requirements.txt 文件][1]
|
||||
|
||||
2. [创建 Nginx 虚拟主机文件 django.conf][2]
|
||||
|
||||
3. [创建 Dockerfile][3]
|
||||
|
||||
4. [创建 Docker-compose 脚本][4]
|
||||
|
||||
5. [配置 Django 项目][5]
|
||||
|
||||
5. [步骤 4 - 构建并运行 Docker 镜像][10]
|
||||
|
||||
6. [步骤 5 - 测试][11]
|
||||
|
||||
7. [参考][12]
|
||||
|
||||
|
||||
Docker 是一个开源项目,为开发人员和系统管理员提供了一个开放平台,作为一个轻量级容器,它可以在任何地方构建,打包和运行应用程序。Docker 在软件容器中自动部署应用程序。
|
||||
Docker 是一个开源项目,为开发人员和系统管理员提供了一个开放平台,可以将应用程序构建、打包为一个轻量级容器,并在任何地方运行。Docker 会在软件容器中自动部署应用程序。
|
||||
|
||||
Django 是一个用 Python 编写的 Web 应用程序框架,遵循 MVC(模型-视图-控制器)架构。它是免费的,并在开源许可下发布。它速度很快,旨在帮助开发人员尽快将他们的应用程序上线。
|
||||
|
||||
在本教程中,我将逐步向你展示在 Ubuntu 16.04 如何为现有的 Django 应用程序创建 docker 镜像。我们将学习如何 docker 化一个 Python Django 应用程序,然后使用一个 docker-compose 脚本将应用程序作为容器部署到 docker 环境。
|
||||
在本教程中,我将逐步向你展示在 Ubuntu 16.04 中如何为现有的 Django 应用程序创建 docker 镜像。我们将学习如何 docker 化一个 Python Django 应用程序,然后使用一个 `docker-compose` 脚本将应用程序作为容器部署到 docker 环境。
|
||||
|
||||
为了部署我们的 Python Django 应用程序,我们需要其他 docker 镜像:一个用于 Web 服务器的 nginx docker 镜像和用于数据库的 PostgreSQL 镜像。
|
||||
为了部署我们的 Python Django 应用程序,我们需要其它 docker 镜像:一个用于 Web 服务器的 nginx docker 镜像和用于数据库的 PostgreSQL 镜像。
|
||||
|
||||
### 我们要做什么?
|
||||
|
||||
1. 安装 Docker-ce
|
||||
|
||||
2. 安装 Docker-compose
|
||||
|
||||
3. 配置项目环境
|
||||
|
||||
4. 构建并运行
|
||||
|
||||
5. 测试
|
||||
|
||||
### 步骤 1 - 安装 Docker-ce
|
||||
|
||||
在本教程中,我们将重 docker 仓库安装 docker-ce 社区版。我们将安装 docker-ce 社区版和 docker-compose,其支持 compose 文件版本 3(to 校正者:此处不太明白具体意思)。
|
||||
在本教程中,我们将从 docker 仓库安装 docker-ce 社区版。我们将安装 docker-ce 社区版和 `docker-compose`(其支持 compose 文件版本 3)。
|
||||
|
||||
在安装 docker-ce 之前,先使用 apt 命令安装所需的 docker 依赖项。
|
||||
在安装 docker-ce 之前,先使用 `apt` 命令安装所需的 docker 依赖项。
|
||||
|
||||
```
|
||||
sudo apt install -y \
|
||||
@ -71,7 +41,7 @@ sudo add-apt-repository \
|
||||
stable"
|
||||
```
|
||||
|
||||
[![安装 Docker-ce](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/1.png)][14]
|
||||
[![安装 Docker-ce](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/1.png)][14]
|
||||
|
||||
更新仓库并安装 docker-ce。
|
||||
|
||||
@ -87,16 +57,16 @@ systemctl start docker
|
||||
systemctl enable docker
|
||||
```
|
||||
|
||||
接着,我们将添加一个名为 'omar' 的新用户并将其添加到 docker 组。
|
||||
接着,我们将添加一个名为 `omar` 的新用户并将其添加到 `docker` 组。
|
||||
|
||||
```
|
||||
useradd -m -s /bin/bash omar
|
||||
usermod -a -G docker omar
|
||||
```
|
||||
|
||||
[![启动 Docker](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/2.png)][15]
|
||||
[![启动 Docker](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/2.png)][15]
|
||||
|
||||
以 omar 用户身份登录并运行 docker 命令,如下所示。
|
||||
以 `omar` 用户身份登录并运行 `docker` 命令,如下所示。
|
||||
|
||||
```
|
||||
su - omar
|
||||
@ -107,13 +77,13 @@ docker run hello-world
|
||||
|
||||
[![检查 Docker 安装](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/3.png)][16]
|
||||
|
||||
Docker-ce 安装已经完成。
|
||||
Docker-ce 安装已经完成。
|
||||
|
||||
### 步骤 2 - 安装 Docker-compose
|
||||
### 步骤 2 - 安装 Docker-compose
|
||||
|
||||
在本教程中,我们将使用最新的 docker-compose 支持 compose 文件版本 3。我们将手动安装 docker-compose。
|
||||
在本教程中,我们将使用支持 compose 文件版本 3 的最新 `docker-compose`。我们将手动安装 `docker-compose`。
|
||||
|
||||
使用 curl 命令将最新版本的 docker-compose 下载到 `/usr/local/bin` 目录,并使用 chmod 命令使其有执行权限。
|
||||
使用 `curl` 命令将最新版本的 `docker-compose` 下载到 `/usr/local/bin` 目录,并使用 `chmod` 命令使其有执行权限。
|
||||
|
||||
运行以下命令:
|
||||
|
||||
@ -122,7 +92,7 @@ sudo curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-c
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
现在检查 docker-compose 版本。
|
||||
现在检查 `docker-compose` 版本。
|
||||
|
||||
```
|
||||
docker-compose version
|
||||
@ -132,26 +102,26 @@ docker-compose version
|
||||
|
||||
[![安装 Docker-compose](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/4.png)][17]
|
||||
|
||||
已安装支持 compose 文件版本 3 的 docker-compose 最新版本。
|
||||
已安装支持 compose 文件版本 3 的 `docker-compose` 最新版本。
|
||||
|
||||
### 步骤 3 - 配置项目环境
|
||||
|
||||
在这一步中,我们将配置 Python Django 项目环境。我们将创建新目录 'guide01',并使其成为我们项目文件的主目录,例如 Dockerfile,Django 项目,nginx 配置文件等。
|
||||
在这一步中,我们将配置 Python Django 项目环境。我们将创建新目录 `guide01`,并使其成为我们项目文件的主目录,例如包括 Dockerfile、Django 项目、nginx 配置文件等。
|
||||
|
||||
登录到 'omar' 用户。
|
||||
登录到 `omar` 用户。
|
||||
|
||||
```
|
||||
su - omar
|
||||
```
|
||||
|
||||
创建一个新目录 'guide01',并进入目录。
|
||||
创建一个新目录 `guide01`,并进入目录。
|
||||
|
||||
```
|
||||
mkdir -p guide01
|
||||
cd guide01/
|
||||
```
|
||||
|
||||
现在在 'guide01' 目录下,创建两个新目录 'project' 和 'config'。
|
||||
现在在 `guide01` 目录下,创建两个新目录 `project` 和 `config`。
|
||||
|
||||
```
|
||||
mkdir project/ config/
|
||||
@ -159,118 +129,151 @@ mkdir project/ config/
|
||||
|
||||
注意:
|
||||
|
||||
* 'project' 目录:我们所有的 python Django 项目文件都将放在该目录中。
|
||||
* `project` 目录:我们所有的 python Django 项目文件都将放在该目录中。
|
||||
* `config` 目录:项目配置文件的目录,包括 nginx 配置文件、python pip 的`requirements.txt` 文件等。
|
||||
|
||||
* 'config' 目录:项目配置文件的目录,包括 nginx 配置文件,python pip requirements 文件等。
|
||||
#### 创建一个新的 requirements.txt 文件
|
||||
|
||||
### 创建一个新的 requirements.txt 文件
|
||||
|
||||
接下来,使用 vim 命令在 'config' 目录中创建一个新的 requirements.txt 文件
|
||||
接下来,使用 `vim` 命令在 `config` 目录中创建一个新的 `requirements.txt` 文件。
|
||||
|
||||
```
|
||||
vim config/requirements.txt
|
||||
```
|
||||
|
||||
粘贴下面的配置。
|
||||
粘贴下面的配置:
|
||||
|
||||
```
|
||||
Django==2.0.4
|
||||
gunicorn==19.7.0
|
||||
psycopg2==2.7.4
|
||||
gunicorn==19.7.0
|
||||
psycopg2==2.7.4
|
||||
```
|
||||
|
||||
保存并退出。
|
||||
|
||||
### 创建 Dockerfile
|
||||
#### 创建 Nginx 虚拟主机文件 django.conf
|
||||
|
||||
在 'guide01' 目录下创建新文件 'Dockerfile'。
|
||||
在 `config` 目录下创建 nginx 配置目录并添加虚拟主机配置文件 `django.conf`。
|
||||
|
||||
运行以下命令。
|
||||
```
|
||||
mkdir -p config/nginx/
|
||||
vim config/nginx/django.conf
|
||||
```
|
||||
|
||||
粘贴下面的配置:
|
||||
|
||||
```
|
||||
upstream web {
|
||||
ip_hash;
|
||||
server web:8000;
|
||||
}
|
||||
|
||||
# portal
|
||||
server {
|
||||
location / {
|
||||
proxy_pass http://web/;
|
||||
}
|
||||
listen 8000;
|
||||
server_name localhost;
|
||||
|
||||
location /static {
|
||||
autoindex on;
|
||||
alias /src/static/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
保存并退出。
|
||||
|
||||
#### 创建 Dockerfile
|
||||
|
||||
在 `guide01` 目录下创建新文件 `Dockerfile`。
|
||||
|
||||
运行以下命令:
|
||||
|
||||
```
|
||||
vim Dockerfile
|
||||
```
|
||||
|
||||
现在粘贴下面的 Dockerfile 脚本。
|
||||
现在粘贴下面的 Dockerfile 脚本:
|
||||
|
||||
```
|
||||
FROM python:3.5-alpine
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN apk update && \
|
||||
apk add --virtual build-deps gcc python-dev musl-dev && \
|
||||
apk add postgresql-dev bash
|
||||
RUN apk update && \
|
||||
apk add --virtual build-deps gcc python-dev musl-dev && \
|
||||
apk add postgresql-dev bash
|
||||
|
||||
RUN mkdir /config
|
||||
ADD /config/requirements.txt /config/
|
||||
RUN pip install -r /config/requirements.txt
|
||||
RUN mkdir /src
|
||||
WORKDIR /src
|
||||
RUN mkdir /config
|
||||
ADD /config/requirements.txt /config/
|
||||
RUN pip install -r /config/requirements.txt
|
||||
RUN mkdir /src
|
||||
WORKDIR /src
|
||||
```
|
||||
|
||||
保存并退出。
|
||||
|
||||
注意:
|
||||
|
||||
我们想要为我们的 Django 项目构建基于 Alpine Linux 的 Docker 镜像,Alpine 是最小的 Linux 版本。我们的 Django 项目将运行在带有 Python3.5 的 Alpine Linux 上,并添加 postgresql-dev 包以支持 PostgreSQL 数据库。然后,我们将使用 python pip 命令安装在 'requirements.txt' 上列出的所有 Python 包,并为我们的项目创建新目录 '/src'。
|
||||
我们想要为我们的 Django 项目构建基于 Alpine Linux 的 Docker 镜像,Alpine 是最小的 Linux 版本。我们的 Django 项目将运行在带有 Python 3.5 的 Alpine Linux 上,并添加 postgresql-dev 包以支持 PostgreSQL 数据库。然后,我们将使用 python `pip` 命令安装在 `requirements.txt` 上列出的所有 Python 包,并为我们的项目创建新目录 `/src`。
|
||||
|
||||
### 创建 Docker-compose 脚本
|
||||
#### 创建 Docker-compose 脚本
|
||||
|
||||
使用 [vim][18] 命令在 'guide01' 目录下创建 'docker-compose.yml' 文件。
|
||||
使用 [vim][18] 命令在 `guide01` 目录下创建 `docker-compose.yml` 文件。
|
||||
|
||||
```
|
||||
vim docker-compose.yml
|
||||
```
|
||||
|
||||
粘贴以下配置内容。
|
||||
粘贴以下配置内容:
|
||||
|
||||
```
|
||||
version: '3'
|
||||
services:
|
||||
db:
|
||||
image: postgres:10.3-alpine
|
||||
container_name: postgres01
|
||||
nginx:
|
||||
image: nginx:1.13-alpine
|
||||
container_name: nginx01
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./project:/src
|
||||
- ./config/nginx:/etc/nginx/conf.d
|
||||
depends_on:
|
||||
- web
|
||||
web:
|
||||
build: .
|
||||
container_name: django01
|
||||
command: bash -c "python manage.py makemigrations && python manage.py migrate && python manage.py collectstatic --noinput && gunicorn hello_django.wsgi -b 0.0.0.0:8000"
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
- ./project:/src
|
||||
expose:
|
||||
- "8000"
|
||||
restart: always
|
||||
services:
|
||||
db:
|
||||
image: postgres:10.3-alpine
|
||||
container_name: postgres01
|
||||
nginx:
|
||||
image: nginx:1.13-alpine
|
||||
container_name: nginx01
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./project:/src
|
||||
- ./config/nginx:/etc/nginx/conf.d
|
||||
depends_on:
|
||||
- web
|
||||
web:
|
||||
build: .
|
||||
container_name: django01
|
||||
command: bash -c "python manage.py makemigrations && python manage.py migrate && python manage.py collectstatic --noinput && gunicorn hello_django.wsgi -b 0.0.0.0:8000"
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
- ./project:/src
|
||||
expose:
|
||||
- "8000"
|
||||
restart: always
|
||||
```
|
||||
|
||||
保存并退出。
|
||||
|
||||
注意:
|
||||
|
||||
使用这个 docker-compose 文件脚本,我们将创建三个服务。使用 PostgreSQL alpine Linux 创建名为 'db' 的数据库服务,再次使用 Nginx alpine Linux 创建 'nginx' 服务,并使用从 Dockerfile 生成的自定义 docker 镜像创建我们的 python Django 容器。
|
||||
使用这个 `docker-compose` 文件脚本,我们将创建三个服务。使用 alpine Linux 版的 PostgreSQL 创建名为 `db` 的数据库服务,再次使用 alpine Linux 版的 Nginx 创建 `nginx` 服务,并使用从 Dockerfile 生成的自定义 docker 镜像创建我们的 python Django 容器。
|
||||
|
||||
[![配置项目环境](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/5.png)][19]
|
||||
[![配置项目环境](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/5.png)][19]
|
||||
|
||||
### 配置 Django 项目
|
||||
#### 配置 Django 项目
|
||||
|
||||
将 Django 项目文件复制到 'project' 目录。
|
||||
将 Django 项目文件复制到 `project` 目录。
|
||||
|
||||
```
|
||||
cd ~/django
|
||||
cp -r * ~/guide01/project/
|
||||
```
|
||||
|
||||
进入 'project' 目录并编辑应用程序设置 'settings.py'。
|
||||
进入 `project` 目录并编辑应用程序设置 `settings.py`。
|
||||
|
||||
```
|
||||
cd ~/guide01/project/
|
||||
@ -279,29 +282,29 @@ vim hello_django/settings.py
|
||||
|
||||
注意:
|
||||
|
||||
我们将部署名为 'hello_django' 的简单 Django 应用程序。
|
||||
我们将部署名为 “hello_django” 的简单 Django 应用程序。
|
||||
|
||||
在 'ALLOW_HOSTS' 行中,添加服务名称 'web'。
|
||||
在 `ALLOW_HOSTS` 行中,添加服务名称 `web`。
|
||||
|
||||
```
|
||||
ALLOW_HOSTS = ['web']
|
||||
```
|
||||
|
||||
现在更改数据库设置,我们将使用 PostgreSQL 数据库,'db' 数据库作为服务运行,使用默认用户和密码。
|
||||
现在更改数据库设置,我们将使用 PostgreSQL 数据库来运行名为 `db` 的服务,使用默认用户和密码。
|
||||
|
||||
```
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': 'postgres',
|
||||
'USER': 'postgres',
|
||||
'HOST': 'db',
|
||||
'PORT': 5432,
|
||||
}
|
||||
}
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': 'postgres',
|
||||
'USER': 'postgres',
|
||||
'HOST': 'db',
|
||||
'PORT': 5432,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
至于 'STATIC_ROOT' 配置目录,将此行添加到文件行的末尾。
|
||||
至于 `STATIC_ROOT` 配置目录,将此行添加到文件行的末尾。
|
||||
|
||||
```
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
|
||||
@ -309,21 +312,21 @@ STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
|
||||
|
||||
保存并退出。
|
||||
|
||||
[![配置 Django 项目](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/6.png)][20]
|
||||
[![配置 Django 项目](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/6.png)][20]
|
||||
|
||||
现在我们准备在 docker 容器下构建和运行 Django 项目。
|
||||
|
||||
### 步骤 4 - 构建并运行 Docker 镜像
|
||||
|
||||
在这一步中,我们想要使用 'guide01' 目录中的配置为我们的 Django 项目构建一个 Docker 镜像。
|
||||
在这一步中,我们想要使用 `guide01` 目录中的配置为我们的 Django 项目构建一个 Docker 镜像。
|
||||
|
||||
进入 'guide01' 目录。
|
||||
进入 `guide01` 目录。
|
||||
|
||||
```
|
||||
cd ~/guide01/
|
||||
```
|
||||
|
||||
现在使用 docker-compose 命令构建 docker 镜像。
|
||||
现在使用 `docker-compose` 命令构建 docker 镜像。
|
||||
|
||||
```
|
||||
docker-compose build
|
||||
@ -331,7 +334,7 @@ docker-compose build
|
||||
|
||||
[![运行 docker 镜像](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/7.png)][21]
|
||||
|
||||
启动 docker-compose 脚本中的所有服务。
|
||||
启动 `docker-compose` 脚本中的所有服务。
|
||||
|
||||
```
|
||||
docker-compose up -d
|
||||
@ -348,7 +351,7 @@ docker-compose ps
|
||||
docker-compose images
|
||||
```
|
||||
|
||||
现在,你将在系统上运行三个容器并列出 Docker 镜像,如下所示。
|
||||
现在,你将在系统上运行三个容器,列出 Docker 镜像,如下所示。
|
||||
|
||||
[![docke-compose ps 命令](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/9.png)][23]
|
||||
|
||||
@ -356,17 +359,19 @@ docker-compose images
|
||||
|
||||
### 步骤 5 - 测试
|
||||
|
||||
打开 Web 浏览器并使用端口 8000 键入服务器地址,我的是:http://ovh01:8000/
|
||||
打开 Web 浏览器并使用端口 8000 键入服务器地址,我的是:`http://ovh01:8000/`。
|
||||
|
||||
现在你将获得默认的 Django 主页。
|
||||
现在你将看到默认的 Django 主页。
|
||||
|
||||
[![默认 Django 项目主页](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/10.png)][24]
|
||||
|
||||
接下来,通过在 URL 上添加 “/admin” 路径来测试管理页面。
|
||||
接下来,通过在 URL 上添加 `/admin` 路径来测试管理页面。
|
||||
|
||||
```
|
||||
http://ovh01:8000/admin/
|
||||
```
|
||||
|
||||
然后你将会看到 Django admin 登录页面。
|
||||
然后你将会看到 Django 管理登录页面。
|
||||
|
||||
[![Django administration](https://www.howtoforge.com/images/docker_guide_dockerizing_python_django_application/11.png)][25]
|
||||
|
||||
@ -383,7 +388,7 @@ via: https://www.howtoforge.com/tutorial/docker-guide-dockerizing-python-django-
|
||||
|
||||
作者:[Muhammad Arul][a]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,22 +1,19 @@
|
||||
在 RxJS 中创建流的延伸教程
|
||||
============================================================
|
||||
全面教程:在 RxJS 中创建流
|
||||
================================
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/900/1*hj8mGnl5tM_lAlx5_vqS-Q.jpeg)
|
||||
|
||||
对大多数开发者来说,RxJS 是以库的形式与之接触,就像 Angular。一些函数会返回流,要使用它们就得把注意力放在操作符上。
|
||||
对大多数开发者来说,与 RxJS 的初次接触是通过库的形式,就像 Angular。一些函数会返回<ruby>流<rt>stream</rt></ruby>,要使用它们就得把注意力放在操作符上。
|
||||
|
||||
有些时候,混用响应式和非响应式代码似乎很有用。然后大家就开始热衷流的创造。不论是在编写异步代码或者是数据处理时,流都是一个不错的方案。
|
||||
|
||||
RxJS 提供很多方式来创建流。不管你遇到的是什么情况,都会有一个完美的创建流的方式。你可能根本用不上它们,但了解它们可以节省你的时间,让你少码一些代码。
|
||||
|
||||
我把所有可能的方法,按它们的主要目的,分放在四个目录中:
|
||||
我把所有可能的方法,按它们的主要目的,放在四个分类当中:
|
||||
|
||||
* 流式化现有数据
|
||||
|
||||
* 生成数据
|
||||
|
||||
* 使用现有 APIs 进行交互
|
||||
|
||||
* 使用现有 API 进行交互
|
||||
* 选择现有的流,并结合起来
|
||||
|
||||
注意:示例用的是 RxJS 6,可能会以前的版本有所不同。已知的区别是你导入函数的方式不同了。
|
||||
@ -25,9 +22,7 @@ RxJS 6
|
||||
|
||||
```
|
||||
import {of, from} from 'rxjs';
|
||||
```
|
||||
|
||||
```
|
||||
of(...);
|
||||
from(...);
|
||||
```
|
||||
@ -38,36 +33,24 @@ RxJS < 6
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/observable/from';
|
||||
```
|
||||
|
||||
```
|
||||
Observable.of(...);
|
||||
Observable.from(...);
|
||||
```
|
||||
|
||||
```
|
||||
//or
|
||||
```
|
||||
//或
|
||||
|
||||
```
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { from } from 'rxjs/observable/from';
|
||||
```
|
||||
|
||||
```
|
||||
of(...);
|
||||
from(...);
|
||||
```
|
||||
|
||||
流的图示中的标记:
|
||||
|
||||
* | 表示流结束了
|
||||
|
||||
* X 表示流出现错误并被终结
|
||||
|
||||
* … 表示流的走向不定
|
||||
|
||||
* * *
|
||||
* `|` 表示流结束了
|
||||
* `X` 表示流出现错误并被终结
|
||||
* `...` 表示流的走向不定
|
||||
|
||||
### 流式化已有数据
|
||||
|
||||
@ -75,7 +58,7 @@ from(...);
|
||||
|
||||
#### of
|
||||
|
||||
如果只有一个或者一些不同的元素,使用 _of_ :
|
||||
如果只有一个或者一些不同的元素,使用 `of`:
|
||||
|
||||
```
|
||||
of(1,2,3)
|
||||
@ -89,13 +72,11 @@ of(1,2,3)
|
||||
|
||||
#### from
|
||||
|
||||
如果有一个数组或者 _可迭代的_ 对象,而且你想要其中的所有元素发送到流中,使用 _from_。你也可以用它来把一个 promise 对象变成可观测的。
|
||||
如果有一个数组或者 _可迭代的对象_ ,而且你想要其中的所有元素发送到流中,使用 `from`。你也可以用它来把一个 promise 对象变成可观测的。
|
||||
|
||||
```
|
||||
const foo = [1,2,3];
|
||||
```
|
||||
|
||||
```
|
||||
from(foo)
|
||||
.subscribe();
|
||||
```
|
||||
@ -111,9 +92,7 @@ from(foo)
|
||||
|
||||
```
|
||||
const foo = { a: 1, b: 2};
|
||||
```
|
||||
|
||||
```
|
||||
pairs(foo)
|
||||
.subscribe();
|
||||
```
|
||||
@ -125,19 +104,16 @@ pairs(foo)
|
||||
|
||||
#### 那么其他的数据结构呢?
|
||||
|
||||
也许你的数据存储在自定义的结构中,而它又没有实现 _Iterable_ 接口,又或者说你的结构是递归的,树状的。也许下面某种选择适合这些情况:
|
||||
也许你的数据存储在自定义的结构中,而它又没有实现 _可迭代的对象_ 接口,又或者说你的结构是递归的、树状的。也许下面某种选择适合这些情况:
|
||||
|
||||
* 先将数据提取到数组里
|
||||
1. 先将数据提取到数组里
|
||||
2. 使用下一节将会讲到的 `generate` 函数,遍历所有数据
|
||||
3. 创建一个自定义流(见下一节)
|
||||
4. 创建一个迭代器
|
||||
|
||||
* 使用下一节将会讲到的 _generate_ 函数,遍历所有数据
|
||||
稍后会讲到选项 2 和 3 ,因此这里的重点是创建一个迭代器。我们可以对一个 _可迭代的对象_ 调用 `from` 创建一个流。 _可迭代的对象_ 是一个对象,可以产生一个迭代器(如果你对细节感兴趣,参考 [这篇 mdn 文章][6])。
|
||||
|
||||
* 创建一个自定义流(见下一节)
|
||||
|
||||
* 创建一个迭代器
|
||||
|
||||
稍后会讲到选项 2 和 3 ,因此这里的重点是创建一个迭代器。我们可以对一个 _iterable_ 对象调用 _from_ 创建一个流。 _iterable_ 是一个对象,可以产生一个迭代器(如果你对细节感兴趣,参考 [这篇 mdn 文章][6])。
|
||||
|
||||
创建一个迭代器的简单方式是 [generator function][7]。当你调用一个生成函数(generator function)时,它返回一个对象,该对象同时遵循 _iterable_ 接口和 _iterator_ 接口。
|
||||
创建一个迭代器的简单方式是 <ruby>[生成函数][7]<rt>generator function</rt></ruby>。当你调用一个生成函数时,它返回一个对象,该对象同时遵循 _可迭代的对象_ 接口和 _迭代器_ 接口。
|
||||
|
||||
```
|
||||
// 自定义的数据结构
|
||||
@ -147,23 +123,17 @@ class List {
|
||||
get size() ...
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
function* listIterator(list) {
|
||||
for (let i = 0; i<list.size; i++) {
|
||||
yield list.get(i);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
const myList = new List();
|
||||
myList.add(1);
|
||||
myList.add(3);
|
||||
```
|
||||
|
||||
```
|
||||
from(listIterator(myList))
|
||||
.subscribe(console.log);
|
||||
```
|
||||
@ -173,15 +143,13 @@ from(listIterator(myList))
|
||||
// 1 3 |
|
||||
```
|
||||
|
||||
调用 `listIterator` 函数时,返回值是一个 _iterable_ / _iterator_。函数里面的代码在调用 _subscribe_ 前不会执行。
|
||||
|
||||
* * *
|
||||
调用 `listIterator` 函数时,返回值是一个 _可迭代的对象_ / _迭代器_ 。函数里面的代码在调用 `subscribe` 前不会执行。
|
||||
|
||||
### 生成数据
|
||||
|
||||
你知道要发送哪些数据,但想(或者不得不)动态生成它。所有函数的最后一个参数都可以用来接收一个调度器。他们产生静态的流。
|
||||
你知道要发送哪些数据,但想(或者必须)动态生成它。所有函数的最后一个参数都可以用来接收一个调度器。他们产生静态的流。
|
||||
|
||||
#### range
|
||||
#### 范围(`range`)
|
||||
|
||||
从初始值开始,发送一系列数字,直到完成了指定次数的迭代。
|
||||
|
||||
@ -195,9 +163,9 @@ range(10, 2) // 从 10 开始,发送两个值
|
||||
// 10 11 |
|
||||
```
|
||||
|
||||
#### 间隔/定时器
|
||||
#### 间隔(`interval`) / 定时器(`timer`)
|
||||
|
||||
有点像 _range_,但定时器是周期性的发送累加的数字(就是说,不是立即发送)。两者的区别在于在于 _timer_ 允许你为第一个元素设定一个延迟。也可以只产生一个值,只要不指定周期。
|
||||
有点像范围,但定时器是周期性的发送累加的数字(就是说,不是立即发送)。两者的区别在于在于定时器允许你为第一个元素设定一个延迟。也可以只产生一个值,只要不指定周期。
|
||||
|
||||
```
|
||||
interval(1000) // 每 1000ms = 1 秒 发送数据
|
||||
@ -211,9 +179,7 @@ interval(1000) // 每 1000ms = 1 秒 发送数据
|
||||
|
||||
```
|
||||
delay(5000, 1000) // 和上面相同,在开始前先等待 5000ms
|
||||
```
|
||||
|
||||
```
|
||||
delay(5000)
|
||||
.subscribe(i => console.log("foo");
|
||||
// 5 秒后打印 foo
|
||||
@ -229,7 +195,7 @@ interval(10000).pipe(
|
||||
|
||||
这段代码每 10 秒获取一次数据,更新屏幕。
|
||||
|
||||
#### generate
|
||||
#### 生成(`generate `)
|
||||
|
||||
这是个更加复杂的函数,允许你发送一系列任意类型的对象。它有一些重载,这里你看到的是最有意思的部分:
|
||||
|
||||
@ -246,15 +212,13 @@ generate(
|
||||
// 1 2 4 8 |
|
||||
```
|
||||
|
||||
你也可以用它来迭代值,如果一个结构没有实现 _Iterable_ 接口。我们用前面的 list 例子来进行演示:
|
||||
你也可以用它来迭代值,如果一个结构没有实现 _可迭代的对象_ 接口。我们用前面的列表例子来进行演示:
|
||||
|
||||
```
|
||||
const myList = new List();
|
||||
myList.add(1);
|
||||
myList.add(3);
|
||||
```
|
||||
|
||||
```
|
||||
generate(
|
||||
0, // 从这个值开始
|
||||
i => i < list.size, // 条件:发送数据,直到遍历完整个列表
|
||||
@ -268,15 +232,13 @@ generate(
|
||||
// 1 3 |
|
||||
```
|
||||
|
||||
如你所见,我添加了另一个参数:选择器(selector)。它和 _map_ 操作符作用类似,将生成的值转换为更有用的东西。
|
||||
|
||||
* * *
|
||||
如你所见,我添加了另一个参数:选择器。它和 `map` 操作符作用类似,将生成的值转换为更有用的东西。
|
||||
|
||||
### 空的流
|
||||
|
||||
有时候你要传递或返回一个不用发送任何数据的流。有三个函数分别用于不同的情况。你可以给这三个函数传递调度器。_empty_ 和 _throwError_ 接收一个调度器参数。
|
||||
有时候你要传递或返回一个不用发送任何数据的流。有三个函数分别用于不同的情况。你可以给这三个函数传递调度器。`empty` 和 `throwError` 接收一个调度器参数。
|
||||
|
||||
#### empty
|
||||
#### `empty`
|
||||
|
||||
创建一个空的流,一个值也不发送。
|
||||
|
||||
@ -290,7 +252,7 @@ empty()
|
||||
// |
|
||||
```
|
||||
|
||||
#### never
|
||||
#### `never`
|
||||
|
||||
创建一个永远不会结束的流,仍然不发送值。
|
||||
|
||||
@ -304,7 +266,7 @@ never()
|
||||
// ...
|
||||
```
|
||||
|
||||
#### throwError
|
||||
#### `throwError`
|
||||
|
||||
创建一个流,流出现错误,不发送数据。
|
||||
|
||||
@ -318,15 +280,13 @@ throwError('error')
|
||||
// X
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
### 挂钩已有的 API
|
||||
|
||||
不是所有的库和所有你之前写的代码使用或者支持流。幸运的是 RxJS 提供函数用来桥接非响应式和响应式代码。这一节仅仅讨论 RxJS 为桥接代码提供的模版。
|
||||
|
||||
你可能还对这篇出自 [Ben Lesh][9] 的 [延伸阅读][8] 感兴趣,这篇文章讲了几乎所有能与 promises 交互操作的方式。
|
||||
你可能还对这篇出自 [Ben Lesh][9] 的 [全面的文章][8] 感兴趣,这篇文章讲了几乎所有能与 promises 交互操作的方式。
|
||||
|
||||
#### from
|
||||
#### `from`
|
||||
|
||||
我们已经用过它,把它列在这里是因为,它可以封装一个含有 observable 对象的 promise 对象。
|
||||
|
||||
@ -346,9 +306,7 @@ fromEvent 为 DOM 元素添加一个事件监听器,我确定你知道这个
|
||||
|
||||
```
|
||||
const element = $('#fooButton'); // 从 DOM 元素中创建一个 jQuery 对象
|
||||
```
|
||||
|
||||
```
|
||||
from(element, 'click')
|
||||
.subscribe();
|
||||
```
|
||||
@ -367,31 +325,25 @@ from(document, 'click')
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
这告诉 RxJS 我们想要监听 document 中的点击事件。在提交过程中,RxJS 发现 document 是一个 _EventTarget_ 类型,因此它可以调用它的 _addEventListener_ 方法。如果我们传入的是一个 jQuery 对象而非 document,那么 RxJs 知道它得调用 _on_ 方法。
|
||||
这告诉 RxJS 我们想要监听 document 中的点击事件。在提交过程中,RxJS 发现 document 是一个 _EventTarget_ 类型,因此它可以调用它的 `addEventListener` 方法。如果我们传入的是一个 jQuery 对象而非 document,那么 RxJs 知道它得调用 _on_ 方法。
|
||||
|
||||
这个例子用的是 _fromEventPattern_,和 _fromEvent_ 的工作基本上一样:
|
||||
这个例子用的是 _fromEventPattern_ ,和 _fromEvent_ 的工作基本上一样:
|
||||
|
||||
```
|
||||
function addClickHandler(handler) {
|
||||
document.addEventListener('click', handler);
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
function removeClickHandler(handler) {
|
||||
document.removeEventListener('click', handler);
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
fromEventPattern(
|
||||
addClickHandler,
|
||||
removeClickHandler,
|
||||
)
|
||||
.subscribe(console.log);
|
||||
```
|
||||
|
||||
```
|
||||
// 等效于
|
||||
fromEvent(document, 'click')
|
||||
```
|
||||
@ -402,49 +354,37 @@ RxJS 自动创建实际的监听器( _handler_ )你的工作是添加或者
|
||||
|
||||
```
|
||||
const listeners = [];
|
||||
```
|
||||
|
||||
```
|
||||
class Foo {
|
||||
registerListener(listener) {
|
||||
listeners.push(listener);
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
emit(value) {
|
||||
listeners.forEach(listener => listener(value));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
const foo = new Foo();
|
||||
```
|
||||
|
||||
```
|
||||
fromEventPattern(listener => foo.registerListener(listener))
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
```
|
||||
foo.emit(1);
|
||||
```
|
||||
|
||||
```
|
||||
// Produces
|
||||
// 结果
|
||||
// 1 ...
|
||||
```
|
||||
|
||||
当我们调用 foo.emit(1) 时,RxJS 中的监听器将被调用,然后它就能把值发送到流中。
|
||||
当我们调用 `foo.emit(1)` 时,RxJS 中的监听器将被调用,然后它就能把值发送到流中。
|
||||
|
||||
你也可以用它来监听多个事件类型,或者结合所有可以通过回调进行通讯的 API,例如,WebWorker API:
|
||||
|
||||
```
|
||||
const myWorker = new Worker('worker.js');
|
||||
```
|
||||
|
||||
```
|
||||
fromEventPattern(
|
||||
handler => { myWorker.onmessage = handler },
|
||||
handler => { myWorker.onmessage = undefined }
|
||||
@ -465,20 +405,14 @@ fromEventPattern(
|
||||
function foo(value, callback) {
|
||||
callback(value);
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
// 没有流
|
||||
foo(1, console.log); //prints 1 in the console
|
||||
```
|
||||
|
||||
```
|
||||
// 有流
|
||||
const reactiveFoo = bindCallback(foo);
|
||||
// 当我们调用 reactiveFoo 时,它返回一个 observable 对象
|
||||
```
|
||||
|
||||
```
|
||||
reactiveFoo(1)
|
||||
.subscribe(console.log); // 在控制台打印 1
|
||||
```
|
||||
@ -494,51 +428,39 @@ reactiveFoo(1)
|
||||
|
||||
```
|
||||
import { webSocket } from 'rxjs/webSocket';
|
||||
```
|
||||
|
||||
```
|
||||
let socket$ = webSocket('ws://localhost:8081');
|
||||
```
|
||||
|
||||
```
|
||||
// 接收消息
|
||||
socket$.subscribe(
|
||||
(msg) => console.log('message received: ' + msg),
|
||||
(err) => console.log(err),
|
||||
() => console.log('complete') * );
|
||||
```
|
||||
|
||||
```
|
||||
// 发送消息
|
||||
socket$.next(JSON.stringify({ op: 'hello' }));
|
||||
```
|
||||
|
||||
把 websocket 功能添加到你的应用中真的很简单。_websocket_ 创建一个 subject。这意味着你可以订阅它,通过调用 _next_ 来获得消息和发送消息。
|
||||
把 websocket 功能添加到你的应用中真的很简单。_websocket_ 创建一个 subject。这意味着你可以订阅它,通过调用 `next` 来获得消息和发送消息。
|
||||
|
||||
#### ajax
|
||||
|
||||
如你所知:类似于 websocket,提供 AJAX 查询的功能。你可能用了一个带有 AJAX 功能的库或者框架。或者你没有用,那么我建议使用 fetch(或者必要的话用 polyfill),把返回的 promise 封装到一个 observable 对象中(参考稍后会讲到的 _defer_ 函数)。
|
||||
如你所知:类似于 websocket,提供 AJAX 查询的功能。你可能用了一个带有 AJAX 功能的库或者框架。或者你没有用,那么我建议使用 fetch(或者必要的话用 polyfill),把返回的 promise 封装到一个 observable 对象中(参考稍后会讲到的 `defer` 函数)。
|
||||
|
||||
* * *
|
||||
|
||||
### Custom Streams
|
||||
### 定制流
|
||||
|
||||
有时候已有的函数用起来并不是足够灵活。或者你需要对订阅有更强的控制。
|
||||
|
||||
#### Subject
|
||||
#### 主题(`Subject`)
|
||||
|
||||
subject 是一个特殊的对象,它使得你的能够把数据发送到流中,并且能够控制数据。subject 本身就是一个 observable 对象,但如果你想要把流暴露给其它代码,建议你使用 _asObservable_ 方法。这样你就不能意外调用原始方法。
|
||||
`Subject` 是一个特殊的对象,它使得你的能够把数据发送到流中,并且能够控制数据。`Subject` 本身就是一个可观察对象,但如果你想要把流暴露给其它代码,建议你使用 `asObservable` 方法。这样你就不能意外调用原始方法。
|
||||
|
||||
```
|
||||
const subject = new Subject();
|
||||
const observable = subject.asObservable();
|
||||
```
|
||||
|
||||
```
|
||||
observable.subscribe();
|
||||
```
|
||||
|
||||
```
|
||||
subject.next(1);
|
||||
subject.next(2);
|
||||
subject.complete();
|
||||
@ -554,17 +476,11 @@ subject.complete();
|
||||
```
|
||||
const subject = new Subject();
|
||||
const observable = subject.asObservable();
|
||||
```
|
||||
|
||||
```
|
||||
subject.next(1);
|
||||
```
|
||||
|
||||
```
|
||||
observable.subscribe(console.log);
|
||||
```
|
||||
|
||||
```
|
||||
subject.next(2);
|
||||
subject.complete();
|
||||
```
|
||||
@ -574,20 +490,16 @@ subject.complete();
|
||||
// 2
|
||||
```
|
||||
|
||||
除了常规的 subject,RxJS 还提供了三种特殊的版本。
|
||||
除了常规的 `Subject`,RxJS 还提供了三种特殊的版本。
|
||||
|
||||
_AsyncSubject_ 在结束后只发送最后的一个值。
|
||||
`AsyncSubject` 在结束后只发送最后的一个值。
|
||||
|
||||
```
|
||||
const subject = new AsyncSubject();
|
||||
const observable = subject.asObservable();
|
||||
```
|
||||
|
||||
```
|
||||
observable.subscribe(console.log);
|
||||
```
|
||||
|
||||
```
|
||||
subject.next(1);
|
||||
subject.next(2);
|
||||
subject.complete();
|
||||
@ -598,18 +510,14 @@ subject.complete();
|
||||
// 2
|
||||
```
|
||||
|
||||
_BehaviorSubject_ 使得你能够提供一个(默认的)值,如果当前没有其它值发送的话,这个值会被发送给每个订阅者。否则订阅者收到最后一个发送的值。
|
||||
`BehaviorSubject` 使得你能够提供一个(默认的)值,如果当前没有其它值发送的话,这个值会被发送给每个订阅者。否则订阅者收到最后一个发送的值。
|
||||
|
||||
```
|
||||
const subject = new BehaviorSubject(1);
|
||||
const observable = subject.asObservable();
|
||||
```
|
||||
|
||||
```
|
||||
const subscription1 = observable.subscribe(console.log);
|
||||
```
|
||||
|
||||
```
|
||||
subject.next(2);
|
||||
subscription1.unsubscribe();
|
||||
```
|
||||
@ -622,29 +530,21 @@ subscription1.unsubscribe();
|
||||
|
||||
```
|
||||
const subscription2 = observable.subscribe(console.log);
|
||||
```
|
||||
|
||||
```
|
||||
// 输出
|
||||
// 2
|
||||
```
|
||||
|
||||
The _ReplaySubject_ 存储一定数量、或一定时间或所有的发送过的值。所有新的订阅者将会获得所有存储了的值。
|
||||
`ReplaySubject` 存储一定数量、或一定时间或所有的发送过的值。所有新的订阅者将会获得所有存储了的值。
|
||||
|
||||
```
|
||||
const subject = new ReplaySubject();
|
||||
const observable = subject.asObservable();
|
||||
```
|
||||
|
||||
```
|
||||
subject.next(1);
|
||||
```
|
||||
|
||||
```
|
||||
observable.subscribe(console.log);
|
||||
```
|
||||
|
||||
```
|
||||
subject.next(2);
|
||||
subject.complete();
|
||||
```
|
||||
@ -655,11 +555,11 @@ subject.complete();
|
||||
// 2
|
||||
```
|
||||
|
||||
你可以在 [ReactiveX documentation][10](它提供了一些其它的连接) 里面找到更多关于 subjects 的信息。[Ben Lesh][11] 在 [On The Subject Of Subjects][12] 上面提供了一些关于 subjects 的理解,[Nicholas Jamieson][13] 在 [in RxJS: Understanding Subjects][14] 上也提供了一些理解。
|
||||
你可以在 [ReactiveX 文档][10](它提供了一些其它的连接) 里面找到更多关于 `Subject` 的信息。[Ben Lesh][11] 在 [On The Subject Of Subjects][12] 上面提供了一些关于 `Subject` 的理解,[Nicholas Jamieson][13] 在 [in RxJS: Understanding Subjects][14] 上也提供了一些理解。
|
||||
|
||||
#### Observable
|
||||
#### 可观察对象
|
||||
|
||||
你可以简单地用 new 操作符创建一个 observable 对象。通过你传入的函数,你可以控制流,只要有人订阅了或者它接收到一个可以当成 subject 使用的 observer,这个函数就会被调用,比如,调用 next,complet 和 error。
|
||||
你可以简单地用 new 操作符创建一个可观察对象。通过你传入的函数,你可以控制流,只要有人订阅了或者它接收到一个可以当成 `Subject` 使用的观察者,这个函数就会被调用,比如,调用 `next`、`complet` 和 `error`。
|
||||
|
||||
让我们回顾一下列表示例:
|
||||
|
||||
@ -667,16 +567,12 @@ subject.complete();
|
||||
const myList = new List();
|
||||
myList.add(1);
|
||||
myList.add(3);
|
||||
```
|
||||
|
||||
```
|
||||
new Observable(observer => {
|
||||
for (let i = 0; i<list.size; i++) {
|
||||
observer.next(list.get(i));
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
observer.complete();
|
||||
})
|
||||
.subscribe();
|
||||
@ -687,14 +583,12 @@ new Observable(observer => {
|
||||
// 1 3 |
|
||||
```
|
||||
|
||||
这个函数可以返回一个 unsubcribe 函数,当有订阅者取消订阅时这个函数就会被调用。你可以用它来清楚或者执行一些收尾操作。
|
||||
这个函数可以返回一个 `unsubcribe` 函数,当有订阅者取消订阅时这个函数就会被调用。你可以用它来清楚或者执行一些收尾操作。
|
||||
|
||||
```
|
||||
new Observable(observer => {
|
||||
// 流式化
|
||||
```
|
||||
|
||||
```
|
||||
return () => {
|
||||
//clean up
|
||||
};
|
||||
@ -702,20 +596,18 @@ new Observable(observer => {
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
#### 继承 Observable
|
||||
#### 继承可观察对象
|
||||
|
||||
在有可用的操作符前,这是一种实现自定义操作符的方式。RxJS 在内部扩展了 _Observable_。_Subject_ 就是一个例子,另一个是 _publisher_ 操作符。它返回一个 _ConnectableObservable_ 对象,该对象提供额外的方法 _connect_。
|
||||
在有可用的操作符前,这是一种实现自定义操作符的方式。RxJS 在内部扩展了 _可观察对象_ 。`Subject` 就是一个例子,另一个是 `publisher` 操作符。它返回一个 `ConnectableObservable` 对象,该对象提供额外的方法 `connect`。
|
||||
|
||||
#### 实现 Subscribable 接口
|
||||
#### 实现 `Subscribable` 接口
|
||||
|
||||
有时候你已经用一个对象来保存状态,并且能够发送值。如果你实现了 Subscribable 接口,你可以把它转换成一个 observable 对象。Subscribable 接口中只有一个 subscribe 方法。
|
||||
有时候你已经用一个对象来保存状态,并且能够发送值。如果你实现了 `Subscribable` 接口,你可以把它转换成一个可观察对象。`Subscribable` 接口中只有一个 `subscribe` 方法。
|
||||
|
||||
```
|
||||
interface Subscribable<T> { subscribe(observerOrNext?: PartialObserver<T> | ((value: T) => void), error?: (error: any) => void, complete?: () => void): Unsubscribable}
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
### 结合和选择现有的流
|
||||
|
||||
知道怎么创建一个独立的流还不够。有时候你有好几个流但其实只需要一个。有些函数也可作为操作符,所以我不打算在这里深入展开。推荐看看 [Max NgWizard K][16] 所写的一篇 [文章][15],它还包含一些有趣的动画。
|
||||
@ -724,41 +616,34 @@ interface Subscribable<T> { subscribe(observerOrNext?: PartialObserver<T> | ((v
|
||||
|
||||
#### ObservableInput 类型
|
||||
|
||||
期望接收流的操作符和函数通常不单独和 observables 一起工作。相反,他们实际上期望的参数类型是 ObservableInput,定义如下:
|
||||
期望接收流的操作符和函数通常不单独和可观察对象一起工作。相反,它们实际上期望的参数类型是 ObservableInput,定义如下:
|
||||
|
||||
```
|
||||
type ObservableInput<T> = SubscribableOrPromise<T> | ArrayLike<T> | Iterable<T>;
|
||||
```
|
||||
|
||||
这意味着你可以传递一个 promises 或者数组却不需要事先把他们转换成 observables。
|
||||
这意味着你可以传递一个 promises 或者数组却不需要事先把他们转换成可观察对象。
|
||||
|
||||
#### defer
|
||||
|
||||
主要的目的是把一个 observable 对象的创建延迟(defer)到有人想要订阅的时间。在以下情况,这很有用:
|
||||
|
||||
* 创建 observable 对象的开销较大
|
||||
|
||||
* 你想要给每个订阅者新的 observable 对象
|
||||
|
||||
* 你想要在订阅时候选择不同的 observable 对象
|
||||
主要的目的是把一个 observable 对象的创建延迟(`defer`)到有人想要订阅的时间。在以下情况,这很有用:
|
||||
|
||||
* 创建可观察对象的开销较大
|
||||
* 你想要给每个订阅者新的可观察对象
|
||||
* 你想要在订阅时候选择不同的可观察对象
|
||||
* 有些代码必须在订阅之后执行
|
||||
|
||||
最后一点包含了一个并不起眼的用例:Promises(defer 也可以返回一个 promise 对象)。看看这个用到了 fetch API 的例子:
|
||||
最后一点包含了一个并不起眼的用例:Promises(`defer` 也可以返回一个 promise 对象)。看看这个用到了 fetch API 的例子:
|
||||
|
||||
```
|
||||
function getUser(id) {
|
||||
console.log("fetching data");
|
||||
return fetch(`https://server/user/${id}`);
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
const userPromise = getUser(1);
|
||||
console.log("I don't want that request now");
|
||||
```
|
||||
|
||||
```
|
||||
// 其它地方
|
||||
userPromise.then(response => console.log("done");
|
||||
```
|
||||
@ -770,17 +655,13 @@ userPromise.then(response => console.log("done");
|
||||
// done
|
||||
```
|
||||
|
||||
只要流在你订阅的时候执行了,promise 就会立即执行。我们调用 getUser 的瞬间,就发送了一个请求,哪怕我们这个时候不想发送请求。当然,我们可以使用 from 来把一个 promise 对象转换成 observable 对象,但我们传递的 promise 对象已经创建或执行了。defer 让我们能够等到订阅才发送这个请求:
|
||||
只要流在你订阅的时候执行了,promise 就会立即执行。我们调用 `getUser` 的瞬间,就发送了一个请求,哪怕我们这个时候不想发送请求。当然,我们可以使用 `from` 来把一个 promise 对象转换成可观察对象,但我们传递的 promise 对象已经创建或执行了。`defer` 让我们能够等到订阅才发送这个请求:
|
||||
|
||||
```
|
||||
const user$ = defer(() => getUser(1));
|
||||
```
|
||||
|
||||
```
|
||||
console.log("I don't want that request now");
|
||||
```
|
||||
|
||||
```
|
||||
// 其它地方
|
||||
user$.subscribe(response => console.log("done");
|
||||
```
|
||||
@ -794,7 +675,7 @@ user$.subscribe(response => console.log("done");
|
||||
|
||||
#### iif
|
||||
|
||||
_iif 包含了一个关于 _defer_ 的特殊用例:在订阅时选择两个流中的一个:
|
||||
`iif` 包含了一个关于 `defer` 的特殊用例:在订阅时选择两个流中的一个:
|
||||
|
||||
```
|
||||
iif(
|
||||
@ -810,9 +691,9 @@ iif(
|
||||
// AM before noon, PM afterwards
|
||||
```
|
||||
|
||||
引用了文档:
|
||||
引用该文档:
|
||||
|
||||
> 实际上 `[iif][3]` 能够轻松地用 `[defer][4]` 实现,它仅仅是出于方便和可读性的目的。
|
||||
> 实际上 [iif][3] 能够轻松地用 [defer][4] 实现,它仅仅是出于方便和可读性的目的。
|
||||
|
||||
#### onErrorResumeNext
|
||||
|
||||
@ -822,13 +703,9 @@ iif(
|
||||
const stream1$ = of(1, 2).pipe(
|
||||
tap(i => { if(i>1) throw 'error'}) //fail after first element
|
||||
);
|
||||
```
|
||||
|
||||
```
|
||||
const stream2$ = of(3,4);
|
||||
```
|
||||
|
||||
```
|
||||
onErrorResumeNext(stream1$, stream2$)
|
||||
.subscribe(console.log);
|
||||
```
|
||||
@ -848,9 +725,7 @@ onErrorResumeNext(stream1$, stream2$)
|
||||
function handleResponses([user, account]) {
|
||||
// 执行某些任务
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
forkJoin(
|
||||
fetch("https://server/user/1"),
|
||||
fetch("https://server/account/1")
|
||||
@ -860,9 +735,9 @@ forkJoin(
|
||||
|
||||
#### merge / concat
|
||||
|
||||
发送每一个从源 observables 对象中发出的值。
|
||||
发送每一个从可观察对象源中发出的值。
|
||||
|
||||
_merge_ 接收一个参数,让你定义有多少流能被同时订阅。默认是无限制的。设为 1 就意味着监听一个源流,在它结束的时候订阅下一个。由于这是一个常见的场景,RxJS 为你提供了一个显示的函数:_concat_。
|
||||
`merge` 接收一个参数,让你定义有多少流能被同时订阅。默认是无限制的。设为 1 就意味着监听一个源流,在它结束的时候订阅下一个。由于这是一个常见的场景,RxJS 为你提供了一个显示的函数:`concat`。
|
||||
|
||||
```
|
||||
merge(
|
||||
@ -872,31 +747,20 @@ merge(
|
||||
2 //two concurrent streams
|
||||
)
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
```
|
||||
// 只订阅流 1 和流 2
|
||||
```
|
||||
|
||||
```
|
||||
// 输出
|
||||
// Stream 1 -> after 1000ms
|
||||
// Stream 2 -> after 1200ms
|
||||
// Stream 1 -> after 2000ms
|
||||
```
|
||||
|
||||
```
|
||||
// 流 1 结束后,开始订阅流 3
|
||||
```
|
||||
|
||||
```
|
||||
// 输出
|
||||
// Stream 3 -> after 0 ms
|
||||
// Stream 2 -> after 400 ms (2400ms from beginning)
|
||||
// Stream 3 -> after 1000ms
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
merge(
|
||||
interval(1000).pipe(mapTo("Stream 1"), take(2)),
|
||||
@ -908,9 +772,7 @@ concat(
|
||||
interval(1000).pipe(mapTo("Stream 1"), take(2)),
|
||||
interval(1200).pipe(mapTo("Stream 2"), take(2))
|
||||
)
|
||||
```
|
||||
|
||||
```
|
||||
// 输出
|
||||
// Stream 1 -> after 1000ms
|
||||
// Stream 1 -> after 2000ms
|
||||
@ -920,7 +782,7 @@ concat(
|
||||
|
||||
#### zip / combineLatest
|
||||
|
||||
_merge_ 和 _concat_ 一个接一个的发送所有从源流中读到的值,而 zip 和 combineLatest 是把每个流中的一个值结合起来一起发送。_zip_ 结合所有源流中发送的第一个值。如果流的内容相关联,那么这就很有用。
|
||||
`merge` 和 `concat` 一个接一个的发送所有从源流中读到的值,而 `zip` 和 `combineLatest` 是把每个流中的一个值结合起来一起发送。`zip` 结合所有源流中发送的第一个值。如果流的内容相关联,那么这就很有用。
|
||||
|
||||
```
|
||||
zip(
|
||||
@ -935,7 +797,7 @@ zip(
|
||||
// [0, 0] [1, 1] [2, 2] ...
|
||||
```
|
||||
|
||||
_combineLatest_ 与之类似,但结合的是源流中发送的最后一个值。直到所有源流至少发送一个值之后才会触发事件。这之后每次源流发送一个值,它都会把这个值与其他流发送的最后一个值结合起来。
|
||||
`combineLatest` 与之类似,但结合的是源流中发送的最后一个值。直到所有源流至少发送一个值之后才会触发事件。这之后每次源流发送一个值,它都会把这个值与其他流发送的最后一个值结合起来。
|
||||
|
||||
```
|
||||
combineLatest(
|
||||
@ -983,15 +845,13 @@ race(
|
||||
// foo |
|
||||
```
|
||||
|
||||
由于 _of_ 立即产生一个值,因此它是最快的流,然而这个流就被选中了。
|
||||
|
||||
* * *
|
||||
由于 `of` 立即产生一个值,因此它是最快的流,然而这个流就被选中了。
|
||||
|
||||
### 总结
|
||||
|
||||
已经有很多创建 observables 对象的方式了。如果你想要创造响应式的 APIs 或者想用响应式的 API 结合传统 APIs,那么了解这些方法很重要。
|
||||
已经有很多创建可观察对象的方式了。如果你想要创造响应式的 API 或者想用响应式的 API 结合传统 API,那么了解这些方法很重要。
|
||||
|
||||
我已经向你展示了所有可用的方法,但它们其实还有很多内容可以讲。如果你想更加深入地了解,我极力推荐你查阅 [documentation][20] 或者阅读相关文章。
|
||||
我已经向你展示了所有可用的方法,但它们其实还有很多内容可以讲。如果你想更加深入地了解,我极力推荐你查阅 [文档][20] 或者阅读相关文章。
|
||||
|
||||
[RxViz][21] 是另一种值得了解的有意思的方式。你编写 RxJS 代码,产生的流可以用图形或动画进行显示。
|
||||
|
||||
@ -1001,7 +861,7 @@ via: https://blog.angularindepth.com/the-extensive-guide-to-creating-streams-in-
|
||||
|
||||
作者:[Oliver Flaggl][a]
|
||||
译者:[BriFuture](https://github.com/BriFuture)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,85 +1,86 @@
|
||||
How To Set Up PF Firewall on FreeBSD to Protect a Web Server
|
||||
如何在 FreeBSD 上设置 PF 防火墙来保护 Web 服务器
|
||||
======
|
||||
|
||||
I am a new FreeBSD server user and moved from netfilter on Linux. How do I setup a firewall with PF on FreeBSD server to protect a web server with single public IP address and interface?
|
||||
[![How To Set Up a Firewall with PF on FreeBSD to Protect a Web Server][1]][1]
|
||||
|
||||
我是从 Linux 迁移过来的 FreeBSD 新用户,Linux 中使用的是 netfilter 防火墙框架(LCTT 译注:netfilter 是由 Rusty Russell 提出的 Linux 2.4 内核防火墙框架)。那么在 FreeBSD 上,我该如何设置 PF 防火墙,来保护只有一个公共 IP 地址和端口的 web 服务器呢?
|
||||
|
||||
PF is an acronym for packet filter. It was created for OpenBSD but has been ported to FreeBSD and other operating systems. It is a stateful packet filtering engine. This tutorial will show you how to set up a firewall with PF on FreeBSD 10.x and 11.x server to protect your web server.
|
||||
PF 是<ruby>包过滤器<rt>packet filter</rt></ruby>的简称。它是为 OpenBSD 开发的,但是已经被移植到了 FreeBSD 以及其它操作系统上。PF 是一个包状态过滤引擎。在这篇教程中,我将向你展示如何在 FreeBSD 10.x 以及 11.x 中设置 PF 防火墙,从而来保护 web 服务器。
|
||||
|
||||
### 第一步:开启 PF 防火墙
|
||||
|
||||
## Step 1 - Turn on PF firewall
|
||||
你需要把下面这几行内容添加到文件 `/etc/rc.conf` 文件中:
|
||||
|
||||
You need to add the following three lines to /etc/rc.conf file:
|
||||
```
|
||||
# echo 'pf_enable="YES"' >> /etc/rc.conf
|
||||
# echo 'pf_rules="/usr/local/etc/pf.conf"' >> /etc/rc.conf
|
||||
# echo 'pflog_enable="YES"' >> /etc/rc.conf
|
||||
# echo 'pflog_logfile="/var/log/pflog"' >> /etc/rc.conf
|
||||
```
|
||||
Where,
|
||||
|
||||
1. **pf_enable="YES"** - Turn on PF service.
|
||||
2. **pf_rules="/usr/local/etc/pf.conf"** - Read PF rules from this file.
|
||||
3. **pflog_enable="YES"** - Turn on logging support for PF.
|
||||
4. **pflog_logfile="/var/log/pflog"** - File where pflogd should store the logfile i.e. store logs in /var/log/pflog file.
|
||||
在这里:
|
||||
|
||||
1. `pf_enable="YES"` - 开启 PF 服务
|
||||
2. `pf_rules="/usr/local/etc/pf.conf"` - 从文件 `/usr/local/etc/pf.conf` 中读取 PF 规则
|
||||
3. `pflog_enable="YES"` - 为 PF 服务打开日志支持
|
||||
4. `pflog_logfile="/var/log/pflog"` - 存储日志的文件,即日志存于文件 `/var/log/pflog` 中
|
||||
|
||||
### 第二步:在 `/usr/local/etc/pf.conf` 文件中创建防火墙规则
|
||||
|
||||
[![How To Set Up a Firewall with PF on FreeBSD to Protect a Web Server][1]][1]
|
||||
输入下面这个命令打开文件(超级用户模式下):
|
||||
|
||||
## Step 2 - Creating firewall rules in /usr/local/etc/pf.conf
|
||||
|
||||
Type the following command:
|
||||
```
|
||||
# vi /usr/local/etc/pf.conf
|
||||
```
|
||||
Append the following PF rulesets :
|
||||
|
||||
在文件中添加下面这些 PF 规则集:
|
||||
|
||||
```
|
||||
# vim: set ft=pf
|
||||
# /usr/local/etc/pf.conf
|
||||
|
||||
## Set your public interface ##
|
||||
## 设置公共端口 ##
|
||||
ext_if="vtnet0"
|
||||
|
||||
## Set your server public IP address ##
|
||||
## 设置服务器公共 IP 地址 ##
|
||||
ext_if_ip="172.xxx.yyy.zzz"
|
||||
|
||||
## Set and drop these IP ranges on public interface ##
|
||||
## 设置并删除下面这些公共端口上的 IP 范围 ##
|
||||
martians = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, \
|
||||
10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, \
|
||||
0.0.0.0/8, 240.0.0.0/4 }"
|
||||
|
||||
## Set http(80)/https (443) port here ##
|
||||
## 设置 http(80)/https (443) 端口 ##
|
||||
webports = "{http, https}"
|
||||
|
||||
## enable these services ##
|
||||
## 启用下面这些服务 ##
|
||||
int_tcp_services = "{domain, ntp, smtp, www, https, ftp, ssh}"
|
||||
int_udp_services = "{domain, ntp}"
|
||||
|
||||
## Skip loop back interface - Skip all PF processing on interface ##
|
||||
## 跳过回环端口 - 跳过端口上的所有 PF 处理 ##
|
||||
set skip on lo
|
||||
|
||||
## Sets the interface for which PF should gather statistics such as bytes in/out and packets passed/blocked ##
|
||||
## 设置 PF 应该统计的端口信息,如发送/接收字节数,通过/禁止的包的数目 ##
|
||||
set loginterface $ext_if
|
||||
|
||||
## Set default policy ##
|
||||
## 设置默认策略 ##
|
||||
block return in log all
|
||||
block out all
|
||||
|
||||
# Deal with attacks based on incorrect handling of packet fragments
|
||||
# 基于 IP 分片的错误处理来防御攻击
|
||||
scrub in all
|
||||
|
||||
# Drop all Non-Routable Addresses
|
||||
# 删除所有不可达路由地址
|
||||
block drop in quick on $ext_if from $martians to any
|
||||
block drop out quick on $ext_if from any to $martians
|
||||
|
||||
## Blocking spoofed packets
|
||||
## 禁止欺骗包
|
||||
antispoof quick for $ext_if
|
||||
|
||||
# Open SSH port which is listening on port 22 from VPN 139.xx.yy.zz Ip only
|
||||
# I do not allow or accept ssh traffic from ALL for security reasons
|
||||
# 打开 SSH 端口,SSH 服务仅从 VPN IP 139.xx.yy.zz 监听 22 号端口
|
||||
# 出于安全原因,我不允许/接收 SSH 流量
|
||||
pass in quick on $ext_if inet proto tcp from 139.xxx.yyy.zzz to $ext_if_ip port = ssh flags S/SA keep state label "USER_RULE: Allow SSH from 139.xxx.yyy.zzz"
|
||||
## Use the following rule to enable ssh for ALL users from any IP address #
|
||||
## 使用下面这些规则来为所有来自任何 IP 地址的用户开启 SSH 服务 #
|
||||
## pass in inet proto tcp to $ext_if port ssh
|
||||
### [ OR ] ###
|
||||
## pass in inet proto tcp to $ext_if port 22
|
||||
@ -90,44 +91,67 @@ pass inet proto icmp icmp-type echoreq
|
||||
# All access to our Nginx/Apache/Lighttpd Webserver ports
|
||||
pass proto tcp from any to $ext_if port $webports
|
||||
|
||||
# Allow essential outgoing traffic
|
||||
# 允许重要的发送流量
|
||||
pass out quick on $ext_if proto tcp to any port $int_tcp_services
|
||||
pass out quick on $ext_if proto udp to any port $int_udp_services
|
||||
|
||||
# Add custom rules below
|
||||
# 在下面添加自定义规则
|
||||
```
|
||||
|
||||
Save and close the file. PR [welcome here to improve rulesets][2]. To check for syntax error, run:
|
||||
`# service pf check`
|
||||
OR
|
||||
`/etc/rc.d/pf check`
|
||||
OR
|
||||
`# pfctl -n -f /usr/local/etc/pf.conf `
|
||||
保存并关闭文件。欢迎来参考我的[规则集][2]。如果要检查语法错误,可以运行:
|
||||
|
||||
## Step 3 - Start PF firewall
|
||||
```
|
||||
# service pf check
|
||||
```
|
||||
|
||||
The commands are as follows. Be careful you might be disconnected from your server over ssh based session:
|
||||
或
|
||||
|
||||
### Start PF
|
||||
```
|
||||
/etc/rc.d/pf check
|
||||
```
|
||||
|
||||
`# service pf start`
|
||||
或
|
||||
|
||||
### Stop PF
|
||||
```
|
||||
# pfctl -n -f /usr/local/etc/pf.conf
|
||||
```
|
||||
|
||||
`# service pf stop`
|
||||
### 第三步:开始运行 PF 防火墙
|
||||
|
||||
### Check PF for syntax error
|
||||
命令如下。请小心,如果是基于 SSH 的会话,你可能会和服务器断开连接。
|
||||
|
||||
`# service pf check`
|
||||
#### 开启 PF 防火墙:
|
||||
|
||||
### Restart PF
|
||||
```
|
||||
# service pf start
|
||||
```
|
||||
|
||||
`# service pf restart`
|
||||
#### 停用 PF 防火墙:
|
||||
|
||||
### See PF status
|
||||
```
|
||||
# service pf stop
|
||||
```
|
||||
|
||||
#### 检查语法错误:
|
||||
|
||||
```
|
||||
# service pf check
|
||||
```
|
||||
|
||||
#### 重启服务:
|
||||
|
||||
```
|
||||
# service pf restart
|
||||
```
|
||||
|
||||
#### 查看 PF 状态:
|
||||
|
||||
```
|
||||
# service pf status
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
`# service pf status`
|
||||
Sample outputs:
|
||||
```
|
||||
Status: Enabled for 0 days 00:02:18 Debug: Urgent
|
||||
|
||||
@ -165,24 +189,28 @@ Counters
|
||||
map-failed 0 0.0/s
|
||||
```
|
||||
|
||||
#### 开启/关闭/重启 pflog 服务的命令
|
||||
|
||||
### Command to start/stop/restart pflog service
|
||||
输入下面这些命令:
|
||||
|
||||
Type the following commands:
|
||||
```
|
||||
# service pflog start
|
||||
# service pflog stop
|
||||
# service pflog restart
|
||||
```
|
||||
|
||||
## Step 4 - A quick introduction to pfctl command
|
||||
### 第四步:`pfctl` 命令的简单介绍
|
||||
|
||||
You need to use the pfctl command to see PF ruleset and parameter configuration including status information from the packet filter. Let us see all common commands:
|
||||
你需要使用 `pfctl` 命令来查看 PF 规则集和参数配置,包括来自<ruby>包过滤器<rt>packet filter</rt></ruby>的状态信息。让我们来看一下所有常见命令:
|
||||
|
||||
### Show PF rules information
|
||||
#### 显示 PF 规则信息
|
||||
|
||||
```
|
||||
# pfctl -s rules
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
`# pfctl -s rules`
|
||||
Sample outputs:
|
||||
```
|
||||
block return in log all
|
||||
block drop out all
|
||||
@ -201,15 +229,19 @@ pass out quick on vtnet0 proto udp from any to any port = domain keep state
|
||||
pass out quick on vtnet0 proto udp from any to any port = ntp keep state
|
||||
```
|
||||
|
||||
#### Show verbose output for each rule
|
||||
#### 显示每条规则的详细内容
|
||||
|
||||
`# pfctl -v -s rules`
|
||||
```
|
||||
# pfctl -v -s rules
|
||||
```
|
||||
|
||||
#### Add rule numbers with verbose output for each rule
|
||||
在每条规则的详细输出中添加规则编号:
|
||||
|
||||
`# pfctl -vvsr show`
|
||||
```
|
||||
# pfctl -vvsr show
|
||||
```
|
||||
|
||||
#### Show state
|
||||
#### 显示状态信息
|
||||
|
||||
```
|
||||
# pfctl -s state
|
||||
@ -217,18 +249,26 @@ pass out quick on vtnet0 proto udp from any to any port = ntp keep state
|
||||
# pfctl -s state | grep 'something'
|
||||
```
|
||||
|
||||
### How to disable PF from the CLI
|
||||
#### 如何在命令行中禁止 PF 服务
|
||||
|
||||
`# pfctl -d `
|
||||
```
|
||||
# pfctl -d
|
||||
```
|
||||
|
||||
### How to enable PF from the CLI
|
||||
#### 如何在命令行中启用 PF 服务
|
||||
|
||||
`# pfctl -e `
|
||||
```
|
||||
# pfctl -e
|
||||
```
|
||||
|
||||
### How to flush ALL PF rules/nat/tables from the CLI
|
||||
#### 如何在命令行中刷新 PF 规则/NAT/路由表
|
||||
|
||||
```
|
||||
# pfctl -F all
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
`# pfctl -F all`
|
||||
Sample outputs:
|
||||
```
|
||||
rules cleared
|
||||
nat cleared
|
||||
@ -239,27 +279,40 @@ pf: statistics cleared
|
||||
pf: interface flags reset
|
||||
```
|
||||
|
||||
#### How to flush only the PF RULES from the CLI
|
||||
#### 如何在命令行中仅刷新 PF 规则
|
||||
|
||||
`# pfctl -F rules `
|
||||
```
|
||||
# pfctl -F rules
|
||||
```
|
||||
|
||||
#### How to flush only queue's from the CLI
|
||||
#### 如何在命令行中仅刷新队列
|
||||
|
||||
`# pfctl -F queue `
|
||||
```
|
||||
# pfctl -F queue
|
||||
```
|
||||
|
||||
#### How to flush all stats that are not part of any rule from the CLI
|
||||
#### 如何在命令行中刷新统计信息(它不是任何规则的一部分)
|
||||
|
||||
`# pfctl -F info`
|
||||
```
|
||||
# pfctl -F info
|
||||
```
|
||||
|
||||
#### How to clear all counters from the CLI
|
||||
#### 如何在命令行中清除所有计数器
|
||||
|
||||
`# pfctl -z clear `
|
||||
```
|
||||
# pfctl -z clear
|
||||
```
|
||||
|
||||
## Step 5 - See PF log
|
||||
### 第五步:查看 PF 日志
|
||||
|
||||
PF 日志是二进制格式的。使用下面这一命令来查看:
|
||||
|
||||
```
|
||||
# tcpdump -n -e -ttt -r /var/log/pflog
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
PF logs are in binary format. To see them type:
|
||||
`# tcpdump -n -e -ttt -r /var/log/pflog`
|
||||
Sample outputs:
|
||||
```
|
||||
Aug 29 15:41:11.757829 rule 0/(match) block in on vio0: 86.47.225.151.55806 > 45.FOO.BAR.IP.23: S 757158343:757158343(0) win 52206 [tos 0x28]
|
||||
Aug 29 15:41:44.193309 rule 0/(match) block in on vio0: 5.196.83.88.25461 > 45.FOO.BAR.IP.26941: S 2224505792:2224505792(0) ack 4252565505 win 17520 (DF) [tos 0x24]
|
||||
@ -295,31 +348,36 @@ Aug 29 15:55:07.001743 rule 0/(match) block in on vio0: 190.83.174.214.58863 > 4
|
||||
Aug 29 15:55:51.269549 rule 0/(match) block in on vio0: 142.217.201.69.26112 > 45.FOO.BAR.IP.22: S 757158343:757158343(0) win 22840 <mss 1460>
|
||||
Aug 29 15:58:41.346028 rule 0/(match) block in on vio0: 169.1.29.111.29765 > 45.FOO.BAR.IP.23: S 757158343:757158343(0) win 28509
|
||||
Aug 29 15:59:11.575927 rule 0/(match) block in on vio0: 187.160.235.162.32427 > 45.FOO.BAR.IP.5358: S 22445:22445(0) win 14600 [tos 0x28]
|
||||
Aug 29 15:59:37.826598 rule 0/(match) block in on vio0: 94.74.81.97.54656 > 45.FOO.BAR.IP.3128: S 2720157526:2720157526(0) win 1024 [tos 0x28]
|
||||
Aug 29 15:59:37.826598 rule 0/(match) block in on vio0: 94.74.81.97.54656 > 45.FOO.BAR.IP.3128: S 2720157526:2720157526(0) win 1024 [tos 0x28]stateful
|
||||
Aug 29 15:59:37.991171 rule 0/(match) block in on vio0: 94.74.81.97.54656 > 45.FOO.BAR.IP.3128: R 2720157527:2720157527(0) win 1200 [tos 0x28]
|
||||
Aug 29 16:01:36.990050 rule 0/(match) block in on vio0: 182.18.8.28.23299 > 45.FOO.BAR.IP.445: S 1510146048:1510146048(0) win 16384
|
||||
```
|
||||
|
||||
To see live log run:
|
||||
`# tcpdump -n -e -ttt -i pflog0`
|
||||
For more info the [PF FAQ][3], [FreeBSD HANDBOOK][4] and the following man pages:
|
||||
如果要查看实时日志,可以运行:
|
||||
|
||||
```
|
||||
# tcpdump -n -e -ttt -i pflog0
|
||||
```
|
||||
|
||||
如果你想了解更多信息,可以访问 [PF FAQ][3] 和 [FreeBSD HANDBOOK][4] 以及下面这些 man 页面:
|
||||
|
||||
```
|
||||
# man tcpdump
|
||||
# man pfctl
|
||||
# man pf
|
||||
```
|
||||
|
||||
## about the author:
|
||||
### 关于作者
|
||||
|
||||
The author is the creator of nixCraft and a seasoned sysadmin and a trainer for the Linux operating system/Unix shell scripting. He has worked with global clients and in various industries, including IT, education, defense and space research, and the nonprofit sector. Follow him on [Twitter][5], [Facebook][6], [Google+][7].
|
||||
我是 nixCraft 的创立者,一个经验丰富的系统管理员,同时也是一位 Linux 操作系统/Unix shell 脚本培训师。我在不同的行业与全球客户工作过,包括 IT、教育、国防和空间研究、以及非营利组织。你可以在 [Twitter][5]、[Facebook][6] 或 [Google+][7] 上面关注我。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.cyberciti.biz/faq/how-to-set-up-a-firewall-with-pf-on-freebsd-to-protect-a-web-server/
|
||||
|
||||
作者:[Vivek Gite][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
译者:[ucasFL](https://github.com/ucasFL)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,144 @@
|
||||
Trash-Cli:Linux 上的命令行回收站工具
|
||||
======
|
||||
|
||||
相信每个人都对<ruby>回收站<rt>trashcan</rt></ruby>很熟悉,因为无论是对 Linux 用户,还是 Windows 用户,或者 Mac 用户来说,它都很常见。当你删除一个文件或目录的时候,该文件或目录会被移动到回收站中。
|
||||
|
||||
需要注意的是,当把文件移动到回收站以后,文件系统空间并没有被释放,除非把回收站清空。
|
||||
|
||||
如果不想永久删除文件的话(清空回收站),可以利用回收站临时存储被删除了的文件,从而在必要的时候能够帮助我们恢复删除了的文件。
|
||||
|
||||
但是,如果在命令行使用 `rm` 命令进行删除操作,那么你是不可能在回收站中找到任何被删除了的文件或目录的。所以,在执行 `rm` 命令前请一定要三思。如果你犯了错误(执行了 `rm` 命令),那么文件就被永久删除了,无法再恢复回来,因为存储在磁盘上的元数据已经不在了。
|
||||
|
||||
根据 [freedesktop.org 规范][1],<ruby>垃圾<rt>trash</rt></ruby>是由桌面管理器比如 GNOME、KDE 和 XFCE 等提供的一个特性。当通过文件管理器删除一个文件或目录的时候,该文件或目录将会成为<ruby>垃圾<rt>trash</rt></ruby>,然后被移动到回收站中,回收站对应的目录是 `$HOME/.local/share/Trash` 。
|
||||
|
||||
回收站目录包含两个子目录:`files` 和 `info` 。`files` 目录存储实际被删除了的文件和目录,`info` 目录包含被删除了的文件和目录的信息,比如文件路径、删除日期和时间,每个文件单独存储。
|
||||
|
||||
你可能会问,既然已经有了<ruby>图形用户界面<rt>GUI</rt></ruby>的回收站,为什么还需要命令行工具呢?因为对于大多数使用 *NIX 系统的家伙(包括我)来说,即使使用的是基于图形用户界面的系统,也更喜欢使用命令行而不是图形用户界面。所以,如果有人在寻找一个命令行回收站工具,那么这儿有一个不错的选择。
|
||||
|
||||
### Trash-Cli 是什么
|
||||
|
||||
[trash-cli][2] 是一个命令行回收站工具,并且符合 FreeDesktop.org 的<ruby>垃圾<rt>trash</rt></ruby>规范。它能够存储每一个垃圾文件的名字、原始路径、删除日期和权限。
|
||||
|
||||
### 如何在 Linux 上安装 Trash-Cli
|
||||
|
||||
绝大多数的 Linux 发行版官方仓库都提供了 Trash-Cli 的安装包,所以你可以运行下面这些命令来安装。
|
||||
|
||||
对于 Debian/Ubuntu 用户,使用 [apt-get][3] 或 [apt][4] 命令来安装 Trash-Cli:
|
||||
|
||||
```
|
||||
$ sudo apt install trash-cli
|
||||
```
|
||||
|
||||
对于 RHEL/CentOS 用户,使用 [yum][5] 命令来安装 Trash-Cli:
|
||||
|
||||
```
|
||||
$ sudo yum install trash-cli
|
||||
```
|
||||
|
||||
对于 Fedora 用户,使用 [dnf][6] 命令来安装 Trash-Cli:
|
||||
|
||||
```
|
||||
$ sudo dnf install trash-cli
|
||||
```
|
||||
|
||||
对于 Arch Linux 用户,使用 [pacman][7] 命令来安装 Trash-Cli:
|
||||
|
||||
```
|
||||
$ sudo pacman -S trash-cli
|
||||
```
|
||||
|
||||
对于 openSUSE 用户,使用 [zypper][8] 命令来安装 Trash-Cli:
|
||||
|
||||
```
|
||||
$ sudo zypper in trash-cli
|
||||
```
|
||||
|
||||
如果你的发行版中没有提供 Trash-Cli 的安装包,那么你也可以使用 `pip` 来安装。为了能够安装 python 包,你的系统中应该会有 `pip` 包管理器。
|
||||
|
||||
```
|
||||
$ sudo pip install trash-cli
|
||||
Collecting trash-cli
|
||||
Downloading trash-cli-0.17.1.14.tar.gz
|
||||
Installing collected packages: trash-cli
|
||||
Running setup.py bdist_wheel for trash-cli ... done
|
||||
Successfully installed trash-cli-0.17.1.14
|
||||
```
|
||||
|
||||
### 如何使用 Trash-Cli
|
||||
|
||||
Trash-Cli 的使用不难,因为它提供了一个很简单的语法。Trash-Cli 提供了下面这些命令:
|
||||
|
||||
* `trash-put`: 删除文件和目录(仅放入回收站中)
|
||||
* `trash-list` :列出被删除了的文件和目录
|
||||
* `trash-restore`:从回收站中恢复文件或目录 trash.
|
||||
* `trash-rm`:删除回收站中的文件
|
||||
* `trash-empty`:清空回收站
|
||||
|
||||
下面,让我们通过一些例子来试验一下。
|
||||
|
||||
1) 删除文件和目录:在这个例子中,我们通过运行下面这个命令,将 `2g.txt` 这一文件和 `magi` 这一文件夹移动到回收站中。
|
||||
|
||||
```
|
||||
$ trash-put 2g.txt magi
|
||||
```
|
||||
|
||||
和你在文件管理器中看到的一样。
|
||||
|
||||
2) 列出被删除了的文件和目录:为了查看被删除了的文件和目录,你需要运行下面这个命令。之后,你可以在输出中看到被删除文件和目录的详细信息,比如名字、删除日期和时间,以及文件路径。
|
||||
|
||||
```
|
||||
$ trash-list
|
||||
2017-10-01 01:40:50 /home/magi/magi/2g.txt
|
||||
2017-10-01 01:40:50 /home/magi/magi/magi
|
||||
```
|
||||
|
||||
3) 从回收站中恢复文件或目录:任何时候,你都可以通过运行下面这个命令来恢复文件和目录。它将会询问你来选择你想要恢复的文件或目录。在这个例子中,我打算恢复 `2g.txt` 文件,所以我的选择是 `0` 。
|
||||
|
||||
```
|
||||
$ trash-restore
|
||||
0 2017-10-01 01:40:50 /home/magi/magi/2g.txt
|
||||
1 2017-10-01 01:40:50 /home/magi/magi/magi
|
||||
What file to restore [0..1]: 0
|
||||
```
|
||||
|
||||
4) 从回收站中删除文件:如果你想删除回收站中的特定文件,那么可以运行下面这个命令。在这个例子中,我将删除 `magi` 目录。
|
||||
|
||||
```
|
||||
$ trash-rm magi
|
||||
```
|
||||
|
||||
5)清空回收站:如果你想删除回收站中的所有文件和目录,可以运行下面这个命令。
|
||||
|
||||
```
|
||||
$ trash-empty
|
||||
```
|
||||
|
||||
6)删除超过 X 天的垃圾文件:或者,你可以通过运行下面这个命令来删除回收站中超过 X 天的文件。在这个例子中,我将删除回收站中超过 `10` 天的项目。
|
||||
|
||||
```
|
||||
$ trash-empty 10
|
||||
```
|
||||
|
||||
Trash-Cli 可以工作的很好,但是如果你想尝试它的一些替代品,那么你也可以试一试 [gvfs-trash][9] 和 [autotrash][10] 。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/trash-cli-command-line-trashcan-linux-system/
|
||||
|
||||
作者:[2daygeek][a]
|
||||
译者:[ucasFL](https://github.com/ucasFL)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.2daygeek.com/author/2daygeek/
|
||||
[1]:https://freedesktop.org/wiki/Specifications/trash-spec/
|
||||
[2]:https://github.com/andreafrancia/trash-cli
|
||||
[3]:https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[4]:https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[5]:https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
||||
[6]:https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[7]:https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/
|
||||
[8]:https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
||||
[9]:http://manpages.ubuntu.com/manpages/trusty/man1/gvfs-trash.1.html
|
||||
[10]:https://github.com/bneijt/autotrash
|
@ -1,36 +1,30 @@
|
||||
运营一个 Kubernetes 网络
|
||||
============================================================
|
||||
Kubernetes 网络运维
|
||||
======
|
||||
|
||||
最近我一直在研究 Kubernetes 网络。我注意到一件事情就是,虽然关于如何设置 Kubernetes 网络的文章很多,也写得很不错,但是却没有看到关于如何去运营 Kubernetes 网络的文章、以及如何完全确保它不会给你造成生产事故。
|
||||
最近我一直在研究 Kubernetes 网络。我注意到一件事情就是,虽然关于如何设置 Kubernetes 网络的文章很多,也写得很不错,但是却没有看到关于如何去运维 Kubernetes 网络的文章、以及如何完全确保它不会给你造成生产事故。
|
||||
|
||||
在本文中,我将尽力让你相信三件事情(我觉得这些都很合理 :)):
|
||||
|
||||
* 避免生产系统网络中断非常重要
|
||||
|
||||
* 运营联网软件是很难的
|
||||
|
||||
* 运维联网软件是很难的
|
||||
* 有关你的网络基础设施的重要变化值得深思熟虑,以及这种变化对可靠性的影响。虽然非常“牛x”的谷歌人常说“这是我们在谷歌正在用的”(谷歌工程师在 Kubernetes 上正做着很重大的工作!但是我认为重要的仍然是研究架构,并确保它对你的组织有意义)。
|
||||
|
||||
我肯定不是 Kubernetes 网络方面的专家,但是我在配置 Kubernetes 网络时遇到了一些问题,并且比以前更加了解 Kubernetes 网络了。
|
||||
|
||||
### 运营联网软件是很难的
|
||||
### 运维联网软件是很难的
|
||||
|
||||
在这里,我并不讨论有关运营物理网络的话题(对于它我不懂),而是讨论关于如何让像 DNS 服务、负载均衡以及代理这样的软件正常工作方面的内容。
|
||||
在这里,我并不讨论有关运维物理网络的话题(对于它我不懂),而是讨论关于如何让像 DNS 服务、负载均衡以及代理这样的软件正常工作方面的内容。
|
||||
|
||||
我在一个负责很多网络基础设施的团队工作过一年时间,并且因此学到了一些运营网络基础设施的知识!(显然我还有很多的知识需要继续学习)在我们开始之前有三个整体看法:
|
||||
|
||||
* 联网软件经常重度依赖 Linux 内核。因此除了正确配置软件之外,你还需要确保许多不同的系统控制(sysctl)配置正确,而一个错误配置的系统控制就很容易让你处于“一切都很好”和“到处都出问题”的差别中。
|
||||
我在一个负责很多网络基础设施的团队工作过一年时间,并且因此学到了一些运维网络基础设施的知识!(显然我还有很多的知识需要继续学习)在我们开始之前有三个整体看法:
|
||||
|
||||
* 联网软件经常重度依赖 Linux 内核。因此除了正确配置软件之外,你还需要确保许多不同的系统控制(`sysctl`)配置正确,而一个错误配置的系统控制就很容易让你处于“一切都很好”和“到处都出问题”的差别中。
|
||||
* 联网需求会随时间而发生变化(比如,你的 DNS 查询或许比上一年多了五倍!或者你的 DNS 服务器突然开始返回 TCP 协议的 DNS 响应而不是 UDP 的,它们是完全不同的内核负载!)。这意味着之前正常工作的软件突然开始出现问题。
|
||||
|
||||
* 修复一个生产网络的问题,你必须有足够的经验。(例如,看这篇 [由 Sophie Haskins 写的关于 kube-dns 问题调试的文章][1])我在网络调试方面比以前进步多了,但那也是我花费了大量时间研究 Linux 网络知识之后的事了。
|
||||
|
||||
我距离成为一名网络运营专家还差得很远,但是我认为以下几点很重要:
|
||||
我距离成为一名网络运维专家还差得很远,但是我认为以下几点很重要:
|
||||
|
||||
1. 对生产网络的基础设施做重要的更改是很难得的(因为它会产生巨大的混乱)
|
||||
|
||||
2. 当你对网络基础设施做重大更改时,真的应该仔细考虑如果新网络基础设施失败该如何处理
|
||||
|
||||
3. 是否有很多人都能理解你的网络配置
|
||||
|
||||
切换到 Kubernetes 显然是个非常大的更改!因此,我们来讨论一下可能会导致错误的地方!
|
||||
@ -39,86 +33,72 @@
|
||||
|
||||
在本文中我们将要讨论的 Kubernetes 网络组件有:
|
||||
|
||||
* 网络覆盖后端(像 flannel/calico/weave 网络/romana)
|
||||
|
||||
* <ruby>覆盖网络<rt>overlay network</rt></ruby>的后端(像 flannel/calico/weave 网络/romana)
|
||||
* `kube-dns`
|
||||
|
||||
* `kube-proxy`
|
||||
|
||||
* 入站控制器 / 负载均衡器
|
||||
|
||||
* `kubelet`
|
||||
|
||||
如果你打算配置 HTTP 服务,或许这些你都会用到。这些组件中的大部分我都不会用到,但是我尽可能去理解它们,因此,本文将涉及它们有关的内容。
|
||||
|
||||
### 最简化的方式:为所有容器使用宿主机网络
|
||||
|
||||
我们从你能做到的最简单的东西开始。这并不能让你在 Kubernetes 中运行 HTTP 服务。我认为它是非常安全的,因为在这里面可以让你动的东西很少。
|
||||
让我们从你能做到的最简单的东西开始。这并不能让你在 Kubernetes 中运行 HTTP 服务。我认为它是非常安全的,因为在这里面可以让你动的东西很少。
|
||||
|
||||
如果你为所有容器使用宿主机网络,我认为需要你去做的全部事情仅有:
|
||||
|
||||
1. 配置 kubelet,以便于容器内部正确配置 DNS
|
||||
|
||||
2. 没了,就这些!
|
||||
|
||||
如果你为每个 Pod 直接使用宿主机网络,那就不需要 kube-dns 或者 kube-proxy 了。你都不需要一个作为基础的覆盖网络。
|
||||
如果你为每个 pod 直接使用宿主机网络,那就不需要 kube-dns 或者 kube-proxy 了。你都不需要一个作为基础的覆盖网络。
|
||||
|
||||
这种配置方式中,你的 pod 们都可以连接到外部网络(同样的方式,你的宿主机上的任何进程都可以与外部网络对话),但外部网络不能连接到你的 pod 们。
|
||||
|
||||
这并不是最重要的(我认为大多数人想在 Kubernetes 中运行 HTTP 服务并与这些服务进行真实的通讯),但我认为有趣的是,从某种程度上来说,网络的复杂性并不是绝对需要的,并且有时候你不用这么复杂的网络就可以实现你的需要。如果可以的话,尽可能地避免让网络过于复杂。
|
||||
|
||||
### 运营一个覆盖网络
|
||||
### 运维一个覆盖网络
|
||||
|
||||
我们将要讨论的第一个网络组件是有关覆盖网络的。Kubernetes 假设每个 pod 都有一个 IP 地址,这样你就可以与那个 pod 中的服务进行通讯了。我在说到“覆盖网络”这个词时,指的就是这个意思(“让你通过它的 IP 地址指向到 pod 的系统)。
|
||||
|
||||
所有其它的 Kubernetes 网络的东西都依赖正确工作的覆盖网络。更多关于它的内容,你可以读 [这里的 kubernetes 网络模型][10]。
|
||||
|
||||
Kelsey Hightower 在 [kubernetes the hard way][11] 中描述的方式看起来似乎很好,但是,事实上它的作法在超过 50 个节点的 AWS 上是行不通的,因此,我不打算讨论它了。
|
||||
Kelsey Hightower 在 [kubernetes 艰难之路][11] 中描述的方式看起来似乎很好,但是,事实上它的作法在超过 50 个节点的 AWS 上是行不通的,因此,我不打算讨论它了。
|
||||
|
||||
有许多覆盖网络后端(calico、flannel、weaveworks、romana)并且规划非常混乱。就我的观点来看,我认为一个覆盖网络有 2 个职责:
|
||||
|
||||
1. 确保你的 pod 能够发送网络请求到外部的集群
|
||||
|
||||
2. 保持一个到子网络的稳定的节点映射,并且保持集群中每个节点都可以使用那个映射得以更新。当添加和删除节点时,能够做出正确的反应。
|
||||
|
||||
Okay! 因此!你的覆盖网络可能会出现的问题是什么呢?
|
||||
|
||||
* 覆盖网络负责设置 iptables 规则(最基本的是 `iptables -A -t nat POSTROUTING -s $SUBNET -j MASQUERADE`),以确保那个容器能够向 Kubernetes 之外发出网络请求。如果在这个规则上有错误,你的容器就不能连接到外部网络。这并不很难(它只是几条 iptables 规则而已),但是它非常重要。我发起了一个 [pull request][2],因为我想确保它有很好的弹性。
|
||||
|
||||
* 添加或者删除节点时可能会有错误。我们使用 `flannel hostgw` 后端,我们开始使用它的时候,节点删除 [尚未开始工作][3]。
|
||||
|
||||
* 覆盖网络负责设置 iptables 规则(最基本的是 `iptables -A -t nat POSTROUTING -s $SUBNET -j MASQUERADE`),以确保那个容器能够向 Kubernetes 之外发出网络请求。如果在这个规则上有错误,你的容器就不能连接到外部网络。这并不很难(它只是几条 iptables 规则而已),但是它非常重要。我发起了一个 [拉取请求][2],因为我想确保它有很好的弹性。
|
||||
* 添加或者删除节点时可能会有错误。我们使用 `flannel hostgw` 后端,我们开始使用它的时候,节点删除功能 [尚未开始工作][3]。
|
||||
* 你的覆盖网络或许依赖一个分布式数据库(etcd)。如果那个数据库发生什么问题,这将导致覆盖网络发生问题。例如,[https://github.com/coreos/flannel/issues/610][4] 上说,如果在你的 `flannel etcd` 集群上丢失了数据,最后的结果将是在容器中网络连接会丢失。(现在这个问题已经被修复了)
|
||||
|
||||
* 你升级 Docker 以及其它东西导致的崩溃
|
||||
|
||||
* 还有更多的其它的可能性!
|
||||
|
||||
我在这里主要讨论的是过去发生在 Flannel 中的问题,但是我并不是要承诺不去使用 Flannel —— 事实上我很喜欢 Flannel,因为我觉得它很简单(比如,类似 [vxlan 在后端这一块的部分][12] 只有 500 行代码),并且我觉得对我来说,通过代码来找出问题的根源成为了可能。并且很显然,它在不断地改进。他们在审查 `pull requests` 方面做的很好。
|
||||
我在这里主要讨论的是过去发生在 Flannel 中的问题,但是我并不是要承诺不去使用 Flannel —— 事实上我很喜欢 Flannel,因为我觉得它很简单(比如,类似 [vxlan 在后端这一块的部分][12] 只有 500 行代码),对我来说,通过代码来找出问题的根源成为了可能。并且很显然,它在不断地改进。他们在审查拉取请求方面做的很好。
|
||||
|
||||
到目前为止,我运营覆盖网络的方法是:
|
||||
到目前为止,我运维覆盖网络的方法是:
|
||||
|
||||
* 学习它的工作原理的详细内容以及如何去调试它(比如,Flannel 用于创建路由的 hostgw 网络后端,因此,你只需要使用 `sudo ip route list` 命令去查看它是否正确即可)
|
||||
|
||||
* 如果需要的话,维护一个内部构建版本,这样打补丁比较容易
|
||||
|
||||
* 有问题时,向上游贡献补丁
|
||||
|
||||
我认为去遍历所有已合并的 PR 以及过去已修复的 bug 清单真的是非常有帮助的 —— 这需要花费一些时间,但这是得到一个其它人遇到的各种问题的清单的好方法。
|
||||
我认为去遍历所有已合并的拉取请求以及过去已修复的 bug 清单真的是非常有帮助的 —— 这需要花费一些时间,但这是得到一个其它人遇到的各种问题的清单的好方法。
|
||||
|
||||
对其他人来说,他们的覆盖网络可能工作的很好,但是我并不能从中得到任何经验,并且我也曾听说过其他人报告类似的问题。如果你有一个类似配置的覆盖网络:a) 在 AWS 上并且 b) 在多于 50-100 节点上运行,我想知道你运营这样的一个网络有多大的把握。
|
||||
对其他人来说,他们的覆盖网络可能工作的很好,但是我并不能从中得到任何经验,并且我也曾听说过其他人报告类似的问题。如果你有一个类似配置的覆盖网络:a) 在 AWS 上并且 b) 在多于 50-100 节点上运行,我想知道你运维这样的一个网络有多大的把握。
|
||||
|
||||
### 运营 kube-proxy 和 kube-dns?
|
||||
### 运维 kube-proxy 和 kube-dns?
|
||||
|
||||
现在,我有一些关于运营覆盖网络的想法,我们来讨论一下。
|
||||
现在,我有一些关于运维覆盖网络的想法,我们来讨论一下。
|
||||
|
||||
这个标题的最后面有一个问号,那是因为我并没有真的去运营过。在这里我还有更多的问题要问答。
|
||||
这个标题的最后面有一个问号,那是因为我并没有真的去运维过。在这里我还有更多的问题要问答。
|
||||
|
||||
这里的 Kubernetes 服务是如何工作的!一个服务是一群 pod 们,它们中的每个都有自己的 IP 地址(像 10.1.0.3、10.2.3.5、10.3.5.6 这样)
|
||||
|
||||
1. 每个 Kubernetes 服务有一个 IP 地址(像 10.23.1.2 这样)
|
||||
|
||||
2. `kube-dns` 去解析 Kubernetes 服务 DNS 名字为 IP 地址(因此,my-svc.my-namespace.svc.cluster.local 可能映射到 10.23.1.2 上)
|
||||
|
||||
3. `kube-proxy` 配置 `iptables` 规则是为了在它们之间随机进行均衡负载。Kube-proxy 也有一个用户空间的轮询负载均衡器,但是在我的印象中,他们并不推荐使用它。
|
||||
|
||||
因此,当你发出一个请求到 `my-svc.my-namespace.svc.cluster.local` 时,它将解析为 10.23.1.2,然后,在你本地主机上的 `iptables` 规则(由 kube-proxy 生成)将随机重定向到 10.1.0.3 或者 10.2.3.5 或者 10.3.5.6 中的一个上。
|
||||
@ -126,9 +106,7 @@ Okay! 因此!你的覆盖网络可能会出现的问题是什么呢?
|
||||
在这个过程中我能想像出的可能出问题的地方:
|
||||
|
||||
* `kube-dns` 配置错误
|
||||
|
||||
* `kube-proxy` 挂了,以致于你的 `iptables` 规则没有得以更新
|
||||
|
||||
* 维护大量的 `iptables` 规则相关的一些问题
|
||||
|
||||
我们来讨论一下 `iptables` 规则,因为创建大量的 `iptables` 规则是我以前从没有听过的事情!
|
||||
@ -141,7 +119,6 @@ kube-proxy 像如下这样为每个目标主机创建一个 `iptables` 规则:
|
||||
-A KUBE-SVC-LI77LBOOMGYET5US -m comment --comment "default/showreadiness:showreadiness" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-RKIFTWKKG3OHTTMI
|
||||
-A KUBE-SVC-LI77LBOOMGYET5US -m comment --comment "default/showreadiness:showreadiness" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-CGDKBCNM24SZWCMS
|
||||
-A KUBE-SVC-LI77LBOOMGYET5US -m comment --comment "default/showreadiness:showreadiness" -j KUBE-SEP-RI4SRNQQXWSTGE2Y
|
||||
|
||||
```
|
||||
|
||||
因此,kube-proxy 创建了许多 `iptables` 规则。它们都是什么意思?它对我的网络有什么样的影响?这里有一个来自华为的非常好的演讲,它叫做 [支持 50,000 个服务的可伸缩 Kubernetes][14],它说如果在你的 Kubernetes 集群中有 5,000 服务,增加一个新规则,将需要 **11 分钟**。如果这种事情发生在真实的集群中,我认为这将是一件非常糟糕的事情。
|
||||
@ -152,19 +129,16 @@ kube-proxy 像如下这样为每个目标主机创建一个 `iptables` 规则:
|
||||
|
||||
但是,我觉得使用 HAProxy 更舒服!它能够用于去替换 kube-proxy!我用谷歌搜索了一下,然后发现了这个 [thread on kubernetes-sig-network][15],它说:
|
||||
|
||||
> kube-proxy 是很难用的,我们在生产系统中使用它近一年了,它在大部分的时间都表现的很好,但是,随着我们集群中的服务越来越多,我们发现它的排错和维护工作越来越难。在我们的团队中没有 iptables 方面的专家,我们只有 HAProxy&LVS 方面的专家,由于我们已经使用它们好几年了,因此我们决定使用一个中心化的 HAProxy 去替换分布式的代理。我觉得这可能会对在 Kubernetes 中使用 HAProxy 的其他人有用,因此,我们更新了这个项目,并将它开源:[https://github.com/AdoHe/kube2haproxy][5]。如果你发现它有用,你可以去看一看、试一试。
|
||||
> kube-proxy 是很难用的,我们在生产系统中使用它近一年了,它在大部分的时间都表现的很好,但是,随着我们集群中的服务越来越多,我们发现它的排错和维护工作越来越难。在我们的团队中没有 iptables 方面的专家,我们只有 HAProxy & LVS 方面的专家,由于我们已经使用它们好几年了,因此我们决定使用一个中心化的 HAProxy 去替换分布式的代理。我觉得这可能会对在 Kubernetes 中使用 HAProxy 的其他人有用,因此,我们更新了这个项目,并将它开源:[https://github.com/AdoHe/kube2haproxy][5]。如果你发现它有用,你可以去看一看、试一试。
|
||||
|
||||
因此,那是一个有趣的选择!我在这里确实没有答案,但是,有一些想法:
|
||||
|
||||
* 负载均衡器是很复杂的
|
||||
|
||||
* DNS 也很复杂
|
||||
* 如果你有运维某种类型的负载均衡器(比如 HAProxy)的经验,与其使用一个全新的负载均衡器(比如 kube-proxy),还不如做一些额外的工作去使用你熟悉的那个来替换,或许更有意义。
|
||||
* 我一直在考虑,我们希望在什么地方能够完全使用 kube-proxy 或者 kube-dns —— 我认为,最好是只在 Envoy 上投入,并且在负载均衡&服务发现上完全依赖 Envoy 来做。因此,你只需要将 Envoy 运维好就可以了。
|
||||
|
||||
* 如果你有运营某种类型的负载均衡器(比如 HAProxy)的经验,与其使用一个全新的负载均衡器(比如 kube-proxy),还不如做一些额外的工作去使用你熟悉的那个来替换,或许更有意义。
|
||||
|
||||
* 我一直在考虑,我们希望在什么地方能够完全使用 kube-proxy 或者 kube-dns —— 我认为,最好是只在 Envoy 上投入,并且在负载均衡&服务发现上完全依赖 Envoy 来做。因此,你只需要将 Envoy 运营好就可以了。
|
||||
|
||||
正如你所看到的,我在关于如何运营 Kubernetes 中的内部代理方面的思路还是很混乱的,并且我也没有使用它们的太多经验。总体上来说,kube-proxy 和 kube-dns 还是很好的,也能够很好地工作,但是我仍然认为应该去考虑使用它们可能产生的一些问题(例如,”你不能有超出 5000 的 Kubernetes 服务“)。
|
||||
正如你所看到的,我在关于如何运维 Kubernetes 中的内部代理方面的思路还是很混乱的,并且我也没有使用它们的太多经验。总体上来说,kube-proxy 和 kube-dns 还是很好的,也能够很好地工作,但是我仍然认为应该去考虑使用它们可能产生的一些问题(例如,”你不能有超出 5000 的 Kubernetes 服务“)。
|
||||
|
||||
### 入口
|
||||
|
||||
@ -175,14 +149,12 @@ kube-proxy 像如下这样为每个目标主机创建一个 `iptables` 规则:
|
||||
几个有用的链接,总结如下:
|
||||
|
||||
* [Kubernetes 网络模型][6]
|
||||
|
||||
* GKE 网络是如何工作的:[https://www.youtube.com/watch?v=y2bhV81MfKQ][7]
|
||||
|
||||
* 上述的有关 `kube-proxy` 上性能的讨论:[https://www.youtube.com/watch?v=4-pawkiazEg][8]
|
||||
|
||||
### 我认为网络运营很重要
|
||||
### 我认为网络运维很重要
|
||||
|
||||
我对 Kubernetes 的所有这些联网软件的感觉是,它们都仍然是非常新的,并且我并不能确定我们(作为一个社区)真的知道如何去把它们运营好。这让我作为一个操作者感到很焦虑,因为我真的想让我的网络运行的很好!:) 而且我觉得作为一个组织,运行你自己的 Kubernetes 集群需要相当大的投入,以确保你理解所有的代码片段,这样当它们出现问题时你可以去修复它们。这不是一件坏事,它只是一个事而已。
|
||||
我对 Kubernetes 的所有这些联网软件的感觉是,它们都仍然是非常新的,并且我并不能确定我们(作为一个社区)真的知道如何去把它们运维好。这让我作为一个操作者感到很焦虑,因为我真的想让我的网络运行的很好!:) 而且我觉得作为一个组织,运行你自己的 Kubernetes 集群需要相当大的投入,以确保你理解所有的代码片段,这样当它们出现问题时你可以去修复它们。这不是一件坏事,它只是一个事而已。
|
||||
|
||||
我现在的计划是,继续不断地学习关于它们都是如何工作的,以尽可能多地减少对我动过的那些部分的担忧。
|
||||
|
||||
@ -192,9 +164,9 @@ kube-proxy 像如下这样为每个目标主机创建一个 `iptables` 规则:
|
||||
|
||||
via: https://jvns.ca/blog/2017/10/10/operating-a-kubernetes-network/
|
||||
|
||||
作者:[Julia Evans ][a]
|
||||
作者:[Julia Evans][a]
|
||||
译者:[qhwdw](https://github.com/qhwdw)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,66 @@
|
||||
Scrot:让你在命令行中进行截屏更加简单
|
||||
======
|
||||
|
||||
> Scrot 是一个简单、灵活,并且提供了许多选项的 Linux 命令行截屏工具。
|
||||
|
||||
[![Original photo by Rikki Endsley. CC BY-SA 4.0](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/community-penguins-osdc-lead.png?itok=BmqsAF4A)][1]
|
||||
|
||||
|
||||
Linux 桌面上有许多用于截屏的优秀工具,比如 [Ksnapshot][1] 和 [Shutter][2] 。甚至 GNOME 桌面自带的简易截屏工具也能够很好的工作。但是,如果你很少截屏,或者你使用的 Linux 发行版没有内建截屏工具,或者你使用的是一台资源有限的老电脑,那么你该怎么办呢?
|
||||
|
||||
或许你可以转向命令行,使用一个叫做 [Scrot][4] 的实用工具。它能够完成简单的截屏工作,同时它所具有的一些特性也许会让你感到非常惊喜。
|
||||
|
||||
### 走近 Scrot
|
||||
|
||||
许多 Linux 发行版都会预先安装上 Scrot ,可以输入 `which scrot` 命令来查看系统中是否安装有 Scrot 。如果没有,那么可以使用你的 Linux 发行版的包管理器来安装。如果你想从源代码编译安装,那么也可以从 [GitHub][5] 上下载源代码。
|
||||
|
||||
如果要进行截屏,首先打开一个终端窗口,然后输入 `scrot [filename]` ,`[filename]` 是你想要保存的图片文件的名字(比如 `desktop.png`)。如果缺省了该参数,那么 scrot 会自动创建一个名字,比如 `2017-09-24-185009_1687x938_scrot.png` 。(这个名字缺乏了对图片内容的描述,这就是为什么最好在命令中指定一个名字作为参数。)
|
||||
|
||||
如果不带任何参数运行 Scrot,那么它将会对整个桌面进行截屏。如果不想这样,那么你也可以对屏幕中的一个小区域进行截图。
|
||||
|
||||
### 对单一窗口进行截屏
|
||||
|
||||
可以通过输入 `scrot -u [filename]` 命令来对一个窗口进行截屏。
|
||||
|
||||
`-u` 选项告诉 Scrot 对当前窗口进行截屏,这通常是我们正在工作的终端窗口,也许不是你想要的。
|
||||
|
||||
如果要对桌面上的另一个窗口进行截屏,需要输入 `scrot -s [filename]` 。
|
||||
|
||||
`-s` 选项可以让你做下面两件事的其中一件:
|
||||
|
||||
* 选择一个打开着的窗口
|
||||
* 在一个窗口的周围或一片区域画一个矩形进行捕获
|
||||
|
||||
你也可以设置一个时延,这样让你能够有时间来选择你想要捕获的窗口。可以通过 `scrot -u -d [num] [filename]` 来设置时延。
|
||||
|
||||
`-d` 选项告诉 Scrot 在捕获窗口前先等待一段时间,`[num]` 是需要等待的秒数。指定为 `-d 5` (等待 5 秒)应该能够让你有足够的时间来选择窗口。
|
||||
|
||||
### 更多有用的选项
|
||||
|
||||
Scrot 还提供了许多额外的特性(绝大多数我从来没有使用过)。下面是我发现的一些有用的选项:
|
||||
|
||||
* `-b` 捕获窗口的边界
|
||||
* `-t` 捕获窗口并创建一个缩略图。当你需要把截图张贴到网上的时候,这会非常有用
|
||||
* `-c` 当你同时使用了 `-d` 选项的时候,在终端中创建倒计时
|
||||
|
||||
如果你想了解 Scrot 的其他选项,可以在终端中输入 `man scrot` 来查看它的手册,或者[在线阅读][6]。然后开始使用 Scrot 进行截屏。
|
||||
|
||||
虽然 Scrot 很简单,但它的确能够工作得很好。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/17/11/taking-screen-captures-linux-command-line-scrot
|
||||
|
||||
作者:[Scott Nesbitt][a]
|
||||
译者:[ucasFL](https://github.com/ucasFL)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/scottnesbitt
|
||||
[1]:https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/community-penguins-osdc-lead.png?itok=BmqsAF4A
|
||||
[2]:https://www.kde.org/applications/graphics/ksnapshot/
|
||||
[3]:https://launchpad.net/shutter
|
||||
[4]:https://github.com/dreamer/scrot
|
||||
[5]:http://manpages.ubuntu.com/manpages/precise/man1/scrot.1.html
|
||||
[6]:https://github.com/dreamer/scrot
|
@ -0,0 +1,142 @@
|
||||
面向敏捷开发团队的 7 个开源项目管理工具
|
||||
======
|
||||
|
||||
> 在这篇开源项目管理工具的综述中,让我们来了解一下支持 Scrum、<ruby>看板<rt>Kanban</rt></ruby> 等敏捷开发模式的软件。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/BUSINESS_orgchart1.png?itok=tukiFj89)
|
||||
|
||||
Opensource.com 以前对流行的开源项目管理工具做过相应的调研。但是今年我们增加了一个特点。本次,我们特别关注支持[敏捷][1]方法的工具,包括相关的实践,如 [Scrum][2]、Lean 和 <ruby>看板<rt>Kanban</rt></ruby>。
|
||||
|
||||
对敏捷开发的兴趣和使用的增长是我们今年决定专注于这些工具的原因。大多数组织(71%)的人说他们至少[使用了敏捷方式][3]。此外,敏捷项目比传统方法管理的项目 [要高出 28% 的成功率][4] 。
|
||||
|
||||
我们查看了 [2014][5]、[2015][6] 和 [2016][7] 中涉及的项目管理工具,并挑选了支持敏捷的工具,然后对没有涉及的或变化了的进行了研究。不管您的组织是否已经在使用敏捷开发,或者在 2018 年的众多计划之一是采用敏捷方法,这七个开源项目管理工具之一可能正是您所要找寻的。
|
||||
|
||||
### MyCollab
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/mycollab_kanban-board.png)
|
||||
|
||||
|
||||
[MyCollab][8] 是一套针对中小型企业的三个协作模块套件:项目管理、客户关系管理(CRM)和文档创建和编辑软件。有两个许可证选项:一个商业的“终极”版本,它更快,可以在内部或云中运行;另一个开源的“社区版本”,这个正是我们感兴趣的版本。
|
||||
|
||||
由于没有使用查询缓存,社区版本没有云方式,并且速度较慢,但是提供了基本的项目管理特性,包括任务、问题管理、活动流、路线图视图和敏捷团队看板。虽然它没有单独的移动应用程序,但它也适用于移动设备,包括 Windows、Mac OS、Linux 和 UNIX 计算机。
|
||||
|
||||
MyCollab 的最新版本是 5.4.10,源代码可在 [GitHub][9] 上下载。它是在 AGPLv3 下进行授权的,需要 Java 运行时环境和 MySQL 支持。它可运行于 Windows、Linux、UNIX 和 MacOS。[下载地址][10]。
|
||||
|
||||
### Odoo
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/odoo_projects_screenshots_01a.gif)
|
||||
|
||||
|
||||
[Odoo][11] 不仅仅是项目管理软件;它是一个完整的集成商业应用套件,包括会计、人力资源、网站和电子商务、库存、制造、销售管理(CRM)和其它工具。
|
||||
|
||||
与付费企业套件相比,免费的开源社区版具有有限的 [特性][12] 。它的项目管理应用程序包括敏捷团队的看板式任务跟踪视图,在最新版本 Odoo 11.0 中更新了该视图,以包括用于跟踪项目状态的进度条和动画。项目管理工具还包括甘特图、任务、问题、图表等等。Odoo 有一个繁荣的[社区][13],并提供 [用户指南][14] 及其他培训资源。
|
||||
|
||||
|
||||
它是在 GPLv3 下授权的,需要 Python 和 PostgreSQL 支持。作为[Docker][16] 镜像 可以运行在 Windows、Linux 和 Red Hat 包管理器中,下载地址[download][15],源代码[GitHub][17]。
|
||||
|
||||
### OpenProject
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/openproject-screenshot-agile-scrum.png)
|
||||
|
||||
[OpenProject][18] 是一个强大的开源项目管理工具,以其易用性和丰富的项目管理和团队协作特性而著称。
|
||||
|
||||
它的模块支持项目计划、调度、路线图和发布计划、时间跟踪、成本报告、预算、bug 跟踪以及敏捷和 Scrum。它的敏捷特性,包括创建 Story、确定 sprint 的优先级以及跟踪任务,都与 OpenProject 的其他模块集成在一起。
|
||||
|
||||
OpenProject 在 GPLv3 下获得许可,其源代码可在[GitHub][19]上。最新版本 7.3.2 的 Linux 版本 [在此下载][20];您可以在 Birthe Lindenthal 的文章 “[OpenProject 入门][21]”中了解更多关于安装和配置它的信息。
|
||||
|
||||
### OrangeScrum
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/orangescrum_kanban.png)
|
||||
|
||||
正如从其名称中猜到的,[OrangeScrum][22] 支持敏捷方法,特别是使用 Scrum 任务板和看板式工作流视图。它面向较小的组织自由职业者、中介机构和中小型企业。
|
||||
|
||||
开源版本提供了 OrangeScrum 付费版本中的许多 [特性][23],包括移动应用程序、资源利用率和进度跟踪。其他特性,包括甘特图、时间日志、发票和客户端管理,可以作为付费附加组件提供,付费版本包括云选项,而社区版本不提供。
|
||||
|
||||
OrangeScrum 是基于 GPLv3 授权的,是基于 CakePHP 框架开发。它需要 Apache、PHP 5.3 或更高版本和 MySQL 4.1 或更高版本支持,并可以在 Windows、Linux 和 Mac OS 上运行。其最新版本 1.1.1 [在此下载][24],其源码在 [GitHub] [25]。
|
||||
|
||||
|
||||
### ]project-open[
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/projectopen_dashboard.png)
|
||||
|
||||
[\]project-open\[][26] 是一个双许可证的企业项目管理工具,这意味着其核心是开源的,并且在商业许可的模块中可以使用一些附加特性。根据该项目的社区和企业版本的 [比较][27],开源核心为中小型组织提供了许多特性。
|
||||
|
||||
]project-open[ 支持带有 Scrum 和看板功能的 [敏捷][28] 项目,以及经典的甘特/瀑布项目和混合或混合项目。
|
||||
|
||||
该应用程序是在 GPL 下授权的,并且 [源代码][29]是通过 CVS 访问的。 ]project-open[ 在 Linux 和 Windows 的安装有 [安装程序][26],但也可以在云镜像和虚拟设备中使用。
|
||||
|
||||
### Taiga
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/taiga_screenshot.jpg)
|
||||
|
||||
[Taiga][30] 是一个开源项目管理平台,它专注于 Scrum 和敏捷开发,其特征包括看板、任务、sprints、问题、backlog 和 epics。其他功能包括凭证管理、多项目支持、Wiki 页面和第三方集成。
|
||||
|
||||
它还为 iOS、Android 和 Windows 设备提供免费的移动应用程序,并提供导入工具,使从其他流行的项目管理应用程序迁移变得容易。
|
||||
|
||||
Taiga 对于公共项目是免费的,对项目数量或用户数量没有限制。对于私有项目,在“免费增值”模式下,有很多 [付费计划][31] 可用,但是值得注意的是,无论您属于哪种类型,软件的功能特性都是一样的。
|
||||
|
||||
Taiga 是在 GNU Affero GPLv3 下授权的,并且软件需要 Nginx、Python 和 PostgreSQL 支持。最新版本[3.1.0 Perovskia atriplicifolia][32],可在 [GitHub][33] 上下载。
|
||||
|
||||
### Tuleap
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/tuleap-scrum-prioritized-backlog.png)
|
||||
|
||||
[Tuleap][34] 是一个应用程序生命周期管理(ALM)平台,旨在为每种类型的团队管理项目——小型、中型、大型、瀑布、敏捷或混合型——但是它对敏捷团队的支持是显著的。值得注意的是,它为 Scrum、看板、sprints、任务、报告、持续集成、backlogs 等提供支持.
|
||||
|
||||
其他的 [特性][35] 包括问题跟踪、文档跟踪、协作工具,以及与 Git、SVN 和 Jenkins 的集成,所有这些都使它成为开放源码软件开发项目的吸引人的选择。
|
||||
|
||||
Tuleap 是在 GPLv2 下授权的。更多信息,包括 Docker 和 CentOS 下载,可以在他们的 [入门][36] 页面上找到。您还可以在 Tuleap 的 [Git][37] 上获取其最新版本 9.14 的源代码。
|
||||
|
||||
---
|
||||
|
||||
这种类型的文章的麻烦在于它一发布就过时了。您正在使用哪些开源项目管理工具,而被我们遗漏了?或者您对我们提到的有反馈意见吗?请在下面留下留言。
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/2/agile-project-management-tools
|
||||
|
||||
作者:[Opensource.com][a]
|
||||
译者:[heguangzhi](https://github.com/heguangzhi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com
|
||||
[1]:http://agilemanifesto.org/principles.html
|
||||
[2]:https://opensource.com/resources/scrum
|
||||
[3]:https://www.pmi.org/-/media/pmi/documents/public/pdf/learning/thought-leadership/pulse/pulse-of-the-profession-2017.pdf
|
||||
[4]:https://www.pwc.com/gx/en/actuarial-insurance-services/assets/agile-project-delivery-confidence.pdf
|
||||
[5]:https://opensource.com/business/14/1/top-project-management-tools-2014
|
||||
[6]:https://opensource.com/business/15/1/top-project-management-tools-2015
|
||||
[7]:https://opensource.com/business/16/3/top-project-management-tools-2016
|
||||
[8]:https://community.mycollab.com/
|
||||
[9]:https://github.com/MyCollab/mycollab
|
||||
[10]:https://www.mycollab.com/ce-registration/
|
||||
[11]:https://www.odoo.com/
|
||||
[12]:https://www.odoo.com/page/editions
|
||||
[13]:https://www.odoo.com/page/community
|
||||
[14]:https://www.odoo.com/documentation/user/11.0/
|
||||
[15]:https://www.odoo.com/page/download
|
||||
[16]:https://hub.docker.com/_/odoo/
|
||||
[17]:https://github.com/odoo/odoo
|
||||
[18]:https://www.openproject.org/
|
||||
[19]:https://github.com/opf/openproject
|
||||
[20]:https://www.openproject.org/download-and-installation/
|
||||
[21]:https://opensource.com/article/17/11/how-install-and-use-openproject
|
||||
[22]:https://www.orangescrum.org/
|
||||
[23]:https://www.orangescrum.org/compare-orangescrum
|
||||
[24]:http://www.orangescrum.org/free-download
|
||||
[25]:https://github.com/Orangescrum/orangescrum/
|
||||
[26]:http://www.project-open.com/en/list-installers
|
||||
[27]:http://www.project-open.com/en/products/editions.html
|
||||
[28]:http://www.project-open.com/en/project-type-agile
|
||||
[29]:http://www.project-open.com/en/developers-cvs-checkout
|
||||
[30]:https://taiga.io/
|
||||
[31]:https://tree.taiga.io/support/subscription-and-plans/payment-process-faqs/#q.-what-s-about-custom-plans-private-projects-with-more-than-25-members-?
|
||||
[32]:https://blog.taiga.io/taiga-perovskia-atriplicifolia-release-310.html
|
||||
[33]:https://github.com/taigaio
|
||||
[34]:https://www.tuleap.org/
|
||||
[35]:https://www.tuleap.org/features/project-management
|
||||
[36]:https://www.tuleap.org/get-started
|
||||
[37]:https://tuleap.net/plugins/git/tuleap/tuleap/stable
|
@ -1,29 +1,28 @@
|
||||
|
||||
我从编程面试中学到的
|
||||
============================================================
|
||||
|
||||
======
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/1250/1*DXPdaGPM4oM6p5nSkup7IQ.jpeg)
|
||||
聊聊白板编程面试
|
||||
|
||||
在2017年,我参加了[Grace Hopper Celebration][1]‘计算机行业中的女性’这一活动。这个活动是这类科技活动中最大的一个。共有17,000名女性IT工作者参加。
|
||||
*聊聊白板编程面试*
|
||||
|
||||
这个会议有个大型的配套招聘会,会上有招聘公司来面试会议参加者。有些人甚至现场拿到offer。我在现场晃荡了一下,注意到一些应聘者看上去非常紧张忧虑。我还隐隐听到应聘者之间的谈话,其中一些人谈到在面试中做的并不好。
|
||||
在 2017 年,我参加了 ‘计算机行业中的女性’ 的[Grace Hopper 庆祝活动][1]。这个活动是这类科技活动中最大的一个。共有 17,000 名女性IT工作者参加。
|
||||
|
||||
我走近我听到谈话的那群人并和她们聊了起来并给了一些面试上的小建议。我想我的建议还是比较偏基本的,如“(在面试时)一开始给出个能工作的解决方案也还说的过去”之类的,但是当她们听到我的一些其他的建议时还是颇为吃惊。
|
||||
这个会议有个大型的配套招聘会,会上有招聘公司来面试会议参加者。有些人甚至现场拿到 offer。我在现场晃荡了一下,注意到一些应聘者看上去非常紧张忧虑。我还隐隐听到应聘者之间的谈话,其中一些人谈到在面试中做的并不好。
|
||||
|
||||
为了能更多的帮到像她们一样的白面面试者,我收集了一些过去对我有用的小点子,这些小点子我已经发表在了[prodcast episode][2]上。它们也是这篇文章的主题。
|
||||
我走近我听到谈话的那群人并和她们聊了起来并给了一些面试上的小建议。我想我的建议还是比较偏基本的,如“(在面试时)一开始给出个能工作的解决方案也还说的过去”之类的,但是当她们听到我的一些其他的建议时还是颇为吃惊。
|
||||
|
||||
为了能更多的帮到像她们一样的小白面试者,我收集了一些过去对我有用的小点子,这些小点子我已经发表在了 [prodcast episode][2] 上。它们也是这篇文章的主题。
|
||||
|
||||
为了实习生职位和全职工作,我做过很多次的面试。当我还在大学主修计算机科学时,学校每个秋季学期都有招聘会,第一轮招聘会在校园里举行。(我在第一和最后一轮都搞砸过。)不过,每次面试后,我都会反思哪些方面我能做的更好,我还会和朋友们做模拟面试,这样我就能从他们那儿得到更多的面试反馈。
|
||||
|
||||
不管我们怎么样找工作: 工作中介,网络,或者学校招聘,他们的招聘流程中都会涉及到技术面试:
|
||||
不管我们怎么样找工作: 工作中介、网络,或者学校招聘,他们的招聘流程中都会涉及到技术面试:
|
||||
|
||||
近年来,我注意到了一些新的不同的面试形式出现了:
|
||||
|
||||
* 与招聘方的一位工程师结对编程
|
||||
* 网络在线测试及在线编码
|
||||
* 白板编程(LCTT译者注: 这种形式应该不新了)
|
||||
|
||||
* 白板编程(LCTT 译注: 这种形式应该不新了)
|
||||
|
||||
我将重点谈谈白板面试,这种形式我经历的最多。我有过很多次面试,有些挺不错的,有些被我搞砸了。
|
||||
|
||||
@ -31,7 +30,7 @@
|
||||
|
||||
首先,我想回顾一下我做的不好的地方。知错能改,善莫大焉。
|
||||
|
||||
当面试者提出一个要我解决的问题时, 我立即马上立刻开始在白板上写代码,_什么都不问。_
|
||||
当面试者提出一个要我解决的问题时, 我立即马上立刻开始在白板上写代码,_什么都不问。_
|
||||
|
||||
这里我犯了两个错误:
|
||||
|
||||
@ -41,7 +40,7 @@
|
||||
|
||||
#### 只会默默思考,不去记录想法或和面试官沟通
|
||||
|
||||
在面试中,很多时候我也会傻傻站在那思考,什么都不写。我和一个朋友模拟面试的时候,他告诉我因为他曾经和我一起工作过所以他知道我在思考,但是如果他是个陌生的面试官的话,他会觉得要么我正站在那冥思苦想,毫无头绪。不要急匆匆的直奔解题而去是很重要的。花点时间多想想各种解题的可能性。有时候面试官会乐意和你一起探索解题的步骤。不管怎样,这就是在一家公司开工作会议的的普遍方式,大家各抒己见,一起讨论如何解决问题。
|
||||
在面试中,很多时候我也会傻傻站在那思考,什么都不写。我和一个朋友模拟面试的时候,他告诉我因为他曾经和我一起工作过所以他知道我在思考,但是如果他是个陌生的面试官的话,他会觉得我正站在那冥思苦想,毫无头绪。不要急匆匆的直奔解题而去是很重要的。花点时间多想想各种解题的可能性。有时候面试官会乐意和你一起探索解题的步骤。不管怎样,这就是在一家公司开工作会议的的普遍方式,大家各抒己见,一起讨论如何解决问题。
|
||||
|
||||
### 想到一个解题方法
|
||||
|
||||
@ -50,30 +49,27 @@
|
||||
这是对我管用的步骤:
|
||||
|
||||
1. 头脑风暴
|
||||
|
||||
2. 写代码
|
||||
|
||||
3. 处理错误路径
|
||||
|
||||
4. 测试
|
||||
|
||||
#### 1\. 头脑风暴
|
||||
#### 1、 头脑风暴
|
||||
|
||||
对我来说,我会首先通过一些例子来视觉化我要解决的问题。比如说如果这个问题和数据结构中的树有关,我就会从树底层的空节点开始思考,如何处理一个节点的情况呢?两个节点呢?三个节点呢?这能帮助你从具体例子里抽象出你的解决方案。
|
||||
|
||||
在白板上先写下你的算法要做的事情列表。这样做,你往往能在开始写代码前就发现bug和缺陷(不过你可得掌握好时间)。我犯过的一个错误是我花了过多的时间在澄清问题和头脑风暴上,最后几乎没有留下时间给我写代码。你的面试官可能没有机会看你在白板上写下代码,这可太糟了。你可以带块手表,或者房间有钟的话,你也可以抬头看看时间。有些时候面试者会提醒你你已经得到了所有的信息(这时你就不要再问别的了),'我想我们已经把所有需要的信息都澄清了,让我们写代码实现吧'
|
||||
在白板上先写下你的算法要做的事情列表。这样做,你往往能在开始写代码前就发现 bug 和缺陷(不过你可得掌握好时间)。我犯过的一个错误是我花了过多的时间在澄清问题和头脑风暴上,最后几乎没有留下时间给我写代码。你的面试官可能没有机会看你在白板上写下代码,这可太糟了。你可以带块手表,或者房间有钟的话,你也可以抬头看看时间。有些时候面试者会提醒你你已经得到了所有的信息(这时你就不要再问别的了),“我想我们已经把所有需要的信息都澄清了,让我们写代码实现吧”。
|
||||
|
||||
#### 2\. 开始写代码,一气呵成
|
||||
#### 2、 开始写代码,一气呵成
|
||||
|
||||
如果你还没有得到问题的完美解决方法,从最原始的解法开始总的可以的。当你在向面试官解释最显而易见的解法时,你要想想怎么去完善它,并指明这种做法是最原始,未加优化的。(请熟悉算法中的O()的概念,这对面试非常有用。)在向面试者提交前请仔细检查你的解决方案两三遍。面试者有时会给你些提示, ‘还有更好的方法吗?’,这句话的意思是面试官提示你有更优化的解决方案。
|
||||
如果你还没有得到问题的完美解决方法,从最原始的解法开始总是可以的。当你在向面试官解释最显而易见的解法时,你要想想怎么去完善它,并指明这种做法是最原始的,未加优化的。(请熟悉算法中的 `O()` 的概念,这对面试非常有用。)在向面试者提交前请仔细检查你的解决方案两三遍。面试者有时会给你些提示, “还有更好的方法吗?”,这句话的意思是面试官提示你有更优化的解决方案。
|
||||
|
||||
#### 3\. 错误处理
|
||||
#### 3、 错误处理
|
||||
|
||||
当你在编码时,对你想做错误处理的代码行做个注释。当面试者说,'很好,这里你想到了错误处理。你想怎么处理呢?抛出异常还是返回错误码?',这将给你个机会去引出关于代码质量的一番讨论。当然,这种地方提出几个就够了。有时,面试者为了节省编码的时间,会告诉你可以假设外界输入的参数都已经通过了校验。不管怎样,你都要展现你对错误处理和编码质量的重要性的认识。
|
||||
当你在编码时,对你想做错误处理的代码行做个注释。当面试者说,“很好,这里你想到了错误处理。你想怎么处理呢?抛出异常还是返回错误码?”,这将给你个机会去引出关于代码质量的一番讨论。当然,这种地方提出几个就够了。有时,面试者为了节省编码的时间,会告诉你可以假设外界输入的参数都已经通过了校验。不管怎样,你都要展现你对错误处理和编码质量的重要性的认识。
|
||||
|
||||
#### 4\. 测试
|
||||
#### 4、 测试
|
||||
|
||||
在编码完成后,用你在前面头脑风暴中写的用例来在你脑子里“跑”一下你的代码,确定万无一失。例如你可以说,‘让我用前面写下的树的例子来跑一下我的代码,如果是一个节点是什么结果,如果是两个节点是什么结果。。。’
|
||||
在编码完成后,用你在前面头脑风暴中写的用例来在你脑子里“跑”一下你的代码,确定万无一失。例如你可以说,“让我用前面写下的树的例子来跑一下我的代码,如果是一个节点是什么结果,如果是两个节点是什么结果……”
|
||||
|
||||
在你结束之后,面试者有时会问你你将会怎么测试你的代码,你会涉及什么样的测试用例。我建议你用下面不同的分类来组织你的错误用例:
|
||||
|
||||
@ -83,7 +79,7 @@
|
||||
2. 错误用例
|
||||
3. 期望的正常用例
|
||||
|
||||
对于性能测试,要考虑极端数量下的情况。例如,如果问题是关于列表的,你可以说你将会使用一个非常大的列表以及的非常小的列表来测试。如果和数字有关,你将会测试系统中的最大整数和最小整数。我建议读一些有关软件测试的书来得到更多的知识。在这个领域我最喜欢的书是[How We Test Software at Microsoft][3]。
|
||||
对于性能测试,要考虑极端数量下的情况。例如,如果问题是关于列表的,你可以说你将会使用一个非常大的列表以及的非常小的列表来测试。如果和数字有关,你将会测试系统中的最大整数和最小整数。我建议读一些有关软件测试的书来得到更多的知识。在这个领域我最喜欢的书是 《[我们在微软如何测试软件][3]》。
|
||||
|
||||
对于错误用例,想一下什么是期望的错误情况并一一写下。
|
||||
|
||||
@ -91,50 +87,45 @@
|
||||
|
||||
### “你还有什么要问我的吗?”
|
||||
|
||||
面试最后总是会留几分钟给你问问题。我建议你在面试前写下你想问的问题。千万别说,‘我没什么问题了’,就算你觉得面试砸了或者你对这间公司不怎么感兴趣,你总有些东西可以问问。你甚至可以问面试者他最喜欢自己的工作什么,最讨厌自己的工作什么。或者你可以问问面试官的工作具体是什么,在用什么技术和实践。不要因为觉得自己在面试中做的不好而心灰意冷,不想问什么问题。
|
||||
面试最后总是会留几分钟给你问问题。我建议你在面试前写下你想问的问题。千万别说,“我没什么问题了”,就算你觉得面试砸了或者你对这间公司不怎么感兴趣,你总有些东西可以问问。你甚至可以问面试者他最喜欢自己的工作什么,最讨厌自己的工作什么。或者你可以问问面试官的工作具体是什么,在用什么技术和实践。不要因为觉得自己在面试中做的不好而心灰意冷,不想问什么问题。
|
||||
|
||||
### 申请一份工作
|
||||
|
||||
|
||||
关于找工作申请工作,有人曾经告诉我,你应该去找你真正有激情工作的地方。去找一家你喜欢的公司,或者你喜欢使用的产品,看看你能不能去那儿工作。
|
||||
关于找工作和申请工作,有人曾经告诉我,你应该去找你真正有激情工作的地方。去找一家你喜欢的公司,或者你喜欢使用的产品,看看你能不能去那儿工作。
|
||||
|
||||
我个人并不推荐你用上述的方法去找工作。你会排除很多很好的公司,特别是你是在找实习工作或者入门级的职位时。
|
||||
|
||||
你也可以集中在其他的一些目标上。如:我想从这个工作里得到哪方面的更多经验?这个工作是关于云计算?Web开发?或是人工智能?当在招聘会上与招聘公司沟通是,看看他们的工作单位有没有在这些领域的。你可能会在一家并非在你的想去公司列表上的公司(或非盈利机构)里找到你想找的职位。
|
||||
你也可以集中在其他的一些目标上。如:我想从这个工作里得到哪方面的更多经验?这个工作是关于云计算?Web 开发?或是人工智能?当在招聘会上与招聘公司沟通时,看看他们的工作单位有没有在这些领域的。你可能会在一家并非在你的想去公司列表上的公司(或非盈利机构)里找到你想找的职位。
|
||||
|
||||
#### 换组
|
||||
|
||||
在这家公司里的第一个组里呆了一年半以后,我觉得是时候去探索一下不同的东西了。我找到了一个我喜欢的组并进行了4轮面试。结果我搞砸了。
|
||||
|
||||
|
||||
我什么都没有准备,甚至都没在白板上练练手。我当时的逻辑是,如果我都已经在一家公司干了快2年了,我还需要练什么?我完全错了,我在接下去的白板面试中跌跌撞撞。我的板书写得太小,而且因为没有从最左上角开始写代码,我的代码大大超出了一个白板的空间,这些都导致了白板面试失败。
|
||||
在这家公司里的第一个组里呆了一年半以后,我觉得是时候去探索一下不同的东西了。我找到了一个我喜欢的组并进行了 4 轮面试。结果我搞砸了。
|
||||
|
||||
我什么都没有准备,甚至都没在白板上练练手。我当时的逻辑是,如果我都已经在一家公司干了快 2 年了,我还需要练什么?我完全错了,我在接下去的白板面试中跌跌撞撞。我的板书写得太小,而且因为没有从最左上角开始写代码,我的代码大大超出了一个白板的空间,这些都导致了白板面试失败。
|
||||
|
||||
我在面试前也没有刷过数据结构和算法题。如果我做了的话,我将会在面试中更有信心。就算你已经在一家公司担任了软件工程师,在你去另外一个组面试前,我强烈建议你在一块白板上演练一下如何写代码。
|
||||
|
||||
对于换项目组这件事,如果你是在公司内部换组的话,事先能同那个组的人非正式聊聊会很有帮助。对于这一点,我发现几乎每个人都很乐于和你一起吃个午饭。人一般都会在中午有空,约不到人或者别人正好有会议冲突的风险会很低。这是一种非正式的途径来了解你想去的组正在干什么,以及这个组成员个性是怎么样的。相信我,你能从一次午餐中得到很多信息,这可会对你的正式面试帮助不小。
|
||||
|
||||
对于换项目组这件事,如果你是在公司内部换组的话,事先能同那个组的人非正式聊聊会很有帮助。对于这一点,我发现几乎每个人都很乐于和你一起吃个午饭。人一般都会在中午有空,约不到人或者别人正好有会议冲突的风险会很低。这是一种非正式的途径来了解你想去的组正在干什么,以及这个组成员个性是怎么样的。相信我, 你能从一次午餐中得到很多信息,这可会对你的正式面试帮助不小。
|
||||
非常重要的一点是,你在面试一个特定的组时,就算你在面试中做的很好,因为文化不契合的原因,你也很可能拿不到 offer。这也是为什么我一开始就想去见见组里不同的人的原因(有时这也不太可能),我希望你不要被一次拒绝所击倒,请保持开放的心态,选择新的机会,并多多练习。
|
||||
|
||||
|
||||
非常重要的一点是,你在面试一个特定的组时,就算你在面试中做的很好,因为文化不契合的原因,你也很可能拿不到offer。这也是为什么我一开始就想去见见组里不同的人的原因(有时这也不太可能),我希望你不要被一次拒绝所击倒,请保持开放的心态,选择新的机会,并多多练习。
|
||||
|
||||
|
||||
以上内容来自["Programming interviews"][4] 章节,选自 [The Women in Tech Show: Technical Interviews with Prominent Women in Tech][5]
|
||||
以上内容选自 《[The Women in Tech Show: Technical Interviews with Prominent Women in Tech][5]》的 “[编程面试][4]”章节,
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
作者简介:
|
||||
|
||||
|
||||
微软研究院Software Engineer II, www.thewomenintechshow.com站长,所有观点都只代表本人意见。
|
||||
微软研究院 Software Engineer II, www.thewomenintechshow.com 站长,所有观点都只代表本人意见。
|
||||
|
||||
------------
|
||||
|
||||
via: https://medium.freecodecamp.org/what-i-learned-from-programming-interviews-29ba49c9b851
|
||||
|
||||
作者:[Edaena Salinas ][a]
|
||||
译者:DavidChenLiang (https://github.com/DavidChenLiang)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
作者:[Edaena Salinas][a]
|
||||
译者:[DavidChenLiang](https://github.com/DavidChenLiang)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,34 +1,35 @@
|
||||
API Star: Python 3 的 API 框架 – Polyglot.Ninja()
|
||||
API Star:一个 Python 3 的 API 框架
|
||||
======
|
||||
|
||||
为了在 Python 中快速构建 API,我主要依赖于 [Flask][1]。最近我遇到了一个名为 “API Star” 的基于 Python 3 的新 API 框架。由于几个原因,我对它很感兴趣。首先,该框架包含 Python 新特点,如类型提示和 asyncio。接着它再进一步并且为开发人员提供了很棒的开发体验。我们很快就会讲到这些功能,但在我们开始之前,我首先要感谢 Tom Christie,感谢他为 Django REST Framework 和 API Star 所做的所有工作。
|
||||
为了在 Python 中快速构建 API,我主要依赖于 [Flask][1]。最近我遇到了一个名为 “API Star” 的基于 Python 3 的新 API 框架。由于几个原因,我对它很感兴趣。首先,该框架包含 Python 新特点,如类型提示和 asyncio。而且它再进一步为开发人员提供了很棒的开发体验。我们很快就会讲到这些功能,但在我们开始之前,我首先要感谢 Tom Christie,感谢他为 Django REST Framework 和 API Star 所做的所有工作。
|
||||
|
||||
现在说回 API Star -- 我感觉这个框架很有成效。我可以选择基于 asyncio 编写异步代码,或者可以选择传统后端方式就像 WSGI 那样。它配备了一个命令行工具 - `apistar` 来帮助我们更快地完成工作。它支持 Django ORM 和 SQLAlchemy,这是可选的。它有一个出色类型系统,使我们能够定义输入和输出的约束,API Star 可以自动生成 api 模式(包括文档),提供验证和序列化功能等等。虽然 API Star 专注于构建 API,但你也可以非常轻松地在其上构建 Web 应用程序。在我们自己构建一些东西之前,所有这些可能都没有意义的。
|
||||
现在说回 API Star —— 我感觉这个框架很有成效。我可以选择基于 asyncio 编写异步代码,或者可以选择传统后端方式就像 WSGI 那样。它配备了一个命令行工具 —— `apistar` 来帮助我们更快地完成工作。它支持 Django ORM 和 SQLAlchemy,这是可选的。它有一个出色的类型系统,使我们能够定义输入和输出的约束,API Star 可以自动生成 API 的模式(包括文档),提供验证和序列化功能等等。虽然 API Star 专注于构建 API,但你也可以非常轻松地在其上构建 Web 应用程序。在我们自己构建一些东西之前,所有这些可能都没有意义的。
|
||||
|
||||
### 开始
|
||||
|
||||
我们将从安装 API Star 开始。为此实验创建一个虚拟环境是一个好主意。如果你不知道如何创建一个虚拟环境,不要担心,继续往下看。
|
||||
|
||||
```
|
||||
pip install apistar
|
||||
|
||||
```
|
||||
|
||||
(译注:上面的命令是在 Python 3 虚拟环境下使用的)
|
||||
|
||||
如果你没有使用虚拟环境或者 Python 3 的 `pip`,它被称为 `pip3`,那么使用 `pip3 install apistar` 代替。
|
||||
如果你没有使用虚拟环境或者你的 Python 3 的 `pip` 名为 `pip3`,那么使用 `pip3 install apistar` 代替。
|
||||
|
||||
一旦我们安装了这个包,我们就应该可以使用 `apistar` 命令行工具了。我们可以用它创建一个新项目,让我们在当前目录中创建一个新项目。
|
||||
|
||||
```
|
||||
apistar new .
|
||||
|
||||
```
|
||||
|
||||
现在我们应该创建两个文件:`app.py`,它包含主应用程序,然后是 `test.py`,它用于测试。让我们来看看 `app.py` 文件:
|
||||
|
||||
```
|
||||
from apistar import Include, Route
|
||||
from apistar.frameworks.wsgi import WSGIApp as App
|
||||
from apistar.handlers import docs_urls, static_urls
|
||||
|
||||
|
||||
def welcome(name=None):
|
||||
if name is None:
|
||||
return {'message': 'Welcome to API Star!'}
|
||||
@ -46,34 +47,34 @@ app = App(routes=routes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.main()
|
||||
|
||||
```
|
||||
|
||||
在我们深入研究代码之前,让我们运行应用程序并查看它是否正常工作。我们在浏览器中输入 `http://127.0.0.1:8080/`,我们将得到以下响应:
|
||||
|
||||
```
|
||||
{"message": "Welcome to API Star!"}
|
||||
|
||||
```
|
||||
|
||||
如果我们输入:`http://127.0.0.1:8080/?name=masnun`
|
||||
|
||||
```
|
||||
{"message": "Welcome to API Star, masnun!"}
|
||||
|
||||
```
|
||||
|
||||
同样的,输入 `http://127.0.0.1:8080/docs/`,我们将看到自动生成的 API 文档。
|
||||
|
||||
现在让我们来看看代码。我们有一个 `welcome` 函数,它接收一个名为 `name` 的参数,其默认值为 `None`。API Star 是一个智能的 api 框架。它将尝试在 url 路径或者查询字符串中找到 `name` 键并将其传递给我们的函数,它还基于其生成 API 文档。这真是太好了,不是吗?
|
||||
现在让我们来看看代码。我们有一个 `welcome` 函数,它接收一个名为 `name` 的参数,其默认值为 `None`。API Star 是一个智能的 API 框架。它将尝试在 url 路径或者查询字符串中找到 `name` 键并将其传递给我们的函数,它还基于其生成 API 文档。这真是太好了,不是吗?
|
||||
|
||||
然后,我们创建一个 `Route` 和 `Include` 实例列表,并将列表传递给 `App` 实例。`Route` 对象用于定义用户自定义路由。顾名思义,`Include` 包含了在给定的路径下的其它 url 路径。
|
||||
然后,我们创建一个 `Route` 和 `Include` 实例的列表,并将列表传递给 `App` 实例。`Route` 对象用于定义用户自定义路由。顾名思义,`Include` 包含了在给定的路径下的其它 url 路径。
|
||||
|
||||
### 路由
|
||||
|
||||
路由很简单。当构造 `App` 实例时,我们需要传递一个列表作为 `routes` 参数,这个列表应该有我们刚才看到的 `Route` 或 `Include` 对象组成。对于 `Route`,我们传递一个 url 路径,http 方法和可调用的请求处理程序(函数或者其他)。对于 `Include` 实例,我们传递一个 url 路径和一个 `Routes` 实例列表。
|
||||
|
||||
##### 路径参数
|
||||
#### 路径参数
|
||||
|
||||
我们可以在花括号内添加一个名称来声明 url 路径参数。例如 `/user/{user_id}` 定义了一个 url,其中 `user_id` 是路径参数,或者说是一个将被注入到处理函数(实际上是可调用的)中的变量。这有一个简单的例子:
|
||||
|
||||
```
|
||||
from apistar import Route
|
||||
from apistar.frameworks.wsgi import WSGIApp as App
|
||||
@ -91,22 +92,22 @@ app = App(routes=routes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.main()
|
||||
|
||||
```
|
||||
|
||||
如果我们访问 `http://127.0.0.1:8080/user/23`,我们将得到以下响应:
|
||||
|
||||
```
|
||||
{"message": "Your profile id is: 23"}
|
||||
|
||||
```
|
||||
|
||||
但如果我们尝试访问 `http://127.0.0.1:8080/user/some_string`,它将无法匹配。因为我们定义了 `user_profile` 函数,且为 `user_id` 参数添加了一个类型提示。如果它不是整数,则路径不匹配。但是如果我们继续删除类型提示,只使用 `user_profile(user_id)`,它将匹配此 url。这也展示了 API Star 的智能之处和利用类型和好处。
|
||||
|
||||
#### 包含/分组路由
|
||||
|
||||
有时候将某些 url 组合在一起是有意义的。假设我们有一个处理用户相关功能的 `user` 模块,将所有与用户相关的 url 分组在 `/user` 路径下可能会更好。例如 `/user/new`, `/user/1`, `/user/1/update` 等等。我们可以轻松地在单独的模块或包中创建我们的处理程序和路由,然后将它们包含在我们自己的路由中。
|
||||
有时候将某些 url 组合在一起是有意义的。假设我们有一个处理用户相关功能的 `user` 模块,将所有与用户相关的 url 分组在 `/user` 路径下可能会更好。例如 `/user/new`、`/user/1`、`/user/1/update` 等等。我们可以轻松地在单独的模块或包中创建我们的处理程序和路由,然后将它们包含在我们自己的路由中。
|
||||
|
||||
让我们创建一个名为 `user` 的新模块,文件名为 `user.py`。我们将以下代码放入这个文件:
|
||||
|
||||
```
|
||||
from apistar import Route
|
||||
|
||||
@ -128,10 +129,10 @@ user_routes = [
|
||||
Route("/{user_id}/update", "GET", user_update),
|
||||
Route("/{user_id}/profile", "GET", user_profile),
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
现在我们可以从 app 主文件中导入 `user_routes`,并像这样使用它:
|
||||
|
||||
```
|
||||
from apistar import Include
|
||||
from apistar.frameworks.wsgi import WSGIApp as App
|
||||
@ -146,7 +147,6 @@ app = App(routes=routes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.main()
|
||||
|
||||
```
|
||||
|
||||
现在 `/user/new` 将委托给 `user_new` 函数。
|
||||
@ -154,21 +154,22 @@ if __name__ == '__main__':
|
||||
### 访问查询字符串/查询参数
|
||||
|
||||
查询参数中传递的任何参数都可以直接注入到处理函数中。比如 url `/call?phone=1234`,处理函数可以定义一个 `phone` 参数,它将从查询字符串/查询参数中接收值。如果 url 查询字符串不包含 `phone` 的值,那么它将得到 `None`。我们还可以为参数设置一个默认值,如下所示:
|
||||
|
||||
```
|
||||
def welcome(name=None):
|
||||
if name is None:
|
||||
return {'message': 'Welcome to API Star!'}
|
||||
return {'message': 'Welcome to API Star, %s!' % name}
|
||||
|
||||
```
|
||||
|
||||
在上面的例子中,我们为 `name` 设置了一个默认值 `None`。
|
||||
|
||||
### 注入对象
|
||||
|
||||
通过给一个请求程序添加类型提示,我们可以将不同的对象注入到视图中。注入请求相关对象有助于处理程序直接从内部访问它们。API Star 内置的 `http` 包中有几个内置对象。我们也可以使用它的类型系统来创建我们自己的自定义对象并将它们注入到我们的函数中。API Star 还根据指定的约束进行数据验证。
|
||||
通过给一个请求程序添加类型提示,我们可以将不同的对象注入到视图中。注入请求相关的对象有助于处理程序直接从内部访问它们。API Star 内置的 `http` 包中有几个内置对象。我们也可以使用它的类型系统来创建我们自己的自定义对象并将它们注入到我们的函数中。API Star 还根据指定的约束进行数据验证。
|
||||
|
||||
让我们定义自己的 `User` 类型,并将其注入到我们的请求处理程序中:
|
||||
|
||||
```
|
||||
from apistar import Include, Route
|
||||
from apistar.frameworks.wsgi import WSGIApp as App
|
||||
@ -197,10 +198,10 @@ app = App(routes=routes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.main()
|
||||
|
||||
```
|
||||
|
||||
现在如果我们发送这样的请求:
|
||||
|
||||
```
|
||||
curl -X POST \
|
||||
http://127.0.0.1:8080/ \
|
||||
@ -214,6 +215,7 @@ curl -X POST \
|
||||
### 发送响应
|
||||
|
||||
如果你已经注意到,到目前为止,我们只可以传递一个字典,它将被转换为 JSON 并作为默认返回。但是,我们可以使用 `apistar` 中的 `Response` 类来设置状态码和其它任意响应头。这有一个简单的例子:
|
||||
|
||||
```
|
||||
from apistar import Route, Response
|
||||
from apistar.frameworks.wsgi import WSGIApp as App
|
||||
@ -236,15 +238,13 @@ app = App(routes=routes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.main()
|
||||
|
||||
```
|
||||
|
||||
它应该返回纯文本响应和一个自定义标响应头。请注意,`content` 应该是字节,而不是字符串。这就是我编码它的原因。
|
||||
|
||||
### 继续
|
||||
|
||||
我刚刚介绍了 API
|
||||
Star 的一些特性,API Star 中还有许多非常酷的东西,我建议通过 [Github Readme][2] 文件来了解这个优秀框架所提供的不同功能的更多信息。我还将尝试在未来几天内介绍关于 API Star 的更多简短的,集中的教程。
|
||||
我刚刚介绍了 API Star 的一些特性,API Star 中还有许多非常酷的东西,我建议通过 [Github Readme][2] 文件来了解这个优秀框架所提供的不同功能的更多信息。我还将尝试在未来几天内介绍关于 API Star 的更多简短的,集中的教程。
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -253,7 +253,7 @@ via: http://polyglot.ninja/api-star-python-3-api-framework/
|
||||
|
||||
作者:[MASNUN][a]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,61 @@
|
||||
Linux 虚拟机与 Linux 现场镜像版
|
||||
======
|
||||
|
||||
> Linux 虚拟机与 Linux 现场镜像版各有优势,也有不足。
|
||||
|
||||
首先我得承认,我非常喜欢频繁尝试新的 [Linux 发行版本][1]。然而,我用来测试它们的方法根据每次目标而有所不同。在这篇文章中,我们来看看两种运行 Linux 的模式:虚拟机或<ruby>现场镜像版<rt>live image</rt></ruby>。每一种方式都存在优势,但是也有一些不足。
|
||||
|
||||
### 首次测试一个全新的 Linux 发行版
|
||||
|
||||
当我首次测试一个全新 Linux 发行版时,我使用的方法很大程度上依赖于我当前所拥有的 PC 资源。如果我使用台式机,我会在一台虚拟机中运行该发行版来测试。使用这种方法的原因是,我可以下载并测试该发行版,不只是在一个现场环境中,而且也可以作为一个带有持久存储的安装的系统。
|
||||
|
||||
另一方面,如果我的 PC 不具备强劲的硬件,那么通过 Linux 的虚拟机安装来测试发行版是适得其反的。我会将那台 PC 压榨到它的极限,诚然,更好的是使用现场版的 Linux 映像,而不是从闪存驱动器中运行。
|
||||
|
||||
### 体验新的 Linux 发行版本的软件
|
||||
|
||||
如果你有兴趣查看发行版本的桌面环境或可用的软件,那使用它的现场镜像版就没错了。一个现场版环境可以提供给你所预期的全局视角、其所提供的软件和用户体验的整体感受。
|
||||
|
||||
公平的说,你也可以在虚拟机上达到同样的效果,但是它有一点不好,如果这么做会让更多数据填满你的磁盘空间。毕竟这只是对发行版的一个简单体验。记得我在第一节说过:我喜欢在虚拟机上运行 Linux 来做测试。用这个方式我就能看到如何去安装它、分区是怎么样的等等,而使用现场镜像版时你就看不到这些。
|
||||
|
||||
这种体验方式通常表明你只想对该发行版本有个大致了解,所以在这种情况下,这种只需要付出最小的精力和时间的方式是一种不错的办法。
|
||||
|
||||
### 随身携带一个发行版
|
||||
|
||||
这种方式虽然不像几年前那样普遍,这种随身携带一个 Linux 发行版的能力也许是出于对某些用户的考虑。显然,虚拟机安装对于便携性并无太多帮助。不过,现场镜像版实际上是十分便携的。现场镜像版可以写入到 DVD 当中或复制到一个闪存盘中而便于携带。
|
||||
|
||||
从 Linux 的便携性这个概念上展开来说,当要在一个朋友的电脑上展示 Linux 如何工作,使用一个闪存盘上的现场镜像版也是很方便的。这可以使你能演示 Linux 如何丰富他们的生活,而不用必须在他们的 PC 上运行一个虚拟机。使用现场镜像版这就有点双赢的感觉了。
|
||||
|
||||
### 选择做双引导 Linux
|
||||
|
||||
这接下来的方式是个大工程。考虑一下,也许你是一个 Windows 用户。你喜欢玩 Linux,但又不愿意冒险。除了在某些情况下会出些状况或者识别个别分区时遇到问题,双引导方式就没啥挑剔的。无论如何,使用 Linux 虚拟机或现场镜像版都对于你来说是一个很好的选择。
|
||||
|
||||
现在,我在某些事情上采取了奇怪的立场。我认为长期在闪存盘上运行现场镜像版要比虚拟机更有价值。这有两个原因。首先,您将会习惯于真正运行 Linux,而不是在 Windows 之上的虚拟机中运行它。其次,您可以设置闪存盘以包含持久存储的用户数据。
|
||||
|
||||
我知道你会说用一个虚拟机运行 Linux 也是如此,然而,使用现场镜像版的方式,你绝不会因为更新而被破坏任何东西。为什么?因为你不会更新你的宿主系统或者客户系统。请记住,有整个 Linux 发行版本被设计为持久存储的 Linux 发行版。Puppy Linux 就是一个非常好的例子。它不仅能运行在要被回收或丢弃的个人 PC 上,它也可以让你永远不被频繁的系统升级所困扰,这要感谢该发行版处理安全更新的方式。这不是一个常规的 Linux 发行版,而是以这样的一种方式封闭了安全问题——即持久存储的现场镜像版中没有什么令人担心的坏东西。
|
||||
|
||||
### Linux 虚拟机绝对是一个最好的选择
|
||||
|
||||
在我结束这篇文章时,让我告诉你。有一种场景下,使用 Virtual Box 等虚拟机绝对比现场镜像版更好:记录 Linux 发行版的桌面环境。
|
||||
|
||||
例如,我制作了一个视频,里面介绍和点评了许多 Linux 发行版。使用现场镜像版进行此操作需要我用硬件设备捕获屏幕,或者从现场镜像版的软件仓库中安装捕获软件。显然,虚拟机比 Linux 发行版的现场镜像版更适合这项工作。
|
||||
|
||||
一旦你需要采集音频进行混音,毫无疑问,如果您要使用软件来捕获您的点评语音,那么您肯定希望拥有一个宿主操作系统,里面包含了一个起码的捕获环境的所有基本需求。同样,您可以使用硬件设备来完成所有这一切,但如果您只是做兼职的视频/音频捕获, 那么这可能要付出成本高昂的代价。
|
||||
|
||||
### Linux 虚拟机 VS. Linux 现场镜像版
|
||||
|
||||
你最喜欢尝试新发行版的方式是哪些?也许,你是那种可以很好地格式化磁盘、将风险置之脑后的人,所以这里说的这些都是没用的?
|
||||
|
||||
我在网上互动的大多数人都倾向于遵循我上面提及的方法,但是我很想知道哪种方式更加适合你。点击评论框,让我知道在体验 Linux 发行版世界最伟大和最新的版本时,您更喜欢哪种方法。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.datamation.com/open-source/linux-virtual-machines-vs-linux-live-images.html
|
||||
|
||||
作者:[Matt Hartley][a]
|
||||
译者:[sober-wang](https://github.com/sober-wang)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.datamation.com/author/Matt-Hartley-3080.html
|
||||
[1]:https://www.datamation.com/open-source/best-linux-distro.html
|
89
published/20180308 What is open source programming.md
Normal file
89
published/20180308 What is open source programming.md
Normal file
@ -0,0 +1,89 @@
|
||||
何谓开源编程?
|
||||
======
|
||||
|
||||
> 开源就是丢一些代码到 GitHub 上。了解一下它是什么,以及不是什么?
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/code_computer_development_programming.png?itok=4OM29-82)
|
||||
|
||||
最简单的来说,开源编程就是编写一些大家可以随意取用、修改的代码。但你肯定听过关于 Go 语言的那个老笑话,说 Go 语言“简单到看一眼就可以明白规则,但需要一辈子去学会运用它”。其实写开源代码也是这样的。往 GitHub、Bitbucket、SourceForge 等网站或者是你自己的博客或网站上丢几行代码不是难事,但想要卓有成效,还需要个人的努力付出和高瞻远瞩。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/floorgoban.jpeg?itok=r8gA5jOk)
|
||||
|
||||
### 我们对开源编程的误解
|
||||
|
||||
首先我要说清楚一点:把你的代码放在 GitHub 的公开仓库中并不意味着把你的代码开源了。在几乎全世界,根本不用创作者做什么,只要作品形成,版权就随之而生了。在创作者进行授权之前,只有作者可以行使版权相关的权力。未经创作者授权的代码,不论有多少人在使用,都是一颗定时炸弹,只有愚蠢的人才会去用它。
|
||||
|
||||
有些创作者很善良,认为“很明显我的代码是免费提供给大家使用的。”,他也并不想起诉那些用了他的代码的人,但这并不意味着这些代码可以放心使用。不论在你眼中创作者们多么善良,他们都 *有权力* 起诉任何使用、修改代码,或未经明确授权就将代码嵌入的人。
|
||||
|
||||
很明显,你不应该在没有指定开源许可证的情况下将你的源代码发布到网上然后期望别人使用它并为其做出贡献。我建议你也尽量避免使用这种代码,甚至疑似未授权的也不要使用。如果你开发了一个函数和例程,它和之前一个疑似未授权代码很像,源代码作者就可以对你就侵权提起诉讼。
|
||||
|
||||
举个例子,Jill Schmill 写了 AwesomeLib 然后未明确授权就把它放到了 GitHub 上,就算 Jill Schmill 不起诉任何人,只要她把 AwesomeLib 的完整版权都卖给 EvilCorp,EvilCorp 就会起诉之前违规使用这段代码的人。这种行为就好像是埋下了计算机安全隐患,总有一天会为人所用。
|
||||
|
||||
没有许可证的代码的危险的,切记。
|
||||
|
||||
### 选择恰当的开源许可证
|
||||
|
||||
假设你正要写一个新程序,而且打算让人们以开源的方式使用它,你需要做的就是选择最贴合你需求的[许可证][1]。和宣传中说的一样,你可以从 GitHub 所支持的 [choosealicense.com][2] 开始。这个网站设计得像个简单的问卷,特别方便快捷,点几下就能找到合适的许可证。
|
||||
|
||||
警示:在选择许可证时不要过于自负,如果你选的是 [Apache 许可证][3]或者 [GPLv3][4] 这种广为使用的许可证,人们很容易理解他们和你都有什么权利,你也不需要请律师来排查其中的漏洞。你选择的许可证使用的人越少,带来的麻烦就越多。
|
||||
|
||||
最重要的一点是: *千万不要试图自己制造许可证!* 自己制造许可证会给大家带来更多的困惑和困扰,不要这样做。如果在现有的许可证中确实找不到你需要的条款,你可以在现有的许可证中附加上你的要求,并且重点标注出来,提醒使用者们注意。
|
||||
|
||||
我知道有些人会站出来说:“我才懒得管什么许可证,我已经把代码发到<ruby>公开领域<rt>public domain</rt></ruby>了。”但问题是,公开领域的法律效力并不是受全世界认可的。在不同的国家,公开领域的效力和表现形式不同。在有些国家的政府管控下,你甚至不可以把自己的源代码发到公开领域。万幸,[Unlicense][5] 可以弥补这些漏洞,它语言简洁,使用几个词清楚地描述了“就把它放到公开领域”,但其效力为全世界认可。
|
||||
|
||||
### 怎样引入许可证
|
||||
|
||||
确定使用哪个许可证之后,你需要清晰而无疑义地指定它。如果你是在 GitHub、GitLab 或 BitBucket 这几个网站发布,你需要构建很多个文件夹,在根文件夹中,你应把许可证创建为一个以 `LICENSE.txt` 命名的明文文件。
|
||||
|
||||
创建 `LICENSE.txt` 这个文件之后还有其它事要做。你需要在每个重要文件的头部添加注释块来申明许可证。如果你使用的是一个现有的许可证,这一步对你来说十分简便。一个 `# 项目名 (c)2018 作者名,GPLv3 许可证,详情见 https://www.gnu.org/licenses/gpl-3.0.en.html` 这样的注释块比隐约指代的许可证的效力要强得多。
|
||||
|
||||
如果你是要发布在自己的网站上,步骤也差不多。先创建 `LICENSE.txt` 文件,放入许可证,再表明许可证出处。
|
||||
|
||||
### 开源代码的不同之处
|
||||
|
||||
开源代码和专有代码的一个主要区别是开源代码写出来就是为了给别人看的。我是个 40 多岁的系统管理员,已经写过许许多多的代码。最开始我写代码是为了工作,为了解决公司的问题,所以其中大部分代码都是专有代码。这种代码的目的很简单,只要能在特定场合通过特定方式发挥作用就行。
|
||||
|
||||
开源代码则大不相同。在写开源代码时,你知道它可能会被用于各种各样的环境中。也许你的用例的环境条件很局限,但你仍旧希望它能在各种环境下发挥理想的效果。不同的人使用这些代码时会出现各种用例,你会看到各类冲突,还有你没有考虑过的思路。虽然代码不一定要满足所有人,但至少应该得体地处理他们遇到的问题,就算解决不了,也可以转换回常见的逻辑,不会给使用者添麻烦。(例如“第 583 行出现零除错误”就不能作为错误地提供命令行参数的响应结果)
|
||||
|
||||
你的源代码也可能逼疯你,尤其是在你一遍又一遍地修改错误的函数或是子过程后,终于出现了你希望的结果,这时你不会叹口气就继续下一个任务,你会把过程清理干净,因为你不会愿意别人看出你一遍遍尝试的痕迹。比如你会把 `$variable`、`$lol` 全都换成有意义的 `$iterationcounter` 和 `$modelname`。这意味着你要认真专业地进行注释(尽管对于你所处的背景知识热度来说它并不难懂),因为你期望有更多的人可以使用你的代码。
|
||||
|
||||
这个过程难免有些痛苦沮丧,毕竟这不是你常做的事,会有些不习惯。但它会使你成为一位更好的程序员,也会让你的代码升华。即使你的项目只有你一位贡献者,清理代码也会节约你后期的很多工作,相信我一年后你再看你的 app 代码时,你会庆幸自己写下的是 `$modelname`,还有清晰的注释,而不是什么不知名的数列,甚至连 `$lol` 也不是。
|
||||
|
||||
### 你并不是为你一人而写
|
||||
|
||||
开源的真正核心并不是那些代码,而是社区。更大的社区的项目维持时间更长,也更容易为人们所接受。因此不仅要加入社区,还要多多为社区发展贡献思路,让自己的项目能够为社区所用。
|
||||
|
||||
蝙蝠侠为了完成目标暗中独自花了很大功夫,你用不着这样,你可以登录 Twitter、Reddit,或者给你项目的相关人士发邮件,发布你正在筹备新项目的消息,仔细聊聊项目的设计初衷和你的计划,让大家一起帮忙,向大家征集数据输入,类似的使用案例,把这些信息整合起来,用在你的代码里。你不用接受所有的建议和请求,但你要对它有个大概把握,这样在你之后完善时可以躲过一些陷阱。
|
||||
|
||||
发布了首次通告这个过程还不算完整。如果你希望大家能够接受你的作品并且使用它,你就要以此为初衷来设计。公众说不定可以帮到你,你不必对公开这件事如临大敌。所以不要闭门造车,既然你是为大家而写,那就开设一个真实、公开的项目,想象你在社区的帮助和监督下,认真地一步步完成它。
|
||||
|
||||
### 建立项目的方式
|
||||
|
||||
你可以在 GitHub、GitLab 或 BitBucket 上免费注册账号来管理你的项目。注册之后,创建知识库,建立 `README` 文件,分配一个许可证,一步步写入代码。这样可以帮你建立好习惯,让你之后和现实中的团队一起工作时,也能目的清晰地朝着目标稳妥地开展工作。这样你做得越久,就越有兴趣 —— 通常会有用户先对你的项目产生兴趣。
|
||||
|
||||
用户会开始提一些问题,这会让你开心也会让你不爽,你应该亲切礼貌地对待他们,就算他们很多人对项目有很多误解甚至根本不知道你的项目做的是什么,你也应该礼貌专业地对待。一方面,你可以引导他们,让他们了解你在干什么。另一方面,他们也会慢慢地将你带入更大的社区。
|
||||
|
||||
如果你的项目很受用户青睐,总会有高级开发者出现,并表示出兴趣。这也许是好事,也可能激怒你。最开始你可能只会做简单的问题修复,但总有一天你会收到拉取请求,有可能是硬编码或特殊用例(可能会让项目变得难以维护),它可能改变你项目的作用域,甚至改变你项目的初衷。你需要学会分辨哪个有贡献,根据这个决定合并哪个,婉拒哪个。
|
||||
|
||||
### 我们为什么要开源?
|
||||
|
||||
开源听起来任务繁重,它也确实是这样。但它对你也有很多好处。它可以在无形之中磨练你,让你写出纯净持久的代码,也教会你与人沟通,团队协作。对于一个志向远大的专业开发者来说,它是最好的简历素材。你的未来雇主很有可能点开你的仓库,了解你的能力范围;而社区项目的开发者也有可能给你带来工作。
|
||||
|
||||
最后,为开源工作,意味着个人的提升,因为你在做的事不是为了你一个人,这比养活自己重要得多。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/3/what-open-source-programming
|
||||
|
||||
作者:[Jim Salter][a]
|
||||
译者:[Valoniakim](https://github.com/Valoniakim)
|
||||
校对:[wxy](https://github.com/wxy)、[pityonline](https://github.com/pityonline)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jim-salter
|
||||
[1]: https://opensource.com/tags/licensing
|
||||
[2]: https://choosealicense.com/
|
||||
[3]: https://choosealicense.com/licenses/apache-2.0/
|
||||
[4]: https://choosealicense.com/licenses/gpl-3.0/
|
||||
[5]: https://choosealicense.com/licenses/unlicense/
|
@ -0,0 +1,142 @@
|
||||
如何从 Linux 的文件管理器中加密文件
|
||||
======
|
||||
|
||||
![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/encryption.jpg?itok=Pk3_x5hz)
|
||||
|
||||
Linux 桌面版和服务器版具有卓越的安全性。然而这并不意味着你可以放松警惕。你应该一直认为你的数据总是很快就会被破坏。也就是说,你可能需要使用各种加密工具。比如 GnuPG,它可以让你加密和解密文件等更多功能。GnuPG 的一个问题是一些用户不想在命令行输入那么复杂的命令。如果是这样的话,你可以转向桌面文件管理器。许多 Linux 桌面版包含了简易的加密和解密文件的功能,如果这种功能没有内置,那么也是很容易添加的。
|
||||
|
||||
我将引导你完成从三个流行的 Linux 文件管理器中对文件进行加密和解密过程:
|
||||
|
||||
* Nautilus (即 GNOME Files)
|
||||
* Dolphin
|
||||
* Thunar
|
||||
|
||||
### 安装 GnuPG
|
||||
|
||||
在我们讨论如何处理这个问题之前,我们必须确保你的系统包含了必要的基本组件 —— [GnuPG][1]。大多数发行版本都包含 GnuPG。 在某些偶然情况下,你使用的是没有自带 GnuPG 的发行版,以下是安装方法:
|
||||
|
||||
* 基于 Ubuntu 的发行版: `sudo apt install gnupg`
|
||||
* 基于 Fedora 的发行版: `sudo yum install gnupg`
|
||||
* openSUSE: `sudo zypper in gnupg`
|
||||
* 基于 Arch 的发行版:`sudo pacman -S gnupg`
|
||||
|
||||
|
||||
无论你是刚刚安装了 GnuPG,还是默认安装的,你都必须创建一个 GPG 密钥才能使用。每个桌面版都使用不同的 GUI 工具完成这个工作(或者甚至根本没有包含一个完成这个任务的 GUI 工具),所以让我们从命令行中创建这个密钥。打开终端窗口并输入以下指令:
|
||||
|
||||
```
|
||||
gpg --gen-key
|
||||
```
|
||||
|
||||
然后,你将被要求回答以下问题。除非你有充分的理由,否则你可以接受默认值:
|
||||
|
||||
* 你想要哪种密钥?
|
||||
* 你想要多长的密钥?
|
||||
* 密钥有效期?
|
||||
|
||||
一旦你回答了这些问题,输入 `y` 来表示答案是正确的。接下来你需要提供以下信息:
|
||||
|
||||
* 真实姓名。
|
||||
* Email 地址。
|
||||
* 备注。
|
||||
|
||||
完成上述操作后,然后在提示的时候输入 `O` (即 ok)。然后,你将被要求为新密钥输入一个密码。一旦系统收集到了足够的熵(你需要在桌面上做一些工作才能做到这一点,LCTT 译注:比如随便敲击键盘),你的密钥就会被创建,然后你就可以开始工作了。
|
||||
|
||||
让我们看看如何从文件管理器中加密/解密文件:
|
||||
|
||||
### Nautilus
|
||||
|
||||
让我们从默认的 GNOME 文件管理器开始,因为它是最简单的。Nautilus 不需要额外安装什么或额外的工作就可以从精心设计的界面内加密/解密文件。一旦你创建完 gpg 密钥后,就可以打开文件管理器,导航到包含要加密的文件的目录,右键单击要加密的文件,然后从菜单中选择 “Encrypt” (图1)。
|
||||
|
||||
![nautilus][3]
|
||||
|
||||
*图1:从 Nautilus 中加密文件。*
|
||||
|
||||
你将被要求选择一个收件人(或者收件人列表 —— 图2)。注意:收件人将是那些你已经导入了公钥的用户。选择所需的密钥,然后从签名信息下拉列表中选择你的密钥(电子邮件地址)。
|
||||
|
||||
![nautilus][6]
|
||||
|
||||
*图2:选择收件人和签名者。*
|
||||
|
||||
注意:你还可以选择仅使用密码来加密文件。如果文件将保留在你的本地机器上,这一点非常重要(稍后将详细介绍)。一旦你设置好加密后,单击 “OK” 并(在提示时)输入 gpg 密钥的密码。文件将被加密(现在文件名以 .gpg 结尾)并保存在工作目录中。现在你可以将加密后的文件发送给在加密过程中已选择的收件人。
|
||||
|
||||
比如说(有你的公钥的)某人已经给你发送了一个加密文件。保存该文件,打开文件管理器,导航到该文件所在的目录,右击这个加密文件,选择 “Open With Decrypt File”,给文件一个新名称(不带 .gpg 的扩展名),然后单击“Save”。当提示时,输入你的 gpg 密钥的密码,该文件就会被解密并准备使用。
|
||||
|
||||
### Dolphin
|
||||
|
||||
在 KDE 前端,必须安装一个软件包才能使用 Dolphin 文件管理器进行加密/解密。 登录到你的 KDE 桌面,打开终端窗口,然后输入以下命令(我正在使用 Neon 进行演示。如果你的发行版不是基于 Ubuntu 的,则必须相应地更改命令):
|
||||
|
||||
```
|
||||
sudo apt install kgpg
|
||||
```
|
||||
|
||||
安装完毕后,注销并重新登录 KDE 桌面。 你可以打开 Dolphin 并右键单击要加密的文件。 由于这是你第一次使用 kgpg,因此你必须完成快速设置向导(不言自明)。当完成该向导后,你可以返回该文件,右键单击它(图3),然后选择 Encrypt File。
|
||||
|
||||
![Dolphin][8]
|
||||
|
||||
*图3:在 Dolphin 中加密文件。*
|
||||
|
||||
系统将提示你选择用于加密的密钥(图4)。 进行选择并单击 “OK”。 该文件将被加密,而且你已准备好将其发送给收件人。
|
||||
|
||||
注意:使用 KDE 的 Dolphin 文件管理器市,你无法仅使用密码加密。
|
||||
|
||||
![Dolphin][10]
|
||||
|
||||
*图4:选择要加密的收件人。*
|
||||
|
||||
如果你收到来自有你的公钥的用户的加密文件(或者你有一个自己加密的文件),请打开 Dolphin,导航到相关文件,双击该文件,为该文件指定一个新名称 ,键入加密密码并单击 “OK”。 你现在可以读取到新解密的文件。 如果你使用自己的密钥加密了该文件,则不会提示你键入密码(因为它已经被存储了)。
|
||||
|
||||
### Thunar
|
||||
|
||||
Thunar 文件管理器有点棘手。 没有任何额外的软件包可供安装;相反,你需要为了加密创建新的自定义操作。完成此操作后,你将能够在文件管理器中执行此操作。
|
||||
|
||||
要创建自定义操作,请打开 Thunar 文件管理器,然后单击 “Edit > Configure Custom Actions”。 在所得到的窗口中,单击 “+” 按钮(图5)并为加密操作输入以下内容:
|
||||
|
||||
- 名称:加密
|
||||
- 描述:文件加密
|
||||
- 命令:`gnome-terminal -x gpg --encrypt --recipient %f`
|
||||
|
||||
单击 “OK” 以保存此操作。
|
||||
|
||||
![Thunar][12]
|
||||
|
||||
*图5:在Thunar中创建自定义操作。*
|
||||
|
||||
注意:如果 gnome-terminal 不是你的默认终端,请替换该命令以打开你的默认终端。
|
||||
|
||||
你还可以创建仅使用密码(而非密钥)加密的操作。 为此,该操作的详细动作将会是以下内容:
|
||||
|
||||
- 名称:加密密码
|
||||
- 说明:仅使用密码加密
|
||||
- 命令:`gnome-terminal -x gpg -c %f`
|
||||
|
||||
你不需要为解密过程创建一个自定义操作,因为 Thunar 已经知道如何处理一个加密文件。 要解密文件,只需(在 Thunar 中)右键单击它,选择 “Open With Decrypt File”,为解密文件命名,然后(在提示时)键入加密密码。 Viola,你的加密文件已被解密并可以使用。
|
||||
|
||||
### 一个警告
|
||||
|
||||
请注意:如果你使用自己的密钥加密自己的文件,则无需输入加密密码来解密它们(因为你的公钥已存储)。 但是,如果你收到(拥有你的公钥的)其他人的文件,则需要输入你的密码。 如果你想要存储自己的加密文件,而不是使用密钥来加密它们,请使用仅使用密码加密。 Nautilus 和Thunar(但不是 KDE)可以做到这一点。 通过选择密码加密(通过密钥加密),当你解密文件时,它将始终提示你输入密码。
|
||||
|
||||
### 其他文件管理器
|
||||
|
||||
还有很多其它的文件管理器,它们中的一些可以使用加密,有些则不能。 你很有可能正在使用这三种工具中的一种,因此不仅可以将加密 / 解密添加到上下文菜单,而且是非常容易。 尝试一下,看看它是否会使加密和解密的过程变得更容易。
|
||||
|
||||
从 Linux 基金会和 edX 免费提供的[“Linux 介绍”][13]课程了解更多关于 Linux 的信息。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/learn/intro-to-linux/2018/3/how-encrypt-files-within-file-manager
|
||||
|
||||
作者:[JACK WALLEN][a]
|
||||
译者:[Auk7f7](https://github.com/Auk7f7)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.linux.com/users/jlwallen
|
||||
[1]:https://www.gnupg.org/
|
||||
[3]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/nautilus.jpg?itok=ae7Gtj60 "nautilus"
|
||||
[4]:https://www.linux.com/licenses/category/used-permission
|
||||
[6]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/nautilus_2.jpg?itok=3ht7j63n "nautilus"
|
||||
[8]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/kde_0.jpg?itok=KSTctVw0 "Dolphin"
|
||||
[10]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/kde_2.jpg?itok=CeqWikNl "Dolphin"
|
||||
[12]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/thunar.jpg?itok=fXcHk08B "Thunar"
|
||||
[13]:https://training.linuxfoundation.org/linux-courses/system-administration-training/introduction-to-linux
|
@ -0,0 +1,198 @@
|
||||
如何在 Linux 中压缩和解压缩文件
|
||||
======
|
||||
|
||||
![](https://www.ostechnix.com/wp-content/uploads/2018/03/compress-720x340.jpg)
|
||||
|
||||
当在备份重要文件和通过网络发送大文件的时候,对文件进行压缩非常有用。请注意,压缩一个已经压缩过的文件会增加额外开销,因此你将会得到一个更大一些的文件。所以,请不要压缩已经压缩过的文件。在 GNU/Linux 中,有许多程序可以用来压缩和解压缩文件。在这篇教程中,我们仅学习其中两个应用程序。
|
||||
|
||||
在类 Unix 系统中,最常见的用来压缩文件的程序是:
|
||||
|
||||
1. gzip
|
||||
2. bzip2
|
||||
|
||||
### 1. 使用 gzip 程序来压缩和解压缩文件
|
||||
|
||||
`gzip` 是一个使用 Lempel-Ziv 编码(LZ77)算法来压缩和解压缩文件的实用工具。
|
||||
|
||||
#### 1.1 压缩文件
|
||||
|
||||
如果要压缩一个名为 `ostechnix.txt` 的文件,使之成为 gzip 格式的压缩文件,那么只需运行如下命令:
|
||||
|
||||
```
|
||||
$ gzip ostechnix.txt
|
||||
```
|
||||
|
||||
上面的命令运行结束之后,将会出现一个名为 `ostechnix.txt.gz` 的 gzip 格式压缩文件,代替了原始的 `ostechnix.txt` 文件。
|
||||
|
||||
`gzip` 命令还可以有其他用法。一个有趣的例子是,我们可以将一个特定命令的输出通过管道传递,然后作为 `gzip` 程序的输入来创建一个压缩文件。看下面的命令:
|
||||
|
||||
```
|
||||
$ ls -l Downloads/ | gzip > ostechnix.txt.gz
|
||||
```
|
||||
|
||||
上面的命令将会创建一个 gzip 格式的压缩文件,文件的内容为 `Downloads` 目录的目录项。
|
||||
|
||||
#### 1.2 压缩文件并将输出写到新文件中(不覆盖原始文件)
|
||||
|
||||
默认情况下,`gzip` 程序会压缩给定文件,并以压缩文件替代原始文件。但是,你也可以保留原始文件,并将输出写到标准输出。比如,下面这个命令将会压缩 `ostechnix.txt` 文件,并将输出写入文件 `output.txt.gz`。
|
||||
|
||||
```
|
||||
$ gzip -c ostechnix.txt > output.txt.gz
|
||||
```
|
||||
|
||||
类似地,要解压缩一个 `gzip` 格式的压缩文件并指定输出文件的文件名,只需运行:
|
||||
|
||||
```
|
||||
$ gzip -c -d output.txt.gz > ostechnix1.txt
|
||||
```
|
||||
|
||||
上面的命令将会解压缩 `output.txt.gz` 文件,并将输出写入到文件 `ostechnix1.txt` 中。在上面两个例子中,原始文件均不会被删除。
|
||||
|
||||
#### 1.3 解压缩文件
|
||||
|
||||
如果要解压缩 `ostechnix.txt.gz` 文件,并以原始未压缩版本的文件来代替它,那么只需运行:
|
||||
|
||||
```
|
||||
$ gzip -d ostechnix.txt.gz
|
||||
```
|
||||
|
||||
我们也可以使用 `gunzip` 程序来解压缩文件:
|
||||
|
||||
```
|
||||
$ gunzip ostechnix.txt.gz
|
||||
```
|
||||
|
||||
#### 1.4 在不解压缩的情况下查看压缩文件的内容
|
||||
|
||||
如果你想在不解压缩的情况下,使用 `gzip` 程序查看压缩文件的内容,那么可以像下面这样使用 `-c` 选项:
|
||||
|
||||
```
|
||||
$ gunzip -c ostechnix1.txt.gz
|
||||
```
|
||||
|
||||
或者,你也可以像下面这样使用 `zcat` 程序:
|
||||
|
||||
```
|
||||
$ zcat ostechnix.txt.gz
|
||||
```
|
||||
|
||||
你也可以通过管道将输出传递给 `less` 命令,从而一页一页的来查看输出,就像下面这样:
|
||||
|
||||
```
|
||||
$ gunzip -c ostechnix1.txt.gz | less
|
||||
$ zcat ostechnix.txt.gz | less
|
||||
```
|
||||
|
||||
另外,`zless` 程序也能够实现和上面的管道同样的功能。
|
||||
|
||||
```
|
||||
$ zless ostechnix1.txt.gz
|
||||
```
|
||||
|
||||
#### 1.5 使用 gzip 压缩文件并指定压缩级别
|
||||
|
||||
`gzip` 的另外一个显著优点是支持压缩级别。它支持下面给出的 3 个压缩级别:
|
||||
|
||||
* **1** – 最快 (最差)
|
||||
* **9** – 最慢 (最好)
|
||||
* **6** – 默认级别
|
||||
|
||||
要压缩名为 `ostechnix.txt` 的文件,使之成为“最好”压缩级别的 gzip 压缩文件,可以运行:
|
||||
|
||||
```
|
||||
$ gzip -9 ostechnix.txt
|
||||
```
|
||||
|
||||
#### 1.6 连接多个压缩文件
|
||||
|
||||
我们也可以把多个需要压缩的文件压缩到同一个文件中。如何实现呢?看下面这个例子。
|
||||
|
||||
```
|
||||
$ gzip -c ostechnix1.txt > output.txt.gz
|
||||
$ gzip -c ostechnix2.txt >> output.txt.gz
|
||||
```
|
||||
|
||||
上面的两个命令将会压缩文件 `ostechnix1.txt` 和 `ostechnix2.txt`,并将输出保存到一个文件 `output.txt.gz` 中。
|
||||
|
||||
你可以通过下面其中任何一个命令,在不解压缩的情况下,查看两个文件 `ostechnix1.txt` 和 `ostechnix2.txt` 的内容:
|
||||
|
||||
```
|
||||
$ gunzip -c output.txt.gz
|
||||
$ gunzip -c output.txt
|
||||
$ zcat output.txt.gz
|
||||
$ zcat output.txt
|
||||
```
|
||||
|
||||
如果你想了解关于 `gzip` 的更多细节,请参阅它的 man 手册。
|
||||
|
||||
```
|
||||
$ man gzip
|
||||
```
|
||||
|
||||
### 2. 使用 bzip2 程序来压缩和解压缩文件
|
||||
|
||||
`bzip2` 和 `gzip` 非常类似,但是 `bzip2` 使用的是 Burrows-Wheeler 块排序压缩算法,并使用<ruby>哈夫曼<rt>Huffman</rt></ruby>编码。使用 `bzip2` 压缩的文件以 “.bz2” 扩展结尾。
|
||||
|
||||
正如我上面所说的, `bzip2` 的用法和 `gzip` 几乎完全相同。只需在上面的例子中将 `gzip` 换成 `bzip2`,将 `gunzip` 换成 `bunzip2`,将 `zcat` 换成 `bzcat` 即可。
|
||||
|
||||
要使用 `bzip2` 压缩一个文件,并以压缩后的文件取而代之,只需运行:
|
||||
|
||||
```
|
||||
$ bzip2 ostechnix.txt
|
||||
```
|
||||
|
||||
如果你不想替换原始文件,那么可以使用 `-c` 选项,并把输出写入到新文件中。
|
||||
|
||||
```
|
||||
$ bzip2 -c ostechnix.txt > output.txt.bz2
|
||||
```
|
||||
|
||||
如果要解压缩文件,则运行:
|
||||
|
||||
```
|
||||
$ bzip2 -d ostechnix.txt.bz2
|
||||
```
|
||||
|
||||
或者,
|
||||
|
||||
```
|
||||
$ bunzip2 ostechnix.txt.bz2
|
||||
```
|
||||
|
||||
如果要在不解压缩的情况下查看一个压缩文件的内容,则运行:
|
||||
|
||||
```
|
||||
$ bunzip2 -c ostechnix.txt.bz2
|
||||
```
|
||||
|
||||
或者,
|
||||
|
||||
```
|
||||
$ bzcat ostechnix.txt.bz2
|
||||
```
|
||||
|
||||
如果你想了解关于 `bzip2` 的更多细节,请参阅它的 man 手册。
|
||||
|
||||
```
|
||||
$ man bzip2
|
||||
```
|
||||
|
||||
### 总结
|
||||
|
||||
在这篇教程中,我们学习了 `gzip` 和 `bzip2` 程序是什么,并通过 GNU/Linux 下的一些例子学习了如何使用它们来压缩和解压缩文件。接下来,我们将要学习如何在 Linux 中将文件和目录归档。
|
||||
|
||||
干杯!
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/how-to-compress-and-decompress-files-in-linux/
|
||||
|
||||
作者:[SK][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[ucasFL](https://github.com/ucasFL)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.ostechnix.com/author/sk/
|
@ -0,0 +1,155 @@
|
||||
用 Hugo 30 分钟搭建静态博客
|
||||
======
|
||||
> 了解 Hugo 如何使构建网站变得有趣。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/programming-code-keyboard-laptop-music-headphones.png?itok=EQZ2WKzy)
|
||||
|
||||
你是不是强烈地想搭建博客来将自己对软件框架等的探索学习成果分享呢?你是不是面对缺乏指导文档而一团糟的项目就有一种想去改变它的冲动呢?或者换个角度,你是不是十分期待能创建一个属于自己的个人博客网站呢?
|
||||
|
||||
很多人在想搭建博客之前都有一些严重的迟疑顾虑:感觉自己缺乏内容管理系统(CMS)的相关知识,更缺乏时间去学习这些知识。现在,如果我说不用花费大把的时间去学习 CMS 系统、学习如何创建一个静态网站、更不用操心如何去强化网站以防止它受到黑客攻击的问题,你就可以在 30 分钟之内创建一个博客?你信不信?利用 Hugo 工具,就可以实现这一切。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/hugo_1.png?itok=JgxBSOBG)
|
||||
|
||||
Hugo 是一个基于 Go 语言开发的静态站点生成工具。也许你会问,为什么选择它?
|
||||
|
||||
* 无需数据库、无需需要各种权限的插件、无需跑在服务器上的底层平台,更没有额外的安全问题。
|
||||
* 都是静态站点,因此拥有轻量级、快速响应的服务性能。此外,所有的网页都是在部署的时候生成,所以服务器负载很小。
|
||||
* 极易操作的版本控制。一些 CMS 平台使用它们自己的版本控制软件(VCS)或者在网页上集成 Git 工具。而 Hugo,所有的源文件都可以用你所选的 VCS 软件来管理。
|
||||
|
||||
### 0-5 分钟:下载 Hugo,生成一个网站
|
||||
|
||||
直白的说,Hugo 使得写一个网站又一次变得有趣起来。让我们来个 30 分钟计时,搭建一个网站。
|
||||
|
||||
为了简化 Hugo 安装流程,这里直接使用 Hugo 可执行安装文件。
|
||||
|
||||
1. 下载和你操作系统匹配的 Hugo [版本][2];
|
||||
2. 压缩包解压到指定路径,例如 windows 系统的 `C:\hugo_dir` 或者 Linux 系统的 `~/hugo_dir` 目录;下文中的变量 `${HUGO_HOME}` 所指的路径就是这个安装目录;
|
||||
3. 打开命令行终端,进入安装目录:`cd ${HUGO_HOME}`;
|
||||
4. 确认 Hugo 已经启动:
|
||||
* Unix 系统:`${HUGO_HOME}/[hugo version]`;
|
||||
* Windows 系统:`${HUGO_HOME}\[hugo.exe version]`,例如:cmd 命令行中输入:`c:\hugo_dir\hugo version`。
|
||||
|
||||
为了书写上的简化,下文中的 `hugo` 就是指 hugo 可执行文件所在的路径(包括可执行文件),例如命令 `hugo version` 就是指命令 `c:\hugo_dir\hugo version` 。(LCTT 译注:可以把 hugo 可执行文件所在的路径添加到系统环境变量下,这样就可以直接在终端中输入 `hugo version`)
|
||||
|
||||
如果命令 `hugo version` 报错,你可能下载了错误的版本。当然,有很多种方法安装 Hugo,更多详细信息请查阅 [官方文档][3]。最稳妥的方法就是把 Hugo 可执行文件放在某个路径下,然后执行的时候带上路径名
|
||||
5. 创建一个新的站点来作为你的博客,输入命令:`hugo new site awesome-blog`;
|
||||
6. 进入新创建的路径下: `cd awesome-blog`;
|
||||
|
||||
恭喜你!你已经创建了自己的新博客。
|
||||
|
||||
### 5-10 分钟:为博客设置主题
|
||||
|
||||
Hugo 中你可以自己构建博客的主题或者使用网上已经有的一些主题。这里选择 [Kiera][4] 主题,因为它简洁漂亮。按以下步骤来安装该主题:
|
||||
|
||||
1. 进入主题所在目录:`cd themes`;
|
||||
2. 克隆主题:`git clone https://github.com/avianto/hugo-kiera kiera`。如果你没有安装 Git 工具:
|
||||
* 从 [Github][5] 上下载 hugo 的 .zip 格式的文件;
|
||||
* 解压该 .zip 文件到你的博客主题 `theme` 路径;
|
||||
* 重命名 `hugo-kiera-master` 为 `kiera`;
|
||||
3. 返回博客主路径:`cd awesome-blog`;
|
||||
4. 激活主题;通常来说,主题(包括 Kiera)都自带文件夹 `exampleSite`,里面存放了内容配置的示例文件。激活 Kiera 主题需要拷贝它提供的 `config.toml` 到你的博客下:
|
||||
* Unix 系统:`cp themes/kiera/exampleSite/config.toml .`;
|
||||
* Windows 系统:`copy themes\kiera\exampleSite\config.toml .`;
|
||||
* 选择 `Yes` 来覆盖原有的 `config.toml`;
|
||||
|
||||
5. ( 可选操作 )你可以选择可视化的方式启动服务器来验证主题是否生效:`hugo server -D` 然后在浏览器中输入 `http://localhost:1313`。可用通过在终端中输入 `Crtl+C` 来停止服务器运行。现在你的博客还是空的,但这也给你留了写作的空间。它看起来如下所示:
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/hugo_2.png?itok=PINOIOSU)
|
||||
|
||||
你已经成功的给博客设置了主题!你可以在官方 [Hugo 主题][4] 网站上找到上百种漂亮的主题供你使用。
|
||||
|
||||
### 10-20 分钟:给博客添加内容
|
||||
|
||||
对于碗来说,它是空的时候用处最大,可以用来盛放东西;但对于博客来说不是这样,空博客几乎毫无用处。在这一步,你将会给博客添加内容。Hugo 和 Kiera 主题都为这个工作提供了方便性。按以下步骤来进行你的第一次提交:
|
||||
|
||||
1. archetypes 将会是你的内容模板。
|
||||
2. 添加主题中的 archtypes 至你的博客:
|
||||
* Unix 系统: `cp themes/kiera/archetypes/* archetypes/`
|
||||
* Windows 系统:`copy themes\kiera\archetypes\* archetypes\`
|
||||
* 选择 `Yes` 来覆盖原来的 `default.md` 内容架构类型
|
||||
|
||||
3. 创建博客 posts 目录:
|
||||
* Unix 系统: `mkdir content/posts`
|
||||
* Windows 系统: `mkdir content\posts`
|
||||
|
||||
4. 利用 Hugo 生成你的 post:
|
||||
* Unix 系统:`hugo nes posts/first-post.md`;
|
||||
* Windows 系统:`hugo new posts\first-post.md`;
|
||||
|
||||
5. 在文本编辑器中打开这个新建的 post 文件:
|
||||
* Unix 系统:`gedit content/posts/first-post.md`;
|
||||
* Windows 系统:`notepadd content\posts\first-post.md`;
|
||||
|
||||
此刻,你可以疯狂起来了。注意到你的提交文件中包括两个部分。第一部分是以 `+++` 符号分隔开的。它包括了提交文档的主要数据,例如名称、时间等。在 Hugo 中,这叫做前缀。在前缀之后,才是正文。下面编辑第一个提交文件内容:
|
||||
|
||||
```
|
||||
+++
|
||||
title = "First Post"
|
||||
date = 2018-03-03T13:23:10+01:00
|
||||
draft = false
|
||||
tags = ["Getting started"]
|
||||
categories = []
|
||||
+++
|
||||
|
||||
Hello Hugo world! No more excuses for having no blog or documentation now!
|
||||
```
|
||||
|
||||
现在你要做的就是启动你的服务器:`hugo server -D`;然后打开浏览器,输入 `http://localhost:1313/`。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/hugo_3.png?itok=I-_v0qLx)
|
||||
|
||||
### 20-30 分钟:调整网站
|
||||
|
||||
前面的工作很完美,但还有一些问题需要解决。例如,简单地命名你的站点:
|
||||
|
||||
1. 终端中按下 `Ctrl+C` 以停止服务器。
|
||||
2. 打开 `config.toml`,编辑博客的名称,版权,你的姓名,社交网站等等。
|
||||
|
||||
当你再次启动服务器后,你会发现博客私人订制味道更浓了。不过,还少一个重要的基础内容:主菜单。快速的解决这个问题。返回 `config.toml` 文件,在末尾插入如下一段:
|
||||
|
||||
```
|
||||
[[menu.main]]
|
||||
name = "Home" #Name in the navigation bar
|
||||
weight = 10 #The larger the weight, the more on the right this item will be
|
||||
url = "/" #URL address
|
||||
[[menu.main]]
|
||||
name = "Posts"
|
||||
weight = 20
|
||||
url = "/posts/"
|
||||
```
|
||||
|
||||
上面这段代码添加了 `Home` 和 `Posts` 到主菜单中。你还需要一个 `About` 页面。这次是创建一个 `.md` 文件,而不是编辑 `config.toml` 文件:
|
||||
|
||||
1. 创建 `about.md` 文件:`hugo new about.md` 。注意它是 `about.md`,不是 `posts/about.md`。该页面不是博客提交内容,所以你不想它显示到博客内容提交当中吧。
|
||||
2. 用文本编辑器打开该文件,输入如下一段:
|
||||
|
||||
```
|
||||
+++
|
||||
title = "About"
|
||||
date = 2018-03-03T13:50:49+01:00
|
||||
menu = "main" #Display this page on the nav menu
|
||||
weight = "30" #Right-most nav item
|
||||
meta = "false" #Do not display tags or categories
|
||||
+++
|
||||
|
||||
> Waves are the practice of the water. Shunryu Suzuki
|
||||
```
|
||||
|
||||
当你启动你的服务器并输入:`http://localhost:1313/`,你将会看到你的博客。(访问我 Gihub 主页上的 [例子][6] )如果你想让文章的菜单栏和 Github 相似,给 `themes/kiera/static/css/styles.css` 打上这个 [补丁][7]。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/3/start-blog-30-minutes-hugo
|
||||
|
||||
作者:[Marek Czernek][a]
译者:[jrg](https://github.com/jrglinux)
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/mczernek
|
||||
[1]:https://gohugo.io/
|
||||
[2]:https://github.com/gohugoio/hugo/releases
|
||||
[3]:https://gohugo.io/getting-started/installing/
|
||||
[4]:https://themes.gohugo.io/
|
||||
[5]:https://github.com/avianto/hugo-kiera
|
||||
[6]:https://m-czernek.github.io/awesome-blog/
|
||||
[7]:https://github.com/avianto/hugo-kiera/pull/18/files
|
@ -0,0 +1,258 @@
|
||||
理解 ext4 等 Linux 文件系统
|
||||
======
|
||||
|
||||
> 了解 ext4 的历史,包括其与 ext3 和之前的其它文件系统之间的区别。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003499_01_linux11x_cc.png?itok=XMDOouJR)
|
||||
|
||||
目前的大部分 Linux 文件系统都默认采用 ext4 文件系统,正如以前的 Linux 发行版默认使用 ext3、ext2 以及更久前的 ext。
|
||||
|
||||
对于不熟悉 Linux 或文件系统的朋友而言,你可能不清楚 ext4 相对于上一版本 ext3 带来了什么变化。你可能还想知道在一连串关于替代的文件系统例如 Btrfs、XFS 和 ZFS 不断被发布的情况下,ext4 是否仍然能得到进一步的发展。
|
||||
|
||||
在一篇文章中,我们不可能讲述文件系统的所有方面,但我们尝试让你尽快了解 Linux 默认文件系统的发展历史,包括它的诞生以及未来发展。
|
||||
|
||||
我仔细研究了维基百科里的各种关于 ext 文件系统文章、kernel.org 的 wiki 中关于 ext4 的条目以及结合自己的经验写下这篇文章。
|
||||
|
||||
### ext 简史
|
||||
|
||||
#### MINIX 文件系统
|
||||
|
||||
在有 ext 之前,使用的是 MINIX 文件系统。如果你不熟悉 Linux 历史,那么可以理解为 MINIX 是用于 IBM PC/AT 微型计算机的一个非常小的类 Unix 系统。Andrew Tannenbaum 为了教学的目的而开发了它,并于 1987 年发布了源代码(以印刷版的格式!)。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/ibm_pc_at.jpg?itok=Tfk3hQYB)
|
||||
|
||||
*IBM 1980 中期的 PC/AT,[MBlairMartin](https://commons.wikimedia.org/wiki/File:IBM_PC_AT.jpg),[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)*
|
||||
|
||||
虽然你可以细读 MINIX 的源代码,但实际上它并不是自由开源软件(FOSS)。出版 Tannebaum 著作的出版商要求你花 69 美元的许可费来运行 MINIX,而这笔费用包含在书籍的费用中。尽管如此,在那时来说非常便宜,并且 MINIX 的使用得到迅速发展,很快超过了 Tannebaum 当初使用它来教授操作系统编码的意图。在整个 20 世纪 90 年代,你可以发现 MINIX 的安装在世界各个大学里面非常流行。而此时,年轻的 Linus Torvalds 使用 MINIX 来开发原始 Linux 内核,并于 1991 年首次公布,而后在 1992 年 12 月在 GPL 开源协议下发布。
|
||||
|
||||
但是等等,这是一篇以 *文件系统* 为主题的文章不是吗?是的,MINIX 有自己的文件系统,早期的 Linux 版本依赖于它。跟 MINIX 一样,Linux 的文件系统也如同玩具那般小 —— MINIX 文件系统最多能处理 14 个字符的文件名,并且只能处理 64MB 的存储空间。到了 1991 年,一般的硬盘尺寸已经达到了 40-140 MB。很显然,Linux 需要一个更好的文件系统。
|
||||
|
||||
#### ext
|
||||
|
||||
当 Linus 开发出刚起步的 Linux 内核时,Rémy Card 从事第一代的 ext 文件系统的开发工作。ext 文件系统在 1992 年首次实现并发布 —— 仅在 Linux 首次发布后的一年!—— ext 解决了 MINIX 文件系统中最糟糕的问题。
|
||||
|
||||
1992 年的 ext 使用在 Linux 内核中的新虚拟文件系统(VFS)抽象层。与之前的 MINIX 文件系统不同的是,ext 可以处理高达 2 GB 存储空间并处理 255 个字符的文件名。
|
||||
|
||||
但 ext 并没有长时间占统治地位,主要是由于它原始的时间戳(每个文件仅有一个时间戳,而不是今天我们所熟悉的有 inode、最近文件访问时间和最新文件修改时间的时间戳。)仅仅一年后,ext2 就替代了它。
|
||||
|
||||
#### ext2
|
||||
|
||||
Rémy 很快就意识到 ext 的局限性,所以一年后他设计出 ext2 替代它。当 ext 仍然根植于 “玩具” 操作系统时,ext2 从一开始就被设计为一个商业级文件系统,沿用 BSD 的 Berkeley 文件系统的设计原理。
|
||||
|
||||
ext2 提供了 GB 级别的最大文件大小和 TB 级别的文件系统大小,使其在 20 世纪 90 年代的地位牢牢巩固在文件系统大联盟中。很快它被广泛地使用,无论是在 Linux 内核中还是最终在 MINIX 中,且利用第三方模块可以使其应用于 MacOS 和 Windows。
|
||||
|
||||
但这里仍然有一些问题需要解决:ext2 文件系统与 20 世纪 90 年代的大多数文件系统一样,如果在将数据写入到磁盘的时候,系统发生崩溃或断电,则容易发生灾难性的数据损坏。随着时间的推移,由于碎片(单个文件存储在多个位置,物理上其分散在旋转的磁盘上),它们也遭受了严重的性能损失。
|
||||
|
||||
尽管存在这些问题,但今天 ext2 还是用在某些特殊的情况下 —— 最常见的是,作为便携式 USB 驱动器的文件系统格式。
|
||||
|
||||
#### ext3
|
||||
|
||||
1998 年,在 ext2 被采用后的 6 年后,Stephen Tweedie 宣布他正在致力于改进 ext2。这成了 ext3,并于 2001 年 11 月在 2.4.15 内核版本中被采用到 Linux 内核主线中。
|
||||
|
||||
![Packard Bell 计算机][2]
|
||||
|
||||
*20 世纪 90 年代中期的 Packard Bell 计算机,[Spacekid][3],[CC0][4]*
|
||||
|
||||
在大部分情况下,ext2 在 Linux 发行版中工作得很好,但像 FAT、FAT32、HFS 和当时的其它文件系统一样 —— 在断电时容易发生灾难性的破坏。如果在将数据写入文件系统时候发生断电,则可能会将其留在所谓 *不一致* 的状态 —— 事情只完成一半而另一半未完成。这可能导致大量文件丢失或损坏,这些文件与正在保存的文件无关甚至导致整个文件系统无法卸载。
|
||||
|
||||
ext3 和 20 世纪 90 年代后期的其它文件系统,如微软的 NTFS,使用 *日志* 来解决这个问题。日志是磁盘上的一种特殊的分配区域,其写入被存储在事务中;如果该事务完成磁盘写入,则日志中的数据将提交给文件系统自身。如果系统在该操作提交前崩溃,则重新启动的系统识别其为未完成的事务而将其进行回滚,就像从未发生过一样。这意味着正在处理的文件可能依然会丢失,但文件系统 *本身* 保持一致,且其它所有数据都是安全的。
|
||||
|
||||
在使用 ext3 文件系统的 Linux 内核中实现了三个级别的日志记录方式:<ruby>日记<rt>journal</rt></ruby>、<ruby>顺序<rt>ordered</rt></ruby>和<ruby>回写<rt>writeback</rt></ruby>。
|
||||
|
||||
* **日记** 是最低风险模式,在将数据和元数据提交给文件系统之前将其写入日志。这可以保证正在写入的文件与整个文件系统的一致性,但其显著降低了性能。
|
||||
* **顺序** 是大多数 Linux 发行版默认模式;顺序模式将元数据写入日志而直接将数据提交到文件系统。顾名思义,这里的操作顺序是固定的:首先,元数据提交到日志;其次,数据写入文件系统,然后才将日志中关联的元数据更新到文件系统。这确保了在发生崩溃时,那些与未完整写入相关联的元数据仍在日志中,且文件系统可以在回滚日志时清理那些不完整的写入事务。在顺序模式下,系统崩溃可能导致在崩溃期间文件的错误被主动写入,但文件系统它本身 —— 以及未被主动写入的文件 —— 确保是安全的。
|
||||
* **回写** 是第三种模式 —— 也是最不安全的日志模式。在回写模式下,像顺序模式一样,元数据会被记录到日志,但数据不会。与顺序模式不同,元数据和数据都可以以任何有利于获得最佳性能的顺序写入。这可以显著提高性能,但安全性低很多。尽管回写模式仍然保证文件系统本身的安全性,但在崩溃或崩溃之前写入的文件很容易丢失或损坏。
|
||||
|
||||
跟之前的 ext2 类似,ext3 使用 16 位内部寻址。这意味着对于有着 4K 块大小的 ext3 在最大规格为 16 TiB 的文件系统中可以处理的最大文件大小为 2 TiB。
|
||||
|
||||
#### ext4
|
||||
|
||||
Theodore Ts'o(是当时 ext3 主要开发人员)在 2006 年发表的 ext4,于两年后在 2.6.28 内核版本中被加入到了 Linux 主线。
|
||||
|
||||
Ts'o 将 ext4 描述为一个显著扩展 ext3 但仍然依赖于旧技术的临时技术。他预计 ext4 终将会被真正的下一代文件系统所取代。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/dell_precision_380_workstation.jpeg?itok=3EjYXY2i)
|
||||
|
||||
*Dell Precision 380 工作站,[Lance Fisher](https://commons.wikimedia.org/wiki/File:Dell_Precision_380_Workstation.jpeg),[CC BY-SA 2.0](https://creativecommons.org/licenses/by-sa/2.0/deed.en)*
|
||||
|
||||
ext4 在功能上与 ext3 在功能上非常相似,但支持大文件系统,提高了对碎片的抵抗力,有更高的性能以及更好的时间戳。
|
||||
|
||||
### ext4 vs ext3
|
||||
|
||||
ext3 和 ext4 有一些非常明确的差别,在这里集中讨论下。
|
||||
|
||||
#### 向后兼容性
|
||||
|
||||
ext4 特地设计为尽可能地向后兼容 ext3。这不仅允许 ext3 文件系统原地升级到 ext4;也允许 ext4 驱动程序以 ext3 模式自动挂载 ext3 文件系统,因此使它无需单独维护两个代码库。
|
||||
|
||||
#### 大文件系统
|
||||
|
||||
ext3 文件系统使用 32 位寻址,这限制它仅支持 2 TiB 文件大小和 16 TiB 文件系统系统大小(这是假设在块大小为 4 KiB 的情况下,一些 ext3 文件系统使用更小的块大小,因此对其进一步被限制)。
|
||||
|
||||
ext4 使用 48 位的内部寻址,理论上可以在文件系统上分配高达 16 TiB 大小的文件,其中文件系统大小最高可达 1000000 TiB(1 EiB)。在早期 ext4 的实现中有些用户空间的程序仍然将其限制为最大大小为 16 TiB 的文件系统,但截至 2011 年,e2fsprogs 已经直接支持大于 16 TiB 大小的 ext4 文件系统。例如,红帽企业 Linux 在其合同上仅支持最高 50 TiB 的 ext4 文件系统,并建议 ext4 卷不超过 100 TiB。
|
||||
|
||||
#### 分配方式改进
|
||||
|
||||
ext4 在将存储块写入磁盘之前对存储块的分配方式进行了大量改进,这可以显著提高读写性能。
|
||||
|
||||
##### 区段
|
||||
|
||||
<ruby>区段<rt>extent</rt></ruby>是一系列连续的物理块 (最多达 128 MiB,假设块大小为 4 KiB),可以一次性保留和寻址。使用区段可以减少给定文件所需的 inode 数量,并显著减少碎片并提高写入大文件时的性能。
|
||||
|
||||
##### 多块分配
|
||||
|
||||
ext3 为每一个新分配的块调用一次块分配器。当多个写入同时打开分配器时,很容易导致严重的碎片。然而,ext4 使用延迟分配,这允许它合并写入并更好地决定如何为尚未提交的写入分配块。
|
||||
|
||||
##### 持久的预分配
|
||||
|
||||
在为文件预分配磁盘空间时,大部分文件系统必须在创建时将零写入该文件的块中。ext4 允许替代使用 `fallocate()`,它保证了空间的可用性(并试图为它找到连续的空间),而不需要先写入它。这显著提高了写入和将来读取流和数据库应用程序的写入数据的性能。
|
||||
|
||||
##### 延迟分配
|
||||
|
||||
这是一个耐人寻味而有争议性的功能。延迟分配允许 ext4 等待分配将写入数据的实际块,直到它准备好将数据提交到磁盘。(相比之下,即使数据仍然在往写入缓存中写入,ext3 也会立即分配块。)
|
||||
|
||||
当缓存中的数据累积时,延迟分配块允许文件系统对如何分配块做出更好的选择,降低碎片(写入,以及稍后的读)并显著提升性能。然而不幸的是,它 *增加* 了还没有专门调用 `fsync()` 方法(当程序员想确保数据完全刷新到磁盘时)的程序的数据丢失的可能性。
|
||||
|
||||
假设一个程序完全重写了一个文件:
|
||||
|
||||
```
|
||||
fd=open("file", O_TRUNC); write(fd, data); close(fd);
|
||||
```
|
||||
|
||||
使用旧的文件系统,`close(fd);` 足以保证 `file` 中的内容刷新到磁盘。即使严格来说,写不是事务性的,但如果文件关闭后发生崩溃,则丢失数据的风险很小。
|
||||
|
||||
如果写入不成功(由于程序上的错误、磁盘上的错误、断电等),文件的原始版本和较新版本都可能丢失数据或损坏。如果其它进程在写入文件时访问文件,则会看到损坏的版本。如果其它进程打开文件并且不希望其内容发生更改 —— 例如,映射到多个正在运行的程序的共享库。这些进程可能会崩溃。
|
||||
|
||||
为了避免这些问题,一些程序员完全避免使用 `O_TRUNC`。相反,他们可能会写入一个新文件,关闭它,然后将其重命名为旧文件名:
|
||||
|
||||
```
|
||||
fd=open("newfile"); write(fd, data); close(fd); rename("newfile", "file");
|
||||
```
|
||||
|
||||
在 *没有* 延迟分配的文件系统下,这足以避免上面列出的潜在的损坏和崩溃问题:因为 `rename()` 是原子操作,所以它不会被崩溃中断;并且运行的程序将继续引用旧的文件。现在 `file` 的未链接版本只要有一个打开的文件文件句柄即可。但是因为 ext4 的延迟分配会导致写入被延迟和重新排序,`rename("newfile", "file")` 可以在 `newfile` 的内容实际写入磁盘内容之前执行,这出现了并行进行再次获得 `file` 坏版本的问题。
|
||||
|
||||
为了缓解这种情况,Linux 内核(自版本 2.6.30)尝试检测这些常见代码情况并强制立即分配。这会减少但不能防止数据丢失的可能性 —— 并且它对新文件没有任何帮助。如果你是一位开发人员,请注意:保证数据立即写入磁盘的唯一方法是正确调用 `fsync()`。
|
||||
|
||||
#### 无限制的子目录
|
||||
|
||||
ext3 仅限于 32000 个子目录;ext4 允许无限数量的子目录。从 2.6.23 内核版本开始,ext4 使用 HTree 索引来减少大量子目录的性能损失。
|
||||
|
||||
#### 日志校验
|
||||
|
||||
ext3 没有对日志进行校验,这给处于内核直接控制之外的磁盘或自带缓存的控制器设备带来了问题。如果控制器或具自带缓存的磁盘脱离了写入顺序,则可能会破坏 ext3 的日记事务顺序,从而可能破坏在崩溃期间(或之前一段时间)写入的文件。
|
||||
|
||||
理论上,这个问题可以使用写入<ruby>障碍<rt>barrier</rt></ruby> —— 在安装文件系统时,你在挂载选项设置 `barrier=1`,然后设备就会忠实地执行 `fsync` 一直向下到底层硬件。通过实践,可以发现存储设备和控制器经常不遵守写入障碍 —— 提高性能(和跟竞争对手比较的性能基准),但增加了本应该防止数据损坏的可能性。
|
||||
|
||||
对日志进行校验和允许文件系统崩溃后第一次挂载时意识到其某些条目是无效或无序的。因此,这避免了回滚部分条目或无序日志条目的错误,并进一步损坏的文件系统 —— 即使部分存储设备假做或不遵守写入障碍。
|
||||
|
||||
#### 快速文件系统检查
|
||||
|
||||
在 ext3 下,在 `fsck` 被调用时会检查整个文件系统 —— 包括已删除或空文件。相比之下,ext4 标记了 inode 表未分配的块和扇区,从而允许 `fsck` 完全跳过它们。这大大减少了在大多数文件系统上运行 `fsck` 的时间,它实现于内核 2.6.24。
|
||||
|
||||
#### 改进的时间戳
|
||||
|
||||
ext3 提供粒度为一秒的时间戳。虽然足以满足大多数用途,但任务关键型应用程序经常需要更严格的时间控制。ext4 通过提供纳秒级的时间戳,使其可用于那些企业、科学以及任务关键型的应用程序。
|
||||
|
||||
ext3 文件系统也没有提供足够的位来存储 2038 年 1 月 18 日以后的日期。ext4 在这里增加了两个位,将 [Unix 纪元][5]扩展了 408 年。如果你在公元 2446 年读到这篇文章,你很有可能已经转移到一个更好的文件系统 —— 如果你还在测量自 1970 年 1 月 1 日 00:00(UTC)以来的时间,这会让我死后得以安眠。
|
||||
|
||||
#### 在线碎片整理
|
||||
|
||||
ext2 和 ext3 都不直接支持在线碎片整理 —— 即在挂载时会对文件系统进行碎片整理。ext2 有一个包含的实用程序 `e2defrag`,它的名字暗示 —— 它需要在文件系统未挂载时脱机运行。(显然,这对于根文件系统来说非常有问题。)在 ext3 中的情况甚至更糟糕 —— 虽然 ext3 比 ext2 更不容易受到严重碎片的影响,但 ext3 文件系统运行 `e2defrag` 可能会导致灾难性损坏和数据丢失。
|
||||
|
||||
尽管 ext3 最初被认为“不受碎片影响”,但对同一文件(例如 BitTorrent)采用大规模并行写入过程的过程清楚地表明情况并非完全如此。一些用户空间的手段和解决方法,例如 [Shake][6],以这样或那样方式解决了这个问题 —— 但它们比真正的、文件系统感知的、内核级碎片整理过程更慢并且在各方面都不太令人满意。
|
||||
|
||||
ext4 通过 `e4defrag` 解决了这个问题,且是一个在线、内核模式、文件系统感知、块和区段级别的碎片整理实用程序。
|
||||
|
||||
### 正在进行的 ext4 开发
|
||||
|
||||
ext4,正如 Monty Python 中瘟疫感染者曾经说过的那样,“我还没死呢!”虽然它的[主要开发人员][7]认为它只是一个真正的[下一代文件系统][8]的权宜之计,但是在一段时间内,没有任何可能的候选人准备好(由于技术或许可问题)部署为根文件系统。
|
||||
|
||||
在未来的 ext4 版本中仍然有一些关键功能要开发,包括元数据校验和、一流的配额支持和大分配块。
|
||||
|
||||
#### 元数据校验和
|
||||
|
||||
由于 ext4 具有冗余超级块,因此为文件系统校验其中的元数据提供了一种方法,可以自行确定主超级块是否已损坏并需要使用备用块。可以在没有校验和的情况下,从损坏的超级块恢复 —— 但是用户首先需要意识到它已损坏,然后尝试使用备用方法手动挂载文件系统。由于在某些情况下,使用损坏的主超级块安装文件系统读写可能会造成进一步的损坏,即使是经验丰富的用户也无法避免,这也不是一个完美的解决方案!
|
||||
|
||||
与 Btrfs 或 ZFS 等下一代文件系统提供的极其强大的每块校验和相比,ext4 的元数据校验和的功能非常弱。但它总比没有好。虽然校验 **所有的事情** 都听起来很简单!—— 事实上,将校验和与文件系统连接到一起有一些重大的挑战;请参阅[设计文档][9]了解详细信息。
|
||||
|
||||
#### 一流的配额支持
|
||||
|
||||
等等,配额?!从 ext2 出现的那天开始我们就有了这些!是的,但它们一直都是事后的添加的东西,而且它们总是犯傻。这里可能不值得详细介绍,但[设计文档][10]列出了配额将从用户空间移动到内核中的方式,并且能够更加正确和高效地执行。
|
||||
|
||||
#### 大分配块
|
||||
|
||||
随着时间的推移,那些讨厌的存储系统不断变得越来越大。由于一些固态硬盘已经使用 8K 硬件块大小,因此 ext4 对 4K 模块的当前限制越来越受到限制。较大的存储块可以显著减少碎片并提高性能,代价是增加“松弛”空间(当你只需要块的一部分来存储文件或文件的最后一块时留下的空间)。
|
||||
|
||||
你可以在[设计文档][11]中查看详细说明。
|
||||
|
||||
### ext4 的实际限制
|
||||
|
||||
ext4 是一个健壮、稳定的文件系统。如今大多数人都应该在用它作为根文件系统,但它无法处理所有需求。让我们简单地谈谈你不应该期待的一些事情 —— 现在或可能在未来:
|
||||
|
||||
虽然 ext4 可以处理高达 1 EiB 大小(相当于 1,000,000 TiB)大小的数据,但你 *真的* 不应该尝试这样做。除了能够记住更多块的地址之外,还存在规模上的问题。并且现在 ext4 不会处理(并且可能永远不会)超过 50-100 TiB 的数据。
|
||||
|
||||
ext4 也不足以保证数据的完整性。随着日志记录的重大进展又回到了 ext3 的那个时候,它并未涵盖数据损坏的许多常见原因。如果数据已经在磁盘上被[破坏][12] —— 由于故障硬件,宇宙射线的影响(是的,真的),或者只是数据随时间衰减 —— ext4 无法检测或修复这种损坏。
|
||||
|
||||
基于上面两点,ext4 只是一个纯 *文件系统*,而不是存储卷管理器。这意味着,即使你有多个磁盘 —— 也就是奇偶校验或冗余,理论上你可以从 ext4 中恢复损坏的数据,但无法知道使用它是否对你有利。虽然理论上可以在不同的层中分离文件系统和存储卷管理系统而不会丢失自动损坏检测和修复功能,但这不是当前存储系统的设计方式,并且它将给新设计带来重大挑战。
|
||||
|
||||
### 备用文件系统
|
||||
|
||||
在我们开始之前,提醒一句:要非常小心,没有任何备用的文件系统作为主线内核的一部分而内置和直接支持!
|
||||
|
||||
即使一个文件系统是 *安全的*,如果在内核升级期间出现问题,使用它作为根文件系统也是非常可怕的。如果你没有充分的理由通过一个 chroot 去使用替代介质引导,耐心地操作内核模块、grub 配置和 DKMS……不要在一个很重要的系统中去掉预留的根文件。
|
||||
|
||||
可能有充分的理由使用你的发行版不直接支持的文件系统 —— 但如果你这样做,我强烈建议你在系统启动并可用后再安装它。(例如,你可能有一个 ext4 根文件系统,但是将大部分数据存储在 ZFS 或 Btrfs 池中。)
|
||||
|
||||
#### XFS
|
||||
|
||||
XFS 与非 ext 文件系统在 Linux 中的主线中的地位一样。它是一个 64 位的日志文件系统,自 2001 年以来内置于 Linux 内核中,为大型文件系统和高度并发性提供了高性能(即大量的进程都会立即写入文件系统)。
|
||||
|
||||
从 RHEL 7 开始,XFS 成为 Red Hat Enterprise Linux 的默认文件系统。对于家庭或小型企业用户来说,它仍然有一些缺点 —— 最值得注意的是,重新调整现有 XFS 文件系统是一件非常痛苦的事情,不如创建另一个并复制数据更有意义。
|
||||
|
||||
虽然 XFS 是稳定的且是高性能的,但它和 ext4 之间没有足够具体的最终用途差异,以值得推荐在非默认(如 RHEL7)的任何地方使用它,除非它解决了对 ext4 的特定问题,例如大于 50 TiB 容量的文件系统。
|
||||
|
||||
XFS 在任何方面都不是 ZFS、Btrfs 甚至 WAFL(一个专有的 SAN 文件系统)的“下一代”文件系统。就像 ext4 一样,它应该被视为一种更好的方式的权宜之计。
|
||||
|
||||
#### ZFS
|
||||
|
||||
ZFS 由 Sun Microsystems 开发,以 zettabyte 命名 —— 相当于 1 万亿 GB —— 因为它理论上可以解决大型存储系统。
|
||||
|
||||
作为真正的下一代文件系统,ZFS 提供卷管理(能够在单个文件系统中处理多个单独的存储设备),块级加密校验和(允许以极高的准确率检测数据损坏),[自动损坏修复][12](其中冗余或奇偶校验存储可用),[快速异步增量复制][13],内联压缩等,[以及更多][14]。
|
||||
|
||||
从 Linux 用户的角度来看,ZFS 的最大问题是许可证问题。ZFS 许可证是 CDDL 许可证,这是一种与 GPL 冲突的半许可的许可证。关于在 Linux 内核中使用 ZFS 的意义存在很多争议,其争议范围从“它是 GPL 违规”到“它是 CDDL 违规”到“它完全没问题,它还没有在法庭上进行过测试。”最值得注意的是,自 2016 年以来 Canonical 已将 ZFS 代码内联在其默认内核中,而且目前尚无法律挑战。
|
||||
|
||||
此时,即使我作为一个非常狂热于 ZFS 的用户,我也不建议将 ZFS 作为 Linux 的根文件系统。如果你想在 Linux 上利用 ZFS 的优势,用 ext4 设置一个小的根文件系统,然后将 ZFS 用在你剩余的存储上,把数据、应用程序以及你喜欢的东西放在它上面 —— 但把 root 分区保留在 ext4 上,直到你的发行版明确支持 ZFS 根目录。
|
||||
|
||||
#### Btrfs
|
||||
|
||||
Btrfs 是 B-Tree Filesystem 的简称,通常发音为 “butter” —— 由 Chris Mason 于 2007 年在 Oracle 任职期间发布。Btrfs 旨在跟 ZFS 有大部分相同的目标,提供多种设备管理、每块校验、异步复制、直列压缩等,[还有更多][8]。
|
||||
|
||||
截至 2018 年,Btrfs 相当稳定,可用作标准的单磁盘文件系统,但可能不应该依赖于卷管理器。与许多常见用例中的 ext4、XFS 或 ZFS 相比,它存在严重的性能问题,其下一代功能 —— 复制、多磁盘拓扑和快照管理 —— 可能非常多,其结果可能是从灾难性地性能降低到实际数据的丢失。
|
||||
|
||||
Btrfs 的维持状态是有争议的;SUSE Enterprise Linux 在 2015 年采用它作为默认文件系统,而 Red Hat 于 2017 年宣布它从 RHEL 7.4 开始不再支持 Btrfs。可能值得注意的是,该产品支持 Btrfs 部署用作单磁盘文件系统,而不是像 ZFS 中的多磁盘卷管理器,甚至 Synology 在它的存储设备使用 Btrfs,但是它在传统 Linux 内核 RAID(mdraid)之上分层来管理磁盘。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/4/ext4-filesystem
|
||||
|
||||
作者:[Jim Salter][a]
|
||||
译者:[HardworkFish](https://github.com/HardworkFish)
|
||||
校对:[wxy](https://github.com/wxy), [pityonline](https://github.com/pityonline)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jim-salter
|
||||
[1]: https://opensource.com/file/391546
|
||||
[2]: https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/packard_bell_pc.jpg?itok=VI8dzcwp (Packard Bell computer)
|
||||
[3]: https://commons.wikimedia.org/wiki/File:Old_packard_bell_pc.jpg
|
||||
[4]: https://creativecommons.org/publicdomain/zero/1.0/deed.en
|
||||
[5]: https://en.wikipedia.org/wiki/Unix_time
|
||||
[6]: https://vleu.net/shake/
|
||||
[7]: http://www.linux-mag.com/id/7272/
|
||||
[8]: https://arstechnica.com/information-technology/2014/01/bitrot-and-atomic-cows-inside-next-gen-filesystems/
|
||||
[9]: https://ext4.wiki.kernel.org/index.php/Ext4_Metadata_Checksums
|
||||
[10]: https://ext4.wiki.kernel.org/index.php/Design_For_1st_Class_Quota_in_Ext4
|
||||
[11]: https://ext4.wiki.kernel.org/index.php/Design_for_Large_Allocation_Blocks
|
||||
[12]: https://en.wikipedia.org/wiki/Data_degradation#Visual_example_of_data_degradation
|
||||
[13]: https://arstechnica.com/information-technology/2015/12/rsync-net-zfs-replication-to-the-cloud-is-finally-here-and-its-fast/
|
||||
[14]: https://arstechnica.com/information-technology/2014/02/ars-walkthrough-using-the-zfs-next-gen-filesystem-on-linux/
|
@ -1,11 +1,12 @@
|
||||
Free DOS 的简单介绍
|
||||
FreeDOS 的简单介绍
|
||||
======
|
||||
> 学习如何穿行于 C:\ 提示符下,就像上世纪 90 年代的 DOS 高手一样。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/freedos-fish-laptop-color.png?itok=vfv_Lpph)
|
||||
|
||||
FreeDOS 是一个古老的操作系统,但是对于多数人而言它又是陌生的。在 1994 年,我和几个开发者一起 [开发 FreeDOS][1]--一个完整、自由、DOS 兼容的操作系统,你可以用它来玩经典的 DOS 游戏、运行遗留的商业软件或者开发嵌入式系统。任何在 MS-DOS 下工作的程序在 FreeDOS 下也可以运行。
|
||||
FreeDOS 是一个古老的操作系统,但是对于多数人而言它又是陌生的。在 1994 年,我和几个开发者一起 [开发了 FreeDOS][1] —— 这是一个完整、自由、兼容 DOS 的操作系统,你可以用它来玩经典的 DOS 游戏、运行过时的商业软件或者开发嵌入式系统。任何在 MS-DOS 下工作的程序在 FreeDOS 下也可以运行。
|
||||
|
||||
在 1994 年,任何一个曾经使用过微软专利的 MS-DOS 的人都会迅速地熟悉 FreeDOS。这是设计而为之的;FreeDOS 尽可能地去模仿 MS-DOS。结果,1990 年代的 DOS 用户能够直接转换到 FreeDOS。但是,时代变了。今天,开源的开发者们对于 Linux 命令行更熟悉或者他们可能倾向于像 [GNOME][2] 一样的图形桌面环境,这导致 FreeDOS 命令行界面最初看起来像个异类。
|
||||
在 1994 年,任何一个曾经使用过微软的商业版 MS-DOS 的人都会迅速地熟悉 FreeDOS。这是设计而为之的;FreeDOS 尽可能地去模仿 MS-DOS。结果,1990 年代的 DOS 用户能够直接转换到 FreeDOS。但是,时代变了。今天,开源的开发者们对于 Linux 命令行更熟悉,或者他们可能倾向于像 [GNOME][2] 一样的图形桌面环境,这导致 FreeDOS 命令行界面最初看起来像个异类。
|
||||
|
||||
新的用户通常会问,“我已经安装了 [FreeDOS][3],但是如何使用呢?”。如果你之前并没有使用过 DOS,那么闪烁的 `C:\>` DOS 提示符看起来会有点不太友好,而且可能有点吓人。这份 FreeDOS 的简单介绍将带你起步。它只提供了基础:如何浏览以及如何查看文件。如果你想了解比这里提及的更多的知识,访问 [FreeDOS 维基][4]。
|
||||
|
||||
@ -15,13 +16,13 @@ FreeDOS 是一个古老的操作系统,但是对于多数人而言它又是陌
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/0-prompt.png)
|
||||
|
||||
DOS 是在个人电脑从软盘运行时期创建的一个“磁盘操作系统”。甚至当电脑支持硬盘了,在 1980 年代和 1990 年代,频繁地在不同的驱动器之间切换也是很普遍的。举例来说,你可能想将最重要的文件都备份一份拷贝到软盘中。
|
||||
DOS 是在个人电脑从软盘运行时期创建的一个“<ruby>磁盘操作系统<rt>disk operating system</rt></ruby>”。甚至当电脑支持硬盘了,在 1980 年代和 1990 年代,频繁地在不同的驱动器之间切换也是很普遍的。举例来说,你可能想将最重要的文件都备份一份拷贝到软盘中。
|
||||
|
||||
DOS 使用一个字母来指代每个驱动器。早期的电脑仅拥有两个软盘驱动器,他们被分配了 `A:` 和 `B:` 盘符。硬盘上的第一个分区盘符是 `C:` ,然后其它的盘符依次这样分配下去。提示符中的 `C:` 表示你正在使用第一个硬盘的第一个分区。
|
||||
|
||||
从 1983 年的 PC-DOS 2.0 开始,DOS 也支持目录和子目录,非常类似 Linux 文件系统中的目录和子目录。但是跟 Linux 不一样的是,DOS 目录名由 `\` 分隔而不是 `/`。将这个与驱动器字母合起来看,提示符中的 `C:\` 表示你正在 `C:` 盘的顶端或者“根”目录。
|
||||
|
||||
`>` 修饰符提示你输入 DOS 命令的地方,就像众多 Linux shell 的 `$`。`>` 前面的部分告诉你当前的工作目录,然后你在 `>` 提示符这输入命令。
|
||||
`>` 符号是提示你输入 DOS 命令的地方,就像众多 Linux shell 的 `$`。`>` 前面的部分告诉你当前的工作目录,然后你在 `>` 提示符这输入命令。
|
||||
|
||||
### 在 DOS 中找到你的使用方式
|
||||
|
||||
@ -33,7 +34,7 @@ DOS 使用一个字母来指代每个驱动器。早期的电脑仅拥有两个
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/1-dir.png)
|
||||
|
||||
如果你不想显示单个文件大小的额外细节,你可以在 `DIR` 命令中使用 `/w` 选项来显示一个“宽泛”文件夹。注意,Linux 用户使用连字号(`-`)或者双连字号(`--`)来开启命令行选项,而 DOS 使用斜线字符(`/`)。
|
||||
如果你不想显示单个文件大小的额外细节,你可以在 `DIR` 命令中使用 `/w` 选项来显示一个“宽”的目录列表。注意,Linux 用户使用连字号(`-`)或者双连字号(`--`)来开始命令行选项,而 DOS 使用斜线字符(`/`)。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/2-dirw.png)
|
||||
|
||||
@ -64,7 +65,7 @@ FreeDOS 也从 Linux 那借鉴了一些特性:你可以使用 `CD -` 跳转回
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/8-d-dirw.png)
|
||||
|
||||
小心不要尝试切换到一个不存在的磁盘。DOS 可能会将它设置为工作磁盘,但是如果你尝试在那做任何事,你将会遇到略微臭名昭著的“退出、重试、失败” DOS 错误信息。
|
||||
小心不要尝试切换到一个不存在的磁盘。DOS 可能会将它设置为工作磁盘,但是如果你尝试在那做任何事,你将会遇到略微臭名昭著的“<ruby>退出、重试、失败<rt>Abort, Retry, Fail</rt></ruby>” DOS 错误信息。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/9-e-fail.png)
|
||||
|
||||
@ -86,7 +87,7 @@ FreeDOS 也从 Linux 那借鉴了一些特性:你可以使用 `CD -` 跳转回
|
||||
|
||||
在 FreeDOS 下,针对每个命令你都能够使用 `/?` 参数来获取简要的说明。举例来说,`EDIT /?` 会告诉你编辑器的用法和选项。或者你可以输入 `HELP` 来使用交互式帮助系统。
|
||||
|
||||
像任何一个 DOS 一样,FreeDOS 被认为是一个简单的操作系统。仅使用一些基本命令就可以轻松浏览 DOS 文件系统。那么启动一个 QEMU 会话,安装 FreeDOS,然后尝试一下 DOS 命令行界面。也许它现在看起来就没那么吓人了。
|
||||
像任何一个 DOS 一样,FreeDOS 被认为是一个简单的操作系统。仅使用一些基本命令就可以轻松浏览 DOS 文件系统。那么,启动一个 QEMU 会话,安装 FreeDOS,然后尝试一下 DOS 命令行界面吧。也许它现在看起来就没那么吓人了。
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -96,7 +97,7 @@ via: https://opensource.com/article/18/4/gentle-introduction-freedos
|
||||
作者:[Jim Hall][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[icecoobe](https://github.com/icecoobe)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,22 +1,16 @@
|
||||
# 理解指标和使用 Python 去监视
|
||||
理解监测指标,并使用 Python 去监测它们
|
||||
======
|
||||
> 通过学习这些关键的术语和概念来理解 Python 应用监测。
|
||||
|
||||
![Understanding metrics and monitoring with Python](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/metrics_graph_stats_blue.png?itok=OKCc_60D "Understanding metrics and monitoring with Python")
|
||||
|
||||
Image by :
|
||||
当我第一次看到术语“<ruby>计数器<rt>counter</rt></ruby>”和“<ruby>计量器<rt>gauge</rt></ruby>”和使用颜色及标记着“平均数”和“大于 90%”的数字图表时,我的反应之一是逃避。就像我看到它们一样,我并不感兴趣,因为我不理解它们是干什么的或如何去使用。因为我的工作不需要我去注意它们,它们被我完全无视。
|
||||
|
||||
opensource.com
|
||||
这都是在两年以前的事了。随着我的职业发展,我希望去了解更多关于我们的网络应用程序的知识,而那个时候就是我开始去学习<ruby>监测指标<rt>metrics</rt></ruby>的时候。
|
||||
|
||||
## 获取订阅
|
||||
我的理解监测的学习之旅共有三个阶段(到目前为止),它们是:
|
||||
|
||||
加入我们吧,我们有 85,000 位开源支持者,加入后会定期接收到我们免费提供的提示和文章摘要。
|
||||
|
||||
当我第一次看到术语“计数器”和“计量器”和使用颜色及标记着“意思”和“最大 90”的数字图表时,我的反应之一是逃避。就像我看到它们一样,我并不感兴趣,因为我不理解它们是干什么的或如何去使用。因为我的工作不需要我去注意它们,它们被我完全无视。
|
||||
|
||||
这都是在两年以前的事了。随着我的职业发展,我希望去了解更多关于我们的网络应用程序的知识,而那个时候就是我开始去学习指标的时候。
|
||||
|
||||
我的理解监视的学习之旅共有三个阶段(到目前为止),它们是:
|
||||
|
||||
* 阶段 1:什么?(看别处)
|
||||
* 阶段 1:什么?(王顾左右)
|
||||
* 阶段 2:没有指标,我们真的是瞎撞。
|
||||
* 阶段 3:出现不合理的指标我们该如何做?
|
||||
|
||||
@ -24,21 +18,13 @@ opensource.com
|
||||
|
||||
我们开始吧!
|
||||
|
||||
## 需要的软件
|
||||
|
||||
更多关于 Python 的资源
|
||||
|
||||
* [Python 是什么?][1]
|
||||
* [Python IDE 排行榜][2]
|
||||
* [Python GUI 框架排行榜][3]
|
||||
* [最新的 Python 主题][4]
|
||||
* [更多开发者资源][5]
|
||||
### 需要的软件
|
||||
|
||||
在文章中讨论时用到的 demo 都可以在 [我的 GitHub 仓库][6] 中找到。你需要安装 docker 和 docker-compose 才能使用它们。
|
||||
|
||||
## 为什么要监视?
|
||||
### 为什么要监测?
|
||||
|
||||
关于监视的主要原因是:
|
||||
关于监测的主要原因是:
|
||||
|
||||
* 理解 _正常的_ 和 _不正常的_ 系统和服务的特征
|
||||
* 做容量规划、弹性伸缩
|
||||
@ -47,43 +33,43 @@ opensource.com
|
||||
* 测量响应中的系统行为变化
|
||||
* 当系统出现意外行为时发出警报
|
||||
|
||||
## 指标和指标类型
|
||||
### 指标和指标类型
|
||||
|
||||
从我们的用途来看,一个**指标**就是在一个给定时间点上的某些数量的 _测量_ 值。博客文章的总点击次数、参与讨论的总人数、在缓存系统中数据没有被找到的次数、你的网站上的已登录用户数 —— 这些都是指标的例子。
|
||||
从我们的用途来看,一个**指标**就是在一个给定*时间*点上的某些数量的 _测量_ 值。博客文章的总点击次数、参与讨论的总人数、在缓存系统中数据没有被找到的次数、你的网站上的已登录用户数 —— 这些都是指标的例子。
|
||||
|
||||
它们总体上可以分为三类:
|
||||
|
||||
### 计数器
|
||||
#### 计数器
|
||||
|
||||
以你的个人博客为例。你发布一篇文章后,过一段时间后,你希望去了解有多少点击量,数字只会增加。这就是一个**计数器**指标。在你的博客文章的生命周期中,它的值从 0 开始增加。用图表来表示,一个计数器看起来应该像下面的这样:
|
||||
以你的个人博客为例。你发布一篇文章后,过一段时间后,你希望去了解有多少点击量,这是一个只会增加的数字。这就是一个<ruby>计数器<rt>counter</rt></ruby>指标。在你的博客文章的生命周期中,它的值从 0 开始增加。用图表来表示,一个计数器看起来应该像下面的这样:
|
||||
|
||||
![Counter metric](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/counter-graph.png?itok=BZYrB-Hn "Counter metric")
|
||||
|
||||
一个计数器指标总是在增加的。
|
||||
*一个计数器指标总是在增加的。*
|
||||
|
||||
### 计量器
|
||||
#### 计量器
|
||||
|
||||
如果你想去跟踪你的博客每天或每周的点击量,而不是基于时间的总点击量。这种指标被称为一个**计量器**,它的值可上可下。用图表来表示,一个计量器看起来应该像下面的样子:
|
||||
如果你想去跟踪你的博客每天或每周的点击量,而不是基于时间的总点击量。这种指标被称为一个<ruby>计量器<rt>gauge</rt></ruby>,它的值可上可下。用图表来表示,一个计量器看起来应该像下面的样子:
|
||||
|
||||
![gauge metric](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/gauge-graph.png?itok=UA4u4cwz "gauge metric")
|
||||
|
||||
一个计量器指标可以增加或减少。
|
||||
*一个计量器指标可以增加或减少。*
|
||||
|
||||
一个计量器的值在某些时间窗口内通常有一个_最大值_ 和 _最小值_ 。
|
||||
一个计量器的值在某些时间窗口内通常有一个<ruby>最大值<rt>ceiling</rt></ruby>和<ruby最小值<rt>floor</rt></ruby>。
|
||||
|
||||
### 柱状图和计时器
|
||||
#### 柱状图和计时器
|
||||
|
||||
一个**柱状图**(在 Prometheus 中这么叫它)或一个**计时器**(在 StatsD 中这么叫它)是跟踪已采样的_观测结果_ 的指标。不像一个计数器类或计量器类指标,柱状图指标的值并不是显示为上或下的样式。我知道这可能并没有太多的意义,并且可能和一个计量器图看上去没有什么不同。它们的这同之处在于,你期望使用柱状图数据来做什么,而不是与一个计量器图做比较。因此,监视系统需要知道那个指标是一个柱状图类型,它允许你去做哪些事情。
|
||||
<ruby>柱状图<rt>histogram</rt></ruby>(在 Prometheus 中这么叫它)或<ruby>计时器<rt> timer</rt></ruby>(在 StatsD 中这么叫它)是一个跟踪 _已采样的观测结果_ 的指标。不像一个计数器类或计量器类指标,柱状图指标的值并不是显示为上或下的样式。我知道这可能并没有太多的意义,并且可能和一个计量器图看上去没有什么不同。它们的不同之处在于,你期望使用柱状图数据来做什么,而不是与一个计量器图做比较。因此,监测系统需要知道那个指标是一个柱状图类型,它允许你去做哪些事情。
|
||||
|
||||
![Histogram metric](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/histogram-graph.png?itok=cHI1v7Ly "Histogram metric")
|
||||
|
||||
一个柱状图指标可以增加或减少。
|
||||
*一个柱状图指标可以增加或减少。*
|
||||
|
||||
## Demo 1:计算和报告指标
|
||||
### Demo 1:计算和报告指标
|
||||
|
||||
[Demo 1][7] 是使用 [Flask][8] 框架写的一个基本的 web 应用程序。它演示了我们如何去 _计算_ 和 _报告_ 指标。
|
||||
|
||||
在 src 目录中有 `app.py` 和 `src/helpers/middleware.py` 应用程序,包含以下内容:
|
||||
在 `src` 目录中有 `app.py` 和 `src/helpers/middleware.py` 应用程序,包含以下内容:
|
||||
|
||||
```
|
||||
from flask import request
|
||||
@ -110,9 +96,9 @@ def setup_metrics(app):
|
||||
app.after_request(stop_timer)
|
||||
```
|
||||
|
||||
当在应用程序中调用 `setup_metrics()` 时,它在请求处理之前被配置为调用 `start_timer()` 函数,然后在请求处理之后、响应发送之前调用 `stop_timer()` 函数。在上面的函数中,我们写了时间戳并用它来计算处理请求所花费的时间。
|
||||
当在应用程序中调用 `setup_metrics()` 时,它配置在一个请求被处理之前调用 `start_timer()` 函数,然后在该请求处理之后、响应发送之前调用 `stop_timer()` 函数。在上面的函数中,我们写了时间戳并用它来计算处理请求所花费的时间。
|
||||
|
||||
当我们在 demo1 目录中的 docker-compose 上开始去启动 web 应用程序,然后在一个客户端容器中生成一些对 web 应用程序的请求。你将会看到创建了一个 `src/metrics.csv` 文件,它有两个字段:timestamp 和 request_latency。
|
||||
当我们在 `demo1` 目录中运行 `docker-compose up`,它会启动这个 web 应用程序,然后在一个客户端容器中可以生成一些对 web 应用程序的请求。你将会看到创建了一个 `src/metrics.csv` 文件,它有两个字段:`timestamp` 和 `request_latency`。
|
||||
|
||||
通过查看这个文件,我们可以推断出两件事情:
|
||||
|
||||
@ -121,45 +107,47 @@ def setup_metrics(app):
|
||||
|
||||
没有观测到与指标相关的特征,我们就不能说这个指标与哪个 HTTP 端点有关联,或这个指标是由哪个应用程序的节点所生成的。因此,我们需要使用合适的元数据去限定每个观测指标。
|
||||
|
||||
## Statistics 101~~(译者注:这是一本统计学入门教材的名字)~~
|
||||
### 《Statistics 101》
|
||||
|
||||
假如我们回到高中数学,我们应该回忆起一些统计术语,虽然不太确定,但应该包括平均数、中位数、百分位、和柱状图。我们来简要地回顾一下它们,不用去管他们的用法,就像是在上高中一样。
|
||||
(LCTT 译注:这是一本统计学入门教材的名字)
|
||||
|
||||
### 平均数
|
||||
假如我们回到高中数学,我们应该回忆起一些统计术语,虽然不太确定,但应该包括平均数、中位数、百分位和柱状图。我们来简要地回顾一下它们,不用去管它们的用法,就像是在上高中一样。
|
||||
|
||||
**平均数**,或一系列数字的平均值,是将数字汇总然后除以列表的个数。3、2、和 10 的平均数是 (3+2+10)/3 = 5。
|
||||
#### 平均数
|
||||
|
||||
### 中位数
|
||||
<ruby>平均数<rt>mean</rt></ruby>,即一系列数字的平均值,是将数字汇总然后除以列表的个数。3、2 和 10 的平均数是 (3+2+10)/3 = 5。
|
||||
|
||||
**中位数**是另一种类型的平均,但它的计算方式不同;它是列表从小到大排序(反之亦然)后取列表的中间数字。以我们上面的列表中(2、3、10),中位数是 3。计算并不简单,它取决于列表中数字的个数。
|
||||
#### 中位数
|
||||
|
||||
### 百分位
|
||||
<ruby>中位数<rt>median</rt></ruby>是另一种类型的平均,但它的计算方式不同;它是列表从小到大排序(反之亦然)后取列表的中间数字。以我们上面的列表中(2、3、10),中位数是 3。计算并不是非常直观,它取决于列表中数字的个数。
|
||||
|
||||
**百分位**是指那个百(千)分比数字低于我们给定的百分数的程度。在一些场景中,百分位是指这个测量值低于我们数据的百(千)分比数字的程度。比如,上面列表中 95% 是 9.29999。百分位的测量范围是 0 到 100(不包括)。0% 是一组数字的最小分数。你可能会想到它的中位数是 50%,它的结果是 3。
|
||||
#### 百分位
|
||||
|
||||
一些监视系统将百分位称为 `upper_X`,其中 _X_ 就是百分位;`_upper 90_` 指的是值在 90%的位置。
|
||||
<ruby>百分位<rt>percentile</rt></ruby>是指那个百(千)分比数字低于我们给定的百分数的程度。在一些场景中,它是指这个测量值低于我们数据的百(千)分比数字的程度。比如,上面列表中 95% 是 9.29999。百分位的测量范围是 0 到 100(不包括)。0% 是一组数字的最小分数。你可能会想到它的中位数是 50%,它的结果是 3。
|
||||
|
||||
### 分位数
|
||||
一些监测系统将百分位称为 `upper_X`,其中 _X_ 就是百分位;`upper 90` 指的是在 90% 的位置的值。
|
||||
|
||||
**q-Quantile** 是将有 _N_ 个数的集合等分为 q_N_ 个集合。**q** 的取值范围为 0 到 1(全部都包括)。当 **q** 取值为 0.5 时,值就是中位数。分位数和百分位数的关系是,分位数值 **q** 等于 **100_q_** 百分位值。
|
||||
#### 分位数
|
||||
|
||||
### 柱状图
|
||||
“q-分位数”是将有 _N_ 个数的集合等分为 `qN` 级。`q` 的取值范围为 0 到 1(全部都包括)。当 `q` 取值为 0.5 时,值就是中位数。(<ruby>分位数<rt>quantile</rt></ruby>)和百分位数的关系是,分位数值 `q` 等于 `100` 百分位值。
|
||||
|
||||
**柱状图**这个指标,我们早期学习过,它是监视系统中一个_详细的实现_。在统计学中,一个柱状图是一个将数据分组为 _桶_ 的图表。我们来考虑一个人为的、不同的示例:阅读你的博客的人的年龄。如果你有一些这样的数据,并想将它进行大致的分组,绘制成的柱状图将看起来像下面的这样:
|
||||
#### 柱状图
|
||||
|
||||
<ruby柱状图<rt>histogram</rt></ruby>这个指标,我们前面学习过,它是监测系统中一个_实现细节_。在统计学中,一个柱状图是一个将数据分组为 _桶_ 的图表。我们来考虑一个人为的不同示例:阅读你的博客的人的年龄。如果你有一些这样的数据,并想将它进行大致的分组,绘制成的柱状图将看起来像下面的这样:
|
||||
|
||||
![Histogram graph](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/histogram.png?itok=gqfhjB8p "Histogram graph")
|
||||
|
||||
### 累积柱状图
|
||||
#### 累积柱状图
|
||||
|
||||
一个**累积柱状图**也是一个柱状图,它的每个桶的数包含前一个桶的数,因此命名为_累积_。将上面的数据集做成累积柱状图后,看起来应该是这样的:
|
||||
一个<ruby>累积柱状图<rt>cumulative histogram</rt></ruby>也是一个柱状图,它的每个桶的数包含前一个桶的数,因此命名为_累积_。将上面的数据集做成累积柱状图后,看起来应该是这样的:
|
||||
|
||||
![Cumulative histogram](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/cumulative-histogram.png?itok=wIGQdZnT "Cumulative histogram")
|
||||
|
||||
### 我们为什么需要做统计?
|
||||
#### 我们为什么需要做统计?
|
||||
|
||||
在上面的 Demo 1 中,我们注意到在我们报告指标时,这里生成了许多数据。当我们将它用于指标时我们需要做统计,因为它们实在是太多了。我们需要的是整体行为,我们没法去处理单个值。我们预期展现出来的值的行为应该是代表我们观察的系统的行为。
|
||||
在上面的 Demo 1 中,我们注意到在我们报告指标时,这里生成了许多数据。当我们将它们用于指标时我们需要做统计,因为它们实在是太多了。我们需要的是整体行为,我们没法去处理单个值。我们预期展现出来的值的行为应该是代表我们观察的系统的行为。
|
||||
|
||||
## Demo 2:指标上增加特征
|
||||
### Demo 2:在指标上增加特征
|
||||
|
||||
在我们上面的的 Demo 1 应用程序中,当我们计算和报告一个请求的延迟时,它指向了一个由一些_特征_ 唯一标识的特定请求。下面是其中一些:
|
||||
|
||||
@ -169,7 +157,7 @@ def setup_metrics(app):
|
||||
|
||||
如果我们将这些特征附加到要观察的指标上,每个指标将有更多的内容。我们来解释一下 [Demo 2][9] 中添加到我们的指标上的特征。
|
||||
|
||||
在写入指标时,src/helpers/middleware.py 文件将在 CSV 文件中写入多个列:
|
||||
在写入指标时,`src/helpers/middleware.py` 文件将在 CSV 文件中写入多个列:
|
||||
|
||||
```
|
||||
node_ids = ['10.0.1.1', '10.1.3.4']
|
||||
@ -194,20 +182,20 @@ def stop_timer(response):
|
||||
return response
|
||||
```
|
||||
|
||||
因为这只是一个演示,在报告指标时,我们将随意的报告一些随机 IP 作为节点的 ID。当我们在 demo2 目录下运行 docker-compose 时,我们的结果将是一个有多个列的 CSV 文件。
|
||||
因为这只是一个演示,在报告指标时,我们将随意的报告一些随机 IP 作为节点的 ID。当我们在 `demo2` 目录下运行 `docker-compose up` 时,我们的结果将是一个有多个列的 CSV 文件。
|
||||
|
||||
### 用 pandas 分析指标
|
||||
#### 用 pandas 分析指标
|
||||
|
||||
我们将使用 [pandas][10] 去分析这个 CSV 文件。运行中的 docker-compose 将打印出一个 URL,我们将使用它来打开一个 [Jupyter][11] 会话。一旦我们上传 `Analysis.ipynb notebook` 到会话中,我们就可以将 CSV 文件读入到一个 pandas 数据帧中:
|
||||
我们将使用 [pandas][10] 去分析这个 CSV 文件。运行 `docker-compose up` 将打印出一个 URL,我们将使用它来打开一个 [Jupyter][11] 会话。一旦我们上传 `Analysis.ipynb notebook` 到会话中,我们就可以将 CSV 文件读入到一个 pandas <ruby>数据帧<rt>DataFrame</rt></ruby>中:
|
||||
|
||||
```
|
||||
import pandas as pd
|
||||
metrics = pd.read_csv('/data/metrics.csv', index_col=0)
|
||||
```
|
||||
|
||||
index_col 指定时间戳作为索引。
|
||||
`index_col` 表明我们要指定时间戳作为索引。
|
||||
|
||||
因为每个特征我们都在数据帧中添加一个列,因此我们可以基于这些列进行分组和聚合:
|
||||
因为每个特征我们都要在数据帧中添加一个列,因此我们可以基于这些列进行分组和聚合:
|
||||
|
||||
```
|
||||
import numpy as np
|
||||
@ -216,48 +204,48 @@ metrics.groupby(['node_id', 'http_status']).latency.aggregate(np.percentile, 99.
|
||||
|
||||
更多内容请参考 Jupyter notebook 在数据上的分析示例。
|
||||
|
||||
## 我应该监视什么?
|
||||
### 我应该监测什么?
|
||||
|
||||
一个软件系统有许多的变量,这些变量的值在它的生命周期中不停地发生变化。软件是运行在某种操作系统上的,而操作系统同时也在不停地变化。在我看来,当某些东西出错时,你所拥有的数据越多越好。
|
||||
|
||||
我建议去监视的关键操作系统指标有:
|
||||
我建议去监测的关键操作系统指标有:
|
||||
|
||||
* CPU 使用
|
||||
* 系统内存使用
|
||||
* 文件描述符使用
|
||||
* 磁盘使用
|
||||
|
||||
还需要监视的其它关键指标根据你的软件应用程序不同而不同。
|
||||
还需要监测的其它关键指标根据你的软件应用程序不同而不同。
|
||||
|
||||
### 网络应用程序
|
||||
#### 网络应用程序
|
||||
|
||||
如果你的软件是一个监听客户端请求和为它提供服务的网络应用程序,需要测量的关键指标还有:
|
||||
|
||||
* 入站请求数(计数器)
|
||||
* 未处理的错误(计数器)
|
||||
* 请求延迟(柱状图/计时器)
|
||||
* 队列时间,如果在你的应用程序中有队列(柱状图/计时器)
|
||||
* 排队时间,如果在你的应用程序中有队列(柱状图/计时器)
|
||||
* 队列大小,如果在你的应用程序中有队列(计量器)
|
||||
* 工作进程/线程使用(计量器)
|
||||
* 工作进程/线程用量(计量器)
|
||||
|
||||
如果你的网络应用程序在一个客户端请求的环境中向其它服务发送请求,那么它应该有一个指标去记录它与那个服务之间的通讯行为。需要监视的关键指标包括请求数、请求延迟、和响应状态。
|
||||
如果你的网络应用程序在一个客户端请求的环境中向其它服务发送请求,那么它应该有一个指标去记录它与那个服务之间的通讯行为。需要监测的关键指标包括请求数、请求延迟、和响应状态。
|
||||
|
||||
### HTTP web 应用程序后端
|
||||
#### HTTP web 应用程序后端
|
||||
|
||||
HTTP 应用程序应该监视上面所列出的全部指标。除此之外,还应该按 HTTP 状态代码分组监视所有非 200 的 HTTP 状态代码的大致数据。如果你的 web 应用程序有用户注册和登录功能,同时也应该为这个功能设置指标。
|
||||
HTTP 应用程序应该监测上面所列出的全部指标。除此之外,还应该按 HTTP 状态代码分组监测所有非 200 的 HTTP 状态代码的大致数据。如果你的 web 应用程序有用户注册和登录功能,同时也应该为这个功能设置指标。
|
||||
|
||||
### 长周期运行的进程
|
||||
#### 长时间运行的进程
|
||||
|
||||
长周期运行的进程如 Rabbit MQ 消费者或 task-queue 工作进程,虽然它们不是网络服务,它们以选取一个任务并处理它的工作模型来运行。因此,我们应该监视请求的进程数和这些进程的请求延迟。
|
||||
长时间运行的进程如 Rabbit MQ 消费者或任务队列的工作进程,虽然它们不是网络服务,它们以选取一个任务并处理它的工作模型来运行。因此,我们应该监测请求的进程数和这些进程的请求延迟。
|
||||
|
||||
不管是什么类型的应用程序,都有指标与合适的**元数据**相关联。
|
||||
|
||||
## 将监视集成到一个 Python 应用程序中
|
||||
### 将监测集成到一个 Python 应用程序中
|
||||
|
||||
将监视集成到 Python 应用程序中需要涉及到两个组件:
|
||||
将监测集成到 Python 应用程序中需要涉及到两个组件:
|
||||
|
||||
* 更新你的应用程序去计算和报告指标
|
||||
* 配置一个监视基础设施来容纳应用程序的指标,并允许去查询它们
|
||||
* 配置一个监测基础设施来容纳应用程序的指标,并允许去查询它们
|
||||
|
||||
下面是记录和报告指标的基本思路:
|
||||
|
||||
@ -276,15 +264,15 @@ def work():
|
||||
|
||||
考虑到上面的模式,我们经常使用修饰符、内容管理器、中间件(对于网络应用程序)所带来的好处去计算和报告指标。在 Demo 1 和 Demo 2 中,我们在一个 Flask 应用程序中使用修饰符。
|
||||
|
||||
### 指标报告时的拉取和推送模型
|
||||
#### 指标报告时的拉取和推送模型
|
||||
|
||||
大体来说,在一个 Python 应用程序中报告指标有两种模式。在 _拉取_ 模型中,监视系统在一个预定义的 HTTP 端点上“刮取”应用程序。在_推送_ 模型中,应用程序发送数据到监视系统。
|
||||
大体来说,在一个 Python 应用程序中报告指标有两种模式。在 _拉取_ 模型中,监测系统在一个预定义的 HTTP 端点上“刮取”应用程序。在_推送_ 模型中,应用程序发送数据到监测系统。
|
||||
|
||||
![Pull and push models](https://opensource.com/sites/default/files/styles/panopoly_image_original/public/u128651/pull_push_model.png?itok=U093wSy8 "Pull and push models")
|
||||
|
||||
工作在 _拉取_ 模型中的监视系统的一个例子是 [Prometheus][12]。而 [StatsD][13] 是 _推送_ 模型的一个例子。
|
||||
工作在 _拉取_ 模型中的监测系统的一个例子是 [Prometheus][12]。而 [StatsD][13] 是 _推送_ 模型的一个例子。
|
||||
|
||||
### 集成 StatsD
|
||||
#### 集成 StatsD
|
||||
|
||||
将 StatsD 集成到一个 Python 应用程序中,我们将使用 [StatsD Python 客户端][14],然后更新我们的指标报告部分的代码,调用合适的库去推送数据到 StatsD 中。
|
||||
|
||||
@ -308,13 +296,13 @@ statsd.timing(key, resp_time)
|
||||
statsd.incr(key)
|
||||
```
|
||||
|
||||
将指标关联到元数据上,一个键的定义为:metadata1.metadata2.metric,其中每个 metadataX 是一个可以进行聚合和分组的字段。
|
||||
将指标关联到元数据上,一个键的定义为:`metadata1.metadata2.metric`,其中每个 metadataX 是一个可以进行聚合和分组的字段。
|
||||
|
||||
这个演示应用程序 [StatsD][15] 是将 statsd 与 Python Flask 应用程序集成的一个完整示例。
|
||||
|
||||
### 集成 Prometheus
|
||||
#### 集成 Prometheus
|
||||
|
||||
去使用 Prometheus 监视系统,我们使用 [Promethius Python 客户端][16]。我们将首先去创建有关的指标类对象:
|
||||
要使用 Prometheus 监测系统,我们使用 [Promethius Python 客户端][16]。我们将首先去创建有关的指标类对象:
|
||||
|
||||
```
|
||||
REQUEST_LATENCY = Histogram('request_latency_seconds', 'Request latency',
|
||||
@ -340,76 +328,76 @@ def metrics():
|
||||
|
||||
这个演示应用程序 [Prometheus][17] 是将 prometheus 与 Python Flask 应用程序集成的一个完整示例。
|
||||
|
||||
### 哪个更好:StatsD 还是 Prometheus?
|
||||
#### 哪个更好:StatsD 还是 Prometheus?
|
||||
|
||||
本能地想到的下一个问题便是:我应该使用 StatsD 还是 Prometheus?关于这个主题我写了几篇文章,你可能发现它们对你很有帮助:
|
||||
|
||||
* [Your options for monitoring multi-process Python applications with Prometheus][18]
|
||||
* [Monitoring your synchronous Python web applications using Prometheus][19]
|
||||
* [Monitoring your asynchronous Python web applications using Prometheus][20]
|
||||
* [使用 Prometheus 监测多进程 Python 应用的方式][18]
|
||||
* [使用 Prometheus 监测你的同步 Python 应用][19]
|
||||
* [使用 Prometheus 监测你的异步 Python 应用][20]
|
||||
|
||||
## 指标的使用方式
|
||||
### 指标的使用方式
|
||||
|
||||
我们已经学习了一些关于为什么要在我们的应用程序上配置监视的原因,而现在我们来更深入地研究其中的两个用法:报警和自动扩展。
|
||||
我们已经学习了一些关于为什么要在我们的应用程序上配置监测的原因,而现在我们来更深入地研究其中的两个用法:报警和自动扩展。
|
||||
|
||||
### 使用指标进行报警
|
||||
#### 使用指标进行报警
|
||||
|
||||
指标的一个关键用途是创建警报。例如,假如过去的五分钟,你的 HTTP 500 的数量持续增加,你可能希望给相关的人发送一封电子邮件或页面提示。对于配置警报做什么取决于我们的监视设置。对于 Prometheus 我们可以使用 [Alertmanager][21],而对于 StatsD,我们使用 [Nagios][22]。
|
||||
指标的一个关键用途是创建警报。例如,假如过去的五分钟,你的 HTTP 500 的数量持续增加,你可能希望给相关的人发送一封电子邮件或页面提示。对于配置警报做什么取决于我们的监测设置。对于 Prometheus 我们可以使用 [Alertmanager][21],而对于 StatsD,我们使用 [Nagios][22]。
|
||||
|
||||
### 使用指标进行自动扩展
|
||||
#### 使用指标进行自动扩展
|
||||
|
||||
在一个云基础设施中,如果我们当前的基础设施供应过量或供应不足,通过指标不仅可以让我们知道,还可以帮我们实现一个自动伸缩的策略。例如,如果在过去的五分钟里,在我们服务器上的工作进程使用率达到 90%,我们可以水平扩展。我们如何去扩展取决于云基础设施。AWS 的自动扩展,缺省情况下,扩展策略是基于系统的 CPU 使用率、网络流量、以及其它因素。然而,让基础设施伸缩的应用程序指标,我们必须发布 [自定义的 CloudWatch 指标][23]。
|
||||
|
||||
## 在多服务架构中的应用程序监视
|
||||
### 在多服务架构中的应用程序监测
|
||||
|
||||
当我们超越一个单应用程序架构时,比如当客户端的请求在响应被发回之前,能够触发调用多个服务,就需要从我们的指标中获取更多的信息。我们需要一个统一的延迟视图指标,这样我们就能够知道响应这个请求时每个服务花费了多少时间。这可以用 [distributed tracing][24] 来实现。
|
||||
当我们超越一个单应用程序架构时,比如当客户端的请求在响应被发回之前,能够触发调用多个服务,就需要从我们的指标中获取更多的信息。我们需要一个统一的延迟视图指标,这样我们就能够知道响应这个请求时每个服务花费了多少时间。这可以用 [分布式跟踪][24] 来实现。
|
||||
|
||||
你可以在我的博客文章 [在你的 Python 应用程序中通过 Zipkin 引入分布式跟踪][25] 中看到在 Python 中进行分布式跟踪的示例。
|
||||
你可以在我的博客文章 《[在你的 Python 应用程序中通过 Zipkin 引入分布式跟踪][25]》 中看到在 Python 中进行分布式跟踪的示例。
|
||||
|
||||
## 划重点
|
||||
### 划重点
|
||||
|
||||
总之,你需要记住以下几点:
|
||||
|
||||
* 理解你的监视系统中指标类型的含义
|
||||
* 知道监视系统需要的你的数据的测量单位
|
||||
* 监视你的应用程序中的大多数关键组件
|
||||
* 监视你的应用程序在它的大多数关键阶段的行为
|
||||
* 理解你的监测系统中指标类型的含义
|
||||
* 知道监测系统需要的你的数据的测量单位
|
||||
* 监测你的应用程序中的大多数关键组件
|
||||
* 监测你的应用程序在它的大多数关键阶段的行为
|
||||
|
||||
以上要点是假设你不去管理你的监视系统。如果管理你的监视系统是你的工作的一部分,那么你还要考虑更多的问题!
|
||||
以上要点是假设你不去管理你的监测系统。如果管理你的监测系统是你的工作的一部分,那么你还要考虑更多的问题!
|
||||
|
||||
## 其它资源
|
||||
### 其它资源
|
||||
|
||||
以下是我在我的监视学习过程中找到的一些非常有用的资源:
|
||||
以下是我在我的监测学习过程中找到的一些非常有用的资源:
|
||||
|
||||
### 综合的
|
||||
#### 综合的
|
||||
|
||||
* [监视分布式系统][26]
|
||||
* [观测和监视最佳实践][27]
|
||||
* [监测分布式系统][26]
|
||||
* [观测和监测最佳实践][27]
|
||||
* [谁想使用秒?][28]
|
||||
|
||||
### StatsD/Graphite
|
||||
#### StatsD/Graphite
|
||||
|
||||
* [StatsD 指标类型][29]
|
||||
|
||||
### Prometheus
|
||||
#### Prometheus
|
||||
|
||||
* [Prometheus 指标类型][30]
|
||||
* [How does a Prometheus gauge work?][31]
|
||||
* [Why are Prometheus histograms cumulative?][32]
|
||||
* [在 Python 中监视批作业][33]
|
||||
* [Prometheus:监视 SoundCloud][34]
|
||||
* [Prometheus 计量器如何工作?][31]
|
||||
* [为什么用 Prometheus 累积柱形图?][32]
|
||||
* [在 Python 中监测批量作业][33]
|
||||
* [Prometheus:监测 SoundCloud][34]
|
||||
|
||||
## 避免犯错(即第 3 阶段的学习)
|
||||
### 避免犯错(即第 3 阶段的学习)
|
||||
|
||||
在我们学习监视的基本知识时,时刻注意不要犯错误是很重要的。以下是我偶然发现的一些很有见解的资源:
|
||||
在我们学习监测的基本知识时,时刻注意不要犯错误是很重要的。以下是我偶然发现的一些很有见解的资源:
|
||||
|
||||
* [How not to measure latency][35]
|
||||
* [Histograms with Prometheus: A tale of woe][36]
|
||||
* [Why averages suck and percentiles are great][37]
|
||||
* [Everything you know about latency is wrong][38]
|
||||
* [Who moved my 99th percentile latency?][39]
|
||||
* [Logs and metrics and graphs][40]
|
||||
* [HdrHistogram: A better latency capture method][41]
|
||||
* [如何不测量延迟][35]
|
||||
* [Prometheus 柱形图:悲伤的故事][36]
|
||||
* [为什么平均值很讨厌,而百分位很棒][37]
|
||||
* [对延迟的认知错误][38]
|
||||
* [谁动了我的 99% 延迟?][39]
|
||||
* [日志、指标和图形][40]
|
||||
* [HdrHistogram:一个更好的延迟捕获方式][41]
|
||||
|
||||
---
|
||||
|
||||
@ -419,7 +407,7 @@ def metrics():
|
||||
|
||||
[![](https://opensource.com/sites/default/files/styles/profile_pictures/public/osdc_default_avatar_1.png?itok=mmbfqFXm)][44]
|
||||
|
||||
Amit Saha — 我是一名对基础设施、监视、和工具感兴趣的软件工程师。我是“用 Python 做数学”的作者和创始人,以及 Fedora Scientific Spin 维护者。
|
||||
Amit Saha — 我是一名对基础设施、监测、和工具感兴趣的软件工程师。我是“用 Python 做数学”的作者和创始人,以及 Fedora Scientific Spin 维护者。
|
||||
|
||||
[关于我的更多信息][45]
|
||||
|
||||
@ -429,7 +417,7 @@ Amit Saha — 我是一名对基础设施、监视、和工具感兴趣的软件
|
||||
|
||||
via: [https://opensource.com/article/18/4/metrics-monitoring-and-python][47]
|
||||
|
||||
作者: [Amit Saha][48] 选题者: [@lujun9972][49] 译者: [qhwdw][50] 校对: [校对者ID][51]
|
||||
作者: [Amit Saha][48] 选题者: [lujun9972][49] 译者: [qhwdw][50] 校对: [wxy][51]
|
||||
|
||||
本文由 [LCTT][52] 原创编译,[Linux中国][53] 荣誉推出
|
||||
|
||||
@ -483,6 +471,6 @@ via: [https://opensource.com/article/18/4/metrics-monitoring-and-python][47]
|
||||
[48]: https://opensource.com/users/amitsaha
|
||||
[49]: https://github.com/lujun9972
|
||||
[50]: https://github.com/qhwdw
|
||||
[51]: https://github.com/校对者ID
|
||||
[51]: https://github.com/wxy
|
||||
[52]: https://github.com/LCTT/TranslateProject
|
||||
[53]: https://linux.cn/
|
@ -1,63 +1,59 @@
|
||||
Go 编译器介绍
|
||||
======
|
||||
|
||||
> Copyright 2018 The Go Authors. All rights reserved.
|
||||
> Use of this source code is governed by a BSD-style
|
||||
> license that can be found in the LICENSE file.
|
||||
|
||||
`cmd/compile` 包含构成 Go 编译器主要的包。编译器在逻辑上可以被分为四个阶段,我们将简要介绍这几个阶段以及包含相应代码的包的列表。
|
||||
|
||||
在谈到编译器时,有时可能会听到<ruby>前端<rt>front-end</rt></ruby>和<ruby>后端<rt>back-end</rt></ruby>这两个术语。粗略地说,这些对应于我们将在此列出的前两个和后两个阶段。第三个术语<ruby>中间端<rt>middle-end</rt></ruby>通常指的是第二阶段执行的大部分工作。
|
||||
|
||||
请注意,`go/parser` 和 `go/types` 等 `go/*` 系列的包与编译器无关。由于编译器最初是用 C 编写的,所以这些 `go/*` 包被开发出来以便于能够写出和 `Go` 代码一起工作的工具,例如 `gofmt` 和 `vet`。
|
||||
|
||||
需要澄清的是,名称 “gc” 代表 “Go 编译器”,与大写 GC 无关,后者代表<ruby>垃圾收集<rt>garbage collection</rt></ruby>。
|
||||
需要澄清的是,名称 “gc” 代表 “<ruby>Go 编译器<rt>Go compiler</rt></ruby>”,与大写 GC 无关,后者代表<ruby>垃圾收集<rt>garbage collection</rt></ruby>。
|
||||
|
||||
### 1. 解析
|
||||
### 1、解析
|
||||
|
||||
* `cmd/compile/internal/syntax`(<ruby>词法分析器<rt>lexer</rt></ruby>、<ruby>解析器<rt>parser</rt></ruby>、<ruby>语法树<rt>syntax tree</rt></ruby>)
|
||||
|
||||
在编译的第一阶段,源代码被标记化(词法分析),解析(语法分析),并为每个源文件构造语法树(译注:这里标记指 token,它是一组预定义的、能够识别的字符串,通常由名字和值构成,其中名字一般是词法的类别,如标识符、关键字、分隔符、操作符、文字和注释等;语法树,以及下文提到的<ruby>抽象语法树<rt>Abstract Syntax Tree</rt></ruby>(AST),是指用树来表达程序设计语言的语法结构,通常叶子节点是操作数,其它节点是操作码)。
|
||||
在编译的第一阶段,源代码被标记化(词法分析)、解析(语法分析),并为每个源文件构造语法树(LCTT 译注:这里标记指 token,它是一组预定义的、能够识别的字符串,通常由名字和值构成,其中名字一般是词法的类别,如标识符、关键字、分隔符、操作符、文字和注释等;语法树,以及下文提到的<ruby>抽象语法树<rt>Abstract Syntax Tree</rt></ruby>(AST),是指用树来表达程序设计语言的语法结构,通常叶子节点是操作数,其它节点是操作码)。
|
||||
|
||||
每个语法树都是相应源文件的确切表示,其中节点对应于源文件的各种元素,例如表达式、声明和语句。语法树还包括位置信息,用于错误报告和创建调试信息。
|
||||
|
||||
### 2. 类型检查和 AST 变形
|
||||
### 2、类型检查和 AST 变换
|
||||
|
||||
* `cmd/compile/internal/gc`(创建编译器 AST,<ruby>类型检查<rt>type-checking</rt></ruby>,<ruby>AST 变形<rt>AST transformation</rt></ruby>)
|
||||
* `cmd/compile/internal/gc`(创建编译器 AST,<ruby>类型检查<rt>type-checking</rt></ruby>,<ruby>AST 变换<rt>AST transformation</rt></ruby>)
|
||||
|
||||
gc 包中包含一个继承自(早期)C 语言实现的版本的 AST 定义。所有代码都是基于它编写的,所以 gc 包必须做的第一件事就是将 syntax 包(定义)的语法树转换为编译器的 AST 表示法。这个额外步骤可能会在将来重构。
|
||||
|
||||
然后对 AST 进行类型检查。第一步是名字解析和类型推断,它们确定哪个对象属于哪个标识符,以及每个表达式具有的类型。类型检查包括特定的额外检查,例如“声明但未使用”以及确定函数是否会终止。
|
||||
|
||||
特定转换也基于 AST 完成。一些节点被基于类型信息而细化,例如把字符串加法从算术加法的节点类型中拆分出来。其它一些例子是<ruby>死代码消除<rt>dead code elimination</rt></ruby>,<ruby>函数调用内联<rt>function call inlining</rt></ruby>和<ruby>逃逸分析<rt>escape analysis</rt></ruby>(译注:逃逸分析是一种分析指针有效范围的方法)。
|
||||
特定变换也基于 AST 完成。一些节点被基于类型信息而细化,例如把字符串加法从算术加法的节点类型中拆分出来。其它一些例子是<ruby>死代码消除<rt>dead code elimination</rt></ruby>,<ruby>函数调用内联<rt>function call inlining</rt></ruby>和<ruby>逃逸分析<rt>escape analysis</rt></ruby>(LCTT 译注:逃逸分析是一种分析指针有效范围的方法)。
|
||||
|
||||
### 3. 通用 SSA
|
||||
### 3、通用 SSA
|
||||
|
||||
* `cmd/compile/internal/gc`(转换成 SSA)
|
||||
* `cmd/compile/internal/ssa`(SSA 相关的 pass 和规则)
|
||||
* `cmd/compile/internal/ssa`(SSA 相关的<ruby>环节<rt>pass</rt></ruby>和规则)
|
||||
|
||||
(译注:许多常见高级语言的编译器无法通过一次扫描源代码或 AST 就完成所有编译工作,取而代之的做法是多次扫描,每次完成一部分工作,并将输出结果作为下次扫描的输入,直到最终产生目标代码。这里每次扫描称作一遍 pass;最后一遍 pass 之前所有的 pass 得到的结果都可称作中间表示法,本文中 AST、SSA 等都属于中间表示法。SSA,静态单赋值形式,是中间表示法的一种性质,它要求每个变量只被赋值一次且在使用前被定义)。
|
||||
(LCTT 译注:许多常见高级语言的编译器无法通过一次扫描源代码或 AST 就完成所有编译工作,取而代之的做法是多次扫描,每次完成一部分工作,并将输出结果作为下次扫描的输入,直到最终产生目标代码。这里每次扫描称作一个<ruby>环节<rt>pass</rt></ruby>;最后一个环节之前所有的环节得到的结果都可称作中间表示法,本文中 AST、SSA 等都属于中间表示法。SSA,静态单赋值形式,是中间表示法的一种性质,它要求每个变量只被赋值一次且在使用前被定义)。
|
||||
|
||||
在此阶段,AST 将被转换为<ruby>静态单赋值<rt>Static Single Assignment</rt></ruby>(SSA)形式,这是一种具有特定属性的低级<ruby>中间表示法<rt>intermediate representation</rt></ruby>,可以更轻松地实现优化并最终从它生成机器码。
|
||||
|
||||
在这个转换过程中,将完成<ruby>内置函数<rt>function intrinsics</rt></ruby>的处理。这些是特殊的函数,编译器被告知逐个分析这些函数并决定是否用深度优化的代码替换它们(译注:内置函数指由语言本身定义的函数,通常编译器的处理方式是使用相应实现函数的指令序列代替对函数的调用指令,有点类似内联函数)。
|
||||
在这个转换过程中,将完成<ruby>内置函数<rt>function intrinsics</rt></ruby>的处理。这些是特殊的函数,编译器被告知逐个分析这些函数并决定是否用深度优化的代码替换它们(LCTT 译注:内置函数指由语言本身定义的函数,通常编译器的处理方式是使用相应实现函数的指令序列代替对函数的调用指令,有点类似内联函数)。
|
||||
|
||||
在 AST 转化成 SSA 的过程中,特定节点也被低级化为更简单的组件,以便于剩余的编译阶段可以基于它们工作。例如,内建的拷贝被替换为内存移动,range 循环被改写为 for 循环。由于历史原因,目前这里面有些在转化到 SSA 之前发生,但长期计划则是把它们都移到这里(转化 SSA)。
|
||||
在 AST 转化成 SSA 的过程中,特定节点也被低级化为更简单的组件,以便于剩余的编译阶段可以基于它们工作。例如,内建的拷贝被替换为内存移动,`range` 循环被改写为 `for` 循环。由于历史原因,目前这里面有些在转化到 SSA 之前发生,但长期计划则是把它们都移到这里(转化 SSA)。
|
||||
|
||||
然后,一系列机器无关的规则和 pass 会被执行。这些并不考虑特定计算机体系结构,因此对所有 `GOARCH` 变量的值都会运行。
|
||||
然后,一系列机器无关的规则和编译环节会被执行。这些并不考虑特定计算机体系结构,因此对所有 `GOARCH` 变量的值都会运行。
|
||||
|
||||
这类通用 pass 的一些例子包括,死代码消除,移除不必要的空值检查,以及移除无用的分支等。通用改写规则主要考虑表达式,例如将一些表达式替换为常量,优化乘法和浮点操作。
|
||||
这类通用的编译环节的一些例子包括,死代码消除、移除不必要的空值检查,以及移除无用的分支等。通用改写规则主要考虑表达式,例如将一些表达式替换为常量,优化乘法和浮点操作。
|
||||
|
||||
### 4. 生成机器码
|
||||
### 4、生成机器码
|
||||
|
||||
* `cmd/compile/internal/ssa`(SSA 低级化和架构特定的 pass)
|
||||
* `cmd/compile/internal/ssa`(SSA 低级化和架构特定的环节)
|
||||
* `cmd/internal/obj`(机器码生成)
|
||||
|
||||
编译器中机器相关的阶段开始于“低级”的 pass,该阶段将通用变量改写为它们的特定的机器码形式。例如,在 amd64 架构中操作数可以在内存中操作,这样许多<ruby>加载-存储<rt>load-store</rt></ruby>操作就可以被合并。
|
||||
编译器中机器相关的阶段开始于“低级”的编译环节,该阶段将通用变量改写为它们的特定的机器码形式。例如,在 amd64 架构中操作数可以在内存中操作,这样许多<ruby>加载-存储<rt>load-store</rt></ruby>操作就可以被合并。
|
||||
|
||||
注意低级的 pass 运行所有机器特定的重写规则,因此当前它也应用了大量优化。
|
||||
注意低级的编译环节运行所有机器特定的重写规则,因此当前它也应用了大量优化。
|
||||
|
||||
一旦 SSA 被“低级化”并且更具体地针对目标体系结构,就要运行最终代码优化的 pass 了。这包含了另外一个死代码消除的 pass,它将变量移动到更靠近它们使用的地方,移除从来没有被读过的局部变量,以及<ruby>寄存器<rt>register</rt></ruby>分配。
|
||||
一旦 SSA 被“低级化”并且更具体地针对目标体系结构,就要运行最终代码优化的编译环节了。这包含了另外一个死代码消除的环节,它将变量移动到更靠近它们使用的地方,移除从来没有被读过的局部变量,以及<ruby>寄存器<rt>register</rt></ruby>分配。
|
||||
|
||||
本步骤中完成的其它重要工作包括<ruby>堆栈布局<rt>stack frame layout</rt></ruby>,它将堆栈偏移位置分配给局部变量,以及<ruby>指针活性分析<rt>pointer liveness analysis</rt></ruby>,后者计算每个垃圾收集安全点上的哪些堆栈上的指针仍然是活动的。
|
||||
|
||||
@ -65,7 +61,7 @@ gc 包中包含一个继承自(早期)C 语言实现的版本的 AST 定义
|
||||
|
||||
### 扩展阅读
|
||||
|
||||
要深入了解 SSA 包的工作方式,包括它的 pass 和规则,请转到 [cmd/compile/internal/ssa/README.md][1]。
|
||||
要深入了解 SSA 包的工作方式,包括它的环节和规则,请转到 [cmd/compile/internal/ssa/README.md][1]。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -73,7 +69,7 @@ via: https://github.com/golang/go/blob/master/src/cmd/compile/README.md
|
||||
|
||||
作者:[mvdan][a]
|
||||
译者:[stephenxs](https://github.com/stephenxs)
|
||||
校对:[pityonline](https://github.com/pityonline)
|
||||
校对:[pityonline](https://github.com/pityonline), [wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,50 +1,59 @@
|
||||
显卡工作原理简介
|
||||
极致技术探索:显卡工作原理
|
||||
======
|
||||
|
||||
![AMD-Polaris][1]
|
||||
|
||||
自从 sdfx 推出最初的 Voodoo 加速器以来,不起眼的显卡对你的 PC 是否可以玩游戏起到决定性作用,PC 上任何其它设备都无法与其相比。其它组件当然也很重要,但对于一个拥有 32GB 内存、价值 500 美金的 CPU 和 基于 PCIe 的存储设备的高端 PC,如果使用 10 年前的显卡,都无法以最高分辨率和细节质量运行当前<ruby>最高品质的游戏<rt>AAA titles</rt></ruby>,会发生卡顿甚至无响应。显卡(也常被称为 GPU, 或<ruby>图形处理单元<rt>Graphic Processing Unit</rt></ruby>)对游戏性能影响极大,我们反复强调这一点;但我们通常并不会深入了解显卡的工作原理。
|
||||
自从 3dfx 推出最初的 Voodoo 加速器以来,不起眼的显卡对你的 PC 是否可以玩游戏起到决定性作用,PC 上任何其它设备都无法与其相比。其它组件当然也很重要,但对于一个拥有 32GB 内存、价值 500 美金的 CPU 和 基于 PCIe 的存储设备的高端 PC,如果使用 10 年前的显卡,都无法以最高分辨率和细节质量运行当前<ruby>最高品质的游戏<rt>AAA titles</rt></ruby>,会发生卡顿甚至无响应。显卡(也常被称为 GPU,即<ruby>图形处理单元<rt>Graphic Processing Unit</rt></ruby>),对游戏性能影响极大,我们反复强调这一点;但我们通常并不会深入了解显卡的工作原理。
|
||||
|
||||
出于实际考虑,本文将概述 GPU 的上层功能特性,内容包括 AMD 显卡、Nvidia 显卡、Intel 集成显卡以及 Intel 后续可能发布的独立显卡之间共同的部分。也应该适用于 Apple, Imagination Technologies, Qualcomm, ARM 和 其它显卡生产商发布的移动平台 GPU。
|
||||
出于实际考虑,本文将概述 GPU 的上层功能特性,内容包括 AMD 显卡、Nvidia 显卡、Intel 集成显卡以及 Intel 后续可能发布的独立显卡之间共同的部分。也应该适用于 Apple、Imagination Technologies、Qualcomm、ARM 和其它显卡生产商发布的移动平台 GPU。
|
||||
|
||||
### 我们为何不使用 CPU 进行渲染?
|
||||
|
||||
我要说明的第一点是我们为何不直接使用 CPU 完成游戏中的渲染工作。坦率的说,在理论上你确实可以直接使用 CPU 完成<ruby>渲染<rt>rendering</rt></ruby>工作。在显卡没有广泛普及之前,早期的 3D 游戏就是完全基于 CPU 运行的,例如 Ultima Underworld(LCTT 译注:中文名为 _地下创世纪_ ,下文中简称 UU)。UU 是一个很特别的例子,原因如下:与 Doom (LCTT 译注:中文名 _毁灭战士_)相比,UU 具有一个更高级的渲染引擎,全面支持<ruby>向上或向下查找<rt>looking up and down</rt></ruby>以及一些在当时比较高级的特性,例如<ruby>纹理映射<rt>texture mapping</rt></ruby>。但为支持这些高级特性,需要付出高昂的代价,很少有人可以拥有真正能运行起 UU 的 PC。
|
||||
我要说明的第一点是我们为何不直接使用 CPU 完成游戏中的渲染工作。坦率的说,在理论上你确实可以直接使用 CPU 完成<ruby>渲染<rt>rendering</rt></ruby>工作。在显卡没有广泛普及之前,早期的 3D 游戏就是完全基于 CPU 运行的,例如 《<ruby>地下创世纪<rt>Ultima Underworld</rt></ruby>(下文中简称 UU)。UU 是一个很特别的例子,原因如下:与《<ruby>毁灭战士<rt>Doom</rt></ruby>相比,UU 具有一个更高级的渲染引擎,全面支持“向上或向下看”以及一些在当时比较高级的特性,例如<ruby>纹理映射<rt>texture mapping</rt></ruby>。但为支持这些高级特性,需要付出高昂的代价,很少有人可以拥有真正能运行起 UU 的 PC。
|
||||
|
||||
![](https://www.extremetech.com/wp-content/uploads/2018/05/UU.jpg)
|
||||
|
||||
对于早期的 3D 游戏,包括 Half Life 和 Quake II 在内的很多游戏,内部包含一个软件渲染器,让没有 3D 加速器的玩家也可以玩游戏。但现代游戏都弃用了这种方式,原因很简单:CPU 是设计用于通用任务的微处理器,意味着缺少 GPU 提供的<ruby>专用硬件<rt>specialized hardware</rt></ruby>和<ruby>功能<rt>capabilities</rt></ruby>。对于 18 年前使用软件渲染的那些游戏,当代 CPU 可以轻松胜任;但对于当代最高品质的游戏,除非明显降低<ruby>景象质量<rt>scene</rt></ruby>、分辨率和各种虚拟特效,否则现有的 CPU 都无法胜任。
|
||||
*地下创世纪,图片来自 [GOG](https://www.gog.com/game/ultima_underworld_1_2)*
|
||||
|
||||
对于早期的 3D 游戏,包括《<ruby>半条命<rt>Half Life</rt></ruby>》和《<ruby>雷神之锤 2<rt>Quake II</rt></ruby>》在内的很多游戏,内部包含一个软件渲染器,让没有 3D 加速器的玩家也可以玩游戏。但现代游戏都弃用了这种方式,原因很简单:CPU 是设计用于通用任务的微处理器,意味着缺少 GPU 提供的<ruby>专用硬件<rt>specialized hardware</rt></ruby>和<ruby>功能<rt>capabilities</rt></ruby>。对于 18 年前使用软件渲染的那些游戏,当代 CPU 可以轻松胜任;但对于当代最高品质的游戏,除非明显降低<ruby>景象质量<rt>scene</rt></ruby>、分辨率和各种虚拟特效,否则现有的 CPU 都无法胜任。
|
||||
|
||||
### 什么是 GPU ?
|
||||
|
||||
GPU 是一种包含一系列专用硬件特性的设备,其中这些特性可以让各种 3D 引擎更好地执行代码,包括<ruby>形状构建<rt>geometry setup</rt></ruby>,纹理映射,<ruby>访存<rt>memory access</rt></ruby>和<ruby>着色器<rt>shaders</rt></ruby>等。3D 引擎的功能特性影响着设计者如何设计 GPU。可能有人还记得,AMD HD5000 系列使用 VLIW5 <ruby>架构<rt>archtecture</rt></ruby>;但在更高端的 HD 6000 系列中使用了 VLIW4 架构。通过 GCN (LCTT 译注:GCN 是 Graphics Core Next 的缩写,字面意思是下一代图形核心,既是若干代微体系结构的代号,也是指令集的名称),AMD 改变了并行化的实现方法,提高了每个时钟周期的有效性能。
|
||||
GPU 是一种包含一系列专用硬件特性的设备,其中这些特性可以让各种 3D 引擎更好地执行代码,包括<ruby>形状构建<rt>geometry setup</rt></ruby>,纹理映射,<ruby>访存<rt>memory access</rt></ruby>和<ruby>着色器<rt>shaders</rt></ruby>等。3D 引擎的功能特性影响着设计者如何设计 GPU。可能有人还记得,AMD HD5000 系列使用 VLIW5 <ruby>架构<rt>archtecture</rt></ruby>;但在更高端的 HD 6000 系列中使用了 VLIW4 架构。通过 GCN (LCTT 译注:GCN 是 Graphics Core Next 的缩写,字面意思是“下一代图形核心”,既是若干代微体系结构的代号,也是指令集的名称),AMD 改变了并行化的实现方法,提高了每个时钟周期的有效性能。
|
||||
|
||||
![](https://www.extremetech.com/wp-content/uploads/2018/05/GPU-Evolution.jpg)
|
||||
|
||||
*“GPU 革命”的前两块奠基石属于 AMD 和 NV;而“第三个时代”则独属于 AMD。*
|
||||
|
||||
Nvidia 在发布首款 GeForce 256 时(大致对应 Microsoft 推出 DirectX7 的时间点)提出了 GPU 这个术语,这款 GPU 支持在硬件上执行转换和<ruby>光照计算<rt>lighting calculation</rt></ruby>。将专用功能直接集成到硬件中是早期 GPU 的显著技术特点。很多专用功能还在(以一种极为不同的方式)使用,毕竟对于特定类型的工作任务,使用<ruby>片上<rt>on-chip</rt></ruby>专用计算资源明显比使用一组<ruby>可编程单元<rt>programmable cores</rt></ruby>要更加高效和快速。
|
||||
|
||||
GPU 和 CPU 的核心有很多差异,但我们可以按如下方式比较其上层特性。CPU 一般被设计成尽可能快速和高效的执行单线程代码。虽然 <ruby>同时多线程<rt>SMT, Simultaneous multithreading</rt></ruby> 或 <ruby>超线程<rt>Hyper-Threading</rt></ruby>在这方面有所改进,但我们实际上通过堆叠众多高效率的单线程核心来扩展多线程性能。AMD 的 32 核心/64 线程 Epyc CPU 已经是我们能买到的核心数最多的 CPU;相比而言,Nvidia 最低端的 Pascal GPU 都拥有 384 个核心。但相比 CPU 的核心,GPU 所谓的核心是处理能力低得多的的处理单元。
|
||||
GPU 和 CPU 的核心有很多差异,但我们可以按如下方式比较其上层特性。CPU 一般被设计成尽可能快速和高效的执行单线程代码。虽然 <ruby>同时多线程<rt> Simultaneous multithreading</rt></ruby>(SMT)或 <ruby>超线程<rt>Hyper-Threading</rt></ruby>(HT)在这方面有所改进,但我们实际上通过堆叠众多高效率的单线程核心来扩展多线程性能。AMD 的 32 核心/64 线程 Epyc CPU 已经是我们能买到的核心数最多的 CPU;相比而言,Nvidia 最低端的 Pascal GPU 都拥有 384 个核心。但相比 CPU 的核心,GPU 所谓的核心是处理能力低得多的的处理单元。
|
||||
|
||||
**注意:** 简单比较 GPU 核心数,无法比较或评估 AMD 与 Nvidia 的相对游戏性能。在同样 GPU 系列(例如 Nvidia 的 GeForce GTX 10 系列,或 AMD 的 RX 4xx 或 5xx 系列)的情况下,更高的 GPU 核心数往往意味着更高的性能。
|
||||
|
||||
你无法只根据核心数比较不同供应商或核心系列的 GPU 之间的性能,这是因为不同的架构对应的效率各不相同。与 CPU 不同,GPU 被设计用于并行计算。AMD 和 Nvidia 在结构上都划分为计算资源<ruby>块<rt>block</rt></ruby>。Nvidia 将这些块称之为<ruby>流处理器<rt>SM, Streaming Multiprocessor</rt></ruby>,而 AMD 则称之为<ruby>计算单元<rt>Compute Unit</rt></ruby>。
|
||||
你无法只根据核心数比较不同供应商或核心系列的 GPU 之间的性能,这是因为不同的架构对应的效率各不相同。与 CPU 不同,GPU 被设计用于并行计算。AMD 和 Nvidia 在结构上都划分为计算资源<ruby>块<rt>block</rt></ruby>。Nvidia 将这些块称之为<ruby>流处理器<rt>Streaming Multiprocessor</rt></ruby>(SM),而 AMD 则称之为<ruby>计算单元<rt>Compute Unit</rt></ruby>(CU)。
|
||||
|
||||
![](https://www.extremetech.com/wp-content/uploads/2018/05/PascalSM.png)
|
||||
|
||||
每个块都包含如下组件:一组核心,一个<ruby>调度器<rt>scheduler</rt></ruby>,一个<ruby>寄存器文件<rt>register file</rt></ruby>,指令缓存,纹理和 L1 缓存以及纹理<ruby>映射单元<rt>mapping units</rt></ruby>。SM/CU 可以被认为是 GPU 中最小的可工作块。SM/CU 没有涵盖全部的功能单元,例如视频解码引擎,实际在屏幕绘图所需的渲染输出,以及与<ruby>板载<rt>onboard</rt></ruby><ruby>显存<rt>VRAM, Video Memory</rt></ruby>通信相关的<ruby>内存接口<rt>memory interfaces</rt></ruby>都不在 SM/CU 的范围内;但当 AMD 提到一个 APU 拥有 8 或 11 个 Vega 计算单元时,所指的是(等价的)<ruby>硅晶块<rt>block of silicon</rt></ruby>数目。如果你查看任意一款 GPU 的模块设计图,你会发现图中 SM/CU 是反复出现很多次的部分。
|
||||
*一个 Pascal 流处理器(SM)。*
|
||||
|
||||
每个块都包含如下组件:一组核心、一个<ruby>调度器<rt>scheduler</rt></ruby>、一个<ruby>寄存器文件<rt>register file</rt></ruby>、指令缓存、纹理和 L1 缓存以及纹理<ruby>映射单元<rt>mapping unit</rt></ruby>。SM/CU 可以被认为是 GPU 中最小的可工作块。SM/CU 没有涵盖全部的功能单元,例如视频解码引擎,实际在屏幕绘图所需的渲染输出,以及与<ruby>板载<rt>onboard</rt></ruby><ruby>显存<rt>Video Memory</rt></ruby>(VRAM)通信相关的<ruby>内存接口<rt>memory interfaces</rt></ruby>都不在 SM/CU 的范围内;但当 AMD 提到一个 APU 拥有 8 或 11 个 Vega 计算单元时,所指的是(等价的)<ruby>硅晶块<rt>block of silicon</rt></ruby>数目。如果你查看任意一款 GPU 的模块设计图,你会发现图中 SM/CU 是反复出现很多次的部分。
|
||||
|
||||
![](https://www.extremetech.com/wp-content/uploads/2016/11/Pascal-Diagram.jpg)
|
||||
|
||||
*这是 Pascal 的全平面图*
|
||||
|
||||
GPU 中的 SM/CU 数目越多,每个时钟周期内可以并行完成的工作也越多。渲染是一种通常被认为是“高度并行”的计算问题,意味着随着核心数增加带来的可扩展性很高。
|
||||
|
||||
当我们讨论 GPU 设计时,我们通常会使用一种形如 4096:160:64 的格式,其中第一个数字代表核心数。在核心系列(如 GTX970/GTX 980/GTX 980 Ti, 如 RX 560/RX 580 等等)一致的情况下,核心数越高,GPU 也就相对更快。
|
||||
当我们讨论 GPU 设计时,我们通常会使用一种形如 4096:160:64 的格式,其中第一个数字代表核心数。在核心系列(如 GTX970/GTX 980/GTX 980 Ti,如 RX 560/RX 580 等等)一致的情况下,核心数越高,GPU 也就相对更快。
|
||||
|
||||
### 纹理映射和渲染输出
|
||||
|
||||
GPU 的另外两个主要组件是纹理映射单元和渲染输出。设计中的纹理映射单元数目决定了最大的<ruby>纹素<rt>texel</rt></ruby>输出以及可以多快的处理并将纹理映射到对象上。早期的 3D 游戏很少用到纹理,这是因为绘制 3D 多边形形状的工作有较大的难度。纹理其实并不是 3D 游戏必须的,但不使用纹理的现代游戏屈指可数。
|
||||
|
||||
GPU 中的纹理映射单元数目用 4096:160:64 指标中的第二个数字表示。AMD,Nvidia 和 Intel 一般都等比例变更指标中的数字。换句话说,如果你找到一个指标为 4096:160:64 的 GPU,同系列中不会出现指标为 4096:320:64 的 GPU。纹理映射绝对有可能成为游戏的瓶颈,但产品系列中次高级别的 GPU 往往提供更多的核心和纹理映射单元(是否拥有更高的渲染输出单元取决于 GPU 系列和显卡的指标)。
|
||||
GPU 中的纹理映射单元数目用 4096:160:64 指标中的第二个数字表示。AMD、Nvidia 和 Intel 一般都等比例变更指标中的数字。换句话说,如果你找到一个指标为 4096:160:64 的 GPU,同系列中不会出现指标为 4096:320:64 的 GPU。纹理映射绝对有可能成为游戏的瓶颈,但产品系列中次高级别的 GPU 往往提供更多的核心和纹理映射单元(是否拥有更高的渲染输出单元取决于 GPU 系列和显卡的指标)。
|
||||
|
||||
<ruby>渲染输出单元<rt>Render outputs, ROPs</rt></ruby>(有时也叫做<ruby>光栅操作管道<rt>raster operations pipelines</rt></ruby>是 GPU 输出汇集成图像的场所,图像最终会在显示器或电视上呈现。渲染输出单元的数目乘以 GPU 的时钟频率决定了<ruby>像素填充速率<rt>pixel fill rate</rt></ruby>。渲染输出单元数目越多意味着可以同时输出的像素越多。渲染输出单元还处理<ruby>抗锯齿<rt>antialiasing</rt></ruby>,启用抗锯齿(尤其是<ruby>超级采样<rt>supersampled</rt></ruby>抗锯齿)会导致游戏填充速率受限。
|
||||
<ruby>渲染输出单元<rt>Render outputs</rt></ruby>(ROP),有时也叫做<ruby>光栅操作管道<rt>raster operations pipelines</rt></ruby>是 GPU 输出汇集成图像的场所,图像最终会在显示器或电视上呈现。渲染输出单元的数目乘以 GPU 的时钟频率决定了<ruby>像素填充速率<rt>pixel fill rate</rt></ruby>。渲染输出单元数目越多意味着可以同时输出的像素越多。渲染输出单元还处理<ruby>抗锯齿<rt>antialiasing</rt></ruby>,启用抗锯齿(尤其是<ruby>超级采样<rt>supersampled</rt></ruby>抗锯齿)会导致游戏填充速率受限。
|
||||
|
||||
### 显存带宽与显存容量
|
||||
|
||||
@ -52,11 +61,11 @@ GPU 中的纹理映射单元数目用 4096:160:64 指标中的第二个数字表
|
||||
|
||||
在某些情况下,显存带宽不足会成为 GPU 的显著瓶颈。以 Ryzen 5 2400G 为例的 AMD APU 就是严重带宽受限的,以至于提高 DDR4 的时钟频率可以显著提高整体性能。导致瓶颈的显存带宽阈值,也与游戏引擎和游戏使用的分辨率相关。
|
||||
|
||||
板载内存大小也是 GPU 的重要指标。如果按指定细节级别或分辨率运行所需的显存量超过了可用的资源量,游戏通常仍可以运行,但会使用 CPU 的主存存储额外的纹理数据;而从 DRAM 中提取数据比从板载显存中提取数据要慢得多。这会导致游戏在板载的快速访问内存池和系统内存中共同提取数据时出现明显的卡顿。
|
||||
板载内存大小也是 GPU 的重要指标。如果按指定细节级别或分辨率运行所需的显存量超过了可用的资源量,游戏通常仍可以运行,但会使用 CPU 的主存来存储额外的纹理数据;而从 DRAM 中提取数据比从板载显存中提取数据要慢得多。这会导致游戏在板载的快速访问内存池和系统内存中共同提取数据时出现明显的卡顿。
|
||||
|
||||
有一点我们需要留意,GPU 生产厂家通常为一款低端或中端 GPU 配置比通常更大的显存,这是他们为产品提价的一种常用手段。很难说大显存是否更具有吸引力,毕竟需要具体问题具体分析。大多数情况下,用更高的价格购买一款仅显存更高的显卡是不划算的。经验规律告诉我们,低端显卡遇到显存瓶颈之前就会碰到其它瓶颈。如果存在疑问,可以查看相关评论,例如 4G 版本或其它数目的版本是否性能超过 2G 版本。更多情况下,如果其它指标都相同,购买大显存版本并不值得。
|
||||
有一点我们需要留意,GPU 生产厂家通常为一款低端或中端 GPU 配置比通常更大的显存,这是他们为产品提价的一种常用手段。很难说大显存是否更具有吸引力,毕竟需要具体问题具体分析。大多数情况下,用更高的价格购买一款仅是显存更高的显卡是不划算的。经验规律告诉我们,低端显卡遇到显存瓶颈之前就会碰到其它瓶颈。如果存在疑问,可以查看相关评论,例如 4G 版本或其它数目的版本是否性能超过 2G 版本。更多情况下,如果其它指标都相同,购买大显存版本并不值得。
|
||||
|
||||
查看我们的[极致技术讲解][2]系列,深入了解更多当前最热的技术话题。
|
||||
查看我们的[极致技术探索][2]系列,深入了解更多当前最热的技术话题。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -65,7 +74,7 @@ via: https://www.extremetech.com/gaming/269335-how-graphics-cards-work
|
||||
作者:[Joel Hruska][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[pinewall](https://github.com/pinewall)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
53
published/20180518 Mastering CI-CD at OpenDev.md
Normal file
53
published/20180518 Mastering CI-CD at OpenDev.md
Normal file
@ -0,0 +1,53 @@
|
||||
在 OpenDev 大会上学习 CI/CD
|
||||
======
|
||||
> 未来的开发工作需要非常精通 CI/CD 流程。
|
||||
|
||||
![在OpenDev上,掌握CI/CD](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/BUSINESS_opennature_3.png?itok=J1OSECM_)
|
||||
|
||||
在 2017 年启动后,OpenDev 大会现在已是一个年度活动。在去年 9 月的首届活动上,会议的重点是边缘计算。今年的活动,于 5 月 22 - 23 日举行,会议的重点是持续集成和持续发布 (CI/CD),并与 OpenStack 峰会一起在温哥华举行。
|
||||
|
||||
基于我在 OpenStack 项目的 CI/CD 系统的技术背景和我近期进入容器下的 CI/CD 方面的经验,我被邀请加入 OpenDev CI/CD 计划委员会。今天我经常借助很多开源技术,例如 [Jenkins][3]、[GitLab][2]、[Spinnaker][4] 和 [Artifactory][5] 来讨论 CI/CD 流程。
|
||||
|
||||
这次活动对我来说是很激动人心的,因为我们将在这个活动中融合两个开源基础设施理念。首先,我们将讨论可以被任何组织使用的 CI/CD 工具。为此目的,在 [讲演][6] 中,我们将听到关于开源 CI/CD 工具的使用讲演,一场来自 Boris Renski 的关于 Spinnaker 的讲演,和一场来自 Jim Blair 的关于 [Zuul][7] 的讲演。同时,讲演会涉及关于开源技术的偏好的高级别话题,特别是那种跨社区的和本身就是开源项目的。从Fatih Degirmenci 和 Daniel Farrel 那里,我们将听到关于在不同社区分享持续发布实践经历,接着 Benjamin Mako Hill 会为我们带来一场关于为什么自由软件需要自由工具的分享。
|
||||
|
||||
在分享 CI/CD 相对新颖的特性后,接下来的活动是对话、研讨会和协作讨论的混合组合。当从人们所提交的讲座和研讨会中进行选择,并提出协作讨论主题时,我们希望确保有一个多样灵活的日程表,这样任何参与者都能在 CI/CD 活动进程中发现有趣的东西。
|
||||
|
||||
这些讲座会是标准的会议风格,选择涵盖关键主题,如制定 CI/CD 流程,在实践 DevOps 时提升安全性,以及更具体的解决方案,如基于容器关于 Kubernetes 的 [Aptomi][8] 和在 ETSI NFV 环境下 CI/CD。这些会话的大部分将会是作为给新接触 CI/CD 或这些特定技术的参与者关于这些话题和理念的简介。
|
||||
|
||||
交互式的研讨会会持续相对比较长的时间,参与者将会在思想上得到特定的体验。这些研讨会包括 “[在持续集成任务中的异常检测][9]”、“[如何安装 Zuul 和配置第一个任务][10]”,和“[Spinnake 101:快速可靠的软件发布][11]”。(注意这些研讨会空间是有限的,所以设立了一个 RSVP 系统。你们将会在会议的链接里找到一个 RSVP 的按钮。)
|
||||
|
||||
可能最让我最兴奋的是协作讨论,这些协作讨论占据了一半以上的活动安排。协作讨论的主题由计划委员会选取。计划委员会根据我们在社区里所看到来选取对应的主题。这是“鱼缸”风格式的会议,通常是几个人聚在一个房间里围绕着 CI/CD 讨论某一个主题。
|
||||
|
||||
这次会议风格的理念是来自于开发者峰会,最初是由 Ubuntu 社区提出,接着 OpenStack 社区也在活动上采纳。这些协作讨论的主题包含不同的会议,这些会议是关于 CI/CD 基础,可以鼓励跨社区协作的提升举措,在组织里推行 CI/CD 文化,和为什么开源 CI/CD 工具如此重要。采用共享文档来做会议笔记,以确保尽可能的在会议的过程中分享知识。在讨论过程中,提出行动项目也是很常见的,因此社区成员可以推动和所涉及的主题相关的倡议。
|
||||
|
||||
活动将以联合总结会议结束。联合总结会议将总结来自协同讨论的关键点和为即将在这个领域工作的参与者指出可选的职业范围。
|
||||
|
||||
可以在 [OpenStack 峰会注册页][13] 上注册参加活动。或者可以在温哥华唯一指定售票的会议中心购买活动的入场券,价格是 $199。更多关于票和全部的活动安排见官网 [OpenDev 网站][1]。
|
||||
|
||||
我希望你们能够加入我们,并在温哥华渡过令人激动的两天,并且在这两天的活动中学习,协作和在 CI/CD 取得进展。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/5/opendev
|
||||
|
||||
作者:[Elizabeth K.Joseph][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[jamelouis](https://github.com/jamelouis)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/pleia2
|
||||
[1]:http://2018.opendevconf.com/
|
||||
[2]:https://about.gitlab.com/
|
||||
[3]:https://jenkins.io/
|
||||
[4]:https://www.spinnaker.io/
|
||||
[5]:https://jfrog.com/artifactory/
|
||||
[6]:http://2018.opendevconf.com/schedule/
|
||||
[7]:https://zuul-ci.org/
|
||||
[8]:http://aptomi.io/
|
||||
[9]:https://www.openstack.org/summit/vancouver-2018/summit-schedule/events/21692/anomaly-detection-in-continuous-integration-jobs
|
||||
[10]:https://www.openstack.org/summit/vancouver-2018/summit-schedule/events/21693/how-to-install-zuul-and-configure-your-first-jobs
|
||||
[11]:https://www.openstack.org/summit/vancouver-2018/summit-schedule/events/21699/spinnaker-101-releasing-software-with-velocity-and-confidence
|
||||
[12]:https://www.openstack.org/summit/vancouver-2018/summit-schedule/events/21831/opendev-cicd-joint-collab-conclusion
|
||||
[13]:https://www.eventbrite.com/e/openstack-summit-may-2018-vancouver-tickets-40845826968?aff=VancouverSummit2018
|
@ -1,19 +1,19 @@
|
||||
开始使用Python调试器
|
||||
Python 调试器入门
|
||||
======
|
||||
|
||||
![](https://fedoramagazine.org/wp-content/uploads/2018/05/pdb-816x345.jpg)
|
||||
|
||||
Python生态系统包含丰富的工具和库,可以改善开发人员的生活。 例如,杂志之前已经介绍了如何[使用交互式shell增强Python][1]。 本文重点介绍另一种可以节省时间并提高Python技能的工具:Python调试器。
|
||||
Python 生态系统包含丰富的工具和库,可以让开发人员更加舒适。 例如,我们之前已经介绍了如何[使用交互式 shell 增强 Python][1]。本文重点介绍另一种可以节省时间并提高 Python 技能的工具:Python 调试器。
|
||||
|
||||
### Python调试器
|
||||
### Python 调试器
|
||||
|
||||
Python标准库提供了一个名为pdb的调试器。 此调试器提供了调试所需的大多数功能,如断点,单行步进,堆栈帧的检查等等。
|
||||
Python 标准库提供了一个名为 pdb 的调试器。此调试器提供了调试所需的大多数功能,如断点、单行步进、堆栈帧的检查等等。
|
||||
|
||||
pdb的基本知识很有用,因为它是标准库的一部分。 你可以在无法安装其他增强的调试器的环境中使用它。
|
||||
了解一些pdb 的基本知识很有用,因为它是标准库的一部分。 你可以在无法安装其他增强的调试器的环境中使用它。
|
||||
|
||||
#### 运行pdb
|
||||
#### 运行 pdb
|
||||
|
||||
运行pdb的最简单方法是从命令行,将程序作为参数传递给debug。 考虑以下脚本:
|
||||
运行 pdb 的最简单方法是从命令行,将程序作为参数传递来调试。 看看以下脚本:
|
||||
|
||||
```
|
||||
# pdb_test.py
|
||||
@ -32,7 +32,7 @@ if __name__ == "__main__":
|
||||
countdown(seconds)
|
||||
```
|
||||
|
||||
你可以从命令行运行pdb,如下所示:
|
||||
你可以从命令行运行 pdb,如下所示:
|
||||
|
||||
```
|
||||
$ python3 -m pdb pdb_test.py
|
||||
@ -41,7 +41,7 @@ $ python3 -m pdb pdb_test.py
|
||||
(Pdb)
|
||||
```
|
||||
|
||||
使用pdb的另一种方法是在程序中设置断点。 为此,请导入pdb模块并使用set_trace函数:
|
||||
使用 pdb 的另一种方法是在程序中设置断点。为此,请导入 `pdb` 模块并使用`set_trace` 函数:
|
||||
|
||||
```
|
||||
# pdb_test.py
|
||||
@ -60,23 +60,24 @@ def countdown(number):
|
||||
if __name__ == "__main__":
|
||||
seconds = 10
|
||||
countdown(seconds)
|
||||
```
|
||||
|
||||
```
|
||||
$ python3 pdb_test.py
|
||||
> /tmp/pdb_test.py(6)countdown()
|
||||
-> print(i)
|
||||
(Pdb)
|
||||
```
|
||||
|
||||
脚本在断点处停止,pdb显示脚本中的下一行。 你也可以在失败后执行调试器。 这称为*事后调试(postmortem debugging)*。
|
||||
脚本在断点处停止,pdb 显示脚本中的下一行。 你也可以在失败后执行调试器。 这称为<ruby>事后调试<rt>postmortem debugging</rt></ruby>。
|
||||
|
||||
#### 导航执行堆栈
|
||||
#### 穿行于执行堆栈
|
||||
|
||||
调试中的一个常见用例是导航执行堆栈。 Python调试器运行后,以下命令很有用:
|
||||
调试中的一个常见用例是在执行堆栈中穿行。 Python 调试器运行后,可以使用以下命令:
|
||||
|
||||
+ w(here) : 显示当前执行的行以及执行堆栈的位置。
|
||||
+ `w(here)`:显示当前执行的行以及执行堆栈的位置。
|
||||
|
||||
|
||||
```
|
||||
```
|
||||
$ python3 test_pdb.py
|
||||
> /tmp/test_pdb.py(10)countdown()
|
||||
-> print(i)
|
||||
@ -88,10 +89,9 @@ $ python3 test_pdb.py
|
||||
(Pdb)
|
||||
```
|
||||
|
||||
+ l(ist) : 显示当前位置周围更多的上下文(代码)。
|
||||
+ `l(ist)`:显示当前位置周围更多的上下文(代码)。
|
||||
|
||||
|
||||
```
|
||||
```
|
||||
$ python3 test_pdb.py
|
||||
> /tmp/test_pdb.py(10)countdown()
|
||||
-> print(i)
|
||||
@ -109,10 +109,9 @@ $ python3 test_pdb.py
|
||||
15 seconds = 10
|
||||
```
|
||||
|
||||
+ u(p)/d(own) : 向上或向下导航调用堆栈。
|
||||
+ `u(p)`/`d(own)`:向上或向下穿行调用堆栈。
|
||||
|
||||
|
||||
```
|
||||
```
|
||||
$ py3 test_pdb.py
|
||||
> /tmp/test_pdb.py(10)countdown()
|
||||
-> print(i)
|
||||
@ -129,12 +128,11 @@ $ py3 test_pdb.py
|
||||
|
||||
pdb提供以下命令来执行和单步执行代码:
|
||||
|
||||
+ n(ext): 继续执行,直到达到当前函数中的下一行,否则返回
|
||||
+ s(tep): 执行当前行并在第一个可能的场合停止(在被调用的函数或当前函数中)
|
||||
+ c(ontinue): 继续执行,仅在断点处停止。
|
||||
+ `n(ext)`:继续执行,直到达到当前函数中的下一行,或者返回
|
||||
+ `s(tep)`:执行当前行并在第一个可能的场合停止(在被调用的函数或当前函数中)
|
||||
+ `c(ontinue)`:继续执行,仅在断点处停止。
|
||||
|
||||
|
||||
```
|
||||
```
|
||||
$ py3 test_pdb.py
|
||||
> /tmp/test_pdb.py(10)countdown()
|
||||
-> print(i)
|
||||
@ -162,13 +160,13 @@ $ py3 test_pdb.py
|
||||
(Pdb)
|
||||
```
|
||||
|
||||
该示例显示了next和step之间的区别。 实际上,当使用step时,调试器会进入pdb模块源代码,而接下来就会执行set_trace函数。
|
||||
该示例显示了 `next` 和 `step` 之间的区别。 实际上,当使用 `step` 时,调试器会进入 `pdb` 模块源代码,而接下来就会执行 `set_trace` 函数。
|
||||
|
||||
#### 检查变量内容
|
||||
|
||||
pdb非常有用的地方是检查执行堆栈中存储的变量的内容。 例如,a(rgs)命令打印当前函数的变量,如下所示:
|
||||
+ pdb 非常有用的地方是检查执行堆栈中存储的变量的内容。 例如,`a(rgs)` 命令打印当前函数的变量,如下所示:
|
||||
|
||||
```
|
||||
```
|
||||
py3 test_pdb.py
|
||||
> /tmp/test_pdb.py(10)countdown()
|
||||
-> print(i)
|
||||
@ -182,11 +180,11 @@ number = 10
|
||||
(Pdb)
|
||||
```
|
||||
|
||||
pdb打印变量的值,在本例中是10。
|
||||
pdb 打印变量的值,在本例中是 10。
|
||||
|
||||
可用于打印变量值的另一个命令是p(rint)。
|
||||
+ 可用于打印变量值的另一个命令是 `p(rint)`。
|
||||
|
||||
```
|
||||
```
|
||||
$ py3 test_pdb.py
|
||||
> /tmp/test_pdb.py(10)countdown()
|
||||
-> print(i)
|
||||
@ -211,19 +209,19 @@ $ py3 test_pdb.py
|
||||
(Pdb)
|
||||
```
|
||||
|
||||
如示例中最后的命令所示,print可以在显示结果之前计算表达式。
|
||||
如示例中最后的命令所示,`print` 可以在显示结果之前计算表达式。
|
||||
|
||||
[Python文档][2]包含每个pdb命令的参考和示例。 对于开始使用Python调试器人来说,这是一个有用的读物。
|
||||
[Python 文档][2]包含每个 pdb 命令的参考和示例。 对于开始使用 Python 调试器人来说,这是一个有用的读物。
|
||||
|
||||
### 增强的调试器
|
||||
|
||||
一些增强的调试器提供了更好的用户体验。 大多数为pdb添加了有用的额外功能,例如语法突出高亮,更好的回溯和自我检查。 流行的增强调试器包括[IPython的ipdb][3]和[pdb ++][4]。
|
||||
一些增强的调试器提供了更好的用户体验。 大多数为 pdb 添加了有用的额外功能,例如语法突出高亮、更好的回溯和自省。 流行的增强调试器包括 [IPython 的 ipdb][3] 和 [pdb++][4]。
|
||||
|
||||
这些示例显示如何在虚拟环境中安装这两个调试器。 这些示例使用新的虚拟环境,但在调试应用程序的情况下,应使用应用程序的虚拟环境。
|
||||
|
||||
#### 安装IPython的ipdb
|
||||
#### 安装 IPython 的 ipdb
|
||||
|
||||
要安装IPython ipdb,请在虚拟环境中使用pip:
|
||||
要安装 IPython ipdb,请在虚拟环境中使用 `pip`:
|
||||
|
||||
```
|
||||
$ python3 -m venv .test_pdb
|
||||
@ -231,21 +229,21 @@ $ source .test_pdb/bin/activate
|
||||
(test_pdb)$ pip install ipdb
|
||||
```
|
||||
|
||||
要在脚本中调用ipdb,必须使用以下命令。 请注意,该模块称为ipdb而不是pdb:
|
||||
要在脚本中调用 ipdb,必须使用以下命令。 请注意,该模块称为 ipdb 而不是 pdb:
|
||||
|
||||
```
|
||||
import ipdb; ipdb.set_trace()
|
||||
```
|
||||
|
||||
IPython的ipdb也可以在Fedora包中使用,所以你可以使用Fedora的包管理器dnf来安装它:
|
||||
IPython 的 ipdb 也可以用 Fedora 包安装,所以你可以使用 Fedora 的包管理器 `dnf` 来安装它:
|
||||
|
||||
```
|
||||
$ sudo dnf install python3-ipdb
|
||||
```
|
||||
|
||||
#### 安装pdb++
|
||||
#### 安装 pdb++
|
||||
|
||||
你可以类似地安装pdb++:
|
||||
你可以类似地安装 pdb++:
|
||||
|
||||
```
|
||||
$ python3 -m venv .test_pdb
|
||||
@ -253,15 +251,15 @@ $ source .test_pdb/bin/activate
|
||||
(test_pdb)$ pip install pdbp
|
||||
```
|
||||
|
||||
pdb++重写了pdb模块,因此你可以使用相同的语法在程序中添加断点:
|
||||
pdb++ 重写了 pdb 模块,因此你可以使用相同的语法在程序中添加断点:
|
||||
|
||||
```
|
||||
import pdb; pdb.set_trace()
|
||||
```
|
||||
|
||||
### Conclusion
|
||||
### 总结
|
||||
|
||||
学习如何使用Python调试器可以节省你在排查应用程序问题时的时间。 对于了解应用程序或某些库的复杂部分如何工作也是有用的,从而提高Python开发人员的技能。
|
||||
学习如何使用 Python 调试器可以节省你在排查应用程序问题时的时间。 对于了解应用程序或某些库的复杂部分如何工作也是有用的,从而提高 Python 开发人员的技能。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -270,7 +268,7 @@ via: https://fedoramagazine.org/getting-started-python-debugger/
|
||||
作者:[Clément Verna][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[Flowsnow](https://github.com/Flowsnow)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,341 @@
|
||||
如何在 Ubuntu 系统中添加一个辅助 IP 地址
|
||||
======
|
||||
|
||||
Linux 管理员应该意识到这一点,因为这是一项例行任务。很多人想知道为什么我们需要在服务器中添加多个 IP 地址,以及为什么我们需要将它添加到单块网卡中?我说的对吗?
|
||||
|
||||
你可能也会有类似的问题:在 Linux 中如何为单块网卡分配多个 IP 地址?在本文中,你可以得到答案。
|
||||
|
||||
当我们对一个新服务器进行设置时,理想情况下它将有一个 IP 地址,即服务器主 IP 地址,它与服务器主机名对应。
|
||||
|
||||
我们不应在服务器主 IP 地址上托管任何应用程序,这是不可取的。如果要在服务器上托管任何应用程序,我们应该为此添加辅助 IP。
|
||||
|
||||
这是业界的最佳实践,它允许用户安装 SSL 证书。大多数系统都配有单块网卡,这足以添加额外的 IP 地址。
|
||||
|
||||
**建议阅读:**
|
||||
|
||||
- [在 Linux 命令行中 9 种方法检查公共 IP 地址][1]
|
||||
- [在 Linux 终端中 3 种简单的方式来检查 DNS(域名服务器)记录][2]
|
||||
- [在 Linux 上使用 Dig 命令检查 DNS(域名服务器)记录][3]
|
||||
- [在 Linux 上使用 Nslookup 命令检查 DNS(域名服务器)记录][4]
|
||||
- [在 Linux 上使用 Host 命令检查 DNS(域名服务器)记录][5]
|
||||
|
||||
我们可以在同一个接口上添加 IP 地址,或者在同一设备上创建子接口,然后在其中添加 IP。默认情况下,一直到 Ubuntu 14.04 LTS,接口给名称为 `ethX (eth0)`,但是从 Ubuntu 15.10 之后网络接口名称已从 `ethX` 更改为 `enXXXXX`(对于服务器是 ens33,桌面版是 enp0s3)。
|
||||
|
||||
在本文中,我们将教你如何在 Ubuntu 上执行此操作,并且衍生到其它发行版(to 校正:这句自己加的)。
|
||||
|
||||
**注意:**别在 DNS 详细信息后添加 IP 地址。如果是这样,DNS 将无法正常工作。
|
||||
|
||||
### 如何在 Ubuntu 14.04 LTS 中添加临时辅助 IP 地址
|
||||
|
||||
在系统中添加 IP 地址之前,运行以下任一命令即可验证服务器主 IP 地址:
|
||||
|
||||
```
|
||||
# ifconfig
|
||||
或
|
||||
# ip addr
|
||||
|
||||
# ip addr
|
||||
|
||||
eth0 Link encap:Ethernet HWaddr 08:00:27:98:b7:36
|
||||
inet addr:192.168.56.150 Bcast:192.168.56.255 Mask:255.255.255.0
|
||||
inet6 addr: fe80::a00:27ff:fe98:b736/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:4 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:105 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:902 (902.0 B) TX bytes:16423 (16.4 KB)
|
||||
|
||||
eth1 Link encap:Ethernet HWaddr 08:00:27:6a:cf:d3
|
||||
inet addr:10.0.3.15 Bcast:10.0.3.255 Mask:255.255.255.0
|
||||
inet6 addr: fe80::a00:27ff:fe6a:cfd3/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:80 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:146 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:8698 (8.6 KB) TX bytes:17047 (17.0 KB)
|
||||
|
||||
lo Link encap:Local Loopback
|
||||
inet addr:127.0.0.1 Mask:255.0.0.0
|
||||
inet6 addr: ::1/128 Scope:Host
|
||||
UP LOOPBACK RUNNING MTU:65536 Metric:1
|
||||
RX packets:25 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:25 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1
|
||||
RX bytes:1730 (1.7 KB) TX bytes:1730 (1.7 KB)
|
||||
```
|
||||
|
||||
如我所见,服务器主 IP 地址是 `192.168.56.150`,我将下一个 IP `192.168.56.151` 作为辅助 IP,使用以下方法完成:
|
||||
|
||||
```
|
||||
# ip addr add 192.168.56.151/24 broadcast 192.168.56.255 dev eth0 label eth0:1
|
||||
```
|
||||
|
||||
输入以下命令以检查新添加的 IP 地址。如果你重新启动服务器,那么新添加的 IP 地址会消失,因为我们的 IP 是临时添加的。
|
||||
|
||||
```
|
||||
# ip addr
|
||||
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
inet 127.0.0.1/8 scope host lo
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 ::1/128 scope host
|
||||
valid_lft forever preferred_lft forever
|
||||
2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 08:00:27:98:b7:36 brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.56.150/24 brd 192.168.56.255 scope global eth0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet 192.168.56.151/24 brd 192.168.56.255 scope global secondary eth0:1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::a00:27ff:fe98:b736/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
3: eth1: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 08:00:27:6a:cf:d3 brd ff:ff:ff:ff:ff:ff
|
||||
inet 10.0.3.15/24 brd 10.0.3.255 scope global eth1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::a00:27ff:fe6a:cfd3/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
### 如何在 Ubuntu 14.04 LTS 中添加永久辅助 IP 地址
|
||||
|
||||
要在 Ubuntu 系统上添加永久辅助 IP 地址,只需编辑 `/etc/network/interfaces` 文件并添加所需的 IP 详细信息。
|
||||
|
||||
```
|
||||
# vi /etc/network/interfaces
|
||||
```
|
||||
|
||||
```
|
||||
# vi /etc/network/interfaces
|
||||
|
||||
# The loopback network interface
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
# The primary network interface
|
||||
auto eth0
|
||||
iface eth0 inet static
|
||||
address 192.168.56.150
|
||||
netmask 255.255.255.0
|
||||
network 192.168.56.0
|
||||
broadcast 192.168.56.255
|
||||
gateway 192.168.56.1
|
||||
|
||||
auto eth0:1
|
||||
iface eth0:1 inet static
|
||||
address 192.168.56.151
|
||||
netmask 255.255.255.0
|
||||
```
|
||||
|
||||
保存并关闭文件,然后重启网络接口服务。
|
||||
|
||||
```
|
||||
# service networking restart
|
||||
或
|
||||
# ifdown eth0:1 && ifup eth0:1
|
||||
```
|
||||
|
||||
验证新添加的 IP 地址:
|
||||
|
||||
```
|
||||
# ifconfig
|
||||
eth0 Link encap:Ethernet HWaddr 08:00:27:98:b7:36
|
||||
inet addr:192.168.56.150 Bcast:192.168.56.255 Mask:255.255.255.0
|
||||
inet6 addr: fe80::a00:27ff:fe98:b736/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:5 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:84 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:962 (962.0 B) TX bytes:11905 (11.9 KB)
|
||||
|
||||
eth0:1 Link encap:Ethernet HWaddr 08:00:27:98:b7:36
|
||||
inet addr:192.168.56.151 Bcast:192.168.56.255 Mask:255.255.255.0
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
|
||||
eth1 Link encap:Ethernet HWaddr 08:00:27:6a:cf:d3
|
||||
inet addr:10.0.3.15 Bcast:10.0.3.255 Mask:255.255.255.0
|
||||
inet6 addr: fe80::a00:27ff:fe6a:cfd3/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:4924 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:3185 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:4037636 (4.0 MB) TX bytes:422516 (422.5 KB)
|
||||
|
||||
lo Link encap:Local Loopback
|
||||
inet addr:127.0.0.1 Mask:255.0.0.0
|
||||
inet6 addr: ::1/128 Scope:Host
|
||||
UP LOOPBACK RUNNING MTU:65536 Metric:1
|
||||
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1
|
||||
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
||||
```
|
||||
|
||||
### 如何在 Ubuntu 16.04 LTS 中临时添加辅助 IP 地址
|
||||
|
||||
正如本文开头所述,网络接口名称从 Ubuntu 15.10 就开始从 ‘ethX’ 更改为 ‘enXXXX’ (enp0s3),所以,替换你的接口名称。
|
||||
|
||||
在执行此操作之前,先检查系统上的 IP 信息:
|
||||
|
||||
```
|
||||
# ifconfig
|
||||
或
|
||||
# ip addr
|
||||
|
||||
enp0s3: flags=4163 mtu 1500
|
||||
inet 192.168.56.201 netmask 255.255.255.0 broadcast 192.168.56.255
|
||||
inet6 fe80::a00:27ff:fe97:132e prefixlen 64 scopeid 0x20
|
||||
ether 08:00:27:97:13:2e txqueuelen 1000 (Ethernet)
|
||||
RX packets 7 bytes 420 (420.0 B)
|
||||
RX errors 0 dropped 0 overruns 0 frame 0
|
||||
TX packets 294 bytes 24747 (24.7 KB)
|
||||
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
||||
|
||||
enp0s8: flags=4163 mtu 1500
|
||||
inet 10.0.3.15 netmask 255.255.255.0 broadcast 10.0.3.255
|
||||
inet6 fe80::344b:6259:4dbe:eabb prefixlen 64 scopeid 0x20
|
||||
ether 08:00:27:12:e8:c1 txqueuelen 1000 (Ethernet)
|
||||
RX packets 1 bytes 590 (590.0 B)
|
||||
RX errors 0 dropped 0 overruns 0 frame 0
|
||||
TX packets 97 bytes 10209 (10.2 KB)
|
||||
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
||||
|
||||
lo: flags=73 mtu 65536
|
||||
inet 127.0.0.1 netmask 255.0.0.0
|
||||
inet6 ::1 prefixlen 128 scopeid 0x10
|
||||
loop txqueuelen 1000 (Local Loopback)
|
||||
RX packets 325 bytes 24046 (24.0 KB)
|
||||
RX errors 0 dropped 0 overruns 0 frame 0
|
||||
TX packets 325 bytes 24046 (24.0 KB)
|
||||
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
||||
```
|
||||
|
||||
如我所见,服务器主 IP 地址是 `192.168.56.201`,所以,我将下一个 IP `192.168.56.202` 作为辅助 IP,使用以下命令完成。
|
||||
|
||||
```
|
||||
# ip addr add 192.168.56.202/24 broadcast 192.168.56.255 dev enp0s3
|
||||
```
|
||||
|
||||
运行以下命令来检查是否已分配了新的 IP。当你重启机器时,它会消失。
|
||||
|
||||
```
|
||||
# ip addr
|
||||
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
inet 127.0.0.1/8 scope host lo
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 ::1/128 scope host
|
||||
valid_lft forever preferred_lft forever
|
||||
2: enp0s3: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 08:00:27:97:13:2e brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.56.201/24 brd 192.168.56.255 scope global enp0s3
|
||||
valid_lft forever preferred_lft forever
|
||||
inet 192.168.56.202/24 brd 192.168.56.255 scope global secondary enp0s3
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::a00:27ff:fe97:132e/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
3: enp0s8: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 08:00:27:12:e8:c1 brd ff:ff:ff:ff:ff:ff
|
||||
inet 10.0.3.15/24 brd 10.0.3.255 scope global dynamic enp0s8
|
||||
valid_lft 86353sec preferred_lft 86353sec
|
||||
inet6 fe80::344b:6259:4dbe:eabb/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
### 如何在 Ubuntu 16.04 LTS 中添加永久辅助 IP 地址
|
||||
|
||||
要在 Ubuntu 系统上添加永久辅助 IP 地址,只需编辑 `/etc/network/interfaces` 文件并添加所需 IP 的详细信息。
|
||||
|
||||
我们不应该在 `dns-nameservers` 行之后添加辅助 IP 地址,因为它不会起作用,应该以下面的格式添加 IP 详情。
|
||||
|
||||
此外,我们不需要添加子接口(我们之前在 Ubuntu 14.04 LTS 中的做法):
|
||||
|
||||
```
|
||||
# vi /etc/network/interfaces
|
||||
|
||||
# interfaces(5) file used by ifup(8) and ifdown(8)
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
# The primary network interface
|
||||
auto enp0s3
|
||||
iface enp0s3 inet static
|
||||
address 192.168.56.201
|
||||
netmask 255.255.255.0
|
||||
|
||||
iface enp0s3 inet static
|
||||
address 192.168.56.202
|
||||
netmask 255.255.255.0
|
||||
|
||||
gateway 192.168.56.1
|
||||
network 192.168.56.0
|
||||
broadcast 192.168.56.255
|
||||
dns-nameservers 8.8.8.8 8.8.4.4
|
||||
dns-search 2daygeek.local
|
||||
```
|
||||
|
||||
保存并关闭文件,然后重启网络接口服务:
|
||||
|
||||
```
|
||||
# systemctl restart networking
|
||||
或
|
||||
# ifdown enp0s3 && ifup enp0s3
|
||||
```
|
||||
|
||||
运行以下命令来检查是否已经分配了新的 IP:
|
||||
|
||||
```
|
||||
# ip addr
|
||||
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
inet 127.0.0.1/8 scope host lo
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 ::1/128 scope host
|
||||
valid_lft forever preferred_lft forever
|
||||
2: enp0s3: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 08:00:27:97:13:2e brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.56.201/24 brd 192.168.56.255 scope global enp0s3
|
||||
valid_lft forever preferred_lft forever
|
||||
inet 192.168.56.202/24 brd 192.168.56.255 scope global secondary enp0s3
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::a00:27ff:fe97:132e/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
3: enp0s8: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 08:00:27:12:e8:c1 brd ff:ff:ff:ff:ff:ff
|
||||
inet 10.0.3.15/24 brd 10.0.3.255 scope global dynamic enp0s8
|
||||
valid_lft 86353sec preferred_lft 86353sec
|
||||
inet6 fe80::344b:6259:4dbe:eabb/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
让我来 ping 一下新 IP 地址:
|
||||
|
||||
```
|
||||
# ping 192.168.56.202 -c 4
|
||||
PING 192.168.56.202 (192.168.56.202) 56(84) bytes of data.
|
||||
64 bytes from 192.168.56.202: icmp_seq=1 ttl=64 time=0.019 ms
|
||||
64 bytes from 192.168.56.202: icmp_seq=2 ttl=64 time=0.087 ms
|
||||
64 bytes from 192.168.56.202: icmp_seq=3 ttl=64 time=0.034 ms
|
||||
64 bytes from 192.168.56.202: icmp_seq=4 ttl=64 time=0.042 ms
|
||||
|
||||
--- 192.168.56.202 ping statistics ---
|
||||
4 packets transmitted, 4 received, 0% packet loss, time 3068ms
|
||||
rtt min/avg/max/mdev = 0.019/0.045/0.087/0.026 ms
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/how-to-add-additional-ip-secondary-ip-in-ubuntu-debian-system/
|
||||
|
||||
作者:[Prakash Subramanian][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.2daygeek.com/author/prakash/
|
||||
[1]:https://www.2daygeek.com/check-find-server-public-ip-address-linux/
|
||||
[2]:https://www.2daygeek.com/check-find-dns-records-of-domain-in-linux-terminal/
|
||||
[3]:https://www.2daygeek.com/dig-command-check-find-dns-records-lookup-linux/
|
||||
[4]:https://www.2daygeek.com/nslookup-command-check-find-dns-records-lookup-linux/
|
||||
[5]:https://www.2daygeek.com/host-command-check-find-dns-records-lookup-linux/
|
@ -6,27 +6,25 @@
|
||||
|
||||
如果你想知道大家对某件事情的看法,Twitter 是最好的地方了。Twitter 是观点持续不断的涌现出来的地方,每秒钟大概有 6000 条新 Twitter 发送出来。因特网上的发展很快,如果你想与时俱进或者跟上潮流,Twitter 就是你要去的地方。
|
||||
|
||||
现在,我们生活在一个数据为王的时代,很多公司都善于运用 Twitter 上的数据。根据测量到的他们新产品的人气,尝试预测之后的市场趋势,分析 Twitter 上的数据有很多用处。通过数据,商人把产品卖给合适的用户,收集关于他们品牌和改进的反馈,或者获取他们产品或促销活动失败的原因。不仅仅是商人,很多政治和经济上的决定是在观察人们意见的基础上所作的。今天,我会试着让你感受下关于 Twitter 的简单 [情感分析][1],判断这个 Twitter 是正能量,负能量还是中性的。这不会像专业人士所用的那么复杂,但至少,它会让你知道挖掘观念的想法。
|
||||
现在,我们生活在一个数据为王的时代,很多公司都善于运用 Twitter 上的数据。根据测量到的他们新产品的人气,尝试预测之后的市场趋势,分析 Twitter 上的数据有很多用处。通过数据,商人把产品卖给合适的用户,收集关于他们品牌和改进的反馈,或者获取他们产品或促销活动失败的原因。不仅仅是商人,很多政治和经济上的决定是在观察人们意见的基础上所作的。今天,我会试着让你感受下关于 Twitter 的简单 [情感分析][1],判断这个 Twitter 是正能量、负能量还是中性的。这不会像专业人士所用的那么复杂,但至少,它会让你知道挖掘观念的想法。
|
||||
|
||||
我们将使用 NodeJs,因为 JavaScript 太常用了,而且它还是最容易入门的语言。
|
||||
|
||||
### 前置条件:
|
||||
|
||||
* 安装了 NodeJs 和 NPM
|
||||
|
||||
* 有 NodeJs 和 NPM 包的经验
|
||||
|
||||
* 熟悉命令行。
|
||||
|
||||
好了,就是这样。开始吧
|
||||
好了,就是这样。开始吧。
|
||||
|
||||
### 开始
|
||||
|
||||
为了你的项目新建一个目录,进入这个目录下面。打开终端(或是命令行)。进入刚创建的目录下面,运行命令 `npm init -y`。这会在这个目录下创建一个 `package.json` 文件。现在我们可以安装需要的 npm 包了。只需要创建一个新文件,命名为 `index.js` 然后我们就完成了初始的编码。
|
||||
|
||||
### 获取 tweets
|
||||
### 获取推文
|
||||
|
||||
好了,我们想要分析 Twitter ,为了实现这个目的,我们需要获取 Twitter 的标题。为此,我们要用到 [twit][2] 包。因此,先用 `npm i wit` 命令安装它。我们还需要在 APP 上注册账户,用来访问 Twitter 的 API。点击这个 [链接][3],填写所有项目,从 “Keys and Access Token” 标签页中复制 “Consumer Key”,“Consumer Secret”,“Access token” 和 “Access Token Secret” 这几项到 `.env` 文件中,就像这样:
|
||||
好了,我们想要分析 Twitter ,为了实现这个目的,我们需要以编程的方式访问 Twitter。为此,我们要用到 [twit][2] 包。因此,先用 `npm i wit` 命令安装它。我们还需要注册一个 App,以通过我们的账户来访问 Twitter 的 API。点击这个 [链接][3],填写所有项目,从 “Keys and Access Token” 标签页中复制 “Consumer Key”、“Consumer Secret”、“Access token” 和 “Access Token Secret” 这几项到一个 `.env` 文件中,就像这样:
|
||||
|
||||
```
|
||||
# .env
|
||||
@ -35,7 +33,6 @@ CONSUMER_KEY=************
|
||||
CONSUMER_SECRET=************
|
||||
ACCESS_TOKEN=************
|
||||
ACCESS_TOKEN_SECRET=************
|
||||
|
||||
```
|
||||
|
||||
现在开始。
|
||||
@ -63,22 +60,20 @@ const config_twitter = {
|
||||
};
|
||||
|
||||
let api = new Twit(config_twitter);
|
||||
|
||||
```
|
||||
|
||||
这里已经用所需的配置文件建立了到 Twitter 上的连接。但我们什么事情都没做。先定义个获取 Twitter 的函数:
|
||||
这里已经用所需的配置文件建立了到 Twitter 上的连接。但我们什么事情都没做。先定义个获取推文的函数:
|
||||
|
||||
```
|
||||
async function get_tweets(q, count) {
|
||||
let tweets = await api.get('search/tweets', {q, count, tweet_mode: 'extended'});
|
||||
return tweets.data.statuses.map(tweet => tweet.full_text);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
这是个 async 函数,因为 `api.get` 函数返回一个 promise 对象,而不是 `then` 链,我想通过这种简单的方式获取推文。它接收两个参数 -q 和 count,`q` 是查询或者我们想要搜索的关键字,`count` 是让这个 `api` 返回的 Twitter 数量。
|
||||
这是个 async 函数,因为 `api.get` 函数返回一个 promise 对象,而不是 `then` 链,我想通过这种简单的方式获取推文。它接收两个参数 `q` 和 `count`,`q` 是查询或者我们想要搜索的关键字,`count` 是让这个 `api` 返回的推文数量。
|
||||
|
||||
目前为止我们拥有了一个从 Twitter 上获取完整文本的简单方法,我们要获取的文本中可能包含某些连接或者原推文可能已经被删除了。所以我们会编写另一个函数,获取并返回即便是转发的 Twitter 的文本,,并且删除其中存在的链接。
|
||||
目前为止我们拥有了一个从 Twitter 上获取完整文本的简单方法。不过这里有个问题,现在我们要获取的文本中可能包含某些连接或者由于转推而被截断了。所以我们会编写另一个函数,拆解并返回推文的文本,即便是转发的推文,并且其中有链接的话就删除。
|
||||
|
||||
```
|
||||
function get_text(tweet) {
|
||||
@ -90,21 +85,18 @@ async function get_tweets(q, count) {
|
||||
let tweets = await api.get('search/tweets', {q, count, 'tweet_mode': 'extended'});
|
||||
return tweets.data.statuses.map(get_text);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
现在我们拿到了文本。下一步是从文本中获取情感。为此我们会使用 `npm` 中的另一个包 —— [`sentiment`][4]。让我们像安装其他包那样安装 `sentiment`,添加到脚本中。
|
||||
|
||||
```
|
||||
const sentiment = require('sentiment')
|
||||
|
||||
```
|
||||
|
||||
`sentiment` 用起来很简单。我们只用把 `sentiment` 函数用在我们想要分析的文本上,它就能返回文本的相对分数。如果分数小于 0,它表达的就是消极情感,大于 0 的分数是积极情感,而 0,如你所料,表示中性的情感。基于此,我们将会把 tweets 打印成不同的颜色 —— 绿色表示积极,红色表示消极,蓝色表示中性。为此,我们会用到 [`colors`][5] 包。先安装这个包,然后添加到脚本中。
|
||||
`sentiment` 用起来很简单。我们只用把 `sentiment` 函数用在我们想要分析的文本上,它就能返回文本的相对分数。如果分数小于 0,它表达的就是消极情感,大于 0 的分数是积极情感,而 0,如你所料,表示中性的情感。基于此,我们将会把推文打印成不同的颜色 —— 绿色表示积极,红色表示消极,蓝色表示中性。为此,我们会用到 [`colors`][5] 包。先安装这个包,然后添加到脚本中。
|
||||
|
||||
```
|
||||
const colors = require('colors/safe');
|
||||
|
||||
```
|
||||
|
||||
好了,现在把所有东西都整合到 `main` 函数中。
|
||||
@ -127,17 +119,15 @@ async function main() {
|
||||
console.log(tweet);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
最后,执行 `main` 函数。
|
||||
|
||||
```
|
||||
main();
|
||||
|
||||
```
|
||||
|
||||
就是这样,一个简单的分析 tweet 中的基本情感的脚本。
|
||||
就是这样,一个简单的分析推文中的基本情感的脚本。
|
||||
|
||||
```
|
||||
\\ full script
|
||||
@ -201,7 +191,7 @@ via: https://boostlog.io/@anshulc95/twitter-sentiment-analysis-using-nodejs-5ad1
|
||||
|
||||
作者:[Anshul Chauhan][a]
|
||||
译者:[BriFuture](https://github.com/BriFuture)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,25 +1,27 @@
|
||||
在市场工作时如何建立一个职业网络
|
||||
在开源“集市”工作时如何建立一个职业网络
|
||||
======
|
||||
|
||||
> 在组织内建立联系遇到问题了吗?你或许是采用了错误的策略。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/connection_people_team_collaboration.png?itok=0_vQT8xV)
|
||||
|
||||
职业社交网络——在同事或专业人员之间建立人际联系——可以采用多种形式、在产业内跨组织进行。建立职业网络需要花费时间和精力,并且当某位成员加入或离开一个组织时,此人的网络通常需要被在一个新的工作环境中重建。
|
||||
职业社交网络 —— 在同事或专业人员之间建立人际联系 —— 可以采用多种形式、在产业内跨组织进行。建立职业网络需要花费时间和精力,并且当某位成员加入或离开一个组织时,此人的网络通常需要被在一个新的工作环境中重建。
|
||||
|
||||
职业社交网络在不同组织中起相似作用——信息共享,导师制,机会,工作利益和其他作用——然而传统组织与开放组织在组织内构建特定联系的方法和原因可能不尽相同。这些差异有其影响:同事联系方式、如何建立信任、组织内多元化的程度和种类以及建立合作的能力,所有这些因素都是相互关联的,而且他们参与并塑造了人们所建立的社交网络。
|
||||
职业社交网络在不同组织中起相似作用 —— 信息共享、导师制、机会、工作利益和其他作用 —— 然而传统组织与开放组织在组织内构建特定联系的*方法*和*原因*可能不尽相同。这些差异有其影响:同事联系方式、如何建立信任、组织内多元化的程度和种类以及建立合作的能力,所有这些因素都是相互关联的,而且它们参与并塑造了人们所建立的社交网络。
|
||||
|
||||
一个开放的组织对包容性的强调可以使社交网络在解决商业问题上比传统等级制组织更加高效。这种观念在开源的思考中有很久的历史。例如,在<ruby>[《教堂与市场》][1]<rt>The Cathedral and the Bazaar</rt></ruby>中,埃里克·雷蒙德写道:“许多年前社会学家发现, 相比一个随机选择的观察者的观点,许多同等专业的(或是同等无知的)观察家的普遍观点是可靠得多的预言。”所以让我们了解社交网络的结构和目的如何影响各类组织的价值观。
|
||||
一个开放的组织对包容性的强调可以使社交网络在解决商业问题上比传统等级制组织更加高效。这种观念在开源的思考中有很久的历史。例如,在<ruby>[《教堂与集市》][1]<rt>The Cathedral and the Bazaar</rt></ruby>中,埃里克·雷蒙德写道:“许多年前社会学家发现,相比一个随机选择的观察者的观点,许多同等专业的(或是同等无知的)观察家的普遍观点是可靠得多的预言。”所以让我们了解社交网络的结构和目的如何影响各类组织的价值观。
|
||||
|
||||
### 传统组织中的社交网络
|
||||
|
||||
当我在传统组织工作并要描述我为工作做了什么时,人们问我的第一件事就是我与其他人如何关联,通常是主任级的领导。“你在希拉手下吗?”他们会这么问。“你为马尔科姆工作吗?”这意味着以一种上下级的视角看待传统组织的作用;当试图安排工作或雇员时,人们想要从上下级的角度理解网络结构。
|
||||
当我在传统组织工作并要描述我为工作做了什么时,人们问我的第一件事就是我与其他人(通常是总监级的领导)的关系。“你在希拉手下吗?”他们会这么问。“你为马尔科姆工作吗?”这意味着以一种上下级的视角看待传统组织的作用;当试图安排工作或雇员时,人们想要从上下级的角度理解网络结构。
|
||||
|
||||
换言之,在传统组织中社交网络依赖于等级制结构,因此他们彼此追寻。事实上,甚至弄清一个雇员在关系网中处于怎样的位置也算得上是一种“上下级组织”式的担忧。
|
||||
换言之,在传统组织中社交网络依赖于等级制结构,因此他们彼此跟随。事实上,甚至弄清一个雇员在关系网中处于怎样的位置也算得上是一种“上下级组织”式的担忧。
|
||||
|
||||
然而并非所有潜在等级制都是如此。它还视相关人员而定。对于上下级网络的关注会决定雇员在网络中的“价值”,因为网络本身是一个持续的权力关系的系统,它会根据人不同水平的价值给予他们不同的定位。它淡化了个人的能力和技能的重要性。因此,一个人在传统组织的联系促使其能力具有前瞻性,为人所知,有影响力并在其事业中起到支持作用。
|
||||
|
||||
相比传统等级制组织,一个开放的组织对包容性的强调能使网络解决商业问题更加高效。
|
||||
|
||||
传统组织的正式结构以特定方式决定着雇员的社交网络——有些可能是优点,有些可能是缺点,这取决于具体环境——例如:
|
||||
传统组织的正式结构以特定方式决定着雇员的社交网络 —— 有些可能是优点,有些可能是缺点,这取决于具体环境——例如:
|
||||
|
||||
* 要更快速地了解“谁是谁”并看到人们如何关联是较为便捷的(通常这在特定层级内建立信任网络)。
|
||||
* 通常,这种对关系的进一步的理解意味着会有更少的过剩工作(在一个特定网络中项目有清晰的相应的归属者)和过多交流(人们知道谁对交流什么负责)。
|
||||
@ -28,12 +30,10 @@
|
||||
* 权力转让缓慢;一个人的参与能力更多地决定于等级结构所创造的网络的结盟而非其他因素(比如个人能力),减少了被看做社区和成员利益的东西。
|
||||
* 竞争似乎更加清晰;理解“谁在竞争什么”通常发生在一个公认的、被限定了的等级结构中(权力网络中职位的缺乏增进了竞争因此竞争会更激烈)。
|
||||
* 当更严格的网络决定了灵活性的限度时,适应能力会受损。网络的“夙愿”和合作的限度也会以同样的方式受影响。
|
||||
* 在严格的网络中,方向明确,并且领导人通常靠过度指导经营,在这里,破坏更容易发生。
|
||||
* 当社交网络不那么灵活时,风险下降;人们知道什么需要发生,怎样发生,何时发生(但是考虑到在一个组织中工作的广度,这不见得总是“坏事”;一些工作的职能需要较小的风险,例如:<ruby>人力资源管理<rt>H R</rt></ruby>),企业并购和法律工作等。
|
||||
* 在严格的网络中,方向明确,并且领导人通常靠过度指导来进行管理,在这里,破坏更容易发生。
|
||||
* 当社交网络不那么灵活时,风险下降;人们知道什么需要发生,怎样发生,何时发生(但是考虑到在一个组织中工作的广度,这不见得总是“坏事”;一些工作的职能需要较小的风险,例如:人力资源管理 HR),企业并购和法律工作等。
|
||||
* 在网络中的信任是更大的,尤其当受雇者是正式网络的一部分的时候(当某人不是网络的一份子时,被排斥的人可能特别难管理或改正)。
|
||||
|
||||
|
||||
|
||||
### 开放组织中的社交网络
|
||||
|
||||
尽管开放组织必定会有等级结构,但他们并不根据那个网络运作。他们的职业网络结构更加灵活(或者说是“随时随地”)。
|
||||
@ -53,22 +53,21 @@
|
||||
* 灵活的社交网络同样会增加变革和风险;创意会流通得更快而且更神奇,并且执行会更加自信。
|
||||
* 信任建立在同事合作之上(它本该如此!),而不是在对架构的尊重之上。
|
||||
|
||||
|
||||
|
||||
### 让它有效
|
||||
|
||||
如果你正在考虑从一种组织架构转变为另一种,当你在构建并维持你的职业社交网络时思考一下如下所述内容。
|
||||
|
||||
#### 来自传统组织的小建议
|
||||
|
||||
* 对决策的架构和管控不是坏事; 运作中的框架需要明晰透明,而且决策者需要考虑他们的决定。
|
||||
* 对决策的架构和管控不是坏事;运作中的框架需要明晰透明,而且决策者需要考虑他们的决定。
|
||||
* 在执行上突出需要经理提供关注,还需要有在滤出任何让人分心或混乱的事务的同时仍能提供足够的来龙去脉的能力。
|
||||
* 已经确立的网络帮助了一大批人同步工作并且能管控风险。
|
||||
|
||||
#### 来自开放组织的小建议
|
||||
* 能力强的领导人是那些可以根据多样的风格和对同事、团队的不同偏好提供不同层次的透明度和指导,同时又不会构建出不灵活的网络的人。。
|
||||
#### 来自开放组织的小建议
|
||||
|
||||
* 能力强的领导人是那些可以根据多样的风格和对同事、团队的不同偏好提供不同层次的透明度和指导,同时又不会构建出不灵活的网络的人。
|
||||
* 伟大的想法比已建立的组织会赢得更多。
|
||||
* 人们对他们得名声会更加负责任。
|
||||
* 人们对他们的名声会更加负责任。
|
||||
* 创意和信息的流转是变革的关键。松散组织中的关系网络可以使这两种元素生发的频度更高、幅度更广。
|
||||
|
||||
|
||||
@ -76,10 +75,10 @@
|
||||
|
||||
via: https://opensource.com/open-organization/18/6/building-professional-social-networks-openly
|
||||
|
||||
作者:[Heidi Hess;von Ludewig][a]
|
||||
作者:[Heidi Hess von Ludewig][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[ZenMoore](https://github.com/ZenMoore)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,5 +1,6 @@
|
||||
查看一个归档或压缩文件的内容而无需解压它
|
||||
======
|
||||
|
||||
![](https://www.ostechnix.com/wp-content/uploads/2018/07/View-The-Contents-Of-An-Archive-Or-Compressed-File-720x340.png)
|
||||
|
||||
在本教程中,我们将学习如何在类 Unix 系统中查看一个归档或者压缩文件的内容而无需实际解压它。在深入之前,让我们先厘清归档和压缩文件的概念,它们之间有显著不同。归档是将多个文件或者目录归并到一个文件的过程,因此这个生成的文件是没有被压缩过的。而压缩则是结合多个文件或者目录到一个文件并最终压缩这个文件的方法。归档文件不是一个压缩文件,但压缩文件可以是一个归档文件,清楚了吗?好,那就让我们进入今天的主题。
|
||||
@ -8,44 +9,44 @@
|
||||
|
||||
得益于 Linux 社区,有很多命令行工具可以来达成上面的目标。下面就让我们来看看使用它们的一些示例。
|
||||
|
||||
**1 使用 Vim 编辑器**
|
||||
#### 1、使用 vim 编辑器
|
||||
|
||||
Vim 不只是一个编辑器,使用它我们可以干很多事情。下面的命令展示的是在没有解压的情况下使用 Vim 查看一个压缩的归档文件的内容:
|
||||
vim 不只是一个编辑器,使用它我们可以干很多事情。下面的命令展示的是在没有解压的情况下使用 vim 查看一个压缩的归档文件的内容:
|
||||
|
||||
```
|
||||
$ vim ostechnix.tar.gz
|
||||
|
||||
```
|
||||
|
||||
![][2]
|
||||
|
||||
你甚至还可以浏览归档文件的内容,打开其中的文本文件(假如有的话)。要打开一个文本文件,只需要用方向键将鼠标的游标放置到文件的前面,然后敲 ENTER 键来打开它。
|
||||
|
||||
**2 使用 Tar 命令**
|
||||
#### 2、使用 tar 命令
|
||||
|
||||
为了列出一个 tar 归档文件的内容,可以运行:
|
||||
|
||||
```
|
||||
$ tar -tf ostechnix.tar
|
||||
ostechnix/
|
||||
ostechnix/image.jpg
|
||||
ostechnix/file.pdf
|
||||
ostechnix/song.mp3
|
||||
|
||||
```
|
||||
|
||||
或者使用 **-v** 选项来查看归档文件的具体属性,例如它的文件所有者、属组、创建日期等等。
|
||||
或者使用 `-v` 选项来查看归档文件的具体属性,例如它的文件所有者、属组、创建日期等等。
|
||||
|
||||
```
|
||||
$ tar -tvf ostechnix.tar
|
||||
drwxr-xr-x sk/users 0 2018-07-02 19:30 ostechnix/
|
||||
-rw-r--r-- sk/users 53632 2018-06-29 15:57 ostechnix/image.jpg
|
||||
-rw-r--r-- sk/users 156831 2018-06-04 12:37 ostechnix/file.pdf
|
||||
-rw-r--r-- sk/users 9702219 2018-04-25 20:35 ostechnix/song.mp3
|
||||
|
||||
```
|
||||
|
||||
**3 使用 Rar 命令**
|
||||
#### 3、使用 rar 命令
|
||||
|
||||
要查看一个 rar 文件的内容,只需要执行:
|
||||
|
||||
```
|
||||
$ rar v ostechnix.rar
|
||||
|
||||
@ -62,12 +63,12 @@ Attributes Size Packed Ratio Date Time Checksum Name
|
||||
-rw-r--r-- 9702219 9658527 99% 2018-04-25 20:35 DD875AC4 ostechnix/song.mp3
|
||||
----------- --------- -------- ----- ---------- ----- -------- ----
|
||||
9912682 9849787 99% 3
|
||||
|
||||
```
|
||||
|
||||
**4 使用 Unrar 命令**
|
||||
#### 4、使用 unrar 命令
|
||||
|
||||
你也可以使用带有 `l` 选项的 `unrar` 来做到与上面相同的事情,展示如下:
|
||||
|
||||
你也可以使用带有 **l** 选项的 **Unrar** 来做到与上面相同的事情,展示如下:
|
||||
```
|
||||
$ unrar l ostechnix.rar
|
||||
|
||||
@ -83,23 +84,23 @@ Attributes Size Date Time Name
|
||||
-rw-r--r-- 9702219 2018-04-25 20:35 ostechnix/song.mp3
|
||||
----------- --------- ---------- ----- ----
|
||||
9912682 3
|
||||
|
||||
```
|
||||
|
||||
**5 使用 Zip 命令**
|
||||
#### 5、使用 zip 命令
|
||||
|
||||
为了查看一个 zip 文件的内容而无需解压它,可以使用下面的 `zip` 命令:
|
||||
|
||||
为了查看一个 zip 文件的内容而无需解压它,可以使用下面的 **zip** 命令:
|
||||
```
|
||||
$ zip -sf ostechnix.zip
|
||||
Archive contains:
|
||||
Life advices.jpg
|
||||
Total 1 entries (597219 bytes)
|
||||
|
||||
```
|
||||
|
||||
**6. 使用 Unzip 命令**
|
||||
#### 6、使用 unzip 命令
|
||||
|
||||
你也可以像下面这样使用 `-l` 选项的 `unzip` 命令来呈现一个 zip 文件的内容:
|
||||
|
||||
你也可以像下面这样使用 **-l** 选项的 **Unzip** 命令来呈现一个 zip 文件的内容:
|
||||
```
|
||||
$ unzip -l ostechnix.zip
|
||||
Archive: ostechnix.zip
|
||||
@ -108,10 +109,9 @@ Length Date Time Name
|
||||
597219 2018-04-09 12:48 Life advices.jpg
|
||||
--------- -------
|
||||
597219 1 file
|
||||
|
||||
```
|
||||
|
||||
**7 使用 Zipinfo 命令**
|
||||
#### 7、使用 zipinfo 命令
|
||||
|
||||
```
|
||||
$ zipinfo ostechnix.zip
|
||||
@ -119,43 +119,42 @@ Archive: ostechnix.zip
|
||||
Zip file size: 584859 bytes, number of entries: 1
|
||||
-rw-r--r-- 6.3 unx 597219 bx defN 18-Apr-09 12:48 Life advices.jpg
|
||||
1 file, 597219 bytes uncompressed, 584693 bytes compressed: 2.1%
|
||||
|
||||
```
|
||||
|
||||
如你所见,上面的命令展示了一个 zip 文件的内容、它的权限、创建日期和压缩百分比等等信息。
|
||||
|
||||
**8. 使用 Zcat 命令**
|
||||
#### 8、使用 zcat 命令
|
||||
|
||||
要一个压缩的归档文件的内容而不解压它,使用 `zcat` 命令,我们可以得到:
|
||||
|
||||
要一个压缩的归档文件的内容而不解压它,使用 **zcat** 命令,我们可以得到:
|
||||
```
|
||||
$ zcat ostechnix.tar.gz
|
||||
|
||||
```
|
||||
|
||||
zcat 和 `gunzip -c` 命令相同。所以你可以使用下面的命令来查看归档或者压缩文件的内容:
|
||||
`zcat` 和 `gunzip -c` 命令相同。所以你可以使用下面的命令来查看归档或者压缩文件的内容:
|
||||
|
||||
```
|
||||
$ gunzip -c ostechnix.tar.gz
|
||||
|
||||
```
|
||||
|
||||
**9. 使用 Zless 命令**
|
||||
#### 9、使用 zless 命令
|
||||
|
||||
要使用 zless 命令来查看一个归档或者压缩文件的内容,只需:
|
||||
|
||||
要使用 Zless 命令来查看一个归档或者压缩文件的内容,只需:
|
||||
```
|
||||
$ zless ostechnix.tar.gz
|
||||
|
||||
```
|
||||
|
||||
这个命令类似于 `less` 命令,它将一页一页地展示其输出。
|
||||
|
||||
**10. 使用 Less 命令**
|
||||
#### 10、使用 less 命令
|
||||
|
||||
可能你已经知道 **less** 命令可以打开文件来交互式地阅读它,并且它支持滚动和搜索。
|
||||
可能你已经知道 `less` 命令可以打开文件来交互式地阅读它,并且它支持滚动和搜索。
|
||||
|
||||
运行下面的命令来使用 `less` 命令查看一个归档或者压缩文件的内容:
|
||||
|
||||
运行下面的命令来使用 less 命令查看一个归档或者压缩文件的内容:
|
||||
```
|
||||
$ less ostechnix.tar.gz
|
||||
|
||||
```
|
||||
|
||||
上面便是全部的内容了。现在你知道了如何在 Linux 中使用各种命令查看一个归档或者压缩文件的内容了。希望本文对你有用。更多好的内容将呈现给大家,希望继续关注我们!
|
||||
@ -169,7 +168,7 @@ via: https://www.ostechnix.com/how-to-view-the-contents-of-an-archive-or-compres
|
||||
作者:[SK][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[FSSlc](https://github.com/FSSlc)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,28 +1,29 @@
|
||||
理解 Python 的 Dataclasses -- 第一部分
|
||||
理解 Python 的 Dataclasses(一)
|
||||
======
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/900/1*7pr8EL8EDsP296pxL7Wz_g.png)
|
||||
|
||||
如果你正在阅读本文,那么你已经意识到了 Python 3.7 以及它所包含的新特性。就我个人而言,我对 `Dataclasses` 感到非常兴奋,因为我有一段时间在等待它了。
|
||||
如果你正在阅读本文,那么你已经意识到了 Python 3.7 以及它所包含的新特性。就我个人而言,我对 `Dataclasses` 感到非常兴奋,因为我等了它一段时间了。
|
||||
|
||||
本系列包含两部分:
|
||||
1\. Dataclass 特点概述
|
||||
2\. 在下一篇文章概述 Dataclass 的 `fields`
|
||||
|
||||
1. Dataclass 特点概述
|
||||
2. 在下一篇文章概述 Dataclass 的 `fields`
|
||||
|
||||
### 介绍
|
||||
|
||||
`Dataclasses` 是 Python 的类(译注:更准确的说,它是一个模块),适用于存储数据对象。你可能会问什么是数据对象?下面是定义数据对象的一个不太详细的特性列表:
|
||||
`Dataclasses` 是 Python 的类(LCTT 译注:更准确的说,它是一个模块),适用于存储数据对象。你可能会问什么是数据对象?下面是定义数据对象的一个不太详细的特性列表:
|
||||
|
||||
* 它们存储数据并代表某种数据类型。例如:一个数字。对于熟悉 ORM 的人来说,模型实例是一个数据对象。它代表一种特定的实体。它包含那些定义或表示实体的属性。
|
||||
|
||||
* 它们可以与同一类型的其他对象进行比较。例如:一个数字可以是 `greater than(大于)`, `less than(小于)` 或 `equal(等于)` 另一个数字。
|
||||
* 它们存储数据并代表某种数据类型。例如:一个数字。对于熟悉 ORM 的人来说,模型实例就是一个数据对象。它代表一种特定的实体。它包含那些定义或表示实体的属性。
|
||||
* 它们可以与同一类型的其他对象进行比较。例如:一个数字可以是 `greater than`(大于)、`less than`(小于) 或 `equal`(等于) 另一个数字。
|
||||
|
||||
当然还有更多的特性,但是这个列表足以帮助你理解问题的关键。
|
||||
|
||||
为了理解 `Dataclasses`,我们将实现一个包含数字的简单类,并允许我们执行上面提到的操作。
|
||||
|
||||
首先,我们将使用普通类,然后我们再使用 `Dataclasses` 来实现相同的结果。
|
||||
|
||||
但在我们开始之前,先来谈谈 `dataclasses` 的用法。
|
||||
但在我们开始之前,先来谈谈 `Dataclasses` 的用法。
|
||||
|
||||
Python 3.7 提供了一个装饰器 [dataclass][2],用于将类转换为 `dataclass`。
|
||||
|
||||
@ -33,7 +34,7 @@ from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class A:
|
||||
…
|
||||
...
|
||||
```
|
||||
|
||||
现在,让我们深入了解一下 `dataclass` 带给我们的变化和用途。
|
||||
@ -65,10 +66,10 @@ class Number:
|
||||
>>> 1
|
||||
```
|
||||
|
||||
以下是 dataclass 装饰器带来的变化:
|
||||
以下是 `dataclass` 装饰器带来的变化:
|
||||
|
||||
1\. 无需定义 `__init__`,然后将值赋给 `self.d` 负责处理它(to 校正:这里真不知道 d 在哪里)
|
||||
2\. 我们以更加易读的方式预先定义了成员属性,以及[类型提示][3]。我们现在立即能知道 `val` 是 `int` 类型。这无疑比一般定义类成员的方式更具可读性。
|
||||
1. 无需定义 `__init__`,然后将值赋给 `self`,`dataclass` 负责处理它(LCTT 译注:此处原文可能有误,提及一个不存在的 `d`)
|
||||
2. 我们以更加易读的方式预先定义了成员属性,以及[类型提示][3]。我们现在立即能知道 `val` 是 `int` 类型。这无疑比一般定义类成员的方式更具可读性。
|
||||
|
||||
> Python 之禅: 可读性很重要
|
||||
|
||||
@ -133,15 +134,11 @@ class Number:
|
||||
|
||||
两个对象 `a` 和 `b` 之间的比较通常包括以下操作:
|
||||
|
||||
* a < b
|
||||
|
||||
* a > b
|
||||
|
||||
* a == b
|
||||
|
||||
* a >= b
|
||||
|
||||
* a <= b
|
||||
* `a < b`
|
||||
* `a > b`
|
||||
* `a == b`
|
||||
* `a >= b`
|
||||
* `a <= b`
|
||||
|
||||
在 Python 中,能够在可以执行上述操作的类中定义[方法][4]。为了简单起见,不让这篇文章过于冗长,我将只展示 `==` 和 `<` 的实现。
|
||||
|
||||
@ -200,7 +197,7 @@ def __eq__(self, other):
|
||||
return (self.name, self.age) == ( other.name, other.age)
|
||||
```
|
||||
|
||||
请注意属性的顺序。它们总是按照你在 dataclass 类中定义的顺序生成。
|
||||
请注意属性的顺序。它们总是按照你在 `dataclass` 类中定义的顺序生成。
|
||||
|
||||
同样,等效的 `__le__` 函数类似于:
|
||||
|
||||
@ -234,7 +231,7 @@ def __le__(self, other):
|
||||
|
||||
### `dataclass` 作为一个可调用的装饰器
|
||||
|
||||
定义所有的 `dunder`(译注:这是指双下划线方法,即魔法方法)方法并不总是值得的。你的用例可能只包括存储值和检查相等性。因此,你只需定义 `__init__` 和 `__eq__` 方法。如果我们可以告诉装饰器不生成其他方法,那么它会减少一些开销,并且我们将在数据对象上有正确的操作。
|
||||
定义所有的 `dunder`(LCTT 译注:这是指双下划线方法,即魔法方法)方法并不总是值得的。你的用例可能只包括存储值和检查相等性。因此,你只需定义 `__init__` 和 `__eq__` 方法。如果我们可以告诉装饰器不生成其他方法,那么它会减少一些开销,并且我们将在数据对象上有正确的操作。
|
||||
|
||||
幸运的是,这可以通过将 `dataclass` 装饰器作为可调用对象来实现。
|
||||
|
||||
@ -247,11 +244,8 @@ class C:
|
||||
```
|
||||
|
||||
1. `init`:默认将生成 `__init__` 方法。如果传入 `False`,那么该类将不会有 `__init__` 方法。
|
||||
|
||||
2. `repr`:`__repr__` 方法默认生成。如果传入 `False`,那么该类将不会有 `__repr__` 方法。
|
||||
|
||||
3. `eq`:默认将生成 `__eq__` 方法。如果传入 `False`,那么 `__eq__` 方法将不会被 `dataclass` 添加,但默认为 `object.__eq__`。
|
||||
|
||||
4. `order`:默认将生成 `__gt__`、`__ge__`、`__lt__`、`__le__` 方法。如果传入 `False`,则省略它们。
|
||||
|
||||
我们在接下来会讨论 `frozen`。由于 `unsafe_hash` 参数复杂的用例,它值得单独发布一篇文章。
|
||||
@ -332,7 +326,6 @@ dataclasses.FrozenInstanceError: cannot assign to field ‘val’
|
||||
因此,一个 `frozen` 实例是一种很好方式来存储:
|
||||
|
||||
* 常数
|
||||
|
||||
* 设置
|
||||
|
||||
这些通常不会在应用程序的生命周期内发生变化,任何企图修改它们的行为都应该被禁止。
|
||||
@ -476,7 +469,7 @@ class B(A):
|
||||
|
||||
### 结论
|
||||
|
||||
因此,以上是 dataclasses 使 Python 开发人员变得更轻松的几种方法。
|
||||
因此,以上是 `dataclass` 使 Python 开发人员变得更轻松的几种方法。
|
||||
|
||||
我试着彻底覆盖大部分的用例,但是,没有人是完美的。如果你发现了错误,或者想让我注意相关的用例,请联系我。
|
||||
|
||||
@ -493,7 +486,7 @@ via: https://medium.com/mindorks/understanding-python-dataclasses-part-1-c3ccd43
|
||||
|
||||
作者:[Shikhar Chauhan][a]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -8,7 +8,7 @@ Linux DNS 查询剖析(第三部分)
|
||||
* `/etc/resolv.conf`
|
||||
* `ping` 与 `host` 查询方式的对比
|
||||
|
||||
and in [Linux DNS 查询剖析(第二部分)][2],我们介绍了:
|
||||
而在 [Linux DNS 查询剖析(第二部分)][2],我们介绍了:
|
||||
|
||||
* `systemd` 和对应的 `networking` 服务
|
||||
* `ifup` 和 `ifdown`
|
||||
@ -17,21 +17,17 @@ and in [Linux DNS 查询剖析(第二部分)][2],我们介绍了:
|
||||
|
||||
剖析进展如下:
|
||||
|
||||
* * *
|
||||
|
||||
![linux-dns-2 (2)][4]
|
||||
|
||||
_(大致)准确的关系图_
|
||||
|
||||
很可惜,故事还没有结束,还有不少东西也会影响 DNS 查询。在第三部分中,我将介绍 `NetworkManager` 和 `dnsmasq`,简要说明它们如何影响 DNS 查询。
|
||||
|
||||
* * *
|
||||
|
||||
### 1) NetworkManager
|
||||
|
||||
在第二部分已经提到,我们现在介绍的内容已经偏离 POSIX 标准,涉及的 DNS 解析管理部分在各个发行版上形式并不统一。
|
||||
|
||||
在我使用的发行版 (Ubuntu)中,有一个名为 [NetworkManager][3] 的服务,它通常作为一些其它软件包的依赖被安装而且处于<ruby>激活<rt>available</rt></ruby>状态。它实际上是 RedHat 在 2004 年开发的一个服务,用于帮助你管理网络接口。
|
||||
在我使用的发行版 (Ubuntu)中,有一个名为 [NetworkManager][3] 的<ruby>可用<rt>available</rt></ruby>服务,它通常作为一些其它软件包的依赖被安装。它实际上是 RedHat 在 2004 年开发的一个服务,用于帮助你管理网络接口。
|
||||
|
||||
它与 DNS 查询有什么关系呢?让我们安装这个服务并找出答案:
|
||||
|
||||
@ -53,8 +49,6 @@ managed=false
|
||||
|
||||
看到 `dns=dnsmasq` 了吧?这意味着 `NetworkManager` 将使用 `dnsmasq` 管理主机上的 DNS。
|
||||
|
||||
* * *
|
||||
|
||||
### 2) dnsmasq
|
||||
|
||||
`dnsmasq` 程序是我们很熟悉的程序:只是 `/etc/resolv.conf` 之上的又一个间接层。
|
||||
@ -89,13 +83,13 @@ search home
|
||||
|
||||
可见,并没有被 `NetworkManager` 修改。
|
||||
|
||||
如果安装 `dnsmasq`:
|
||||
如果安装 `dnsmasq`:
|
||||
|
||||
```
|
||||
$ apt-get install -y dnsmasq
|
||||
```
|
||||
|
||||
这时,`dnsmasq` 已经启动运行:
|
||||
然后启动运行 `dnsmasq`:
|
||||
|
||||
```
|
||||
$ ps -ef | grep dnsmasq
|
||||
@ -130,14 +124,11 @@ udp 0 0 0.0.0.0:68 0.0.0.0:*
|
||||
udp 0 0 0.0.0.0:68 0.0.0.0:* 10185/dhclient
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
### 3) 分析 dnsmasq
|
||||
|
||||
在目前的情况下,所有的 DNS 查询都会使用 `127.0.0.1:53` 这个 DNS 服务器,下一步会发生什么呢?
|
||||
|
||||
我再次查看 `/var/run` 目录,可以发现一个线索:
|
||||
`resolvconf` 目录下 `resolv.conf` 文件中的配置也相应变更,变更为 `dnsmasq` 对应的 DNS 服务器:
|
||||
我再次查看 `/var/run` 目录,可以发现一个线索:`resolvconf` 目录下 `resolv.conf` 文件中的配置也相应变更,变更为 `dnsmasq` 对应的 DNS 服务器:
|
||||
|
||||
```
|
||||
$ cat /var/run/resolvconf/resolv.conf
|
||||
@ -160,8 +151,6 @@ nameserver 10.0.2.2
|
||||
|
||||
虽然可以推导出这个结论,但如何查看具体的调用逻辑呢?
|
||||
|
||||
* * *
|
||||
|
||||
### 4) 调试 dnsmasq
|
||||
|
||||
我经常思考 `dnsmasq` (在整个过程中)的功能定位。幸运的是,如果你将 `/etc/dnsmasq.conf` 中的一行做如下调整,你可以获取大量 `dnsmasq` 状态的信息:
|
||||
@ -200,7 +189,7 @@ Jul 3 19:56:07 ubuntu-xenial dnsmasq[15372]: query[A] bbc.co.uk from 127.0.0.1
|
||||
|
||||
可以清晰看出 `dnsmasq` 收到的查询、查询被转发到了哪里以及收到的回复。
|
||||
|
||||
如果查询被缓存命中(或者说,本地的查询结果还在<ruby>存活时间<rt>time-to-live</rt></ruby>内,并未过期),日志显示如下:
|
||||
如果查询被缓存命中(或者说,本地的查询结果还在<ruby>存活时间<rt>time-to-live</rt></ruby> TTL 内,并未过期),日志显示如下:
|
||||
|
||||
```
|
||||
[...] query[A] bbc.co.uk from 127.0.0.1
|
||||
@ -219,7 +208,7 @@ $ kill -SIGUSR1 $(cat /run/dnsmasq/dnsmasq.pid)
|
||||
|
||||
(LCTT 译注:原文中命令执行报错,已变更成最接近且符合作者意图的命令)
|
||||
|
||||
导记录对应如下输出:
|
||||
导出记录对应如下输出:
|
||||
|
||||
```
|
||||
Jul 3 15:08:08 ubuntu-xenial dnsmasq[15697]: time 1530630488
|
||||
@ -245,7 +234,7 @@ Jul 3 15:08:08 ubuntu-xenial dnsmasq[15697]: time 1530630488
|
||||
[...] ip6-allrouters ff02::2 6FRI H
|
||||
```
|
||||
|
||||
在上面的输出中,我猜测(并不确认,`?` 代表我比较疯狂的猜测)如下:
|
||||
在上面的输出中,我猜测(并不确认,`?` 代表我比较无根据的猜测)如下:
|
||||
|
||||
* `4` 代表 IPv4
|
||||
* `6` 代表 IPv6
|
||||
@ -263,8 +252,6 @@ Jul 3 15:08:08 ubuntu-xenial dnsmasq[15697]: time 1530630488
|
||||
|
||||
`NetworkManager` 配置中的 `dns` 字段并不是只能使用 `dnsmasq`,可选项包括 `none`,`default`,`unbound` 和 `dnssec-triggered` 等。使用 `none` 时,`NetworkManager` 不会改动 `/etc/resolv.conf`;使用 `default` 时,`NetworkManager` 会根据当前的<ruby>活跃连接<rt>active connections</rt></ruby>更新 `resolv.conf`;使用 `unbound` 时,`NetworkManager` 会与 `unbound` 服务通信;`dnssec-triggered` 与 DNS 安全相关,不在本文讨论范围。
|
||||
|
||||
* * *
|
||||
|
||||
### 第三部分总结
|
||||
|
||||
第三部分到此结束,其中我们介绍了 `NetworkManager` 服务及其 `dns=dnsmasq` 的配置。
|
||||
@ -288,7 +275,7 @@ via: https://zwischenzugs.com/2018/07/06/anatomy-of-a-linux-dns-lookup-part-iii/
|
||||
|
||||
作者:[ZWISCHENZUGS][a]
|
||||
译者:[pinewall](https://github.com/pinewall)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,431 @@
|
||||
|
||||
如何在 Linux 上检查一个软件包的详细信息
|
||||
======
|
||||
|
||||
我们可以就这个已经被广泛讨论的话题写出大量的文章,大多数情况下,因为各种各样的原因,我们都愿意让<ruby>包管理器<rt>package manager</rt></ruby>来帮我们做这些事情。
|
||||
|
||||
每个 Linux 发行版都有自己的包管理器,并且每个都有各自有不同的特性,这些特性包括允许用户执行安装新软件包,删除无用的软件包,更新现存的软件包,搜索某些具体的软件包,以及更新整个系统到其最新的状态之类的操作。
|
||||
|
||||
习惯于命令行的用户大多数时间都会使用基于命令行方式的包管理器。对于 Linux 而言,这些基于命令行的包管理器有 `yum`、`dnf`、`rpm`、`apt`、`apt-get`、`dpkg`、`pacman` 和 `zypper`。
|
||||
|
||||
**推荐阅读**
|
||||
|
||||
- [Linux 命令行包管理器列表及其用法][1]
|
||||
- [一个图形化的 Linux 包管理器前端工具][2]
|
||||
- [如何搜索知道一个软件包是否存在于你的 Linux 发行版][3]
|
||||
- [如何使用 dnf/yum 配置包管理器在 Linux 上来添加、启用和禁用软件仓库][4]
|
||||
|
||||
作为一个系统管理员你应该清楚地知道:安装包来自何方,具体来自哪个软件仓库,包的具体版本,包的大小,版本,包的源代码 URL,包的许可证信息,等等。
|
||||
|
||||
这篇短文将用尽可能简单的方式帮你从随包自带的总结和描述中了解该包的用法。按你所使用的 Linux 发行版的不同,运行下面相应的命令,你能得到你所使用的发行版下的包的详细信息。
|
||||
|
||||
### YUM 命令:在 RHEL 和 CentOS 系统上获得包的信息
|
||||
|
||||
[YUM][5] 英文直译是“<ruby>黄狗更新器--修改版<rt>Yellowdog Updater, Modified</rt></ruby>”,它是一个开源的基于命令行的包管理器前端实用工具。它被广泛应用在基于 RPM 的系统上,例如:RHEL 和 CentOS。
|
||||
|
||||
Yum 是用于在官方发行版仓库以及其他第三方发行版仓库下获取、安装、删除、查询 RPM 包的主要工具。
|
||||
|
||||
(LCTT 译注:用 `yum info` 获取 python 包的信息)
|
||||
|
||||
```
|
||||
# yum info python
|
||||
Loaded plugins: fastestmirror, security
|
||||
Loading mirror speeds from cached hostfile
|
||||
* epel: epel.mirror.constant.com
|
||||
Installed Packages
|
||||
Name : python
|
||||
Arch : x86_64
|
||||
Version : 2.6.6
|
||||
Release : 66.el6_8
|
||||
Size : 78 k
|
||||
Repo : installed
|
||||
From repo : updates
|
||||
Summary : An interpreted, interactive, object-oriented programming language
|
||||
URL : http://www.python.org/
|
||||
License : Python
|
||||
Description : Python is an interpreted, interactive, object-oriented programming
|
||||
: language often compared to Tcl, Perl, Scheme or Java. Python includes
|
||||
: modules, classes, exceptions, very high level dynamic data types and
|
||||
: dynamic typing. Python supports interfaces to many system calls and
|
||||
: libraries, as well as to various windowing systems (X11, Motif, Tk,
|
||||
: Mac and MFC).
|
||||
:
|
||||
: Programmers can write new built-in modules for Python in C or C++.
|
||||
: Python can be used as an extension language for applications that need
|
||||
: a programmable interface.
|
||||
:
|
||||
: Note that documentation for Python is provided in the python-docs
|
||||
: package.
|
||||
:
|
||||
: This package provides the "python" executable; most of the actual
|
||||
: implementation is within the "python-libs" package.
|
||||
|
||||
```
|
||||
|
||||
### YUMDB 命令:查看 RHEL 和 CentOS 系统上的包信息
|
||||
|
||||
`yumdb info` 这个命令提供与 `yum info` 相类似的的信息,不过它还额外提供了诸如包校验值、包类型、用户信息(由何人安装)。从 yum 3.2.26 版本后,`yum` 开始在 rpm 数据库外储存额外的信息了(此处如显示 `user` 表明该包由用户安装,而 `dep` 说明该包是被作为被依赖的包而被安装的)。
|
||||
|
||||
(LCTT 译注:用 `yumdb info` 来获取 python 包的信息)
|
||||
|
||||
```
|
||||
# yumdb info python
|
||||
Loaded plugins: fastestmirror
|
||||
python-2.6.6-66.el6_8.x86_64
|
||||
changed_by = 4294967295
|
||||
checksum_data = 53c75a1756e5b4f6564c5229a37948c9b4561e0bf58076bd7dab7aff85a417f2
|
||||
checksum_type = sha256
|
||||
command_line = update -y
|
||||
from_repo = updates
|
||||
from_repo_revision = 1488370672
|
||||
from_repo_timestamp = 1488371100
|
||||
installed_by = 4294967295
|
||||
reason = dep
|
||||
releasever = 6
|
||||
```
|
||||
|
||||
### RPM 命令:在 RHEL/CentOS/Fedora 系统上查看包的信息
|
||||
|
||||
[RPM][6] 英文直译为“<ruby>红帽包管理器<rt>Red Hat Package Manager</rt></ruby>”,这是一个在 RedHat 以及其变种发行版(如RHEL、CentOS、Fedora、openSUSE、Megeia)下的功能强大的命令行包管理工具。它能让你轻松的安装、升级、删除、查询以及校验你的系统或服务器上的软件。RPM 文件以 `.rpm` 结尾。RPM 包由它所依赖的软件库以及其他依赖构成,它不会与系统上已经安装的包冲突。
|
||||
|
||||
(LCTT 译注:用 `rpm -qi` 查询 nano 包的具体信息)
|
||||
|
||||
```
|
||||
# rpm -qi nano
|
||||
Name : nano Relocations: (not relocatable)
|
||||
Version : 2.0.9 Vendor: CentOS
|
||||
Release : 7.el6 Build Date: Fri 12 Nov 2010 02:18:36 AM EST
|
||||
Install Date: Fri 03 Mar 2017 08:57:47 AM EST Build Host: c5b2.bsys.dev.centos.org
|
||||
Group : Applications/Editors Source RPM: nano-2.0.9-7.el6.src.rpm
|
||||
Size : 1588347 License: GPLv3+
|
||||
Signature : RSA/8, Sun 03 Jul 2011 12:46:50 AM EDT, Key ID 0946fca2c105b9de
|
||||
Packager : CentOS BuildSystem
|
||||
URL : http://www.nano-editor.org
|
||||
Summary : A small text editor
|
||||
Description :
|
||||
GNU nano is a small and friendly text editor.
|
||||
```
|
||||
|
||||
### DNF 命令:在 Fedora 系统上查看包信息
|
||||
|
||||
[DNF][7] 指“<ruby>时髦版的 Yum<rt>Dandified yum</rt></ruby>”,我们也可以认为 DNF 是下一代的 yum 包管理器(Yum 的一个分支),它在后台使用了 hawkey/libsolv 库。Aleš Kozumplík 在Fedora 18 上开始开发 DNF,在 Fedora 22 上正式最后发布。 `dnf` 命令用来在 Fedora 22 及以后的系统上安装、更新、搜索以及删除包。它能自动的解决包安装过程中的包依赖问题。
|
||||
|
||||
(LCTT 译注: 用 `dnf info` 查看 tilix 包信息)
|
||||
|
||||
```
|
||||
$ dnf info tilix
|
||||
Last metadata expiration check: 27 days, 10:00:23 ago on Wed 04 Oct 2017 06:43:27 AM IST.
|
||||
Installed Packages
|
||||
Name : tilix
|
||||
Version : 1.6.4
|
||||
Release : 1.fc26
|
||||
Arch : x86_64
|
||||
Size : 3.6 M
|
||||
Source : tilix-1.6.4-1.fc26.src.rpm
|
||||
Repo : @System
|
||||
From repo : @commandline
|
||||
Summary : Tiling terminal emulator
|
||||
URL : https://github.com/gnunn1/tilix
|
||||
License : MPLv2.0 and GPLv3+ and CC-BY-SA
|
||||
Description : Tilix is a tiling terminal emulator with the following features:
|
||||
:
|
||||
: - Layout terminals in any fashion by splitting them horizontally or vertically
|
||||
: - Terminals can be re-arranged using drag and drop both within and between
|
||||
: windows
|
||||
: - Terminals can be detached into a new window via drag and drop
|
||||
: - Input can be synchronized between terminals so commands typed in one
|
||||
: terminal are replicated to the others
|
||||
: - The grouping of terminals can be saved and loaded from disk
|
||||
: - Terminals support custom titles
|
||||
: - Color schemes are stored in files and custom color schemes can be created by
|
||||
: simply creating a new file
|
||||
: - Transparent background
|
||||
: - Supports notifications when processes are completed out of view
|
||||
:
|
||||
: The application was written using GTK 3 and an effort was made to conform to
|
||||
: GNOME Human Interface Guidelines (HIG).
|
||||
```
|
||||
|
||||
### Zypper 命令:在 openSUSE 系统上查看包信息
|
||||
|
||||
[zypper][8] 是一个使用 libzypp 库的命令行包管理器。`zypper` 提供诸如软件仓库访问,安装依赖解决,软件包安装等等功能。
|
||||
|
||||
(LCTT 译注: 用 `zypper info` 查询 nano 包的信息)
|
||||
|
||||
```
|
||||
$ zypper info nano
|
||||
|
||||
Loading repository data...
|
||||
Reading installed packages...
|
||||
|
||||
|
||||
Information for package nano:
|
||||
-----------------------------
|
||||
Repository : Main Repository (OSS)
|
||||
Name : nano
|
||||
Version : 2.4.2-5.3
|
||||
Arch : x86_64
|
||||
Vendor : openSUSE
|
||||
Installed Size : 1017.8 KiB
|
||||
Installed : No
|
||||
Status : not installed
|
||||
Source package : nano-2.4.2-5.3.src
|
||||
Summary : Pico editor clone with enhancements
|
||||
Description :
|
||||
GNU nano is a small and friendly text editor. It aims to emulate
|
||||
the Pico text editor while also offering a few enhancements.
|
||||
```
|
||||
|
||||
### Pacman 命令:在 ArchLinux 及 Manjaro 系统上查看包信息
|
||||
|
||||
[Pacman][9] 意即<ruby>包管理器<rt>package manager</rt></ruby>实用工具。`pacman` 是一个用于安装、构建、删除、管理 Arch Linux 上包的命令行工具。它后端使用 libalpm(Arch Linux package Manager(ALPM)库)来完成所有功能。
|
||||
|
||||
(LCTT 译注: 用 `pacman -Qi` 来查询 bash 包信息)
|
||||
|
||||
```
|
||||
$ pacman -Qi bash
|
||||
Name : bash
|
||||
Version : 4.4.012-2
|
||||
Description : The GNU Bourne Again shell
|
||||
Architecture : x86_64
|
||||
URL : http://www.gnu.org/software/bash/bash.html
|
||||
Licenses : GPL
|
||||
Groups : base
|
||||
Provides : sh
|
||||
Depends On : readline>=7.0 glibc ncurses
|
||||
Optional Deps : bash-completion: for tab completion
|
||||
Required By : autoconf automake bison bzip2 ca-certificates-utils db
|
||||
dhcpcd diffutils e2fsprogs fakeroot figlet findutils
|
||||
flex freetype2 gawk gdbm gettext gmp grub gzip icu
|
||||
iptables keyutils libgpg-error libksba libpcap libpng
|
||||
libtool lvm2 m4 man-db mkinitcpio nano neofetch nspr
|
||||
nss openresolv os-prober pacman pcre pcre2 shadow
|
||||
systemd texinfo vte-common which xdg-user-dirs xdg-utils
|
||||
xfsprogs xorg-mkfontdir xorg-xpr xz
|
||||
Optional For : None
|
||||
Conflicts With : None
|
||||
Replaces : None
|
||||
Installed Size : 7.13 MiB
|
||||
Packager : Jan Alexander Steffens (heftig)
|
||||
Build Date : Tue 14 Feb 2017 01:16:51 PM UTC
|
||||
Install Date : Thu 24 Aug 2017 06:08:12 AM UTC
|
||||
Install Reason : Explicitly installed
|
||||
Install Script : No
|
||||
Validated By : Signature
|
||||
```
|
||||
|
||||
### apt-cache 命令:在 Debian/Ubuntu/Mint 系统上查看包信息
|
||||
|
||||
[apt-cache][10] 命令能显示 apt 内部数据库中的大量信息。这些信息是从 `sources.list` 中的不同的软件源中搜集而来,因此从某种意义上这些信息也可以被认为是某种缓存。这些信息搜集工作是在运行 `apt update` 命令时执行的。
|
||||
|
||||
(LCTT 译注:用管理员权限查询 apache2 包的信息)
|
||||
|
||||
```
|
||||
$ sudo apt-cache show apache2
|
||||
Package: apache2
|
||||
Priority: optional
|
||||
Section: web
|
||||
Installed-Size: 473
|
||||
Maintainer: Ubuntu Developers
|
||||
Original-Maintainer: Debian Apache Maintainers
|
||||
Architecture: amd64
|
||||
Version: 2.4.12-2ubuntu2
|
||||
Replaces: apache2.2-common
|
||||
Provides: httpd, httpd-cgi
|
||||
Depends: lsb-base, procps, perl, mime-support, apache2-bin (= 2.4.12-2ubuntu2), apache2-utils (>= 2.4), apache2-data (= 2.4.12-2ubuntu2)
|
||||
Pre-Depends: dpkg (>= 1.17.14)
|
||||
Recommends: ssl-cert
|
||||
Suggests: www-browser, apache2-doc, apache2-suexec-pristine | apache2-suexec-custom, ufw
|
||||
Conflicts: apache2.2-common (<< 2.3~)
|
||||
Filename: pool/main/a/apache2/apache2_2.4.12-2ubuntu2_amd64.deb
|
||||
Size: 91348
|
||||
MD5sum: ab0ee0b0d1c6b3d19bd87aa2a9537125
|
||||
SHA1: 350c9a1a954906088ed032aebb77de3d5bb24004
|
||||
SHA256: 03f515f7ebc3b67b050b06e82ebca34b5e83e34a528868498fce020bf1dbbe34
|
||||
Description-en: Apache HTTP Server
|
||||
The Apache HTTP Server Project's goal is to build a secure, efficient and
|
||||
extensible HTTP server as standards-compliant open source software. The
|
||||
result has long been the number one web server on the Internet.
|
||||
.
|
||||
Installing this package results in a full installation, including the
|
||||
configuration files, init scripts and support scripts.
|
||||
Description-md5: d02426bc360345e5acd45367716dc35c
|
||||
Homepage: http://httpd.apache.org/
|
||||
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
|
||||
Origin: Ubuntu
|
||||
Supported: 9m
|
||||
Task: lamp-server, mythbuntu-frontend, mythbuntu-desktop, mythbuntu-backend-slave, mythbuntu-backend-master, mythbuntu-backend-master
|
||||
```
|
||||
|
||||
### APT 命令:查看 Debian/Ubuntu/Mint 系统上的包信息
|
||||
|
||||
[APT][11] 意为<ruby>高级打包工具<rt>Advanced Packaging Tool</rt></ruby>,就像 DNF 将如何替代 YUM 一样,APT 是 apt-get 的替代物。它功能丰富的命令行工具包括了如下所有命令的功能如 `apt-cache`、`apt-search`、`dpkg`、`apt-cdrom`、`apt-config`、`apt-key` 等等,我们可以方便的通过 `apt` 来安装 `.dpkg` 包,但是我们却不能通过 `apt-get` 来完成这一点,还有一些其他的类似的功能也不能用 `apt-get` 来完成,所以 `apt-get` 因为没有解决上述功能缺乏的原因而被 `apt` 所取代。
|
||||
|
||||
(LCTT 译注: 用 `apt show` 查看 nano 包信息)
|
||||
|
||||
```
|
||||
$ apt show nano
|
||||
Package: nano
|
||||
Version: 2.8.6-3
|
||||
Priority: standard
|
||||
Section: editors
|
||||
Origin: Ubuntu
|
||||
Maintainer: Ubuntu Developers
|
||||
Original-Maintainer: Jordi Mallach
|
||||
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
|
||||
Installed-Size: 766 kB
|
||||
Depends: libc6 (>= 2.14), libncursesw5 (>= 6), libtinfo5 (>= 6)
|
||||
Suggests: spell
|
||||
Conflicts: pico
|
||||
Breaks: nano-tiny (<< 2.8.6-2)
|
||||
Replaces: nano-tiny (<< 2.8.6-2), pico
|
||||
Homepage: https://www.nano-editor.org/
|
||||
Task: standard, ubuntu-touch-core, ubuntu-touch
|
||||
Supported: 9m
|
||||
Download-Size: 222 kB
|
||||
APT-Manual-Installed: yes
|
||||
APT-Sources: http://in.archive.ubuntu.com/ubuntu artful/main amd64 Packages
|
||||
Description: small, friendly text editor inspired by Pico
|
||||
GNU nano is an easy-to-use text editor originally designed as a replacement
|
||||
for Pico, the ncurses-based editor from the non-free mailer package Pine
|
||||
(itself now available under the Apache License as Alpine).
|
||||
.
|
||||
However, GNU nano also implements many features missing in pico, including:
|
||||
- undo/redo
|
||||
- line numbering
|
||||
- syntax coloring
|
||||
- soft-wrapping of overlong lines
|
||||
- selecting text by holding Shift
|
||||
- interactive search and replace (with regular expression support)
|
||||
- a go-to line (and column) command
|
||||
- support for multiple file buffers
|
||||
- auto-indentation
|
||||
- tab completion of filenames and search terms
|
||||
- toggling features while running
|
||||
- and full internationalization support
|
||||
```
|
||||
|
||||
### dpkg 命令:查看Debian/Ubuntu/Mint系统上的包信息
|
||||
|
||||
|
||||
[dpkg][12] 意指 <ruby>Debian 包管理器<rt>Debian package manager</rt></ruby>。`dpkg` 是用于 Debian 系统上安装、构建、移除以及管理 Debian 包的命令行工具。`dpkg` 使用 `aptitude`(因为它更为主流及用户友好)作为前端工具来完成所有的功能。其他的工具如` dpkg-deb` 和 `dpkg-query` 使用 `dpkg` 做为前端来实现功能。尽管系统管理员还是时不时会在必要时使用 `dpkg` 来完成一些软件安装的任务,他大多数情况下还是会因为 `apt`、`apt-get` 以及 `aptitude` 的健壮性而使用后者。
|
||||
|
||||
(LCTT 译注: 用 `dpkg -s` 查看 python 包的信息)
|
||||
|
||||
```
|
||||
$ dpkg -s python
|
||||
Package: python
|
||||
Status: install ok installed
|
||||
Priority: optional
|
||||
Section: python
|
||||
Installed-Size: 626
|
||||
Maintainer: Ubuntu Developers
|
||||
Architecture: amd64
|
||||
Multi-Arch: allowed
|
||||
Source: python-defaults
|
||||
Version: 2.7.14-2ubuntu1
|
||||
Replaces: python-dev (<< 2.6.5-2)
|
||||
Provides: python-ctypes, python-email, python-importlib, python-profiler, python-wsgiref
|
||||
Depends: python2.7 (>= 2.7.14-1~), libpython-stdlib (= 2.7.14-2ubuntu1)
|
||||
Pre-Depends: python-minimal (= 2.7.14-2ubuntu1)
|
||||
Suggests: python-doc (= 2.7.14-2ubuntu1), python-tk (>= 2.7.14-1~)
|
||||
Breaks: update-manager-core (<< 0.200.5-2)
|
||||
Conflicts: python-central (<< 0.5.5)
|
||||
Description: interactive high-level object-oriented language (default version)
|
||||
Python, the high-level, interactive object oriented language,
|
||||
includes an extensive class library with lots of goodies for
|
||||
network programming, system administration, sounds and graphics.
|
||||
.
|
||||
This package is a dependency package, which depends on Debian's default
|
||||
Python version (currently v2.7).
|
||||
Homepage: http://www.python.org/
|
||||
Original-Maintainer: Matthias Klose
|
||||
```
|
||||
|
||||
我们也可使用 `dpkg` 的 `-p` 选项,这个选项提供和 `dpkg -s` 相类似的信息,但是它还提供了包的校验值和包类型。
|
||||
|
||||
(LCTT 译注: 用 `dpkg -p` 查看 python3 包的信息)
|
||||
|
||||
```
|
||||
$ dpkg -p python3
|
||||
Package: python3
|
||||
Priority: important
|
||||
Section: python
|
||||
Installed-Size: 67
|
||||
Origin: Ubuntu
|
||||
Maintainer: Ubuntu Developers
|
||||
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
|
||||
Architecture: amd64
|
||||
Multi-Arch: allowed
|
||||
Source: python3-defaults
|
||||
Version: 3.6.3-0ubuntu2
|
||||
Replaces: python3-minimal (<< 3.1.2-2)
|
||||
Provides: python3-profiler
|
||||
Depends: python3.6 (>= 3.6.3-1~), libpython3-stdlib (= 3.6.3-0ubuntu2), dh-python
|
||||
Pre-Depends: python3-minimal (= 3.6.3-0ubuntu2)
|
||||
Suggests: python3-doc (>= 3.6.3-0ubuntu2), python3-tk (>= 3.6.3-1~), python3-venv (>= 3.6.3-0ubuntu2)
|
||||
Filename: pool/main/p/python3-defaults/python3_3.6.3-0ubuntu2_amd64.deb
|
||||
Size: 8712
|
||||
MD5sum: a8bae494c6e5d1896287675faf40d373
|
||||
Description: interactive high-level object-oriented language (default python3 version)
|
||||
Original-Maintainer: Matthias Klose
|
||||
SHA1: 2daec885cea7d4dc83c284301c3bebf42b23e095
|
||||
SHA256: 865e509c91d2504a16c4b573dbe27e260c36fceec2add3fa43a30c1751d7e9bb
|
||||
Homepage: http://www.python.org/
|
||||
Task: minimal, ubuntu-core, ubuntu-core
|
||||
Description-md5: 950ebd8122c0a7340f0a740c295b9eab
|
||||
Supported: 9m
|
||||
```
|
||||
|
||||
### aptitude 命令:查看 Debian/Ubuntu/Mint 系统上的包信息
|
||||
|
||||
|
||||
`aptitude` 是 Debian GNU/Linux 包管理系统的文本界面。它允许用户查看已安装的包的列表,以及完成诸如安装、升级、删除包之类的包管理任务。这些管理行为也能从图形接口来执行。
|
||||
|
||||
(LCTT 译注: 用 `aptitude show` 查看 htop 包信息)
|
||||
|
||||
```
|
||||
$ aptitude show htop
|
||||
Package: htop
|
||||
Version: 2.0.2-1
|
||||
State: installed
|
||||
Automatically installed: no
|
||||
Priority: optional
|
||||
Section: universe/utils
|
||||
Maintainer: Ubuntu Developers
|
||||
Architecture: amd64
|
||||
Uncompressed Size: 216 k
|
||||
Depends: libc6 (>= 2.15), libncursesw5 (>= 6), libtinfo5 (>= 6)
|
||||
Suggests: lsof, strace
|
||||
Conflicts: htop:i386
|
||||
Description: interactive processes viewer
|
||||
Htop is an ncursed-based process viewer similar to top, but it allows one to scroll the list vertically and horizontally to see all processes and their full command lines.
|
||||
|
||||
Tasks related to processes (killing, renicing) can be done without entering their PIDs.
|
||||
Homepage: http://hisham.hm/htop/
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/how-to-view-detailed-information-about-a-package-in-linux/
|
||||
|
||||
作者:[Prakash Subramanian][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[DavidChenLiang](https://github.com/davidchenliang)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.2daygeek.com/author/prakash/
|
||||
[1]:https://www.2daygeek.com/list-of-command-line-package-manager-for-linux/
|
||||
[2]:https://www.2daygeek.com/list-of-graphical-frontend-tool-for-linux-package-manager/
|
||||
[3]:https://www.2daygeek.com/how-to-search-if-a-package-is-available-on-your-linux-distribution-or-not/
|
||||
[4]:https://www.2daygeek.com/how-to-add-enable-disable-a-repository-dnf-yum-config-manager-on-linux/
|
||||
[5]:https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
||||
[6]:https://www.2daygeek.com/rpm-command-examples/
|
||||
[7]:https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[8]:https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
||||
[9]:https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/
|
||||
[10]:https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[11]:https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[12]:https://www.2daygeek.com/dpkg-command-to-manage-packages-on-debian-ubuntu-linux-mint-systems/
|
@ -1,60 +1,56 @@
|
||||
Etcher.io 入门
|
||||
======
|
||||
> 用这个易用的媒体创建工具来创建一个可引导的 USB 盘或 SD 卡。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/community-penguins-osdc-lead.png?itok=BmqsAF4A)
|
||||
|
||||
可启动 USB 盘是尝试新的 Linux 发行版的很好的方式,以便在安装之前查看你是否喜欢它。虽然一些 Linux 发行版(如 [Fedora][1])可以轻松创建可启动媒体,但大多数其他发行版提供 ISO 或镜像文件,并将创建媒体决定留给用户。用户总是可以选择使用 `dd` 在命令行上创建媒体 - 但让我们面对它,即使对于最有经验的用户来说,这仍然很痛苦。还有其他程序,如 Mac 上的 UnetBootIn、Disk Utility 和 Windows 上的 Win32DiskImager,它们都可以创建可启动的 USB。
|
||||
可启动 USB 盘是尝试新的 Linux 发行版的很好的方式,以便在安装之前查看你是否喜欢它。虽然一些 Linux 发行版(如 [Fedora][1])可以轻松创建可启动媒体,但大多数其他发行版提供 ISO 或镜像文件,并将创建媒体决定留给用户。用户总是可以选择使用 `dd` 在命令行上创建媒体——但让我们面对现实,即使对于最有经验的用户来说,这仍然很痛苦。也有一些其它程序,如 Mac 上的 UnetBootIn、Disk Utility 和 Windows 上的 Win32DiskImager,它们都可以创建可启动的 USB。
|
||||
|
||||
### 安装 Etcher
|
||||
|
||||
大约 18 个月前,我遇到了 [Etcher.io][2],这是一个很棒的开源项目,可以在 Linux、Windows 或 MacOS 上轻松,简单地创建媒体。Etcher.io 已成为我为 Linux 创建可启动媒体的“首选”程序。我可以轻松下载 ISO 或 IMG 文件并将其刻录到闪存和 SD 卡。这是一个 [Apache 2.0][3] 许可证下的开源项目,[源代码][4] 可在 GitHub 上获得。
|
||||
大约 18 个月前,我遇到了 [Etcher.io][2],这是一个很棒的开源项目,可以在 Linux、Windows 或 MacOS 上轻松、简单地创建媒体。Etcher.io 已成为我为 Linux 创建可启动媒体的“首选”程序。我可以轻松下载 ISO 或 IMG 文件并将其刻录到闪存和 SD 卡。这是一个 [Apache 2.0][3] 许可证下的开源项目,[源代码][4] 可在 GitHub 上获得。
|
||||
|
||||
进入 [Etcher.io][5] 网站,然后单击适用于你的操作系统-32 位或 64 位 Linux,32 位或 64 位 Windows 或 MacOS 的下载链接。
|
||||
进入 [Etcher.io][5] 网站,然后单击适用于你的操作系统:32 位或 64 位 Linux、32 位或 64 位 Windows 或 MacOS 的下载链接。
|
||||
|
||||
![](https://opensource.com/sites/default/files/uploads/etcher_1.png)
|
||||
|
||||
Etcher 在 GitHub 仓库中提供了很好的指导,用于将 Etcher 添加到你的 Linux 实用程序集合中。
|
||||
Etcher 在 GitHub 仓库中提供了很好的指导,可以将 Etcher 添加到你的 Linux 实用程序集合中。
|
||||
|
||||
如果你使用的是 Debian 或 Ubuntu,请添加 Etcher Debian 仓库:
|
||||
|
||||
```
|
||||
$echo "deb https://dl.bintray.com/resin-io/debian stable etcher" | sudo tee /etc/apt/sources.list.d/etcher.list
|
||||
```
|
||||
$echo "deb https://dl.bintray.com/resin-io/debian stable etcher" | sudo tee
|
||||
|
||||
/etc/apt/sources.list.d/etcher.list
|
||||
|
||||
|
||||
|
||||
信任 Bintray.com GPG 密钥
|
||||
|
||||
```
|
||||
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 379CE192D401AB61
|
||||
|
||||
```
|
||||
|
||||
然后更新你的系统并安装:
|
||||
|
||||
```
|
||||
$ sudo apt-get update
|
||||
|
||||
$ sudo apt-get install etcher-electron
|
||||
|
||||
```
|
||||
|
||||
如果你使用的是 Fedora 或 Red Hat Enterprise Linux,请添加 Etcher RPM 仓库:
|
||||
|
||||
```
|
||||
$ sudo wget https://bintray.com/resin-io/redhat/rpm -O /etc/yum.repos.d/bintray-
|
||||
|
||||
resin-io-redhat.repo
|
||||
|
||||
$ sudo wget https://bintray.com/resin-io/redhat/rpm -O /etc/yum.repos.d/bintray-resin-io-redhat.repo
|
||||
```
|
||||
|
||||
使用以下任一方式更新和安装:
|
||||
|
||||
```
|
||||
$ sudo yum install -y etcher-electron
|
||||
|
||||
```
|
||||
|
||||
或者:
|
||||
|
||||
```
|
||||
$ sudo dnf install -y etcher-electron
|
||||
|
||||
```
|
||||
|
||||
### 创建可启动盘
|
||||
@ -65,13 +61,13 @@ $ sudo dnf install -y etcher-electron
|
||||
|
||||
![](https://opensource.com/sites/default/files/uploads/etcher_2.png)
|
||||
|
||||
单击 **Select Image**。在本例中,我想创建一个可启动的 USB 盘,以便在新计算机上安装 Ubermix。在我选择了我的 Ubermix 镜像文件并将我的 USB 盘插入计算机,Etcher.io “看到”了驱动器,我就可以开始在 USB 上安装 Ubermix 了。
|
||||
单击 “Select Image”。在本例中,我想创建一个可启动的 USB 盘,以便在新计算机上安装 Ubermix。在我选择了我的 Ubermix 镜像文件并将我的 USB 盘插入计算机,Etcher.io “看到”了驱动器,我就可以开始在 USB 上安装 Ubermix 了。
|
||||
|
||||
![](https://opensource.com/sites/default/files/uploads/etcher_3.png)
|
||||
|
||||
在我点击 **Flash** 后,安装就开始了。所需时间取决于镜像的大小。在驱动器上安装镜像后,软件会验证安装。最后,一条提示宣布我的媒体创建已经完成。
|
||||
在我点击 “Flash” 后,安装就开始了。所需时间取决于镜像的大小。在驱动器上安装镜像后,软件会验证安装。最后,一条提示宣布我的媒体创建已经完成。
|
||||
|
||||
如果您需要[ Etcher 的帮助][7],请通过其 [Discourse][8] 论坛联系社区。Etcher 非常易于使用,它已经取代了我所有其他的媒体创建工具,因为它们都不像 Etcher 那样轻松地完成工作。
|
||||
如果您需要 [Etcher 的帮助][7],请通过其 [Discourse][8] 论坛联系社区。Etcher 非常易于使用,它已经取代了我所有其他的媒体创建工具,因为它们都不像 Etcher 那样轻松地完成工作。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -80,7 +76,7 @@ via: https://opensource.com/article/18/7/getting-started-etcherio
|
||||
作者:[Don Watkins][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
174
published/20180720 An Introduction to Using Git.md
Normal file
174
published/20180720 An Introduction to Using Git.md
Normal file
@ -0,0 +1,174 @@
|
||||
Git 使用简介
|
||||
======
|
||||
> 我将向你介绍让 Git 的启动、运行,并和 GitHub 一起使用的基础知识。
|
||||
|
||||
![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/developer-3461405_1920.png?itok=6H3sYe80)
|
||||
|
||||
如果你是一个开发者,那你应该熟悉许多开发工具。你已经花了多年时间来学习一种或者多种编程语言并打磨你的技巧。你可以熟练运用图形工具或者命令行工具开发。在你看来,没有任何事可以阻挡你。你的代码, 好像你的思想和你的手指一样,将会创建一个优雅的,完美评价的应用程序,并会风靡世界。
|
||||
|
||||
然而,如果你和其他人共同开发一个项目会发生什么呢?或者,你开发的应用程序变地越来越大,下一步你将如何去做?如果你想成功地和其他开发者合作,你定会想用一个分布式版本控制系统。使用这样一个系统,合作开发一个项目变得非常高效和可靠。这样的一个系统便是 [Git][1]。还有一个叫 [GitHub][2] 的方便的存储仓库,用来存储你的项目代码,这样你的团队可以检查和修改代码。
|
||||
|
||||
我将向你介绍让 Git 的启动、运行,并和 GitHub 一起使用的基础知识,可以让你的应用程序的开发可以提升到一个新的水平。我将在 Ubuntu 18.04 上进行演示,因此如果您选择的发行版本不同,您只需要修改 Git 安装命令以适合你的发行版的软件包管理器。
|
||||
|
||||
### Git 和 GitHub
|
||||
|
||||
第一件事就是创建一个免费的 GitHub 账号,打开 [GitHub 注册页面][3],然后填上需要的信息。完成这个之后,你就注备好开始安装 Git 了(这两件事谁先谁后都可以)。
|
||||
|
||||
安装 Git 非常简单,打开一个命令行终端,并输入命令:
|
||||
|
||||
```
|
||||
sudo apt install git-all
|
||||
```
|
||||
|
||||
这将会安装大量依赖包,但是你将了解使用 Git 和 GitHub 所需的一切。
|
||||
|
||||
附注:我使用 Git 来下载程序的安装源码。有许多时候,内置的软件管理器不提供某个软件,除了去第三方库中下载源码,我经常去这个软件项目的 Git 主页,像这样克隆:
|
||||
|
||||
```
|
||||
git clone ADDRESS
|
||||
```
|
||||
|
||||
“ADDRESS” 就是那个软件项目的 Git 主页。这样我就可以确保自己安装那个软件的最新发行版了。
|
||||
|
||||
### 创建一个本地仓库并添加一个文件
|
||||
|
||||
下一步就是在你的电脑里创建一个本地仓库(本文称之为 newproject,位于 `~/` 目录下),打开一个命令行终端,并输入下面的命令:
|
||||
|
||||
```
|
||||
cd ~/
|
||||
mkdir newproject
|
||||
cd newproject
|
||||
```
|
||||
|
||||
现在你需要初始化这个仓库。在 `~/newproject` 目录下,输入命令 `git init`,当命令运行完,你就可以看到一个刚刚创建的空的 Git 仓库了(图1)。
|
||||
|
||||
![new repository][5]
|
||||
|
||||
*图 1: 初始化完成的新仓库*
|
||||
|
||||
下一步就是往项目里添加文件。我们在项目根目录(`~/newproject`)输入下面的命令:
|
||||
|
||||
```
|
||||
touch readme.txt
|
||||
```
|
||||
|
||||
现在项目里多了个空文件。输入 `git status` 来验证 Git 已经检测到多了个新文件(图2)。
|
||||
|
||||
![readme][8]
|
||||
|
||||
*图 2: Git 检测到新文件readme.txt*
|
||||
|
||||
即使 Git 检测到新的文件,但它并没有被真正的加入这个项目仓库。为此,你要输入下面的命令:
|
||||
|
||||
```
|
||||
git add readme.txt
|
||||
```
|
||||
|
||||
一旦完成这个命令,再输入 `git status` 命令,可以看到,`readme.txt` 已经是这个项目里的新文件了(图3)。
|
||||
|
||||
![file added][10]
|
||||
|
||||
*图 3: 我们的文件已经被添加进临时环境*
|
||||
|
||||
### 第一次提交
|
||||
|
||||
当新文件添加进临时环境之后,我们现在就准备好创建第一个<ruby>提交<rt>commit</rt></ruby>了。什么是提交呢?简单的说,一个提交就是你更改的项目的文件的记录。创建一个提交也是非常简单的。但是,为提交包含一个描述信息非常重要。通过这样做,你可以添加有关该提交包含的内容的注释,比如你对文件做出的何种修改。然而,在这样做之前,我们需要告知 Git 我们的账户,输入以下命令:
|
||||
|
||||
```
|
||||
git config --global user.email EMAIL
|
||||
git config --global user.name “FULL NAME”
|
||||
```
|
||||
|
||||
“EMAIL” 即你的 email 地址,“FULL NAME” 则是你的姓名。
|
||||
|
||||
现在你可以通过以下命令创建一个提交:
|
||||
|
||||
```
|
||||
git commit -m “Descriptive Message”
|
||||
```
|
||||
|
||||
“Descriptive Message” 即为你的提交的描述性信息。比如,当你第一个提交是提交一个 `readme.txt` 文件,你可以这样提交:
|
||||
|
||||
```
|
||||
git commit -m “First draft of readme.txt file”
|
||||
```
|
||||
|
||||
你可以看到输出表明一个文件已经修改,并且,为 `readme.txt` 创建了一个新的文件模式(图4)
|
||||
|
||||
![success][12]
|
||||
|
||||
*图4:提交成功*
|
||||
|
||||
|
||||
### 创建分支并推送至 GitHub
|
||||
|
||||
分支是很重要的,它允许你在项目状态间中移动。假如,你想给你的应用创建一个新的特性。为了这样做,你创建了个新分支。一旦你完成你的新特性,你可以把这个新分支合并到你的主分支中去,使用以下命令创建一个新分支:
|
||||
|
||||
```
|
||||
git checkout -b BRANCH
|
||||
```
|
||||
|
||||
“BRANCH” 即为你新分支的名字,一旦执行完命令,输入 `git branch` 命令来查看是否创建了新分支(图5)
|
||||
|
||||
![featureX][14]
|
||||
|
||||
*图5:名为 featureX 的新分支*
|
||||
|
||||
接下来,我们需要在 GitHub 上创建一个仓库。 登录 GitHub 帐户,请单击帐户主页上的“New Repository”按钮。 填写必要的信息,然后单击 “Create repository”(图6)。
|
||||
|
||||
![new repository][16]
|
||||
|
||||
*图6:在 GitHub 上新建一个仓库*
|
||||
|
||||
在创建完一个仓库之后,你可以看到一个用于推送本地仓库的地址。若要推送,返回命令行窗口(`~/newproject` 目录中),输入以下命令:
|
||||
|
||||
```
|
||||
git remote add origin URL
|
||||
git push -u origin master
|
||||
```
|
||||
|
||||
“URL” 即为我们 GitHub 上新建的仓库地址。
|
||||
|
||||
系统会提示您,输入 GitHub 的用户名和密码,一旦授权成功,你的项目将会被推送到 GitHub 仓库中。
|
||||
|
||||
### 拉取项目
|
||||
|
||||
如果你的同事改变了你们 GitHub 上项目的代码,并且已经合并那些更改,你可以拉取那些项目文件到你的本地机器,这样,你系统中的文件就可以和远程用户的文件保持匹配。你可以输入以下命令来做这件事(`~/newproject` 在目录中),
|
||||
|
||||
```
|
||||
git pull origin master
|
||||
```
|
||||
|
||||
以上的命令可以拉取任何新文件或修改过的文件到你的本地仓库。
|
||||
|
||||
### 基础
|
||||
|
||||
这就是从命令行使用 Git 来处理存储在 GitHub 上的项目的基础知识。 还有很多东西需要学习,所以我强烈建议你使用 `man git`,`man git-push` 和 `man git-pull` 命令来更深入地了解 `git` 命令可以做什么。
|
||||
|
||||
开发快乐!
|
||||
|
||||
了解更多关于 Linux 的 内容,请访问来自 Linux 基金会和 edX 的免费的 ["Introduction to Linux"][17]课程。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/learn/intro-to-linux/2018/7/introduction-using-git
|
||||
|
||||
作者:[Jack Wallen][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[distant1219](https://github.com/distant1219)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.linux.com/users/jlwallen
|
||||
[1]:https://git-scm.com/
|
||||
[2]:https://github.com/
|
||||
[3]:https://github.com/join?source=header-home
|
||||
[5]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/git_1.jpg?itok=FKkr5Mrk (new repository)
|
||||
[6]:https://www.linux.com/licenses/category/used-permission
|
||||
[8]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/git_2.jpg?itok=54G9KBHS (readme)
|
||||
[10]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/git_3.jpg?itok=KAJwRJIB (file added)
|
||||
[12]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/git_4.jpg?itok=qR0ighDz (success)
|
||||
[14]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/git_5.jpg?itok=6m9RTWg6 (featureX)
|
||||
[16]:https://www.linux.com/sites/lcom/files/styles/rendered_file/public/git_6.jpg?itok=d2toRrUq (new repository)
|
||||
[17]:https://training.linuxfoundation.org/linux-courses/system-administration-training/introduction-to-linux
|
@ -1,10 +1,11 @@
|
||||
如何在 Ubuntu 和其他 Linux 发行版中安装 2048 游戏
|
||||
如何在 Linux 中安装 2048 游戏
|
||||
======
|
||||
**流行的移动益智游戏 2048 也可以在 Ubuntu 和 Linux 发行版上玩。啊!你甚至可以在 Linux 终端上玩 2048。如果你的生产率因为这个让人上瘾的游戏下降,请不要怪我。**
|
||||
|
||||
> 流行的移动益智游戏 2048 也可以在 Ubuntu 和 Linux 发行版上玩。啊!你甚至可以在 Linux 终端上玩 2048。如果你的生产率因为这个让人上瘾的游戏下降,请不要怪我。
|
||||
|
||||
早在 2014 年,2048 就是 iOS 和 Android 上最受欢迎的游戏之一。这款令人上瘾的游戏非常受欢迎,它在 Linux 上有[浏览器版][1]、桌面版和终端版。
|
||||
|
||||
<https://giphy.com/embed/wT8XEi5gckwJW>
|
||||
![](https://media.giphy.com/media/wT8XEi5gckwJW/giphy.gif)
|
||||
|
||||
通过向上和向下,向左和向右移动滑块来玩这个小游戏。这个益智游戏的目的是通过组合匹配的滑块到数字 2048。因此 2+2 变成 4,4+4 变成 16,依此类推。这可能听起来简单而无聊,但相信我是一个令人上瘾的游戏。
|
||||
|
||||
@ -13,9 +14,9 @@
|
||||
在 Ubuntu 和其他 Linux 中有些 2048 游戏。你可以在软件中心中搜索它,你可以在那里找到一些。
|
||||
|
||||
有一个[基于 Qt ][2]的 2048 游戏,你可以在 Ubuntu 和其他基于 Debian 和 Ubuntu 的 Linux 发行版上安装。你可以使用以下命令安装它:
|
||||
|
||||
```
|
||||
sudo apt install 2048-qt
|
||||
|
||||
```
|
||||
|
||||
安装完成后,你可以在菜单中找到该游戏并启动它。你可以使用箭头键移动数字。你的最高分也会保存。
|
||||
@ -28,14 +29,14 @@ sudo apt install 2048-qt
|
||||
|
||||
现在,有几种方法可以在 Linux 终端中玩 2048。我在这里提其中两个。
|
||||
|
||||
#### 1\. term2048 Snap 程序
|
||||
#### 1、term2048 Snap 程序
|
||||
|
||||
有一个名为 [term2048][6] 的[ snap 程序][5]可以安装在任何[支持 Snap 的 Linux 发行版][7]中。
|
||||
有一个名为 [term2048][6] 的 [snap 程序][5]可以安装在任何[支持 Snap 的 Linux 发行版][7]中。
|
||||
|
||||
如果你启用了 Snap,只需使用此命令安装 term2048:
|
||||
|
||||
```
|
||||
sudo snap install term2048
|
||||
|
||||
```
|
||||
|
||||
Ubuntu 用户也可以在软件中心找到这个游戏并从那里安装它。
|
||||
@ -48,17 +49,17 @@ Ubuntu 用户也可以在软件中心找到这个游戏并从那里安装它。
|
||||
|
||||
你可以使用箭头键移动。
|
||||
|
||||
#### 2\. 2048 游戏的 Bash 脚本
|
||||
#### 2、2048 游戏的 Bash 脚本
|
||||
|
||||
这个游戏实际上是一个 shell 脚本,你可以在任何 Linux 终端上运行。从 Github 下载游戏/脚本:
|
||||
|
||||
[下载 Bash2048][10]
|
||||
- [下载 Bash2048][10]
|
||||
|
||||
解压下载的文件。进入解压后的目录,你将看到名为 2048.sh 的 shell 脚本。只需运行 shell 脚本。游戏将立即开始。你可以使用箭头键移动滑块。
|
||||
|
||||
![Linux Terminal game 2048][11]
|
||||
|
||||
#### 你在Linux上玩什么游戏?
|
||||
### 你在Linux上玩什么游戏?
|
||||
|
||||
如果你喜欢在 Linux 终端上玩游戏,你也应该尝试 [Linux 终端中的经典 Snake 游戏][12]。
|
||||
|
||||
@ -71,7 +72,7 @@ via: https://itsfoss.com/2048-game/
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,77 @@
|
||||
如何使用 Apache 构建 URL 缩短服务
|
||||
======
|
||||
> 用 Apache HTTP 服务器的 mod_rewrite 功能创建你自己的短链接。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/openweb-osdc-lead.png?itok=yjU4KliG)
|
||||
|
||||
很久以前,人们开始在 Twitter 上分享链接。140 个字符的限制意味着 URL 可能消耗一条推文的大部分(或全部),因此人们使用 URL 缩短服务。最终,Twitter 加入了一个内置的 URL 缩短服务([t.co][1])。
|
||||
|
||||
字符数现在不重要了,但还有其他原因要缩短链接。首先,缩短服务可以提供分析功能 —— 你可以看到你分享的链接的受欢迎程度。它还简化了制作易于记忆的 URL。例如,[bit.ly/INtravel][2] 比<https://www.in.gov/ai/appfiles/dhs-countyMap/dhsCountyMap.html>更容易记住。如果你想预先共享一个链接,但还不知道最终地址,这时 URL 缩短服务可以派上用场。。
|
||||
|
||||
与任何技术一样,URL 缩短服务并非都是正面的。通过屏蔽最终地址,缩短的链接可用于指向恶意或冒犯性内容。但是,如果你仔细上网,URL 缩短服务是一个有用的工具。
|
||||
|
||||
我们之前在网站上[发布过缩短服务的文章][3],但也许你想要运行一些由简单的文本文件支持的缩短服务。在本文中,我们将展示如何使用 Apache HTTP 服务器的 mod_rewrite 功能来设置自己的 URL 缩短服务。如果你不熟悉 Apache HTTP 服务器,请查看 David Both 关于[安装和配置][4]它的文章。
|
||||
|
||||
### 创建一个 VirtualHost
|
||||
|
||||
在本教程中,我假设你购买了一个很酷的域名,你将它专门用于 URL 缩短服务。例如,我的网站是 [funnelfiasco.com][5],所以我买了 [funnelfias.co][6] 用于我的 URL 缩短服务(好吧,它不是很短,但它可以满足我的虚荣心)。如果你不将缩短服务作为单独的域运行,请跳到下一部分。
|
||||
|
||||
第一步是设置将用于 URL 缩短服务的 VirtualHost。有关 VirtualHost 的更多信息,请参阅 [David Both 的文章][7]。这步只需要几行:
|
||||
|
||||
```
|
||||
<VirtualHost *:80>
|
||||
ServerName funnelfias.co
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
### 创建重写规则
|
||||
|
||||
此服务使用 HTTPD 的重写引擎来重写 URL。如果你在上面的部分中创建了 VirtualHost,则下面的配置跳到你的 VirtualHost 部分。否则跳到服务器的 VirtualHost 或主 HTTPD 配置。
|
||||
|
||||
```
|
||||
RewriteEngine on
|
||||
RewriteMap shortlinks txt:/data/web/shortlink/links.txt
|
||||
RewriteRule ^/(.+)$ ${shortlinks:$1} [R=temp,L]
|
||||
```
|
||||
|
||||
第一行只是启用重写引擎。第二行在文本文件构建短链接的映射。上面的路径只是一个例子。你需要使用系统上使用有效路径(确保它可由运行 HTTPD 的用户帐户读取)。最后一行重写 URL。在此例中,它接受任何字符并在重写映射中查找它们。你可能希望重写时使用特定的字符串。例如,如果你希望所有缩短的链接都是 “slX”(其中 X 是数字),则将上面的 `(.+)` 替换为 `(sl\d+)`。
|
||||
|
||||
我在这里使用了临时重定向(HTTP 302)。这能让我稍后更新目标 URL。如果希望短链接始终指向同一目标,则可以使用永久重定向(HTTP 301)。用 `permanent` 替换第三行的 `temp`。
|
||||
|
||||
### 构建你的映射
|
||||
|
||||
编辑配置文件 `RewriteMap` 行中的指定文件。格式是空格分隔的键值存储。在每一行上放一个链接:
|
||||
|
||||
```
|
||||
osdc https://opensource.com/users/bcotton
|
||||
twitter https://twitter.com/funnelfiasco
|
||||
swody1 https://www.spc.noaa.gov/products/outlook/day1otlk.html
|
||||
```
|
||||
|
||||
### 重启 HTTPD
|
||||
|
||||
最后一步是重启 HTTPD 进程。这是通过 `systemctl restart httpd` 或类似命令完成的(命令和守护进程名称可能因发行版而不同)。你的链接缩短服务现已启动并运行。当你准备编辑映射时,无需重新启动 Web 服务器。你所要做的就是保存文件,Web 服务器将获取到差异。
|
||||
|
||||
### 未来的工作
|
||||
|
||||
此示例为你提供了基本的 URL 缩短服务。如果你想将开发自己的管理接口作为学习项目,它可以作为一个很好的起点。或者你可以使用它分享容易记住的链接到那些容易忘记的 URL。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/7/apache-url-shortener
|
||||
|
||||
作者:[Ben Cotton][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/bcotton
|
||||
[1]:http://t.co
|
||||
[2]:http://bit.ly/INtravel
|
||||
[3]:https://opensource.com/article/17/3/url-link-shortener
|
||||
[4]:https://opensource.com/article/18/2/how-configure-apache-web-server
|
||||
[5]:http://funnelfiasco.com
|
||||
[6]:http://funnelfias.co
|
||||
[7]:https://opensource.com/article/18/3/configuring-multiple-web-sites-apache
|
@ -0,0 +1,102 @@
|
||||
公钥基础设施和密码学中的私钥的角色
|
||||
======
|
||||
> 了解如何验证某人所声称的身份。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/security_privacy_lock.png?itok=ZWjrpFzx)
|
||||
|
||||
在[上一篇文章][1]中,我们概述了密码学并讨论了密码学的核心概念:<ruby>保密性<rt>confidentiality</rt></ruby> (让数据保密)、<ruby>完整性<rt>integrity</rt></ruby> (防止数据被篡改)和<ruby>身份认证<rt>authentication</rt></ruby> (确认数据源的<ruby>身份<rt>identity</rt></ruby>)。由于要在存在各种身份混乱的现实世界中完成身份认证,人们逐渐建立起一个复杂的<ruby>技术生态体系<rt>technological ecosystem</rt></ruby>,用于证明某人就是其声称的那个人。在本文中,我们将大致介绍这些体系是如何工作的。
|
||||
|
||||
### 快速回顾公钥密码学及数字签名
|
||||
|
||||
互联网世界中的身份认证依赖于公钥密码学,其中密钥分为两部分:拥有者需要保密的私钥和可以对外公开的公钥。经过公钥加密过的数据,只能用对应的私钥解密。举个例子,对于希望与[记者][2]建立联系的举报人来说,这个特性非常有用。但就本文介绍的内容而言,私钥更重要的用途是与一个消息一起创建一个<ruby>数字签名<rt>digital signature</rt></ruby>,用于提供完整性和身份认证。
|
||||
|
||||
在实际应用中,我们签名的并不是真实消息,而是经过<ruby>密码学哈希函数<rt>cryptographic hash function</rt></ruby>处理过的消息<ruby>摘要<rt>digest</rt></ruby>。要发送一个包含源代码的压缩文件,发送者会对该压缩文件的 256 比特长度的 [SHA-256][3] 摘要进行签名,而不是文件本身进行签名,然后用明文发送该压缩包(和签名)。接收者会独立计算收到文件的 SHA-256 摘要,然后结合该摘要、收到的签名及发送者的公钥,使用签名验证算法进行验证。验证过程取决于加密算法,加密算法不同,验证过程也相应不同;而且,很微妙的是签名验证[漏洞][4]依然[层出不穷][5]。如果签名验证通过,说明文件在传输过程中没有被篡改而且来自于发送者,这是因为只有发送者拥有创建签名所需的私钥。
|
||||
|
||||
### 方案中缺失的环节
|
||||
|
||||
上述方案中缺失了一个重要的环节:我们从哪里获得发送者的公钥?发送者可以将公钥与消息一起发送,但除了发送者的自我宣称,我们无法核验其身份。假设你是一名银行柜员,一名顾客走过来向你说,“你好,我是 Jane Doe,我要取一笔钱”。当你要求其证明身份时,她指着衬衫上贴着的姓名标签说道,“看,Jane Doe!”。如果我是这个柜员,我会礼貌的拒绝她的请求。
|
||||
|
||||
如果你认识发送者,你们可以私下见面并彼此交换公钥。如果你并不认识发送者,你们可以私下见面,检查对方的证件,确认真实性后接受对方的公钥。为提高流程效率,你可以举办[聚会][6]并邀请一堆人,检查他们的证件,然后接受他们的公钥。此外,如果你认识并信任 Jane Doe(尽管她在银行的表现比较反常),Jane 可以参加聚会,收集大家的公钥然后交给你。事实上,Jane 可以使用她自己的私钥对这些公钥(及对应的身份信息)进行签名,进而你可以从一个[线上密钥库][7]获取公钥(及对应的身份信息)并信任已被 Jane 签名的那部分。如果一个人的公钥被很多你信任的人(即使你并不认识他们)签名,你也可能选择信任这个人。按照这种方式,你可以建立一个<ruby>[信任网络][8]<rt>Web of Trust</rt></ruby>。
|
||||
|
||||
但事情也变得更加复杂:我们需要建立一种标准的编码机制,可以将公钥和其对应的身份信息编码成一个<ruby>数字捆绑<rt>digital bundle</rt></ruby>,以便我们进一步进行签名。更准确的说,这类数字捆绑被称为<ruby>证书<rt>cerificate</rt></ruby>。我们还需要可以创建、使用和管理这些证书的工具链。满足诸如此类的各种需求的方案构成了<ruby>公钥基础设施<rt>public key infrastructure</rt></ruby>(PKI)。
|
||||
|
||||
### 比信任网络更进一步
|
||||
|
||||
你可以用人际关系网类比信任网络。如果人们之间广泛互信,可以很容易找到(两个人之间的)一条<ruby>短信任链<rt>short path of trust</rt></ruby>:就像一个社交圈。基于 [GPG][9] 加密的邮件依赖于信任网络,([理论上][10])只适用于与少量朋友、家庭或同事进行联系的情形。
|
||||
|
||||
(LCTT 译注:作者提到的“短信任链”应该是暗示“六度空间理论”,即任意两个陌生人之间所间隔的人一般不会超过 6 个。对 GPG 的唱衰,一方面是因为密钥管理的复杂性没有改善,另一方面 Yahoo 和 Google 都提出了更便利的端到端加密方案。)
|
||||
|
||||
在实际应用中,信任网络有一些“<ruby>[硬伤][11]<rt>significant problems</rt></ruby>”,主要是在可扩展性方面。当网络规模逐渐增大或者人们之间的连接较少时,信任网络就会慢慢失效。如果信任链逐渐变长,信任链中某人有意或无意误签证书的几率也会逐渐增大。如果信任链不存在,你不得不自己创建一条信任链,与其它组织建立联系,验证它们的密钥以符合你的要求。考虑下面的场景,你和你的朋友要访问一个从未使用过的在线商店。你首先需要核验网站所用的公钥属于其对应的公司而不是伪造者,进而建立安全通信信道,最后完成下订单操作。核验公钥的方法包括去实体店、打电话等,都比较麻烦。这样会导致在线购物变得不那么便利(或者说不那么安全,毕竟很多人会图省事,不去核验密钥)。
|
||||
|
||||
如果世界上有那么几个格外值得信任的人,他们专门负责核验和签发网站证书,情况会怎样呢?你可以只信任他们,那么浏览互联网也会变得更加容易。整体来看,这就是当今互联网的工作方式。那些“格外值得信任的人”就是被称为<ruby>证书颁发机构<rt>cerificate authoritie</rt></ruby>(CA)的公司。当网站希望获得公钥签名时,只需向 CA 提交<ruby>证书签名请求<rt>certificate signing request</rt></ruby>(CSR)。
|
||||
|
||||
CSR 类似于包括公钥和身份信息(在本例中,即服务器的主机名)的<ruby>存根<rt>stub</rt></ruby>证书,但 CA 并不会直接对 CSR 本身进行签名。CA 在签名之前会进行一些验证。对于一些证书类型(LCTT 译注:<ruby>域名证实<rt>Domain Validated</rt></ruby>(DV) 类型),CA 只验证申请者的确是 CSR 中列出主机名对应域名的控制者(例如通过邮件验证,让申请者完成指定的域名解析)。[对于另一些证书类型][12] (LCTT 译注:链接中提到<ruby>扩展证实<rt>Extended Validated</rt></ruby>(EV)类型,其实还有 <ruby>OV<rt>Organization Validated</rt></ruby> 类型),CA 还会检查相关法律文书,例如公司营业执照等。一旦验证完成,CA(一般在申请者付费后)会从 CSR 中取出数据(即公钥和身份信息),使用 CA 自己的私钥进行签名,创建一个(签名)证书并发送给申请者。申请者将该证书部署在网站服务器上,当用户使用 HTTPS (或其它基于 [TLS][13] 加密的协议)与服务器通信时,该证书被分发给用户。
|
||||
|
||||
当用户访问该网站时,浏览器获取该证书,接着检查证书中的主机名是否与当前正在连接的网站一致(下文会详细说明),核验 CA 签名有效性。如果其中一步验证不通过,浏览器会给出安全警告并切断与网站的连接。反之,如果验证通过,浏览器会使用证书中的公钥来核验该服务器发送的签名信息,确认该服务器持有该证书的私钥。有几种算法用于协商后续通信用到的<ruby>共享密钥<rt>shared secret key</rt></ruby>,其中一种也用到了服务器发送的签名信息。<ruby>密钥交换<rt>key exchange</rt></ruby>算法不在本文的讨论范围,可以参考这个[视频][14],其中仔细说明了一种密钥交换算法。
|
||||
|
||||
### 建立信任
|
||||
|
||||
你可能会问,“如果 CA 使用其私钥对证书进行签名,也就意味着我们需要使用 CA 的公钥验证证书。那么 CA 的公钥从何而来,谁对其进行签名呢?” 答案是 CA 对自己签名!可以使用证书公钥对应的私钥,对证书本身进行签名!这类签名证书被称为是<ruby>自签名的<rt>self-signed</rt></ruby>;在 PKI 体系下,这意味着对你说“相信我”。(为了表达方便,人们通常说用证书进行了签名,虽然真正用于签名的私钥并不在证书中。)
|
||||
|
||||
通过遵守[浏览器][15]和[操作系统][16]供应商建立的规则,CA 表明自己足够可靠并寻求加入到浏览器或操作系统预装的一组自签名证书中。这些证书被称为“<ruby>信任锚<rt>trust anchor</rt></ruby>”或 <ruby>CA 根证书<rt>root CA certificate</rt></ruby>,被存储在根证书区,我们<ruby>约定<rt>implicitly</rt></ruby>信任该区域内的证书。
|
||||
|
||||
CA 也可以签发一种特殊的证书,该证书自身可以作为 CA。在这种情况下,它们可以生成一个证书链。要核验证书链,需要从“信任锚”(也就是 CA 根证书)开始,使用当前证书的公钥核验下一层证书的签名(或其它一些信息)。按照这个方式依次核验下一层证书,直到证书链底部。如果整个核验过程没有问题,信任链也建立完成。当向 CA 付费为网站签发证书时,实际购买的是将证书放置在证书链下的权利。CA 将卖出的证书标记为“不可签发子证书”,这样它们可以在适当的长度终止信任链(防止其继续向下扩展)。
|
||||
|
||||
为何要使用长度超过 2 的信任链呢?毕竟网站的证书可以直接被 CA 根证书签名。在实际应用中,很多因素促使 CA 创建<ruby>中间 CA 证书<rt>intermediate CA certificate</rt></ruby>,最主要是为了方便。由于价值连城,CA 根证书对应的私钥通常被存放在特定的设备中,一种需要多人解锁的<ruby>硬件安全模块<rt>hardware security module</rt></ruby>(HSM),该模块完全离线并被保管在配备监控和报警设备的[地下室][18]中。
|
||||
|
||||
<ruby>CA/浏览器论坛<rt>CAB Forum, CA/Browser Forum</rt></ruby>负责管理 CA,[要求][19]任何与 CA 根证书(LCTT 译注:就像前文提到的那样,这里是指对应的私钥)相关的操作必须由人工完成。设想一下,如果每个证书请求都需要员工将请求内容拷贝到保密介质中、进入地下室、与同事一起解锁 HSM、(使用 CA 根证书对应的私钥)签名证书,最后将签名证书从保密介质中拷贝出来;那么每天为大量网站签发证书是相当繁重乏味的工作。因此,CA 创建内部使用的中间 CA,用于证书签发自动化。
|
||||
|
||||
如果想查看证书链,可以在 Firefox 中点击地址栏的锁型图标,接着打开页面信息,然后点击“安全”面板中的“查看证书”按钮。在本文写作时,[opensource.com][20] 使用的证书链如下:
|
||||
|
||||
```
|
||||
DigiCert High Assurance EV Root CA
|
||||
DigiCert SHA2 High Assurance Server CA
|
||||
opensource.com
|
||||
```
|
||||
|
||||
### 中间人
|
||||
|
||||
我之前提到,浏览器需要核验证书中的主机名与已经建立连接的主机名一致。为什么需要这一步呢?要回答这个问题,需要了解所谓的[<ruby>中间人攻击<rt>man-in-the-middle, MIMT</rt></ruby>][22]。有一类[网络攻击][22]可以让攻击者将自己置身于客户端和服务端中间,冒充客户端与服务端连接,同时冒充服务端与客户端连接。如果网络流量是通过 HTTPS 传输的,加密的流量无法被窃听。此时,攻击者会创建一个代理,接收来自受害者的 HTTPS 连接,解密信息后构建一个新的 HTTPS 连接到原始目的地(即服务端)。为了建立假冒的 HTTPS 连接,代理必须返回一个攻击者具有对应私钥的证书。攻击者可以生成自签名证书,但受害者的浏览器并不会信任该证书,因为它并不是根证书库中的 CA 根证书签发的。换一个方法,攻击者使用一个受信任 CA 签发但主机名对应其自有域名的证书,结果会怎样呢?
|
||||
|
||||
再回到银行的那个例子,我们是银行柜员,一位男性顾客进入银行要求从 Jane Doe 的账户上取钱。当被要求提供身份证明时,他给出了 Joe Smith 的有效驾驶执照。如果这个交易可以完成,我们无疑会被银行开除。类似的,如果检测到证书中的主机名与连接对应的主机名不一致,浏览器会给出类似“连接不安全”的警告和查看更多内容的选项。在 Firefox 中,这类错误被标记为 `SSL_ERROR_BAD_CERT_DOMAIN`。
|
||||
|
||||
我希望你阅读完本文起码记住这一点:如果看到这类警告,**不要无视它们**!它们出现意味着,或者该网站配置存在严重问题(不推荐访问),或者你已经是中间人攻击的潜在受害者。
|
||||
|
||||
### 总结
|
||||
|
||||
虽然本文只触及了 PKI 世界的一些皮毛,我希望我已经为你展示了便于后续探索的大致蓝图。密码学和 PKI 是美与复杂性的结合体。越深入研究,越能发现更多的美和复杂性,就像分形那样。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/7/private-keys
|
||||
|
||||
作者:[Alex Wood][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[pinewall](https://github.com/pinewall)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/awood
|
||||
[1]:https://linux.cn/article-9792-1.html
|
||||
[2]:https://theintercept.com/2014/10/28/smuggling-snowden-secrets/
|
||||
[3]:https://en.wikipedia.org/wiki/SHA-2
|
||||
[4]:https://www.ietf.org/mail-archive/web/openpgp/current/msg00999.html
|
||||
[5]:https://www.imperialviolet.org/2014/09/26/pkcs1.html
|
||||
[6]:https://en.wikipedia.org/wiki/Key_signing_party
|
||||
[7]:https://en.wikipedia.org/wiki/Key_server_(cryptographic)
|
||||
[8]:https://en.wikipedia.org/wiki/Web_of_trust
|
||||
[9]:https://www.gnupg.org/gph/en/manual/x547.html
|
||||
[10]:https://blog.cryptographyengineering.com/2014/08/13/whats-matter-with-pgp/
|
||||
[11]:https://lists.torproject.org/pipermail/tor-talk/2013-September/030235.html
|
||||
[12]:https://en.wikipedia.org/wiki/Extended_Validation_Certificate
|
||||
[13]:https://en.wikipedia.org/wiki/Transport_Layer_Security
|
||||
[14]:https://www.youtube.com/watch?v=YEBfamv-_do
|
||||
[15]:https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/policy/
|
||||
[16]:https://technet.microsoft.com/en-us/library/cc751157.aspx
|
||||
[17]:https://en.wikipedia.org/wiki/Hardware_security_module
|
||||
[18]:https://arstechnica.com/information-technology/2012/11/inside-symantecs-ssl-certificate-vault/
|
||||
[19]:https://cabforum.org/baseline-requirements-documents/
|
||||
[20]:http://opensource.com
|
||||
[21]:https://en.wikipedia.org/wiki/Man-in-the-middle_attack
|
||||
[22]:http://www.shortestpathfirst.net/2010/11/18/man-in-the-middle-mitm-attacks-explained-arp-poisoining/
|
@ -3,9 +3,9 @@
|
||||
|
||||
![](https://fedoramagazine.org/wp-content/uploads/2018/07/pythonvscode-816x345.jpg)
|
||||
|
||||
Visual Studio Code,简称 VS Code,是一个开源的文本编辑器,包含用于构建和调试应用程序的工具。安装启用 Python 扩展后,VS Code 可以配置成 Python 开发的理想工作环境。本文将介绍一些有用的 VS Code 扩展,并配置它们以充分提高 Python 开发效率。
|
||||
Visual Studio Code,简称 VS Code,是一个开源的文本编辑器,包含用于构建和调试应用程序的工具。安装启用 Python 扩展后,VS Code 可以配置成理想的 Python 开发工作环境。本文将介绍一些有用的 VS Code 扩展,并配置它们以充分提高 Python 开发效率。
|
||||
|
||||
如果你的计算机上还没有安装 VS Code,可以参考文章 [Using Visual Studio Code on Fedora ](https://fedoramagazine.org/using-visual-studio-code-fedora/) 安装。
|
||||
如果你的计算机上还没有安装 VS Code,可以参考文章 [在 Fedora 上使用 VS Code](https://fedoramagazine.org/using-visual-studio-code-fedora/) 来安装。
|
||||
|
||||
### 在 VS Code 中安装 Python 扩展
|
||||
|
||||
@ -20,11 +20,12 @@ VS Code 通过两个 JSON 文件管理设置:
|
||||
* 一个文件用于 VS Code 的全局设置,作用于所有的项目
|
||||
* 另一个文件用于特殊设置,作用于单独项目
|
||||
|
||||
可以用快捷键 **Ctrl+,** (逗号)打开全局设置,也可以通过 **文件 -> 首选项 -> 设置** 来打开。
|
||||
可以用快捷键 `Ctrl+,` (逗号)打开全局设置,也可以通过 **文件 -> 首选项 -> 设置** 来打开。
|
||||
|
||||
#### 设置 Python 路径
|
||||
|
||||
您可以在全局设置中配置 python.pythonPath 使 VS Code 自动为每个项目选择最适合的 Python 解释器。 。
|
||||
您可以在全局设置中配置 `python.pythonPath` 使 VS Code 自动为每个项目选择最适合的 Python 解释器。
|
||||
|
||||
```
|
||||
// 将设置放在此处以覆盖默认设置和用户设置。
|
||||
// Path to Python, you can use a custom version of Python by modifying this setting to include the full path.
|
||||
@ -33,18 +34,20 @@ VS Code 通过两个 JSON 文件管理设置:
|
||||
}
|
||||
```
|
||||
|
||||
这样,VS Code 将使用虚拟环境目录 .venv 下项目根目录中的 Python 解释器。
|
||||
这样,VS Code 将使用虚拟环境目录 `.venv` 下项目根目录中的 Python 解释器。
|
||||
|
||||
#### 使用环境变量
|
||||
|
||||
默认情况下,VS Code 使用项目根目录下的 .env 文件中定义的环境变量。 这对于设置环境变量很有用,如:
|
||||
默认情况下,VS Code 使用项目根目录下的 `.env` 文件中定义的环境变量。 这对于设置环境变量很有用,如:
|
||||
|
||||
```
|
||||
PYTHONWARNINGS="once"
|
||||
```
|
||||
|
||||
可使程序在运行时显示警告。
|
||||
|
||||
可以通过设置 python.envFile 来加载其他的默认环境变量文件:
|
||||
可以通过设置 `python.envFile` 来加载其他的默认环境变量文件:
|
||||
|
||||
```
|
||||
// Absolute path to a file containing environment variable definitions.
|
||||
"python.envFile": "${workspaceFolder}/.env",
|
||||
@ -52,9 +55,10 @@ PYTHONWARNINGS="once"
|
||||
|
||||
### 代码分析
|
||||
|
||||
Python 扩展还支持不同的代码分析工具(pep8,flake8,pylint)。要启用你喜欢的或者正在进行的项目所使用的分析工具,只需要进行一些简单的配置。
|
||||
Python 扩展还支持不同的代码分析工具(pep8、flake8、pylint)。要启用你喜欢的或者正在进行的项目所使用的分析工具,只需要进行一些简单的配置。
|
||||
|
||||
扩展默认情况下使用 pylint 进行代码分析。你可以这样配置以使用 flake8 进行分析:
|
||||
|
||||
```
|
||||
"python.linting.pylintEnabled": false,
|
||||
"python.linting.flake8Path": "${workspaceRoot}/.venv/bin/flake8",
|
||||
@ -68,7 +72,8 @@ Python 扩展还支持不同的代码分析工具(pep8,flake8,pylint)。
|
||||
|
||||
### 格式化代码
|
||||
|
||||
可以配置 VS Code 使其自动格式化代码。目前支持 autopep8,black 和 yapf。下面的设置将启用 “black” 模式。
|
||||
可以配置 VS Code 使其自动格式化代码。目前支持 autopep8、black 和 yapf。下面的设置将启用 “black” 模式。
|
||||
|
||||
```
|
||||
// Provider for formatting. Possible options include 'autopep8', 'black', and 'yapf'.
|
||||
"python.formatting.provider": "black",
|
||||
@ -77,7 +82,7 @@ Python 扩展还支持不同的代码分析工具(pep8,flake8,pylint)。
|
||||
"editor.formatOnSave": true,
|
||||
```
|
||||
|
||||
如果不需要编辑器在保存时自动格式化代码,可以将 editor.formatOnSave 设置为 false 并手动使用快捷键 **Ctrl + Shift + I** 格式化当前文档中的代码。 注意,项目的虚拟环境中需要安装有 black,此示例方能有效。
|
||||
如果不需要编辑器在保存时自动格式化代码,可以将 `editor.formatOnSave` 设置为 `false` 并手动使用快捷键 `Ctrl + Shift + I` 格式化当前文档中的代码。 注意,项目的虚拟环境中需要安装有 black,此示例方能有效。
|
||||
|
||||
### 运行任务
|
||||
|
||||
@ -89,40 +94,43 @@ VS Code 的一个重要特点是它可以运行任务。需要运行的任务保
|
||||
|
||||
![][4]
|
||||
|
||||
编辑如下所示的 tasks.json 文件,创建新任务来运行 Flask 开发服务:
|
||||
编辑如下所示的 `tasks.json` 文件,创建新任务来运行 Flask 开发服务:
|
||||
|
||||
```
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
|
||||
"label": "Run Debug Server",
|
||||
"type": "shell",
|
||||
"command": "${workspaceRoot}/.venv/bin/flask run -h 0.0.0.0 -p 5000",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
"label": "Run Debug Server",
|
||||
"type": "shell",
|
||||
"command": "${workspaceRoot}/.venv/bin/flask run -h 0.0.0.0 -p 5000",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Flask 开发服务使用环境变量来获取应用程序的入口点。 如 **使用环境变量** 一节所说,可以在 .env 文件中声明这些变量:
|
||||
Flask 开发服务使用环境变量来获取应用程序的入口点。 如 **使用环境变量** 一节所说,可以在 `.env` 文件中声明这些变量:
|
||||
|
||||
```
|
||||
FLASK_APP=wsgi.py
|
||||
FLASK_DEBUG=True
|
||||
```
|
||||
|
||||
这样就可以使用快捷键 **Ctrl + Shift + B** 来执行任务了。
|
||||
这样就可以使用快捷键 `Ctrl + Shift + B` 来执行任务了。
|
||||
|
||||
### 单元测试
|
||||
|
||||
VS Code 还支持单元测试框架 pytest,unittest 和 nosetest。启用测试框架后,可以在 VS Code 中单独运行搜索到的单元测试,通过测试套件运行测试或者运行所有的测试。
|
||||
VS Code 还支持单元测试框架 pytest、unittest 和 nosetest。启用测试框架后,可以在 VS Code 中单独运行搜索到的单元测试,通过测试套件运行测试或者运行所有的测试。
|
||||
|
||||
例如,可以这样启用 pytest 测试框架:
|
||||
|
||||
```
|
||||
"python.unitTest.pyTestEnabled": true,
|
||||
"python.unitTest.pyTestPath": "${workspaceRoot}/.venv/bin/pytest",
|
||||
@ -140,7 +148,7 @@ via: https://fedoramagazine.org/vscode-python-howto/
|
||||
作者:[Clément Verna][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[idea2act](https://github.com/idea2act)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user