标签归档:nginx

Nginx location 匹配顺序整理

Nginx环境

a. 查看当前系统cat /etc/redhat-release

[root@nginx /]# cat /etc/redhat-release

CentOS release 6.7 (Final)

[root@nginx /]#

b. 查看系统内核uname –r

[root@nginx /]# uname -r

2.6.32-573.el6.x86_64

[root@nginx /]#

c. 安装的Nginx版本,/appliation/nginx/sbin/nginx -V

[root@nginx sbin]# ./nginx -V

nginx version: nginx/1.6.2

built by gcc 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)

TLS SNI support enabled

configure arguments: –user=nginx –group=nginx –prefix=/application/nginx1.6.2 –with-http_stub_status_module –with-http_ssl_module

[root@nginx sbin]#

location模块

Nginx location

location 指令的作用是根据用户请求的URI来执行不同的应用,URI就是根据用户请求到的网址URL进行匹配,匹配成功了进行相关的操作。

location语法

下面是官网的语法结构:

Syntax:    location [ = | ~ | ~* | ^~ ] uri { … }

location @name { … }

Default:   —

Context:   server, location

官网解释翻译和理解

下面会结合官网原文进行解释,以下英文部分均从官网摘抄:

http://nginx.org/en/docs/http/ngx_http_core_module.html#location

(翻译的不好勿喷)

Sets configuration depending on a request URI.

根据请求的URI进行配置

URI 变量是待匹配的请求字符串,

A location can either be defined by a prefix string, or by a regular expression.

Regular expressions are specified with the preceding “~*” modifier (for case-insensitive matching), or the “~” modifier (for case-sensitive matching)

一个location可以用prefix string(前缀字符串)定义,也可以通过regular expression(正则表达式来定义)

通俗的说也就是:我们可以通过使用不同的前缀,表达不同的含义,对于不同的前缀可以分为两大类:普通location和正则location

符号:”~”表示uri包含正则,并且区分大小写

符号:“~*”表示uri包含正则,但不区分大小写

注意:如果你的uri用正则,则你的正则前面必须添加~或者~*,之前我在这里存在误区,以为可以不加~或者~*

To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.

Nginx服务器会首先会检查多个location中是否有普通的uri匹配,如果有多个匹配,会先记住匹配度最高的那个。然后再检查正则匹配,这里切记正则匹配是有顺序的,从上到下依次匹配,一旦匹配成功,则结束检查,并就会使用这个location块处理此请求。如果正则匹配全部失败,就会使用刚才记录普通uri匹配度最高的那个location块处理此请求。

If the longest matching prefix location has the “^~” modifier then regular expressions are not checked.

当普通匹配的最长前缀匹配有符号“^~”的时候,就不会在匹配正则

直接使用当前匹配的这个location块处理此请求

Also, using the “=” modifier it is possible to define an exact match of URI and location. If an exact match is found, the search terminates. For example, if a “/” request happens frequently, defining “location = /” will speed up the processing of these requests, as search terminates right after the first comparison. Such a location cannot obviously contain nested locations.

使用符号“=”修饰符可以定义一个精确匹配的URI和位置,如果找到了一个精确的匹配,则搜索终止,例如,如果一个”/”请求频繁发生,定义“location =/”将加快这些请求的处理,一旦精确匹配只有就结束,这样的location显然不能包含嵌套location

这里我们说一下location / {} 和location =/ {}的区别:

“location / {}”是普通的最大前缀匹配,任何的uri肯定是以“/”开头,所以location / {} 可以说是默认匹配,当其他都不匹配了,则匹配默认匹配

根据上述官网内容进行总结

a. ”=”用于普通uri前,要求精确匹配,如果匹配成功,则停止搜索并用当前location处理此请求

b. ”~” 表示uri包含正则,并且区分大小写

c. “~*”表示uri包含正则,但不区分大小写

d. ”^~”表示在普通uri前要求Nginx服务器找到普通uri匹配度最高的那个location后,立即处理此请求,并不再进行正则匹配

e. ”^~”和“=”都可以阻止继续匹配正则location两者的区别:“^~”依然遵守最大前缀原则,然后“=”是需要严格匹配

关于location网上的一些误解

location 的匹配顺序是“先匹配正则,再匹配普通”

这是一个错误的结论,从上面官网的文章中我们可以知道:

先匹配普通uri,然后记住匹配度最高的那个(官网原话:To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered.)然后匹配正则,如果正则匹配则结束查找,如果正则不匹配,则匹配之前普通匹配中匹配度最高的那个将执行该请求(官网原话:Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.)

所以:location 的匹配顺序是“先匹配正则,再匹配普通” 这句话肯定是错误的,况且这里并没有包含”^~”和“=”

location 的执行逻辑跟 location 的编辑顺序无关。

这也是一种错误的理解,我们根据上述内容可以知道:

如果是普通uri 匹配,这个时候是没有顺序的,但是正则匹配则是有顺序的,是从上到下依次匹配,一旦有匹配成功,则停止后面的匹配。

那么顺序到底是怎么匹配呢?

我画了一个location匹配的逻辑图便于理解匹配的顺序规则

通过实验来验证出结果

对www.conf配置如下:

[root@nginx extra]# cat www.conf

    server {

        listen       80;

        server_name  www.zhaofan.com;

        access_log      logs/access_www.log;

        root    html/www;

        location / {

            return 401;

        }

        location = / {

            return 402;

        }

        location  /documents/ {

            return 403;

        }

        location  ^~ /images/ {

            return 404;

        }

        location ~* \.(gif|jpg|jpeg)$ {

            return 500;

        }

    }

