在 CentOS 6 和 Nginx 中部署 Let's Encrypt 的 SSL 证书

上周写了一篇文章讲了讲为什么应该使用 HTTPS 加密你的网站,并笼统的介绍了如何申请和使用 Let’s Encrypt 提供的 SSL 证书。这次我们说说如何在 CentOS 6 和 Nginx 中部署 Let’s Encrypt 的 SSL 证书,过程中可能遇到的问题,以及如何解决。

整体上分成申请证书和使用证书两个部分。

申请证书

准备工作

在申请证书之前,你必须有一个域名,并配置好 DNS 解析服务,使其指向你将要使用的服务器。说白了,就是你要保证所有人都可以通过在浏览器中输入域名打开你的网站。

此时 Nginx 的配置文件如下(为了便于说明只包含了最基本的信息):

1
2
3
4
5
6
7
8
9
[ngxin]
server {
listen 80;
server_name www.bnlt.org;

location / {
root /usr/share/nginx/html;
}
}

其中 80 是 http 协议默认端口,www.bnlt.org 是我的域名,/usr/share/nginx/html 是网站根目录。

安装 Certbot

Certbot 是 Let’s Encrypt 官方推出的,用来获取 SSL 证书的工具。Certbot 的官网为各大主流操作系统和 Web 服务器提供了安装和使用的指南。下面首先介绍官方提供的方法。

下列操作均在 root 用户权限下执行。

官方推荐

针对 CentOS 6 系统,官方推荐的是使用 certbot-auto 脚本。安装方法如下:

1
2
wget https://dl.eff.org/certbot-auto  
chmod a+x certbot-auto

第一个命令将 certbot-auto 下载到当前目录下,第二个命令给予执行权限。

这是一个 shell 脚本,可以通过在当前目录下执行 ./certbot-auto 来调用。

它首先检查你是否已经安装了最新版本的 certbot,如果尚未安装,则调用 yum 检查和安装相关的依赖,其中包括了 python、python-dev 和 python-pip,然后创建一个虚拟环境(相对独立的、运行 python 的虚拟环境,在里面安装的依赖和库独立于操作系统使用的 python 环境,可以避免影响主系统的运行),在这个虚拟环境里安装一个最新版本的 certbot。用户交给 certbot-auto 的参数都被委派给这个安装在虚拟环境中的 certbot 来执行。

由于这个 certbot 被安装在虚拟环境里,你不能直接在命令行里使用 certbot 命令,只能通过 certbot-auto 脚本间接使用它。

不过使用 certbot-auto 脚本有时会遇到问题,表现为虚拟环境创建完毕后,长时间卡在安装 Python 包的步骤:

1
2
Creating virtual environment...  
Installing Python packages...

这通常被认为是 pip 的源在国内访问受限导致。

直接使用 pip 安装 certbot

上面讲到了,certbot-auto 是先安装 python-pip,再通过 python-pip 来安装 certbot 的,所以我们也可以自己手动来完成这个过程。通过这个方法安装的 certbot 会成为一个全局的命令,具体安装在 /usr/bin/certbot 。

如果你想自己安装 certbot,我仍然建议你先执行一下 certbot-auto 命令,让它先帮你安装好相关的依赖。

默认情况下 certbot-auto 也会为你安装 python 和 python-pip,但是版本教旧的 python2.6 和 pip7.1,,虽然用来安装和使用 certbot 是完全没有问题的,但每次执行时都会提示你当前使用的版本较老,官方已不再支持,有安全隐患。

我使用的是 python2.7 以及 pip9.0,可以通过添加 IUS 的源来安装。

添加 IUS 源

1
yum install https://centos6.iuscommunity.org/ius-release.rpm

安装 python2.7 和 pip9.0+

1
yum install python27 python27-devel python-pip

其中 python27-devel 是运行 certbot 必需的。

全局安装 certbot

1
pip install certbot

如果 pip install 速度很慢或卡进度,可以尝试设置 pip 的源为阿里云的镜像,再尝试上面的命令。

在当前用户目录下建立 ~/.pip/pip.conf 文件。内容如下:

1
2
3
4
5
[global]
index-url = http://mirrors.aliyun.com/pypi/simple/

[install]
trusted-host=mirrors.aliyun.com

安装完毕后,你会得到一个新命令 certbot,用 which 命令可以查到其安装的位置。

