竹磬网-邵珠庆の日记 生命只有一次,你可以用它来做些更多伟大的事情–Make the world a little better and easier


31月/210

深入理解 http 反向代理(nginx)

发布在 邵珠庆

反向代理(reverse proxy)

明白了直接访问, 明白了所谓的正向代理, 下面就可以来说说反向代理是怎么回事了.

反向代理与正向代理的一个很大区别就是, 它不需要客户端(浏览器)去做什么配置, 并没有什么配置代理服务器的操作.

如果说正向代理是主动配置, 主动走代理, 那么反向代理则是"被代理", 从这点上看, 反向代理有时又称为"透明代理", 也即是浏览器都不知道自己被代理了, 浏览器以为发给它响应的就是最终的网页服务器, 其实不过是个"代理".

还是举购物的例子来比喻. 有时你在网上购物会看到有商家声称自己就是厂家, 东西都很便宜, 属于厂家直销, 于是你下单了. 过段时间, 你又发现有另一家店声称自己才是真正的厂家直销, 然后你仔细看了两家店铺的信息, 才发现前一个商家是假的, 它不是真的厂家.

但为啥这个假的厂家直销它还是这么便宜呢? 以至于价格跟真的厂家直销的没啥区别. 原因可能则是店家直接就是坐落在厂家旁边, 然后他可能与厂家有那么点关系, 认识里面一些人之类的, 这让他能以很便宜的价格从厂家拿到货, 又因为离得近, 几乎没有任何物流成本, 从某种层面看, 它声称厂家直销也不算怎么骗人. 当然严格来说, 它属于伪厂家直销, 他依然还是个代理商

它声称是李逵, 其实它是李鬼.

用一个图对比一下这两种情形:

深入理解 http 反向代理(nginx)

那么这样的一种模式就有点 反向代理 的味道了, 你以为自己买到了直销, 其实你还是"被代理"了, 还是经过了中间商.

只是这个中间商对你来说不是那么明显, 甚至说对你是透明的, 把你蒙在了鼓里.
虽然都是"代理", 这跟线下店面购买还是很不同的, 在线下你去商店买时, 你很清楚自己经过了代理的中间商, 也即是商店本身, 但在远程线上这种声称自己是厂家直销的情形, 有时你还真不好判断自己是不是被代理了.

那么 http 的反向代理其实也是这样一个道理. 比如你访问我的网站 xiaogd.net, 然后你看下主页的请求里的服务器信息, 它告诉你响应这个主页请求的是一台 Nginx server, 如下图所示:

问题是 Nginx 是最终生成这个网页的 server 吗? 其实不是的! 如果你了解 Nginx, 就会知道它通常只是一个静态资源服务器, 而我的网站主页是一个动态生成的内容, 其实你要是认真看过我网站底部的一个声明, 如下图所示:

深入理解 http 反向代理(nginx)

就会明白这个主页其实是 php 的一个叫 wordpress 的建站应用去生成的. 在我的云主机的内部, Nginx 其实是将主页的请求转发给一个所谓的 php-fpm 网关

这个 php-fpm 网关基本可以看作是个 php 的 web 服务器, 不过严格来说它用的协议不是 http, 而是一种内部简化的 fastcgi 协议.
如果你要较真的话, 这可以算是 反向代理 模式, 但整体不全是 http 反向代理, 但对外而言则确实是.

从它那里取得最终响应的内容, 并再次转发给浏览器, 整个情形见如下的示意图:

深入理解 http 反向代理(nginx)

这是内部配置的一个情况:

location ~ .php$ {
    root           /ftp/wwwroot;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
    include        fastcgi_params;
}

请求被转发到内部一个在 9000 端口上监听的 php 应用服务器.

从外部浏览器的角度看, 请求直接发给了 Nginx server, 响应也从 Nginx server 里回来了, 中间没有任何的(正向)代理. 至于说你内部请求又被怎么转发了, 显然浏览器是无从知道也不需要去知道的.

站在整个体系设计者的角度去看, 当然但很多请求 Nginx 其实是没有能力去响应的, 它只不过在内部把它代理给了另一个内部的 php 应用服务器, 内部的 php 应用服务器才是最终的响应生成者.

在整个体系里面, Nginx 的角色就是一个"反向代理"服务器, 浏览器被代理了, 但它无从知道自己是否被代理了, 这一切对它而言是透明的, 反正它自己是没有主动走(正向)代理的.

当然了, 你现在知道了我内部的配置, 如果直接访问 xiaogd.net:9000, 那就是真正的"直接访问"了, 那就绕过了 Nginx.

不过需要说明的一点是, 直接访问是访问不通的, 因为 9000 端口并没有对外放开. 但是在内部是可以访问到的, 比如这样尝试用 wget 去访问:
wget localhost:9000