[root@nginx extra]#

注意:

        location ~* \.(gif|jpg|jpeg)$ {

            return 500;

这个部分$前面不能有空格,否则会提示如下错误:

[root@nginx extra]# ../../sbin/nginx -s reload

nginx: [emerg] invalid location modifier “~*\.(gif|jpg|jpeg)” in /application/nginx1.6.2/conf/extra/www.conf:19

如果$后面没有空格,则会提示如下错误:

[root@nginx extra]# ../../sbin/nginx -s reload

nginx: [emerg] directive “location” has no opening “{” in /application/nginx1.6.2/conf/extra/www.conf:23

这些都是细节问题,一定要注意

实验一:登录nginx网站,我这里的直接打开:http://192.168.8.105/

可以看出这里是精确匹配

        location = / {

            return 402;

        }

实验二:打开http://192.168.8.105/aaa/

这里可以看出因为都不匹配,所以最后匹配了location / {}

        location / {

            return 401;

        }

实验三:打开http://192.168.8.105/1.gif

这里可以看出是匹配了正则

        location ~* \.(gif|jpg|jpeg)$ {

            return 500;

        }

实验四:打开http://192.168.8.105/aaa/1.gif

这里依然是匹配正则

        location ~* \.(gif|jpg|jpeg)$ {

            return 500;

        }

实验五:打开http://192.168.8.105/images/1.gif

        location / {

            return 401;

        }

        location = / {

            return 402;

        }

        location  /document/ {

            return 403;

        }

        location  ^~ /images/ {

            return 404;

        }

        location ~* \.(gif|jpg|jpeg)$ {

            return 500;

        }

这里通过配置把实验三和实验四的对比就可以看出来因为普通匹配里有“^~”,并且匹配到了images,所以这个就是不进行正则匹配

        location  ^~ /images/ {

            return 404;

        }

实验六:“^~”遵守最大前缀原则

配置如下:

        location / {

            return 401;

        }

        location = / {

            return 402;

        }

        location  = /document/ {

            return 403;

        }

        location  ^~ /images/ {

            return 404;

        }

        location   /images/1/ {

            return 501;

        }

        location ~* \.(gif|jpg|jpeg)$ {

            return 500;

        }

还是关注标红的地方,这个时候我们登陆:http://192.168.1.19/images/

结果如下:

从这里可以看出匹配了:

        location  ^~ /images/ {

            return 404;

        }

但是如果我们登陆:http://192.168.1.19/images/1/

结果如下;

这里匹配了:

        location   /images/1/ {

            return 501;

        }

从这里我们可以看出“^~”遵守最大匹配原则。

实验七:当最长匹配和精确匹配相同时

配置如下:

        location / {

            return 401;

        }

        location = / {

            return 402;

        }

        location  = /document/ {

            return 403;

        }

        location  ^~ /images/ {

            return 404;

        }

        location   /images/1/ {

            return 501;

        }

        location  =  /images/1/ {

            return 502;

        }

        location ~* \.(gif|jpg|jpeg)$ {

            return 500;

        }

登陆:http://192.168.1.19/images/1/

结果如下:

但是如果这个时候登陆:http://192.168.1.19/images/1/aaa/

结果如下:

从这里我们可以看出当精确匹配和最长匹配相同时,匹配的是精确匹配。

不在过多的做实验,根据上面的逻辑图应该可以解决碰到的问题了所有的努力都值得期许,每一份梦想都应该灌溉!

原文:https://www.cnblogs.com/zhaof/p/5945576.html

Linux使用unzip命令解压其中的部分文件到指定文件夹

相信很多人都遇到过Candy的情况:

公司电商平台备份后文件大小多达10个G!海量小文件、图片、js、css等等……;

这造成了一种情况,如果不小心删除了一个文件而无法恢复的情况! 那就只能从备份文件中提取咯! 但是一想到为了提取几KB的一个文件,而unzip整个压缩文件。



请谨记以下命令:

unzip <Your zip file> “*mobile/要解压的文件” -d <要解压的目录>

如:unzip www.dnsdizhi.com.zip “Nginx/* ” Nginx/

* : 可以使用*做通配符,具体使用我就不废话了!
-d : -d 参数后面跟上你要解压文件到哪个目录;

使用 Nginx 实现灰度发布

灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。

灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。

灰度发布常见一般有三种方式:

  • Nginx+LUA方式
  • 根据Cookie实现灰度发布
  • 根据来路IP实现灰度发布

本文主要将讲解根据Cookie和来路IP这两种方式实现简单的灰度发布,Nginx+LUA这种方式涉及内容太多就不再本文展开了。

A/B测试流程

Nginx根据Cookie实现灰度发布

根据Cookie查询Cookie键为version的值,如果该Cookie值为V1则转发到hilinux_01,为V2则转发到hilinux_02。Cookie值都不匹配的情况下默认走hilinux_01所对应的服务器。

两台服务器分别定义为:

1 2 
hilinux_01  192.168.1.100:8080 hilinux_02  192.168.1.200:8080 
  • 用if指令实现
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 
upstream hilinux_01 {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  upstream hilinux_02 {  server 192.168.1.200:8080 max_fails=1 fail_timeout=60; }  upstream default {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  server {  listen 80;  server_name  www.hi-linux.com;  access_log  logs/www.hi-linux.com.log  main;   #match cookie  set $group "default";  if ($http_cookie ~* "version=V1"){  set $group hilinux_01;  }   if ($http_cookie ~* "version=V2"){  set $group hilinux_02;  }   location / {   proxy_pass http://$group;  proxy_set_header   Host             $host;  proxy_set_header   X-Real-IP        $remote_addr;  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;  index  index.html index.htm;  }  } 
  • 用map指令实现

在Nginx里面配置一个映射,$COOKIE_version可以解析出Cookie里面的version字段。$group是一个变量,{}里面是映射规则。

如果一个version为V1的用户来访问,$group就等于hilinux_01。在server里面使用就会代理到http://hilinux_01上。version为V2的用户来访问,$group就等于hilinux_02。在server里面使用就会代理到http://hilinux_02上。Cookie值都不匹配的情况下默认走hilinux_01所对应的服务器。

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 
upstream hilinux_01 {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  upstream hilinux_02 {  server 192.168.1.200:8080 max_fails=1 fail_timeout=60; }  upstream default {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }   map $COOKIE_version $group { ~*V1$ hilinux_01; ~*V2$ hilinux_02; default default; }  server {  listen 80;  server_name  www.hi-linux.com;  access_log  logs/www.hi-linux.com.log  main;   location / {   proxy_pass http://$group;  proxy_set_header   Host             $host;  proxy_set_header   X-Real-IP        $remote_addr;  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;  index  index.html index.htm;  }  } 

Nginx根据来路IP实现灰度发布

如果是内部IP,则反向代理到hilinux_02(预发布环境);如果不是则反向代理到hilinux_01(生产环境)。

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 
upstream hilinux_01 {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  upstream hilinux_02 {  server 192.168.1.200:8080 max_fails=1 fail_timeout=60; }  upstream default {  server 192.168.1.100:8080 max_fails=1 fail_timeout=60; }  server {  listen 80;  server_name  www.hi-linux.com;  access_log  logs/www.hi-linux.com.log  main;    set $group default;  if ($remote_addr ~ "211.118.119.11") {  set $group hilinux_02;  }  location / {   proxy_pass http://$group;  proxy_set_header   Host             $host;  proxy_set_header   X-Real-IP        $remote_addr;  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;  index  index.html index.htm;  } } 

如果你只有单台服务器,可以根据不同的IP设置不同的网站根目录来达到相同的目的。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 
server {  listen 80;  server_name  www.hi-linux.com;  access_log  logs/www.hi-linux.com.log  main;   set $rootdir "/var/www/html";  if ($remote_addr ~ "211.118.119.11") {  set $rootdir "/var/www/test";  }   location / {  root $rootdir;  } } 

到此最基本的实现灰度发布方法就讲解完了,如果要做更细粒度灰度发布可参考ABTestingGateway项目。

ABTestingGateway是新浪开源的一个动态路由系统。ABTestingGateway是一个可以动态设置分流策略的灰度发布系统,工作在7层,基于nginx和ngx-lua开发,使用redis作为分流策略数据库,可以实现动态调度功能。

ABTestingGateway:https://github.com/CNSRE/ABTestingGateway

https://www.hi-linux.com/posts/34319.html

nginx+php-fpm,使用Unix Socket还是tcp方式连接?

将Nginx与FastCGI的通信方式由TCP改为Unix Socket。TCP在高并发访问下比Unix Socket稳定,但Unix Socket速度要比TCP快”,看来这是真的存在。两者各有优缺点啊

1.worker_processes 越大越好(一定数量后性能增加不明显)


2.worker_cpu_affinity 所有cpu平分worker_processes 要比每个worker_processes 都跨cpu分配性能要好;不考虑php的执行,测试结果worker_processes数量是cpu核数的2倍性能最优

3.unix domain socket(共享内存的方式)要比tcp网络端口配置性能要好
不考虑backlog,请求速度有量级的飞跃,但错误率超过50%
加上backlog,性能有10%左右提升

4.调整nginx、php-fpm和内核的backlog(积压),connect() to unix:/tmp/php-fpm.socket failed (11: Resource temporarily unavailable) while connecting to upstream错误的返回会减少
nginx:
配置文件的server块
listen 80 default backlog=1024;

php-fpm:
配置文件的
listen.backlog = 2048

kernel参数:
/etc/sysctl.conf,不能低于上面的配置
net.ipv4.tcp_max_syn_backlog = 4096
net.core.netdev_max_backlog = 4096

5.增加单台服务器上的php-fpm的master实例,会增加fpm的处理能力,也能减少报错返回的几率
多实例启动方法,使用多个配置文件:
/usr/local/php/sbin/php-fpm -y /usr/local/php/etc/php-fpm.conf &
/usr/local/php/sbin/php-fpm -y /usr/local/php/etc/php-fpm1.conf &

nginx的fastcgi配置
    upstream phpbackend {
#      server   127.0.0.1:9000 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9001 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9002 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9003 weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm1.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm2.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm3.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm4.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm5.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm6.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm7.sock weight=100 max_fails=10 fail_timeout=30;
    }

        location ~ \.php* {
            fastcgi_pass   phpbackend;
#           fastcgi_pass   unix:/var/www/php-fpm.sock;
            fastcgi_index index.php;
       ……….
       }

6.测试环境和结果

内存2G
swap2G
cpu 2核 Intel(R) Xeon(R) CPU E5405  @ 2.00GHz
采用ab远程访问测试,测试程序为php的字符串处理程序


1)在开4个php-fpm实例,nginx 8个worker_processes 每个cpu4个worker_processes ,backlog为1024,php的backlog为2048,内核backlog为4096,采用unix domain socket连接的情况下,其他保持参数不变

性能和错误率较为平衡,可接受,超过4个fpm实例,性能开始下降,错误率并没有明显下降
结论是fpm实例数,worker_processes数和cpu保持倍数关系,性能较高
影响性能和报错的参数为
php-fpm实例,nginx worker_processes数量,fpm的max_request,php的backlog,unix domain socket


10W请求,500并发无报错,1000并发报错率为0.9%

500并发:
Time taken for tests:   25 seconds avg.
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    4000 [#/sec] (mean) avg.
Time per request:       122.313 [ms] (mean)
Time per request:       0.245 [ms] (mean, across all concurrent requests)
Transfer rate:          800 [Kbytes/sec] received avg.

1000并发:
Time taken for tests:   25 seconds avg.
Complete requests:      100000
Failed requests:        524
   (Connect: 0, Length: 524, Exceptions: 0)
Write errors:           0
Non-2xx responses:      524
Requests per second:    3903.25 [#/sec] (mean)
Time per request:       256.197 [ms] (mean)
Time per request:       0.256 [ms] (mean, across all concurrent requests)
Transfer rate:          772.37 [Kbytes/sec] received

2)在其他参数不变,unix domain socket换为tcp网络端口连接,结果如下

500并发:
Concurrency Level:      500
Time taken for tests:   26.934431 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    3712.72 [#/sec] (mean)
Time per request:       134.672 [ms] (mean)
Time per request:       0.269 [ms] (mean, across all concurrent requests)
Transfer rate:          732.37 [Kbytes/sec] received

1000并发:
Concurrency Level:      1000
Time taken for tests:   28.385349 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    3522.94 [#/sec] (mean)
Time per request:       283.853 [ms] (mean)
Time per request:       0.284 [ms] (mean, across all concurrent requests)
Transfer rate:          694.94 [Kbytes/sec] received

与1)比较,有大约10%的性能下降

7. 5.16调整fpm的max_request参数为1000,并发1000报错返回降到200个以下,
Transfer rate在800左右

使用nginx配置多个php-fastcgi负载均衡

配置还是非常简单的,充分体现了nginx的强大与配置的简单。

应用的最前端是一台nginx服务器,所有静态的内容都由nginx来处理,而将所有php的 请求都分摊到下游的若干台

运行PHP fastcgi守护进程的服务器中,这样可以以一种廉价的方案来实现对系统负载的分摊,扩展系统的负载能力。

三台php-fastcgi服务器的ip地址分别为:

          172.16.236.110 ,   172.16.236.111,     172.16.236.112

运行php-fastcgi进程时,需要让php-cgi监听到服务器的局域网地址(分别如上所示),而不是之前一般都是监听的

本地地址(127.0.0.1)。

以 172.16.236.110这台服务器为例:

 
/usr/local/php5/bin/php-cgi -b 172.16.236.110:9000

或许你用spawn-fcgi来启动php-fcgi,那么就是这样(供参考,其实也就是修改监听的地址和端口即可):

 
/usr/local/lighttpd/bin/spawn-fcgi -f /usr/local/php5/bin/php-cgi -a 172.16.236.110 -p 9000

又或许你是用php-fpm来管理php-fcgi,那么你需要修改php-fpm的配置:

 
vim  /usr/local/php5/etc/php-fpm.conf

找到这个配置项(其中的地址可能需要根据你自己环境来调整)



<value< span=”” style=”word-wrap: break-word;”> name=”listen_address“>127.0.0.1:9000>

修改为:

<value< span=”” style=”word-wrap: break-word;”> name=”listen_address>172.16.236.110:9000>



修改完毕后,重启你的php-fpm进程。

然后按照上面的步骤,依次修改其他php fastcgi服务器。

php方面的工作暂时就是这些,下面修改nginx。

 
vim  /usr/local/nginx/conf/nginx.conf

在配置文件的http段内增加类似如下的配置:

1
2
3
4
5
upstream myfastcgi { server 172.16.236.110 weight=1; server 172.16.236.111 weight=1; server 172.16.236.112 weight=1; }

我这里三台php fastcgi服务器的权重是相同的,所以其中的weight值都是1,如果你的php fastcgi服务器需要分主次,那么

可以通过调整其weight值来达到目的。比如以第一台服务器为主,其他两台为辅,则就是这样:

1
2
3
4
5
upstream myfastcgi { server 172.16.236.110 weight=1; server 172.16.236.111 weight=2; server 172.16.236.112 weight=2; }

然后找到原来nginx关于php fastcgi配置的部分,比如:

1
2
3
4
5
6
location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }

将其中的fastcgi_pass那一段改为:



fastcgi_pass myfastcgi;

其中的myfastcgi也就是上面刚刚配置的php fastcgi均衡器的名字了。

完了以后,重启nginx即可。

简单吧,就通过这么几个简单的配置,就可以实现一个经济高效的nginx、多php-fcgi的负载均衡解决方案了。

当然了,这样的方案运用到实际项目中 还需要进行一些细化的配置,主要是php方面还需要进一步配置

Caddy nginx服务器QUIC部署

Caddy 简介

Caddy是一个Go语言写的,易于使用的通用Web服务器。它具有如下的一些功能:

  • 配置简单:Caddy服务器的运行可以通过Caddyfile配置文件进行配置,Web服务配置起来非常简单。
  • 自动的HTTPS:它可以自动地为我们申请 Let’s Encrypt 域名证书,管理所有的密码学设施,并进行配置。
  • HTTP/2:默认支持HTTP/2(由Go标准库支持)
  • 虚拟主机托管:Caddy支持TLS的SNI。SNI是在2006年加入TLS的一个TLS扩展。客户端在TLS握手的Client Hello消息中,通过SNI扩展将请求的资源的域名发送给服务器,服务器根据SNI的域名来下发TLS证书。这样就可以在具有单个公网IP的同一台主机上部署多个不同的域名的服务。可以为Caddy服务的不同域名配置不同的证书和密钥。
  • QUIC支持:Caddy实验性地支持QUIC协议,以获取更好的性能。
  • TLS session ticket key rotation for more secure connections
  • 良好的可扩展性:因此Caddy非常方便针对自己的需求做定制。
  • 随处运行:这主要与Go应用程序的特性有关。Go的模块都被编译为静态库,这使得Go的应用程序在编译为可执行文件时都是静态链接的,因而依赖的动态库极少,这使得部署使用非常方便。

自动的HTTPS、HTTP/2支持、QUIC支持和随处运行这些特性非常有吸引力,特别是对QUIC的支持。

此外,Caddy的性能非常好。下面两幅图是我的静态个人博客站点,分别是用Caddy和nginx作为Web服务器,打开主页所需的加载时间对比:
Service with Caddy

Service with nginx

上面的图显示了以Caddy作为Web服务器,主页的加载时间只有680ms;下面的图显示以nginx作为Web服务器,主页的加载时间则长达1.99s,要慢接近2倍。

Caddy部署

Caddy应用程序不依赖于其它组件,且官方已经为不同的平台提供了二进制可执行程序。可以通过如下三种方式之一安装Caddy:

  • 在 下载页,通过浏览器定制自己需要的功能集,并下载相应的二进制可执行程序。
  • 预编译的 最新发行版 二进制可执行程序。
  • curl getcaddy.com 来自动安装:curl https://getcaddy.com | bash

将caddy的路径加如PATH环境变量中。之后可以 cd 进入网站的文件夹,并运行 caddy来提供服务。默认情况下,Caddy在2015端口上为网站提供服务。

要定制网站提供服务的方式,可以为网站创建名为Caddyfile的文件。当运行 caddy 命令时,它会自动地在当前目录下寻找并使用Caddyfile文件来为自己做配置。

要了解更多关于Caddyfile文件的写法,可以参考 Caddyfile 文档

注意生产环境网站默认是通过HTTPS提供服务的。

Caddy还有命令行接口。运行caddy -h 可以查看基本的帮助信息,或参考 CLI文档 来了解更多详情。

以Root运行:建议不要这样做。但依然可以通过像这样使用setcap来监听端口号小于1024的端口:sudo setcap cap_net_bind_service=+ep ./caddy

由源码运行

注意:需要安装 Go 1.7或更新的版本才可以。

  1. go get github.com/mholt/caddy/caddy
  2. cd 进入网站的目录
  3. 执行caddy(假设 $GOPATH/bin 已经在 $PATH 中了)

Caddy的 main() 再caddy子目录下。要编译Caddy,可以使用在那个目录下找到的 build.bash

在生产环境运行

Caddy项目官方不维护任何系统特有的集成方法,但下载的文档中包含了社区共享的 非官方资源,用以帮助在生产环境运行Caddy。

以何种方式运行Caddy全由自己决定。许多用户使用 nohup caddy & 就可以满足需求了。其他人使用 screen。有些用户需要再重启之后就运行Caddy,可以在触发重启的脚本中来做到这一点,通过给init脚本添加一个命令,或给操作系统配置一个service。

可以看一下我的个人博客站点的完整Caddyfile内容:

					
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
					
wolfcstech.com:80 www.wolfcstech.com:80 {
root /home/www-data/www/hanpfei-documents/public
redir 301 {
if {>X-Forwarded-Proto} is http
/ https://{host}{uri}
}
}
wolfcstech.com:443 www.wolfcstech.com:443 {
tls /home/www-data/www/ssl/chained.pem /home/www-data/www/ssl/domain.key
#tls test@admpub.com
root /home/www-data/www/hanpfei-documents/public
gzip
log ../access.log
}

启用QUIC

Caddy 0.9 已经实验性地提供了对QUIC的支持,这主要通过 lucas-clemente/quic-go 来实现。要尝试这个特性,可以在运行caddy时加上 -quic 标记:

					
1
					
$ caddy -quic

这样执行之后,则带有TLS加密的Web服务,在客户端支持QUIC时,将默认通过QUIC协议来完成数据的传输。

不启用QUIC时,在启动caddy之后,在服务器端查看已打开的端口号:

					
1
2
3
4
5
6
7
					
# lsof -i -P
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
AliYunDun 1120 root 10u IPv4 2023899 0t0 TCP 139.196.224.72:40309->106.11.68.13:80 (ESTABLISHED)
. . . . . .
caddy 6163 root 6u IPv6 2338478 0t0 TCP *:80 (LISTEN)
caddy 6163 root 8u IPv6 2338479 0t0 TCP *:443 (LISTEN)
. . . . . .

而在通过如下命令:

					
1
					
# nohup ./caddy -quic &

启用QUIC提供Web服务之后,在服务器端查看已打开端口号,则可以看到如下内容:

					
1
2
3
4
5
6
7
8
9
					
# lsof -i -P
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
AliYunDun 1120 root 10u IPv4 2023899 0t0 TCP 139.196.224.72:40309->106.11.68.13:80 (ESTABLISHED)
. . . . . .
caddy 6222 root 6u IPv6 2338880 0t0 TCP *:80 (LISTEN)
caddy 6222 root 8u IPv6 2338881 0t0 TCP *:443 (LISTEN)
caddy 6222 root 9u IPv6 2338883 0t0 UDP *:80
caddy 6222 root 10u IPv6 2338885 0t0 UDP *:443
. . . . . .

Caddy 除了监听http的TCP 80端口和https 的TCP 443端口之外,还监听了UDP的80和443端口。

客户端支持

Chrome 52+ 支持QUIC而无需白名单,但需要确认 #enable-quic 标记已经被启用了。通过在Chrome浏览器的地址栏输入chrome://flags/

Enable QUIC

并根据需要启用QUIC。

然后通过Chrome打开你的网站,则它应该是以QUIC提供服务的!可以通过打开inspector 工具并进入Security tab来验证这一点。重新加载页面并点击来查看连接详情:

caddy005.png

如果你使用老版的Chrome,则为了省事,可以升级一下。

如果你不想升级,则可以:你将需要以特殊的参数来运行Chrome。再Mac上 (将YOUR_SITE替换为你的网站的实际域名)执行如下命令:

					
1
2
3
4
5
					
$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--user-data-dir=/tmp/chrome \
--no-proxy-server \
--enable-quic \
--quic-host-whitelist="YOUR_SITE" "YOUR_SITE"

QUIC的好处

QUIC是基于UDP的TLS+HTTP的可靠传输协议。它加速了TLS握手为只有一个往返,避免了TCP慢启动,并提供了网络切换时的可靠性。通过QUIC可以让网站加载更快且更可靠。

问题排解

首先,确保在Caddyfile文件中为域名做了适当的设置,还要确保在启动Chrome的命令行中为域名做了适当的设置。

接着,网站必须使用一个真实的可信的证书(至少,是在写的时候)。

如果那都是好的,而且你对Go语言比较了解,则你可以添加 import "github.com/lucas-clemente/quic-go/utils",并在Caddy的main()函数的某个地方调用utils.SetLogLevel(utils.LogLevelDebug)。那将提供非常详细的输出。(注意这个log设施不是一个公共的API)。

当你进入chrome://net-internals/#events,你应该看到一些QUIC事件被标为红色。

Net Events

坚持原创技术分享,您的支持将鼓励我继续创作!

https://www.wolfcstech.com/2017/01/09/Caddy%20Web%E6%9C%8D%E5%8A%A1%E5%99%A8QUIC%E9%83%A8%E7%BD%B2/

nginx 对文件(动态页面) 进行缓存 ( nginx content caching)

refer to: http://nginx.com/resources/admin-guide/caching/ 首先看一个完整的例子:

  proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:100m
                       loader_threshold=300 loader_files=200 max_size=200m;
  server {
          listen 80;
          server_name  www.tidev.in tidev.in;
          client_max_body_size 500m;
          charset utf-8;
          location / {
              proxy_hide_header "cache-control";
              proxy_hide_header Expires;

              proxy_ignore_headers Set-Cookie;
              proxy_ignore_headers Cache-Control;
              proxy_ignore_headers Expires;
              proxy_ignore_headers X-Accel-Expires;

              proxy_cache cache_one;
              proxy_cache_valid any 60s; # 任何内容,都缓存60秒钟 proxy_pass          http://tidev_servers; proxy_redirect default;
              proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header    X-Real-IP $remote_addr;
              proxy_set_header    Host $http_host;
              proxy_next_upstream http_502 http_504 error timeout invalid_header;

              proxy_cache_key $host$uri$is_args$args;
          }
          location ~ ^/assets/ {
              root /opt/app/tidev.in/public;
              expires 1y;
              add_header Cache-Control public;
              add_header ETag ""; break;
          }
  }

  upstream tidev_servers{
         server localhost:3600;
         server localhost:3601;
         server localhost:3602;
         server localhost:3603;
  }

注意:  

1. nginx 建议使用 1.7.9 以上版本, 亲测 1.7.9, 1.8.0 可用. 1.4.x 不可用. 所以,不要使用ubuntu 默认的 apt-get install 方式的nginx. 它是1.4的.

2. proxy_cache_path 务必出现在 proxy_cache 关键字之前, (也就是说,如果你用了include sites-enabled/*; , 那么,这句话要放在 proxy_cache_path之后!) 否则会报错: nginx: [emerg] the size 10485760 of shared memory zone “cache_one” conflicts with already declared size 0  

3. proxy_cache_key 是关键. 

          location / { proxy_hide_header "cache-control"; proxy_hide_header Expires; proxy_ignore_headers Set-Cookie; proxy_ignore_headers Cache-Control; proxy_ignore_headers Expires; proxy_ignore_headers X-Accel-Expires; proxy_cache cache_one; proxy_cache_valid any 60s; # 任何内容,都缓存60秒钟 proxy_pass http://tidev_servers; proxy_redirect default; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_next_upstream http_502 http_504 error timeout invalid_header; proxy_cache_key $host$uri$is_args$args;
          }

nginx可以对某个请求进行缓存,

例子:

http {
    ...
    proxy_cache_path /data/nginx/cache keys_zone=one:10m; server {
        proxy_cache one;
        location / {
            proxy_pass http://localhost:8000;
        }
    }
}

设置好了允许缓存后,进一步可以设置它的过期时间: (iteration 如何解释。。需要动手弄一下)

参与cache 过程 的,有两个角色, cache manager 和 cache loader:

1. cache manager 会循环的检查 cache的状态。当它发现 缓存的文件超过了 max_size 这个数目后,就会删掉最少访问的cache page. 

2. cache loader: 仅仅在nginx启动后 随之启动一次。它把之前的cache 信息加载到 shared memory中去。这在nginx启动的前几分钟会拖累nginx的速度。

以上的iteration, 比 loader_threshold(默认是200ms) 要少。 每次加载的文件数目小于 loader_files(默认是100),每个iteration 间隔 loader_sleeps (默认50ms)。 

下面是个例子: 

proxy_cache_path /data/nginx/cache keys_zone=one:10m
                 loader_threshold=300 loader_files=200; 

指定某个URL 要缓存

如果某个response来自 proxy_server, 并且request是 GET/HEAD 方法,则nginx 默认会把它做缓存. 

而且默认使用的key就是 url ,你也可以指定这个key, 例如:

proxy_cache_key “$host$request_uri$cookie_user”;

如果我们希望某个 url 至少被请求5次之后才被缓存,就这样:

proxy_cache_min_uses 5;

如果希望对POST和 DELETE进行缓存:

proxy_cache_methods GET HEAD POST;

下面的例子:对于 200 , 302的response, 缓存 10分钟, 

proxy_cache_valid 200 302 10m;  # 对于 200, 302,缓存10分钟
proxy_cache_valid 404 1m;       # 缓存1分钟 
proxy_cache_valid any 10m;      # 对于所有的响应,都缓存10分钟。

也可以根据条件来判断是否使用cache: ( cookie 中的变量:nocache, parameter中的变量:nocache 或者 comment, 只要有一个 不是空,也不是 0, 那么这个request就不会使用cache) 

proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;

对于下面的例子:压根就不使用cache:

proxy_no_cache $http_pragma $http_authorization;

下面是一个更大的例子:

http {
    ... # 定义了一个 proxy_cache_path, : proxy_cache_path /data/nginx/cache keys_zone=one:10m
                     loader_threshold=300 loader_files=200 max_size=200m; # 这个server中有两个backend, 对应两种不同的cache策略 server {
        listen 8080; # cache的名字叫做 one (注意上面的 keys_zone=one:10m) proxy_cache one; # 对所有的 / 请求,都尽可能长久的缓存,不存在过期 location / {
            proxy_pass http://backend1; }

        
        location /some/path {
            proxy_cache_valid any 1m; # 任何内容,都缓存1分钟 proxy_cache_min_uses 3; # 访问3次后,触发缓存 proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment; # 设置好不使用缓存的规则 proxy_pass http://backend2; }
    }
}

注意: 如何调试呢?

1. 要设置log format, 把日志打印出来. 例如,配置文件为: (注意其中的 $upstream_cache_status, 这个变量最重要, 从它我们可以知道, 是HIT 还是MISS )

    log_format my_format '$remote_addr - $remote_user [$time_local]  ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" $upstream_cache_status'; access_log logs/my_access.log my_format;

2. 要有对应的 ignore headers, 如果后端返回的结果中,增加了 cache-control (也有一说是 set-cookie) 或者 啥的,就不行了. 

server{
            proxy_ignore_headers "cache-control";
            proxy_hide_header "cache-control";
} 

下面是一个完整的 nginx.conf例子;

http{ # 其他内容  proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:10m
                     loader_threshold=300 loader_files=200 max_size=200m; log_format my_format '$remote_addr - $remote_user [$time_local]  ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" $upstream_cache_status';

    access_log logs/my_access.log my_format;

    server {
        listen 80; 

        location / { 
            proxy_ignore_headers "cache-control";
            proxy_hide_header "cache-control";
            proxy_cache cache_one;
            proxy_cache_valid any 10s; # 任何内容,都缓存10秒钟 proxy_pass http://rails_api;
        }

    }
    upstream rails_api{
        server localhost:3000;
    }

缓存用的哪些文件?

我们可以在 proxy_cache_path中设置, 例如:

    proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:10m loader_threshold=300 loader_files=200 max_size=200m;

然后, 找到 /tmp/nginx_cache 目录, 如果某个 cache被命中过, 就会看到出现一个以md5 结果命名的文件:

:/tmp/nginx_cache$ ll
total 20 drwxrwxrwx 2 nobody sg552 4096 Sep 10 11:39 ./
drwxrwxrwt 10 root   root 12288 Sep 10 11:38 ../
-rw------- 1 nobody nogroup 594 Sep 10 11:39 f8924891f34a941a8342ccd19c4cf290 

上面中, 这个文件 “f89…” 就是缓存文件. 它的内容如下.

���U���������Ud����0""b4945c5f2d4b62faae53f44f44a5e946"
KEY: http://rails_api/prices/say_hi
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
ETag: "b4945c5f2d4b62faae53f44f44a5e946" Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 90fbc91b-e5a4-4279-8832-5d484cba7ba8
X-Runtime: 0.007120 Connection: close Server: thin 1.6.2 codename Doc Brown time is: 2015-09-10 11:39:58 +0800 

可以看出, 该静态文件, 以文本的形式缓存了 所有的response信息.


来源;http://siwei.me/blog/posts/nginx-nginx-content-caching

Nginx配置支持HTTP2

从 2015 年 5 月 14 日 HTTP/2 协议正式版的发布到现在已经快有一年了,越来越多的网站部署了 HTTP2,HTTP2 的广泛应用带来了更好的浏览体验,只要是 Modern 浏览器都支持,所以部署 HTTP2 并不会带来太多困扰。

虽然 h2 有 h2c (HTTP/2 Cleartext) 可以通过非加密通道传输,但是支持的浏览器初期还是比较少的,所以目前部署 h2 还是需要走加密的,不过由于 Let’s Encrypt 大力推行免费证书和证书的廉价化,部署 h2 的成本并不高。

介绍

HTTP 2.0即超文本传输协议 2.0,是下一代HTTP协议。是由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis (httpbis)工作小组进行开发。是自1999年http1.1发布后的首个更新。

HTTP/2 协议是从 SPDY 演变而来,SPDY 已经完成了使命并很快就会退出历史舞台(例如 Chrome 将在「2016 年初结束对 SPDY 的支持」;Nginx、Apache 也已经全面支持 HTTP/2 ,并也不再支持 SPDY)。

一般的大家把 HTTP2 简称为 h2,尽管有些朋友可能不怎么愿意,但是这个简称已经默认化了,特别是体现在浏览器对 HTTP2 都是这个简写的。

配置

普通的 HTTPS 网站浏览会比 HTTP 网站稍微慢一些,因为需要处理加密任务,而配置了 h2 的 HTTPS,在低延时的情况下速度会比 HTTP 更快更稳定!

现在电信劫持事件频发,网站部署了 HTTPS 加密后可以杜绝大部分劫持,但不是完全。像电子商务行业对 HTTPS 加密可是标配啊,因此部署 h2 更是势在必行。

证书

这里是 免费和便宜 SSL 证书介绍 ,大家可以从这里购买或者申请免费的 SSL 证书,免得 Chrome 弹出红色的页面令人不悦,从而拒绝了大多数访客。

Web 服务器

说明

默认编译的 Nginx 并不包含 h2 模块,我们需要加入参数来编译,截止发文,Nginx 1.9 开发版及以上版本源码需要自己加入编译参数,从软件源仓库下载的则默认编译。 Tengine 可以同时部署 h2 和 SPDY 保证兼容性,Nginx 则是一刀切不再支持 SPDY。

安装/编译

如果你编译的 Nginx 不支持,那么在 ./configure 中加入:--with-http_v2_module ,如果没有 SSL 支持,还需要加入 --with-http_ssl_module

然后 make && make install 即可。

配置

主要是配置 Nginx 的 server 块, 。
修改相关虚拟机的 .conf 文件,一般在 /usr/local/nginx/conf/vhost/ 或者 /etc/nginx/conf/,具体参考你的环境指导,不懂请回复。

server { listen 443 ssl http2 default_server; server_name www.mf8.biz; ssl_certificate /path/to/public.crt; ssl_certificate_key /path/to/private.key; 

注:将 server_name www.mf8.biz; 中的 www.mf8.biz 替换为你的域名。

然后通过 /usr/local/nginx/sbin/nginx -t 或者 nginx -t 来检测是否配置正确,然后重启 Nginx ,即可。

检验

在 Chrome 浏览器上可以通过,HTTP/2 and SPDY indicator 来检验,如果地址栏出现蓝色的闪电就是 h2

也可以在 chrome://net-internals/#http2 中检查。注意版本要新,姿势要帅!

配置进阶

大家都知道去年的心血漏洞将 SSL 推到了风口浪尖,所以单单支持了 h2 ,我们任然需要对 SSL 做一些安全的优化!

配置赫尔曼密钥

openssl dhparam -out dhparam.pem 2048 // 在 ssh 运行, openssl 生成 2048 位的密钥而不是当作参数写入 nginx.conf 文件。 ssl_dhparam /path/to/dhparam.pem; //在 .conf 中配置 

禁止不安全的 SSL 协议,使用安全协议

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

禁止已经不安全的加密算法

ssl_ciphers
'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4';

也可以参考:ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;

缓解 BEAST 攻击

ssl_prefer_server_ciphers on;``` **启用 HSTS**

此举直接跳过 301 跳转,还降低了中间人攻击的风险!配置在 .conf 中即可 `add_header Strict-Transport-Security max-age=15768000;` **301 跳转** 80 端口跳转到 443 端口

server {
listen 80;
add_header Strict-Transport-Security max-age=15768000;
return 301 https://www.yourwebsite.com$request_uri;
}

 **缓存连接凭据** 

ssl_session_cache shared:SSL:20m;
ssl_session_timeout 60m;

 **OCSP 缝合** 

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/cert/trustchain.crt;
resolver 119.29.29.29 233.6.6.6 valid=300s;

nginx支持http2,openssl 升级openssl-1.1.0h https A+

首先升级openssl 到 openssl-1.1.0h

wget https://www.openssl.org/source/openssl-1.1.0h.tar.gz

tar -zxvf openssl-1.1.0h.tar.gz
cd openssl-1.1.0h

./config –prefix=/usr

make

make install

openssl version -a

nginx配置参考:

listen 443 ssl http2;
add_header Strict-Transport-Security max-age=15768001;
ssl_certificate /usr/local/nginx/conf/vhost/Nginx/1_www.dnsdizhi.com_bundle.crt;
ssl_certificate_key /usr/local/nginx/conf/vhost/Nginx/2_www.dnsdizhi.com.key;
ssl_session_cache    shared:SSL:10m;
ssl_session_timeout  10m;
ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;
ssl_prefer_server_ciphers   on;

完毕后可以用:https://www.ssllabs.com/ssltest/  或者 https://myssl.com/ 检查就是能达到A+,当然其实达到A就不错了。如facebook、v.qq.com、www.google.com都是A。区别在哪里?就是没有配置add_header Strict-Transport-Security max-age=15768001;,说真的就是不用配置的,没必要。