1
2
which certbot  
/usr/bin/certbot

获取证书

现在可以使用 certbot 获取 SSL 证书了。

如果你使用官方脚本,将下列命令中的所有 certbot 替换为 ./certbot-auto 即可。

这里我们使用 certbot certonly --webroot 方式来获取证书,此命令借助已有的 Web 服务实现认证,并生成证书,命令执行过程中不会对网站的正常运行造成影响,以后给证书续期也更平滑。

完整的命令如下:

1
certbot certonly --webroot -w /usr/share/nginx/html -d www.bnlt.org

-w 参数指定了网站的根目录,-d 参数指定了网站的域名。

背后的原理大致如下:执行命令的计算机会和 Let’s Encrypt 的服务器进行通信,商定一个字符串,这个字符串以文件的形式保存在-w 参数指定的路径下的 .well-known/acme-challenge/ 目录中,服务器再访问 -d 指定的网站目录下的相应文件来进行核实。核对成功就可以基本证明这个域名确实归你所有,Let’s Encrypt 就会为你颁发相应的证书了。

实际上只能证明当前是你控制了这个域名的解析,如果域名解析被他人恶意控制或污染,他也能生成此域名的证书,因此 Let’s Encrypt 提供的证书仅能用于加密,保证信息在传输过程中不被篡改,但不能用来证明域名的所有权。从这个角度来看,只要攻击者能控制或污染域名解析,即使你访问的某个网站显示了绿锁,仍有可能是个钓鱼网站。

执行此命令后,会弹出一个界面问你是否接受相关的协议,选择 OK 并确认即可。你也可以在执行上述命令时加上 –agree-tos 自动接受协议以跳过此步骤。

如果服务器配置正确,命令行参数也无误,那么就能成功完成,提示如下:

1
2
3
4
5
6
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/www.bnlt.org/fullchain.pem. Your cert will
expire on 2017-01-30\. To obtain a new or tweaked version of this
certificate in the future, simply run certbot again. To
non-interactively renew *all* of your certificates, run
"certbot renew"

提示信息告诉你证书存放在 /etc/letsencrypt/live/www.bnlt.org 目录下,过期时间是 2017-01-30,最后还告诉你续期的方法是执行 certbot renew

而最常见的错误提示如下:

1
2
An unexpected error occurred:  
The request message was malformed :: No such challenge

遇到这样的情况是因为 Let’s Encrypt 的服务器无法在你的网站上找到验证用的文件。你首先要检查 -w-d 参数是否有误,是否把验证文件放在了别的目录下或者填错了域名,另外还要确保 Let’s Encrypt 的服务器能访问到你的网站,比如你是刚做的域名解析,可能对它所处的网络而言域名解析尚未生效。

使用证书

要在 Nginx 中用刚才得到的证书来启用 https ,需要将配置文件改为:

1
2
3
4
5
6
7
8
9
10
11
server {  
listen 443 ssl;
server_name www.bnlt.org;

ssl_certificate /etc/letsencrypt/live/www.bnlt.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.bnlt.org/privkey.pem;

location / {
root /usr/share/nginx/html;
}
}

和原来的配置文件相比主要有两处变化,一是将 listen 端口改为 https 协议的默认端口 443,并加上 ssl 说明是加密连接。

还有就是增加了 ssl_certificatessl_certificate_key,前者指定公钥,后者指定私钥。

保存配置文件,执行 /etc/init.d/nginx reload 使新配置生效

http 自动跳转 https

如果你想更进一步,对所有用户强制使用 https,可以在配置文件里增加如下内容:

1
2
3
4
5
server {  
listen 80;
server_name www.bnlt.org;
return 301 https://$server_name$request_uri;
}

此配置会将所有通过 80 端口的 http 协议访问的用户,安全的跳转到对应的 https 版本页面。

同样,执行 /etc/init.d/nginx reload 使新配置生效。

把你的网站升级到 HTTPS

把你的网站升级到 HTTPS

作为一个普通用户,在使用手机浏览网页,或是阅读微信中别人分享的内容时,经常会看到屏幕下方有个弹出广告,什么点击领红包啦,免费送流量啦,或是移动运营商的广告。通常这些广告跟你浏览的网站本身风格完全不搭、画质又非常糟糕、而且在几个毫不相干的网站中都出现了。遇到这样的情况,基本可以肯定是有中间人劫持了你的流量,并篡改了网页内容,在其中嵌入了广告。纯粹做做广告已经算是客气,更恶劣的可以替换你浏览的网页内容,把你引向钓鱼网站,甚至直接窃取你的密码。