这样就是真正的"直接访问"了, 没有任何的代理, 既没有正向代理, 也没有反向代理.
需要说明的一点是, 用 wget 这样去获取响应还是会报错, 因为 wget 使用的是 http 协议, php 的 cgi 网关实际使用的是 fastcgi 协议, 是一个比 http 更为简化的协议, 作为内部通讯更加高效, 不过 wget 不支持这个协议, 但 Nginx 能理解这个协议, 整个过程是这样的:
browser -- [http] --> Nginx -- [fastcgi] --> php-fpm
严格来说, 不完全是 http 代理, 内部的反向代理实际用的是 fastcgi 网关协议, 不过这个原理还是一样的, 如果内部用一个比如 tomcat 来响应, 那么全程就都可以是 http 协议.
browser -- [http] --> Nginx -- [http] --> tomcat
而如果在内部发请求 80, 比如 wget localhost 那就还是被反向代理, 请求先到在 80 端口监听的 Nginx, Nginx 再转给 php-fpm.
另: 关于端口及缺省端口相关知识, 可以参考这篇深入理解端口.

为什么要使用反向代理?

那么到了这一步我们又面临一个新的问题, 那就是为啥要整这个反向代理呢? 类似于碰到正向代理时的诘问那样, 直接访问不香吗? 为啥还要走这个反向代理? 关于正向代理前面已经解释了一些原因, 而反向代理的出现, 正像这个世界上没有无缘无故的爱与恨一样, 自然也有它存在的原因.

一个很直接的原因就是利用反向代理可以作为内部 负载均衡(load balance) 的手段.

举个例子来说, 假如我现在开发了一个 java web 的应用作为我的网站后台, 我直接部署它到 tomcat 服务器上, 让 tomcat 监听 80 端口, 直接对外服务. 一开始访问量也不大, 所以这样也是没有问题的, 如下图所示:

深入理解 http 反向代理(nginx)

注: 因为 http 协议的缺省端口就是 80, 所以用户输入地址时可以省略这个端口号, 也即只需这样: xiaogd.net, 而不是繁琐的像这样: xiaogd.net:80, 关于缺省端口的话题, 还是可以参考前面所提的 深入理解端口.

但过一段时间之后, 访问量可能上来了, 一个 tomcat 进程处理不过来, 那怎么办呢? 于是我打算再起一个新的 tomcat 进程, 但这样就面临一个问题, 只有一个 80 端口, 它已经被第一个 tomcat 进程占用, 如果还要再起另外一个, 则只能选用其它的端口, 比如 8080.

当使用另外一个端口时, 确实可以启动两个 tomcat 的进程, 但用户想访问到第二个 tomcat 进程的服务, 却要这样去访问: xiaogd.net:8080. 显然, 这样的方案是有问题的, 用户根本不知道 8080 端口上服务的存在, 就算你有办法告诉用户, 用户也可能不太理解, 用户同时也很怕麻烦的, 为啥要我输入一个冒号加 8080 呢?

此外, 就算有些用户愿意如你所说转向访问 8080 端口, 你还是不能很好的控制把访问量平均地分配在两个 tomcat 上, 毕竟这是用户随机决定的, 也许很多用户又突然涌过来了 8080 端口的应用上, 造成了这边的拥挤.

又或者只有很少的用户愿意听从你的劝告转到新的 8080 端口上, 访问还是集中在旧的 80 端口上的, 这样旧的应用上响应还是很缓慢, 而新的应用却因为没几个用户访问而显得空闲, 没有得到充分的使用.

那么, 在这种情况下, 反向代理的好处就体现出来了, 具体的操作是这样的, 让 Nginx 作为一个前置的反向代理, 监听在 80 端口上; 而第一个 tomcat 则躲到幕后, 同时它也不再监听 80 端口(需要让给 Nginx), 而改为监听一个其它没有被使用的端口, 比如 8081, 然后让 Nginx 转发请求给它处理.

当然了, 如果只有一个 tomcat, 配置大概是这样的:

location / {
    proxy_pass   http://127.0.0.1:8080;
}

请求处理的流程是这样的:

请求: browser -- [http] --> Nginx -- [http] --> tomcat
响应: browser

自然, 这种情形下反向代理似乎不太必要, 还加多了一个环节, 响应速度反而慢了.

但如果有两个 tomcat, 情况就不一样了, 此时就可以在 Nginx 这个反向代理的层面, 启用负载均衡的策略, 大概的配置如下:

http {
    upstream myapp1 {
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

此时, 如果同时涌入了很多请求, Nginx 会把一半的请求交给 8080 端口上的 tomcat, 另一半的请求交给 8081 端口上的 tomcat, 如下图所示:

深入理解 http 反向代理(nginx)

对外来看, 所有请求还是 Nginx 来处理, 用户不需要去做选择, 也不需要知道什么 8080, 8081 端口上应用的存在, 他们还是继续访问原来的网址 xiaogd.net 即可, 无需做任何改变.

如果你在云上有好几台主机, 甚至还可以将其组成一个内网, 然后将 tomcat 部署在不同的主机上. 比如有三台主机的话, 一台运行 Nginx 监听 80 端口, 其余两台运行 tomcat, 分别监听 8080 和 8081 端口, 同时接受并处理 Nginx 反向代理过来的请求, 如下图所示:

如果两台 tomcat 主机的配置不同, 比如一台的性能更强劲些, 还可以调整负载的比例(即权重, weight), 让性能更强的一台承担更多的请求:

http {
    upstream myapp1 {
        server 192.168.0.20:8080 weight=3;
        server 192.168.0.21:8080 weight=2;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

如上配置 3:2 的权重比, 让其中一台承担 60% 的请求, 而另一台性能较差的则承担 40%, 也即每 5 个请求, 3 个会被转到 ip 为 20 的主机上, 2 个会转到 ip 为 21 的主机上.

自然, 有人可能还会有疑问, 所有请求都还是要经过 Nginx, 它能处理得过来吗? 答案是可以的, 因为它的功能仅仅是转发, 这就有点像美团外卖, 虽然它每天接受成千上万的人的点餐, 但它自己不需要去买菜, 洗菜, 切菜, 炒菜等, 它仅仅需要把订单交给饭店餐馆, 然后把它们做好的饭菜配送出去, 也即那些耗时的做饭过程都交给了饭店餐馆处理.

在这种反向代理的模式中, 同样的, 生成网页这个重任交到了隐藏在背后的 tomcat, 生成一个复杂的动态网页可能需要经过一些复杂的计算, 要查询数据库, 要拼凑各个页面组件, 可能会比较耗时, 但这些请求被两个 tomcat 应用并发地处理了, 因此响应的速度还是得到了保证, 而这些就是反向代理能给我们带来的好处.

总结

至此, 关于直接访问, (正向)代理以及反向代理就介绍完了, 最后总结下三种情形及与购物例子的比喻.

在直接访问的情形中, 浏览器直接访问了最终生成响应的服务器, 类似我们以厂家直销的方式从厂商购物, 如下图所示:

深入理解 http 反向代理(nginx)

在(正向)代理的情形中, 浏览器主动访问代理服务器, 通过它间接获取最终响应, 类似我们从商店购物, 而商店的物品又是从厂家购来的, 如下图所示:

深入理解 http 反向代理(nginx)

在反向代理的情形中, 从浏览器的角度看还是类似于直接访问, 但它的请求在服务端被透明的代理了. 类似于我们在网上从一个声称是厂家直销的"伪厂家"那里购物, 这个伪厂家实际还是把我们的订单转给了真正的厂家, 并从中拿了货给我们, 只是我们无从知道这一切幕后的交易, 如下图所示:

深入理解 http 反向代理(nginx)

在一个复杂的网络中, 浏览器的请求还可能先被正向代理了, 然后又被反向代理了, 如下图所示::

深入理解 http 反向代理(nginx)

关于 http 正向代理和反向代理就讲到这里.

281月/190

使用acme.sh为nginx配置https

发布在 邵珠庆

使用acme.sh一键安装Let's Encrypt提供的免费SSL证书 并为nginx配置https

本文章使用derror.com域名作为示例

安装nginx

正常配置并启动nginx保证http能够正常访问: 配置好root目录, 比如: /home/work/local/www/

安装acme.sh

$ curl https://get.acme.sh | sh

开始生成证书(issue a cert)

$ acme.sh --issue -d derror.com -w /home/work/local/www

成功应该会得到以下消息

[Mon Oct 29 08:12:04 EDT 2018] Your cert is in  /root/.acme.sh/derror.com/mrnil.com.cer
[Mon Oct 29 08:12:04 EDT 2018] Your cert key is in  /root/.acme.sh/derror.com/mrnil.com.key
[Mon Oct 29 08:12:05 EDT 2018] The intermediate CA cert is in  /root/.acme.sh/derror.com/ca.cer
[Mon Oct 29 08:12:05 EDT 2018] And the full chain certs is there:  /root/.acme.sh/derror.com/fullchain.cer

配置自动更新证书

$ acme.sh --install-cert -d derror.com \
--key-file       /home/work/local/cert/derror.com/key.pem  \
--fullchain-file /home/work/local/cert/derror.com/cert.pem \
--reloadcmd     "systemctl restart nginx"
--reloadcmd "systemctl restart nginx" 更新后自动重启nginx激活新证书

生成 dhparan.pem

$ openssl dhparam -out /home/work/local/cert/derror.com/dhparam.pem 2048

nginx配置ssl

www.conf

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    listen       443 ssl;
    server_name  _;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl on;
    ssl_certificate         /home/work/local/cert/derror.com/cert.pem;
    ssl_certificate_key     /home/work/local/cert/derror.com/key.pem;
    # ssl_dhparam
    ssl_dhparam             /home/work/local/cert/derror.com/dhparam.pem;

    root         /home/work/local/www;
    index index.html index.htm;
    location / {
    }
}

重启nginx即可

$ systemctl restart nginx

验证ssl

https://derror.com

https://ssllabs.com/ssltest/analyze.html?d=derror.com

添加二级域名

上面的操作基本就完成了. 下面我们来尝试再添加一个二级域名lab.derror.com

$ acme.sh --issue -d lab.derror.com -w /home/work/local/www
...
[Wed Nov 21 04:19:14 EST 2018] Your cert is in  /root/.acme.sh/lab.derror.com/lab.derror.com.cer 
[Wed Nov 21 04:19:14 EST 2018] Your cert key is in  /root/.acme.sh/lab.derror.com/lab.derror.com.key 
[Wed Nov 21 04:19:16 EST 2018] The intermediate CA cert is in  /root/.acme.sh/lab.derror.com/ca.cer 
[Wed Nov 21 04:19:16 EST 2018] And the full chain certs is there:  /root/.acme.sh/lab.derror.com/fullchain.cer

$ mkdir -p /home/work/local/cert/lab.derror.com

$ acme.sh --install-cert -d lab.derror.com \
--key-file       /home/work/local/cert/lab.derror.com/key.pem  \
--fullchain-file /home/work/local/cert/lab.derror.com/cert.pem \
--reloadcmd     "systemctl restart nginx"

[Wed Nov 21 04:21:57 EST 2018] Installing key to:/home/work/local/cert/lab.derror.com/key.pem
[Wed Nov 21 04:21:57 EST 2018] Installing full chain to:/home/work/local/cert/lab.derror.com/cert.pem
[Wed Nov 21 04:21:57 EST 2018] Run reload cmd: systemctl restart nginx
[Wed Nov 21 04:22:04 EST 2018] Reload success

$ openssl dhparam -out /home/work/local/cert/lab.derror.com/dhparam.pem 2048

nginx配置: lab.conf

server {
    listen       80;
    listen       443 ssl;
    server_name  lab.derror.com;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_certificate         /home/work/local/cert/lab.derror.com/cert.pem;
    ssl_certificate_key     /home/work/local/cert/lab.derror.com/key.pem;
    # ssl_dhparam
    ssl_dhparam             /home/work/local/cert/lab.derror.com/dhparam.pem;

    root         /home/work/local/www;
    index index.html index.htm;
    location / {
    }
}
$ systemctl restart nginx
34月/180

配置ab为Nginx服务器压力测试

发布在 邵珠庆

在运维工作中,压力测试是一项非常重要的工作。比如在一个网站上线之前,能承受多大访问量、在大访问量情况下性能怎样,这些数据指标好坏将会直接影响用户体验。
但是,在压力测试中存在一个共性,那就是压力测试的结果与实际负载结果不会完全相同,就算压力测试工作做的再好,也不能保证100%和线上性能指标相同。面对这些问题,我们只能尽量去想方设法去模拟。所以,压力测试非常有必要,有了这些数据,我们就能对自己做维护的平台做到心中有数。
目前较为常见的网站压力测试工具有webbench、ab(apache bench)、tcpcopy、loadrunner。
webbench由Lionbridge公司开发,主要测试每秒钟请求数和每秒钟数据传输量,同时支持静态、动态、SSL,部署简单,静动态均可测试。适用于小型网站压力测试(单例最多可模拟3万并发) 。
ab(apache bench)Apache自带的压力测试工具,主要功能用于测试网站每秒钟处理请求个数,多见用于静态压力测试,功能较弱,非专业压力测试工具。
tcpcopy基于底层应用请求复制,可转发各种在线请求到测试服务器,具有分布式压力测试功能,所测试数据与实际生产数据较为接近后起之秀,主要用于中大型压力测试,所有基于tcp的packets均可测试。
loadrunner压力测试界的泰斗,可以创建虚拟用户,可以模拟用户真实访问流程从而录制成脚本,其测试结果也最为逼真模拟最为逼真,并可进行独立的单元测试,但是部署配置较为复杂,需要专业人员才可以。
下面,笔者就以ab为例,来讲解一下网站在上线之前压力测试是如何做的。
ab是针对apache的性能测试工具,可以只安装ab工具。

ubuntu安装ab

apt-get install apache2-utils

centos安装ab

yum install httpd-tools

测试之前需要准备一个简单的html、一个php、一个图片文件。

分别对他们进行测试。

我们把这个三个文件放到nginx安装目录默认的html目录下,

自动草稿

准备之后我们就可以测试了

ab -kc 1000 -n 1000 http://localhost/ab.html

这个指令会使用1000个并发,进行连接1000次。结果如下

root@~# ab -kc 1000 -n 1000 http://www.nginx.cn/ab.html
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.nginx.cn (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: nginx/1.2.3
Server Hostname: www.nginx.cn
Server Port: 80

Document Path: /ab.html
Document Length: 192 bytes

Concurrency Level: 1000
Time taken for tests: 60.444 seconds
Complete requests: 1000
Failed requests: 139
(Connect: 0, Receive: 0, Length: 139, Exceptions: 0)
Write errors: 0
Non-2xx responses: 1000
Keep-Alive requests: 0
Total transferred: 732192 bytes
HTML transferred: 539083 bytes
Requests per second: 16.54 [#/sec] (mean)
Time per request: 60443.585 [ms] (mean)
Time per request: 60.444 [ms] (mean, across all concurrent requests)
Transfer
 rate: 11.83 [Kbytes/sec] received

Connection Times (ms)
min mean[ /-sd] median max
Connect: 55 237 89.6 261 328
Processing: 58 5375 13092.8 341 60117
Waiting: 57 5337 12990.0 341 59870
Total: 386 5611 13083.7 572 60443

Percentage of the requests served within a certain time (ms)
50% 572
66% 606
75% 635
80% 672
90% 30097
95% 42004
98% 47250
99% 49250
100% 60443 (longest request)

对于php文件和图片文件可以使用同样指令进行,结果我就不贴出来了。

ab -kc 500 -n 5000 http://localhost/ab.php

ab -kc 500 -n 5000 http://localhost/ab.gif

输出结果我们可以从字面意思就可以理解。

这里对两个比较重要的指标做下说明

比如

Requests per second: 16.54 [#/sec] (mean)
Time per request: 60443.585 [ms] (mean)

Requests per second: 16.54 [#/sec] (mean)

表示当前测试的服务器每秒可以处理16.54个静态html的请求事务,后面的mean表示平均。这个数值表示当前机器的整体性能,值越大越好。

Time per request: 60443.585 [ms] (mean)

单个并发的延迟时间,后面的mean表示平均。
隔离开当前并发,单独完成一个请求需要的平均时间。

顺带说一下两个Time per request区别

Time per request: 60443.585 [ms] (mean)
Time per request: 60.444 [ms] (mean, across all concurrent requests)

前一个衡量单个请求的延迟,cpu是分时间片轮流执行请求的,多并发的情况下,一个并发上的请求时需要等待这么长时间才能得到下一个时间片。
计算方法Time per request: 60.444 [ms] (mean, across all concurrent requests)*并发数

通俗点说就是当以-c 10的并发下完成-n 1000个请求的同时,额外加入一个请求,完成这个求平均需要的时间。

后一个衡量性能的标准,它反映了完成一个请求需要的平均时间,在当前的并发情况下,增加一个请求需要的时间。
计算方法Time taken for tests: 60.444 seconds/Complete requests: 1000

通俗点说就是当以-c 10的并发下完成-n 1001个请求时,比完成-n1000个请求多花的时间。
你可以适当调节-c 和-n大小来测试服务器性能,借助htop指令来直观的查看机器的负载情况。

我的机器是盛大云的超微主机,平时负载cpu是1.7%,htop命令结果截图

自动草稿

加压后的负载100%,负载基本已经上来了。htop命令结果截图

自动草稿

看来我需要好好优化一下,或者就换台机器了。

ab的参数详细解释
普通的测试,使用-c -n参数配合就可以完成任务
格式: ./ab [options] [http://]hostname[:port]/path
参数:
-n 测试的总请求数。默认时,仅执行一个请求
-c 一次并发请求个数。默认是一次一个。
-H 添加请求头,例如 ‘Accept-Encoding: gzip\',以gzip方式请求。
-t 测试所进行的最大秒数。其内部隐含值是-n 50000。它可以使对服务器的测试限制在一个固定的总时间以内。默认时,没有时间限制。
-p 包含了需要POST的数据的文件.
-T POST数据所使用的Content-type头信息。
-v 设置显示信息的详细程度 – 4或更大值会显示头信息, 3或更大值可以显示响应代码(404, 200等), 2或更大值可以显示警告和其他信息。 -V 显示版本号并退出。
-w 以HTML表的格式输出结果。默认时,它是白色背景的两列宽度的一张表。
-i 执行HEAD请求,而不是GET。
-C -C cookie-name=value 对请求附加一个Cookie:行。 其典型形式是name=value的一个参数对。此参数可以重复。

265月/170

Nginx使用的php-fpm的两种进程管理方式及优化

发布在 邵珠庆

背景
最近将Wordpress迁移至阿里云。由于自己的服务器是云服务器,硬盘和内存都比较小,所以内存经常不够使,通过Linux命令查看后,发现启动php-fpm进程数有20多个,占用了将近1G的内存,整个服务器才1.5G的内存,最后通过对php-fpm进程数优化解决了此问题,服务器多节省出600M的内存,将php-fpm的优化方法和大家分享下。
备注:目前根据nginx、fpm-php进行了内存优化,详情见相关资料
php-fpm优化

1、php-fpm优化参数介绍
他们分别是:pm、pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers。

pm:表示使用那种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)。
在更老一些的版本中,dynamic被称作apache-like。这个要注意看配置文件的说明。

下面4个参数的意思分别为:
pm.max_children:静态方式下开启的php-fpm进程数量
pm.start_servers:动态方式下的起始php-fpm进程数量
pm.min_spare_servers:动态方式下的最小php-fpm进程数
pm.max_spare_servers:动态方式下的最大php-fpm进程数量

区别:
如果dm设置为 static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。
如果dm设置为 dynamic,那么pm.max_children参数失效,后面3个参数生效。
系统会在php-fpm运行开始 的时候启动pm.start_servers个php-fpm进程,
然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数

2、服务器具体配置
对于我们的服务器,选择哪种执行方式比较好呢?事实上,跟Apache一样,运行的PHP程序在执行完成后,或多或少会有内存泄露的问题。
这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M的原因了。
对于内存大的服务器(比如8G以上)来说,指定静态的max_children实际上更为妥当,因为这样不需要进行额外的进程数目控制,会提高效率。
因为频繁开关php-fpm进程也会有时滞,所以内存够大的情况下开静态效果会更好。数量也可以根据 内存/30M 得到,比如8GB内存可以设置为100,
那么php-fpm耗费的内存就能控制在 2G-3G的样子。如果内存稍微小点,比如1G,那么指定静态的进程数量更加有利于服务器的稳定。
这样可以保证php-fpm只获取够用的内存,将不多的内存分配给其他应用去使用,会使系统的运行更加畅通。
对于小内存的服务器来说,比如256M内存的VPS,即使按照一个20M的内存量来算,10个php-cgi进程就将耗掉200M内存,那系统的崩溃就应该很正常了。
因此应该尽量地控制php-fpm进程的数量,大体明确其他应用占用的内存后,给它指定一个静态的小数量,会让系统更加平稳一些。或者使用动态方式,
因为动态方式会结束掉多余的进程,可以回收释放一些内存,所以推荐在内存较少的服务器或VPS上使用。具体最大数量根据 内存/20M 得到。
比如说512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服务器的负载情况来设置,比如服务器上只是部署php环境的话,比较合适的值在5~10之间。

本服务器配置

1、服务器基本信息:
硬盘:数据盘30G、系统盘20G
内存:1.5G
CPU:双核
系统:CentOS 6.3 64位
带宽:独享2M
2、部署的应用
Git、SVN、Apache、Tomcat、PHP、Nginx、Mysql、JDK
3、优化后的参数
pm = dynamic
pm.start_servers = 5
pm.min_spare_servers = 2
pm.max_spare_servers = 8

 

max_children=40 , 每个children平均占用20M-30M内存,children越多,可以同时接受的并发数量越多,一般children的值是网站最高并发数+浮动值,这值再×内存占用,就是你需要用到的内存。
max_requests = N 是指当每个children接受了N次请求以后,就会把自己杀死,然后重新建立一个children。
PV / max_children = 每一个children接受的request次数
比如上面的值是1000,而你定义的是10240,那么fpm要超过10天才能杀死children并重建,这样如果存在内存泄露的话,就会导致进程占用过多的内存而无法释放,从而使fpm的处理能力降低,还会产生一些莫名其妙的错误。
但是如果你把这个值设置的过小,fpm频繁的杀死children并重建,也会导致额外的开销。
最好的优化当然是根据你网站的运行情况,去不断的调试,找到一个平衡点。

139月/150

Nginx和PHP-FPM的启动/重启脚本

发布在 邵珠庆

服务器上的Nginx和PHP都是源码编译安装的,不支持类似以前的nginx (start|restart|stop|reload)了。自己动手丰衣足食。以下脚本应该在RHEL, Fedora, CentOS下都适用。

一、Nginx启动脚本/etc/init.d/nginx

#!/bin/bash
#
# Startup script for Nginx - this script starts and stops the nginx daemon
#
# chkconfig:   - 85 15
# description:  Nginx is an HTTP(S) server, HTTP(S) reverse proxy and IMAP/POP3 proxy server
# processname: nginx
# config:      /usr/local/nginx/conf/nginx.conf
# pidfile:     /usr/local/nginx/logs/nginx.pid
 
# Source function library.
. /etc/rc.d/init.d/functions
 
# Source networking configuration.
. /etc/sysconfig/network
 
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
 
nginx="/usr/local/nginx/sbin/nginx"
prog=$(basename $nginx)
 
NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"
 
[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
 
lockfile=/var/lock/subsys/nginx
 
start() {
    [ -x $nginx ] || exit 5
    [ -f $NGINX_CONF_FILE ] || exit 6
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}
 
stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}
 
restart() {
    configtest || return $?
    stop
    sleep 1
    start
}
 
reload() {
    configtest || return $?
    echo -n $"Reloading $prog: "
    killproc $nginx -HUP
    RETVAL=$?
    echo
}
 
force_reload() {
    restart
}
 
configtest() {
  $nginx -t -c $NGINX_CONF_FILE
}
 
rh_status() {
    status $prog
}
 
rh_status_q() {
    rh_status >/dev/null 2>&1
}
 
case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart|configtest)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
            ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
        exit 2
esac

#==============================================================

编辑好后保存,执行以下命令

sudo chmod +x /etc/init.d/nginx
sudo /sbin/chkconfig nginx on
# 检查一下
sudo /sbin/chkconfig --list nginx
nginx 0:off 1:off 2:on 3:on 4:on 5:on 6:off

完成!可以使用以下命令管理Nginx了

service nginx start
service nginx stop
service nginx restart
service nginx reload
 
/etc/init.d/nginx start
/etc/init.d/nginx stop
/etc/init.d/nginx restart
/etc/init.d/nginx reload

二、PHP-FPM启动脚本/etc/init.d/php-fpm

#!/bin/bash
#
# Startup script for the PHP-FPM server.
#
# chkconfig: 345 85 15
# description: PHP is an HTML-embedded scripting language
# processname: php-fpm
# config: /usr/local/php/etc/php.ini
 
# Source function library.
. /etc/rc.d/init.d/functions
 
PHP_PATH=/usr/local
DESC="php-fpm daemon"
NAME=php-fpm
# php-fpm路径
DAEMON=$PHP_PATH/php/sbin/$NAME
# 配置文件路径
CONFIGFILE=$PHP_PATH/php/etc/php-fpm.conf
# PID文件路径(在php-fpm.conf设置)
PIDFILE=$PHP_PATH/php/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
 
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
 
rh_start() {
  $DAEMON -y $CONFIGFILE || echo -n " already running"
}
 
rh_stop() {
  kill -QUIT `cat $PIDFILE` || echo -n " not running"
}
 
rh_reload() {
  kill -HUP `cat $PIDFILE` || echo -n " can't reload"
}
 
case "$1" in
  start)
        echo -n "Starting $DESC: $NAME"
        rh_start
        echo "."
        ;;
  stop)
        echo -n "Stopping $DESC: $NAME"
        rh_stop
        echo "."
        ;;
  reload)
        echo -n "Reloading $DESC configuration..."
        rh_reload
        echo "reloaded."
  ;;
  restart)
        echo -n "Restarting $DESC: $NAME"
        rh_stop
        sleep 1
        rh_start
        echo "."
        ;;
  *)
         echo "Usage: $SCRIPTNAME {start|stop|restart|reload}" >&2
         exit 3
        ;;
esac
exit 0

#=====================================================

编辑好后保存,执行以下命令

sudo chmod +x /etc/init.d/php-fpm
sudo /sbin/chkconfig php-fpm on
# 检查一下
sudo /sbin/chkconfig --list php-fpm
php-fpm 0:off 1:off 2:on 3:on 4:on 5:on 6:off

完成!可以使用以下命令管理php-fpm了

service php-fpm start
service php-fpm stop
service php-fpm restart
service php-fpm reload
 
/etc/init.d/php-fpm start
/etc/init.d/php-fpm stop
/etc/init.d/php-fpm restart
/etc/init.d/php-fpm reload

注意:里面的程序路径必须写对,这里用的都是默认的路径哟!而且对于php-fpm,默认的pid文件是没有设置的,要使用这个必须先在配置文件启用pid文件!~

183月/130

如何在nginx服务器下防盗链

发布在 邵珠庆

 

盗链是指一个网站的资源(图片或附件)未经允许在其它网站提供浏览和下载。
尤其热门资源的盗链,对网站带宽的消耗非常大,本文通过nginx的配置指令location来实现简单的图片和其它类型文件的防盗链。

Nginx 的配置文件 :

location ~ .(jpe?g|png|gif)$ {
     valid_referers none blocked mysite.com *.mysite.com;
     if ($invalid_referer) {
        return   403;
    }
}

用 (“|”) 来分隔你想保护的文件的扩展名。

valid_referers指令包含允许访问资源的网站列表,不在列表中请求的返回403。下面是valid_referers指令参数的解释 :

none - 匹配没有Referer的HTTP请求(Matches the requests with no Referer header).
blocked - 请求有Referer ,但是被防火墙或者代理服务器修改,去掉了https://或http:// (Matches the requests with blocked Referrer header).
*.mydomain.com - 匹配mysite.com的所有二级域名(Matches all the sub domains of mydomain.com. Since v0.5.33, * wildcards can be used in the server names).

除了使用location对文件访问进行限制,也可以对特定目录进行限制,下面的配置会禁止访问images目录下所有文件

location /images/ {
     valid_referers none blocked mysite.com *.mysite.com;
     if ($invalid_referer) {
        return   403;
    }
}

以上配置都是简单通过验证请求头来实现防盗链,如果盗链的网站通过伪造来路的http请求时不能屏蔽。

249月/120

nginx启动,重启,关闭命令

发布在 邵珠庆

停止操作

停止操作是通过向nginx进程发送信号(什么是信号请参阅linux文 章)来进行的
步骤1:查询nginx主进程号
ps -ef | grep nginx
在进程列表里 面找master进程,它的编号就是主进程号了。
步骤2:发送信号
从容停止Nginx:
kill -QUIT 主进程号
快速停止Nginx:
kill -TERM 主进程号
强制停止Nginx:
pkill -9 nginx

另外, 若在nginx.conf配置了pid文件存放路径则该文件存放的就是Nginx主进程号,如果没指定则放在nginx的logs目录下。有了pid文 件,我们就不用先查询Nginx的主进程号,而直接向Nginx发送信号了,命令如下:
kill -信号类型 '/usr/nginx/logs/nginx.pid'

平滑重启
如果更改了配置就要重启Nginx,要先关闭Nginx再打开?不是的,可以向Nginx 发送信号,平滑重启。
平滑重启命令:
kill -HUP 住进称号或进程号文件路径
或者使用

/usr/nginx/sbin/nginx -s reload

注意,修改了配置文件后最好先检查一下修改过的配置文件是否正 确,以免重启后Nginx出现错误影响服务器稳定运行。判断Nginx配置是否正确命令如下:
nginx -t -c /usr/nginx/conf/nginx.conf
或者

/usr/nginx/sbin/nginx -t
 
平滑升级
如果服务器正在运行的Nginx要进行升级、添加或删除模块时,我们需 要停掉服务器并做相应修改,这样服务器就要在一段时间内停止服务,Nginx可以在不停机的情况下进行各种升级动作而不影响服务器运行。
步骤1:
如 果升级Nginx程序,先用新程序替换旧程序文件,编译安装的话新程序直接编译到Nginx安装目录中。
步 骤2:执行命令
kill -USR2 旧版程序的主进程号或进程文件名
此时旧的Nginx主进程将会把自己的进程文件改名为.oldbin,然后执行新版 Nginx。新旧Nginx会同市运行,共同处理请求。
这时要逐步停止旧版 Nginx,输入命令:
kill -WINCH 旧版主进程号
慢慢旧的工作进程就都会随着任务执行完毕而退出,新版的Nginx的工作进程会逐渐取代旧版 工作进程。

此 时,我们可以决定使用新版还是恢复到旧版。
不重载配置启动新/旧工作进程
kill -HUP 旧/新版主进程号
从容关闭旧/新进程
kill -QUIT 旧/新主进程号
如果此时报错,提示还有进程没有结束就用下面命令先关闭旧/新工作进程,再关闭主进程号:
kill -TERM 旧/新工作进程号

这样下来,如果要恢复到旧版本,只需要上面的几个步 骤都是操作新版主进程号,如果要用新版本就上面的几个步骤都操作旧版主进程号就行了。

上面就是Nginx的一些基本的操作,希望以后Nginx能有更好的方法来处理这些操作, 最好是Nginx的命令而不是向Nginx进程发送系统信号。

1711月/100

解决Nginx + PHP(FastCGI)遇到的502 Bad Gateway错误

发布在 邵珠庆

由于服务器版本的不稳定,将Nginx的程序迁移到Apache执行

昨日,有朋友问我,他将Web服务器换成Nginx 0.6.31  + PHP 4.4.7(FastCGI)后,有时候访问会出现“502 Bad Gateway”错误,如何解决。
我让按照以下两个步骤去解决,最后在第2步中将FastCGI的timeout时间增加为300,问题解决:
PS:比较羡慕迅雷的Web服务器,16G内存。
1、查看当前的PHP FastCGI进程数是否够用:

netstat -anpo | grep "php-cgi" | wc -l
如果实际使用的“FastCGI进程数”接近预设的“FastCGI进程数”,那么,说明“FastCGI进程数”不够用,需要增大。


2、部分PHP程序的执行时间超过了Nginx的等待时间,可以适当增加nginx.conf配置文件中FastCGI的timeout时间,例如:

......
http
{
......
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
......
}
......