作为一个开发者,你的境遇更糟糕,因为这一切时刻都发生在你自己的网站或 APP 上。也许是你花费不少心思才做出来的精美页面被一个很粗糙的广告拉低了好几个档次;也许是一个重要活动的报名按钮被广告遮挡,你的用户不得不先小心翼翼关了广告才能继续;也许是为一个大客户制作的严肃的内容下面出现了一段夸张又搞笑的广告语。

但最最糟糕的是,你的大多数客户和用户都不清楚这是运营商或广告商劫持了他们的流量,而是以为你这个家伙为了一点微薄的广告收入在里面嵌了品味低下的广告,毕竟这网站是你的,这应用是你开发的,不是你干的还能是谁呢。你是希望当这一切发生的时候向他们解释这不是你的错,还是从根本上杜绝这样的事情发生呢?更何况你不是一点错都没有,是你的安全工作没做到位,给了无良广告商机会,钱他们赚,黑锅你背。

换了过去,你还有借口,对网站进行 HTTPS(SSL/TLS) 安全加密的证书是要花钱买的,每个独立子域名就要几百元一年,高级的证书则需要几千上万。

不过现在有了 Let’s Encrypt。一个致力于推行互联网安全的非营利性组织,免费为你的网站进行认证并颁发证书,其颁发的证书已被各大浏览器广泛接受。

那么接下来就只剩一个问题了,到底怎么才能把自己的网站从 http 升级到 https 呢?

Let’s Encrypt 和 Certbot

Let’s Encrypt 颁发证书是完全自助的,如果你的网站已经上线运行,那么你只需要用它们提供的工具执行一个命令就能为其申请一个证书。

这个工具是 Certbot。

安装 Certbot
  • 打开 Certbot 的官方网站 https://certbot.eff.org/
  • 在 I’m Using 处选择自己使用的 Web 服务器和操作系统
  • 下方的内容会根据你的选择进行相应的变化
  • 根据网站给出的提示逐步操作

好吧,这个网站是英文的。

不过我简单总结了一下,主要是这两种方式:

首先尝试用操作系统对应的包管理器来安装,比如 ubuntu 用 apt-get install certbot, arch 用 pacman -S certbot,macOS 用 brew install certbot, centos 用 yum install certbot

如果提示包不存在,比如我用的 CentOS 6,那么可以尝试下载一个官方提供的叫做 certbot-auto 的脚本

1
2
wget https://dl.eff.org/certbot-auto  
chmod a+x certbot-auto

如果使用 certbot-auto ,后续的命令中用到 certbot 的地方都换成 ./certbot-auto 即可。

获取证书
1
certbot certonly --webroot -w 网站根目录 -d 你的域名

将网站根目录和域名换成你实际使用的目录和域名即可。

certbot 会自动完成一系列的验证工作,把证书放在你的服务器上,然后弹出一个恭喜你成功获取证书的提示,证书文件的名称也包含在该提示中。通常会是 /etc/letsecrypt/live/你的域名/fullchain.pem

为你的网站启用 HTTPS

接下来就要修改你 Web 服务器的配置文件了,比如 Nginx 默认是修改 /etc/nginx/conf.d/default.conf

将配文件中的

1
listen 80;

改成

1
listen 443 ssl;

然后添加两行配置说明使用哪个证书文件

1
2
ssl_certificate /etc/letsencrypt/live/你的域名/fullchain.pem;  
ssl_certificate_key /etc/letsencrypt/live/你的域名/privkey.pem;

重启 Web 服务器使证书生效。

证书续期

Let’s Encrypt 颁发的证书有效期为 90 天,你可以在证书快到期的时候进行续期操作,续期命令更加简单:

1
certbot renew

该命令会逐一检查你所有的证书文件,并对其中快到期的证书进行续期。

为了使续期后的新证书生效,重启一下 Web 服务器。

完整的例子

本来只是想写一篇如何在 CentOS 6 和 Nginx 下使用 Let’s Encrypt 为你的网站增加 HTTPS ,一不小心把题目写大了收不住。这个具体的例子,会涉及一些官方操作未提及的细节和问题,只好再另开一篇进行详细描述了。

ImageMagick 将 PDF 转换成多张图片

ImageMagick 将 PDF 转换成多张图片

今天遇到一个需求,客户发来几份PDF,想放到他们的微网站(专门给微信用的网站)上,由于是给手机看的,加上文件页数不多,就想到转换成图片再放上去。

第一时间想到用 ImageMagick 的 convert 命令,网上一搜,命令如下1

1
convert -density 600 foo.pdf foo-%02d.jpg

-density 设置了生成的图片的精度,数值越大,图片越清晰(分辨率高),转换也越慢。如果给手机用,普通 A4 大小的 PDF 设置在 200 左右比较合适。

foo-%02d.jpg 是希望生成的文件名,%02d 部分会替换成页码(从 0 开始),用过 printf 函数的应该对这个规则会比较熟悉。

然而在实际使用时遇到了一个意外情况:

1
convert: no images defined `foo-%02d.jpg' @ error/convert.c/ConvertImageCommand/3258\.

继续求助搜索引擎,找到解决方案,缺少 gs 2。gs 即 GhostScript,ImageMagick 用它来解析 PDF 文件。

1
brew install gs

安装完 gs ,再执行一次 convert 命令,问题解决。

Hash, PushState 和微信 JSSDK 授权

最近将 riot.js 升级到了 3.0,并用上了新版本的 riot-route,原先用了一年多的 2.2.4 版本内置的 riot.route 只支持 hash 形式的 SPA 单页面应用,riot-route 则支持 pushState。

Hash 方式有个缺点,就是服务器不知道地址栏中 # 之后的内容,放在微信里,就导致了未授权用户授权后想返回原界面需要借助 JS 来实现,导致历史记录多了一条,用户按返回键无法退出(返回上一页面后又被 JS “前进”了)。

PushState 方式就没有这个问题,可以直接用 HTTP 302 重定向过来,不影响历史记录和返回按钮功能。

于是在新项目中开始采用 pushState 方式。

不过等到应用到了微信环境中,又冒出来一个问题,通过 pushState 改变地址在 iOS 中会导致 JSSDK 的授权失败。

在 config 中开启 debug,可见 invalid signature 。原因是 location.href 中的地址变化了,但是微信客户端认为地址还是打开页面时的地址,微信“复制链接”得到的地址可以作为旁证。

解决方法

先按标准方法用当前地址计算 signature,如果失败,再用打开浏览器时的地址计算 signature。

由于之前已经把加载微信 JSSDK 和相关 Api 的功能独立成函数,因此这次解决起来也比较简单。

修改前的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function runWx(api, fn, loadError) {  
if (typeof api === 'string') {
api = [api];
}

// 用 require.js 加载 JSSDK 文件,如果你已经用 <script> 方式加载,可以省掉这一步
require(['jweixin'], function (wx) {
var url = location.href.split('#')[0];

// 将 url 发往服务器计算相应的 signature
$.get('/signature', { url: url }, function (ans) {
// wx.config 执行成功时调用
wx.ready(function () {
wx.checkJsApi({
jsApiList: _.clone(api), // 坑,微信会改变此参数的内容
success: function success(res) {
if (!res || !res.checkResult || _.any(api, function (v) {
return !res.checkResult[v];
})) {
alert('您的微信版本过低,无法使用此功能,请升级微信');
return;
}
fn(wx);
}
});
});

// wx.config 执行失败时调用
wx.error(function () {
alert('授权失败,您可能无法使用部分功能');
});

// 校验用的参数来自服务器
var config = {
// debug: true,
appId: ans.data.appId,
timestamp: ans.data.timestamp,
nonceStr: ans.data.nonceStr,
signature: ans.data.signature,
jsApiList: _.union(['checkJsApi'], api)
};

// 进行校验
wx.config(config);
});
}, loadError);
}

先简单展示一下这个 runWx 函数的用法:

1
2
3
4
5
6
7
8
// 当需要用到特定的微信接口时,运行 runWx
runWx(['uploadImage', 'chooseImage'], function (wx) {
// 可以在这里使用 wx.uploadImage 和 wx.chooseImage 功能
wx.uploadImage(...);
wx.chooseImage(...);
}, function () {
// 加载失败时要做的事
});

runWx 做了这么几件事:

  • 用 require.js 加载 weixin sdk 的 js 文件
  • 获取当前页面地址 location.href.split(‘#’)[0]
  • 将 url 作为参数发送到服务器计算出 signature
  • 调用 wx.config
  • 成功时调用 wx.ready 并最终调用回调函数 fn
  • 失败时调用 wx.error 提示错误
修改后的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 解决部分机型 pushState 不能正确改变地址导致授权失败

// 在第一次打开页面时加载此文件,记录当时的地址作为原始地址
var originUrl = location.href.split('#')[0];

// 增加 tryOrigin 参数
function runWx(api, fn, loadError, tryOrigin) {
if (typeof api === 'string') {
api = [api];
}
require(['jweixin'], function (wx) {
// tryOrigin 为真时使用原始地址
var url = tryOrigin ? originUrl : location.href.split('#')[0];

$.get('/signature', { url: url }, function (ans) {
wx.ready(function () {
wx.checkJsApi({
jsApiList: _.clone(api), // 坑,微信会改变此参数的内容
success: function success(res) {
if (!res || !res.checkResult || _.any(api, function (v) {
return !res.checkResult[v];
})) {
alert('您的微信版本过低,无法使用此功能,请升级微信');
return;
}
fn(wx);
}
});
});

wx.error(function () {
// 已经试了原始地址
if (originUrl === url) {
alert('授权失败,您可能无法使用部分功能');
return;
}
// 没有使用原始地址且 signature 不匹配,尝试用原始地址计算 signature
runWx(api, fn, loadError, true);
});

var config = {
debug: true,
appId: ans.data.appId,
timestamp: ans.data.timestamp,
nonceStr: ans.data.nonceStr,
signature: ans.data.signature,
jsApiList: _.union(['checkJsApi'], api)
};
wx.config(config);
});
}, loadError);
}

和原来相比,主要变化有:

  • 记录了打开浏览器时的原始地址
  • 先尝试用标准的 location.href.split(‘#’)[0] 计算 signature
  • 失败时用 originUrl 再试一次

这个改动的主要优点是原来用到 runWx 的地方,代码完全不需要进行变动,由 runWx 自己去尝试解决问题。

_.compact(_.map(_.toArray(n.toString(2)).reverse(), (v, i) => 2 ** i * v))

包含 underscore 和 es2016 语法

功能

将一个数转换成多个2的N次方的数的和

| 输入 | 二进制 | 输出 | | - | - | - | | 1 | 0001 | [1] | | 6 | 0110 | [2, 4] | | 7 | 0111 | [1, 2, 4] |

用途

数据库中以整数形式存储,界面上以多选的形式展现

1
2
3
4
5
6
7
8
9
<select multiple>
<option value="1">a</option>
<option value="2">b</option>
<option value="4">c</option>
</select>

<script>
$('select').val([2,4])
</script>

数据库存了6,取到前端,转换成 [2,4],同时选中 b 和 c

分解动作

假设 n = 6

1
2
3
4
5
6
n // 6
n.toString(2) // '110'
_.toArray(n.toString(2)) // ['1', '1', '0']
_.toArray(n.toString(2)).reverse() // ['0', '1', '1']
_.map(_.toArray(n.toString(2)).reverse(), (v, i) => 2 ** i * v) // [0, 2, 4]
_.compact(_.map(_.toArray(n.toString(2)).reverse(), (v, i) => 2 ** i * v)) // [2, 4]

其他写法

_.chain(n.toString(2)).toArray().map((b, i, a) => 2 ** (a.length - i - 1) * b).compact().value()
_.compact(_.range(32).map((v) => 2 ** v & n)) // 最大处理32位整型

CentOS 6 Nginx Mainline 1.9

准备

  • 版本:CentOS 6.7, Nginx 1.9
  • 下列所有操作都在命令行终端中进行
  • 权限:root

以下操作需要 root 权限,如果当前登录的用户不是 root,请输入 su 命令,并在提示中输入正确的密码切换为 root 用户

1
su

实现

1.添加源

在 /etc/yum.repo.d/ 下新建 nginx.repo 文件,文件内容如下:

1
2
3
4
name=nginx repo  
baseurl=http://nginx.org/packages/mainline/centos/6/$basearch/
gpgcheck=0
enabled=1
2.安装

通过 yum 安装 nginx

1
yum install nginx -y
3.启动

启动 Nginx 服务

1
/etc/init.d/nginx start
4.验证

用 curl 访问本机地址 127.0.0.1

1
curl 127.0.0.1

出现如下结果表明安装成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>  
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

你也可通过浏览器访问 127.0.0.1 来查看结果

说明

目录 /etc/yum.repos.d 下存放的是 yum 安装和更新软件时用到的源的配置文件。所谓源,就是软件仓库,里面存放了各种各样的软件,我们通过 yum 安装的软件,都是从这些源中下载过来的。

我们在这个目录下添加了一个新文件 nginx.repo ,这样当我们不再需要这个源的时候,只要将对应的文件删除即可。通过增加和删除配置文件的方式,我们可以实现向 yum 中添加新的源或去除不再使用的源的目的,这比修改原有的配置文件要更方便和安全。

配置文件中,关键的一行是,它指明了源的地址

1
baseurl=http://nginx.org/packages/mainline/centos/6/$basearch/

nginx 官方针对不同的 Linux 发行版提供了不同的源地址,这里的 centos6 对应了我们使用的 CentOS 6 操作系统。

其中的 mainline 表示这是 mainline 分支,这是相对 stable 分支而言的,mainline 会随着开发进度的推进加入一些新的特性,而 stable 的更新仅限于 bug 修复。mainline 是 nginx 官方推荐使用的分支。当然,如果 stable 分支的功能已经能够满足你的需求,并且你担心新特性可能带来 bug,那么可以考虑选择 stable 分支。

安装

yum install 是最常用的 yum 命令之一,其作用是安装指定的软件,这里我们指定了 nginx ,参数 -y 的作用是当遇到提示询问是否(y/N)时,自动确认并继续。输入这个命令,yum 就会在我们刚刚添加的源中找到 nginx,并自动完成下载和安装的过程。

启动

目录 /etc/init.d 下存放的是初始化脚本(init scripts),通过这些脚本,我们可以针对很多软件实现启动、停止、重启、重新加载配置文件、查看软件运行状态等操作。

当我们通过 yum 安装 nginx 的时候,也生成了一个对应的初始化脚本 /etc/init.d/nginx 。

执行 /etc/init.d/nginx start 即可启动 nginx 服务,相应的:

  • /etc/init.d/nginx start 启动
  • /etc/init.d/nginx stop 停止
  • /etc/init.d/nginx restart 重启
  • /etc/init.d/nginx status 显示状态
  • /etc/init.d/nginx reload 重新加载配置

还有另一种形式来调用这些命令:

  • service nginx start 启动
  • service nginx stop 停止
  • service nginx restart 重启
  • service nginx status 显示状态
  • service nginx reload 重新加载配置
验证

curl 是一个用途非常广泛的软件,这里我们使用它来快速的验证 nginx 服务是否已经开启,curl 后面跟上服务器地址,是一种便捷的检测网络服务是否可以正常访问的方法,它会向指定地址发送一个 GET 请求,并将收到的响应主体输出到终端的标准输出当中。

这里,我们收到的内容是 nginx 服务器的欢迎页面,这个页面的本体是 /usr/share/nginx/html/index.html 文件,刚才执行的 curl 命令的返回就是这个文件的内容。

其他

远程访问失败

如果本机访问成功,而其他机器(局域网中的其他机器或外网的机器)无法访问,一般是防火墙导致的,可以执行下面的命令关闭防火墙后再试:

1
/etc/init.d/iptables stop

iptables 是 CentOS 默认安装并启用的防火墙软件,其默认配置是阻止本机以外的其他机器访问本机端口的。在掌握足够多的知识来管理 iptables 配置前,最简单的解决方式就是关闭它。

开机启动

在完成上述操作后,nginx 和 iptables 都是会在开机时自动启动的,如果你想改变这些设置,可以安装 ntsysv 来管理服务器中的所有开机启动项

1
yum install -y ntsysv

安装完毕后,启动它

1
ntsysv

界面如图:
ntsysv

可以通过上下键移动光标,空格键切换启动或不启动,选完之后用 tab 切换至 OK,最后按回车保存。

参考

CentOS 6 Nginx Stable 1.8

准备

  • 版本:CentOS 6.7, Nginx 1.8
  • 下列所有操作都在命令行终端中进行
  • 权限:root

以下操作需要 root 权限,如果当前登录的用户不是 root,请输入 su 命令,并在提示中输入正确的密码切换为 root 用户

1
su

实现

1.添加源

在 /etc/yum.repo.d/ 下新建 nginx.repo 文件,文件内容如下:

1
2
3
4
name=nginx repo  
baseurl=http://nginx.org/packages/centos/6/$basearch/
gpgcheck=0
enabled=1
2.安装

通过 yum 安装 nginx

1
yum install nginx -y
3.启动

启动 Nginx 服务

1
/etc/init.d/nginx start
4.验证

用 curl 访问本机地址 127.0.0.1

1
curl 127.0.0.1

出现如下结果表明安装成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>  
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

你也可通过浏览器访问 127.0.0.1 来查看结果

说明

目录 /etc/yum.repos.d 下存放的是 yum 安装和更新软件时用到的源的配置文件。所谓源,就是软件仓库,里面存放了各种各样的软件,我们通过 yum 安装的软件,都是从这些源中下载过来的。

我们在这个目录下添加了一个新文件 nginx.repo ,这样当我们不再需要这个源的时候,只要将对应的文件删除即可。通过增加和删除配置文件的方式,我们可以实现向 yum 中添加新的源或去除不再使用的源的目的,这比修改原有的配置文件要更方便和安全。

配置文件中,关键的一行是,它指明了源的地址

1
baseurl=http://nginx.org/packages/centos/6/$basearch/

Nginx 官方针对不同的 Linux 发行版提供了不同的源地址,这里的 centos6 对应了我们使用的 CentOS 6 操作系统。

安装

yum install 是最常用的 yum 命令之一,其作用是安装指定的软件,这里我们指定了 nginx ,参数 -y 的作用是当遇到提示询问是否(y/N)时,自动确认并继续。输入这个命令,yum 就会在我们刚刚添加的源中找到 nginx,并自动完成下载和安装的过程。

启动

目录 /etc/init.d 下存放的是初始化脚本(init scripts),通过这些脚本,我们可以针对很多软件实现启动、停止、重启、重新加载配置文件、查看软件运行状态等操作。

当我们通过 yum 安装 nginx 的时候,也生成了一个对应的初始化脚本 /etc/init.d/nginx 。

执行 /etc/init.d/nginx start 即可启动 nginx 服务,相应的:

  • /etc/init.d/nginx start 启动
  • /etc/init.d/nginx stop 停止
  • /etc/init.d/nginx restart 重启
  • /etc/init.d/nginx status 显示状态
  • /etc/init.d/nginx reload 重新加载配置

还有另一种形式来调用这些命令:

  • service nginx start 启动
  • service nginx stop 停止
  • service nginx restart 重启
  • service nginx status 显示状态
  • service nginx reload 重新加载配置
验证

curl 是一个用途非常广泛的软件,这里我们使用它来快速的验证 nginx 服务是否已经开启,curl 后面跟上服务器地址,是一种便捷的检测网络服务是否可以正常访问的方法,它会向指定地址发送一个 GET 请求,并将收到的响应主体输出到终端的标准输出当中。

这里,我们收到的内容是 nginx 服务器的欢迎页面,这个页面的本体是 /usr/share/nginx/html/index.html 文件,刚才执行的 curl 命令的返回就是这个文件的内容。

其他

远程访问失败

如果本机访问成功,而其他机器(局域网中的其他机器或外网的机器)无法访问,一般是防火墙导致的,可以执行下面的命令关闭防火墙后再试:

1
/etc/init.d/iptables stop

iptables 是 CentOS 默认安装并启用的防火墙软件,其默认配置是阻止本机以外的其他机器访问本机端口的。在掌握足够多的知识来管理 iptables 配置前,最简单的解决方法就是关闭它。

开机启动

在完成上述操作后,nginx 和 iptables 都是会在开机时自动启动的,如果你想改变这些设置,可以安装 ntsysv 来管理服务器中的所有开机启动项

1
yum install -y ntsysv

安装完毕后,启动它

1
ntsysv

界面如图:
ntsysv

可以通过上下键移动光标,空格键切换启动或不启动,选完之后用 tab 切换至 OK,最后按回车保存。

参考

CentOS 6 安装 gearman 和它的 php 扩展

0. PHP 中的 gearman 扩展

我的服务器使用的是 ius 的 php5.5 ,如果你使用其他源和版本,请自行替换部分包名

1. 安装 epel 和 ius 源

1
2
yum install http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/epel-release-6-5.noarch.rpm
yum install http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/ius-release-1.0-11.ius.centos6.noarch.rpm

2. 安装 gearman ,用于运行 gearman 服务

1
yum install gearmand

3. 安装 libgearman 和 libgearman-devel ,用于编译 php 扩展

1
yum install libgearman libgearman-devel

4. 安装 pecl 和其他依赖

1
yum install php-pear php55u-devel

5. 安装 php 扩展

1
pecl install gearman

6. 修改配置文件 /etc/php.ini 增加 extension=gearman.so

7.验证安装结果

1
2
$ php -m | grep gearman
gearman

微信 JSSDK 在 Android 和 iOS 的一处不一致

今天在使用微信的 JSSDK 时遇到一个诡异的问题:同一套代码,在 Android 中正确,在 iOS 中却失效。

于是开启了 debug,发现在执行 wx.config() 函数时就出现了不同的结果,Android 是 {"errMsg":"config:ok"},而 iOS 是 {"errMsg":"config:fail"}。即使此时我修改了代码确保传入完全相同的参数。

最终发现是在参数 nonceStr 上出了问题,这是一个用于生成签名的随机字符串,为了方便,我直接使用了一个随机数,因此其真正的类型并不是字符串,结果就出现了 Android 和 iOS 中的不同表现。

我想这处不一致,微信开发团队都未必知道。

其实我们自己在开发程序时也是如此,用户的输入实在有太多的可能,要把每种情况都考虑到要花费太多的精力,甚至有时候根本就是不可能的任务。所以只能妥协,做到确保在输入正确数据时得到正确结果,其他情况下就只能 garbage in garbage out 了。

反过来,从使用者的角度看,就要尽可能的按照规范来做,以避免出现奇怪的结果。

向不支持输入法的软件输入中文

问题

今天试了下 x2go 的 single application 模式,第一感觉很不错,可以把远程系统中的软件在本地打开,看上去就像打开本地的软件一样。 美中不足的就是没办法输入中文,首先远程系统的输入法没办法在这个模式下使用,而本地系统的输入法也同样不能正常工作,搞得我只能另外开个文本框输了中文粘贴过去。

不能忍

这已经不是我第一次遇到没办法输入中文的情况了。更可恶的是软件本身是支持中文的,就是没办法直接往里输中文,只能从别的地方贴过去,切来切去浪费时间不说,还打断思路。

Ditto

我装了 Ditto 代替 Windows 原生的剪贴板,在使用 Ditto 粘贴的时候突然想到,如果有一个工具能像 Ditto 一样通过全局热键呼出,然后把选择的过程(Ditto 通过 `Ctrl+`` 呼出后可以从最近十次复制的内容中选择一个插入到光标所在位置)换成直接输入就好了。也就是把输入流程简化成:

  • 全局热键呼出工具
  • 在弹出窗口中输入中文
  • 回车后自动关闭这个窗口并把内容插入到目标软件

程序员

作为一个程序员,我想我该自己开发这个工具,可是,我是一个 web 程序员啊~~~该用什么工具来开发桌面应用啊~~~全局热键要怎么注册~~~剪贴板又要怎么操作~~~啊啊啊~~~~~~

issue

Visual Studio 好大,Qt 核心语言是 C++,好像还是 node-webkit 比较对口。不过还不确定它支不支持全局热键,搜搜看,发现有个 issue 是关于实现全局热键的,好多人关心这个问题啊,还有人悬赏50美金,然后有人加到了100美金,一条一条往下看,可一直没人明确的说句是不是已经实现了,然后突然就看见有人提到 AutoHotkey。顺便说句,这个问题最后以 @zhchbin 赢得 160 美金告结

AutoHotkey

官网很专业,给人可靠的感觉;扫了一遍简介,立马下载安装;翻翻手册,试试教程;然后就把我要的功能做出来了,总共写了三行代码 @_@,找对工具很重要啊

1
2
3
#`::
InputBox clipboard, Send, , , , 102
Send ^v

依次是:

  • 监听全局热键 `Win+``(参考的 Ditto);
  • 弹出输入框,并把输入内容存入剪贴板;
  • 把剪贴板的内容贴到光标位置。

exe

写完后用自带的工具做了一个可执行文件 http://pan.baidu.com/s/1hqy2Z8g ,为了容易辨认还换了个 icon,以前学 Inkscape 画的。

结束

结束了。