分类 "Apache/Nginx" 的存档.

openresty/nginx 中如何替换正文字符串

最近搞了个海外的阿里云主机后,就把自己的个人站迁移了。然后就想办法做优化,用上了openresty + php7 + http2。
因为历史原因,自己搞了几个不同的域名,一个是fukun.org,还有一个之前一直用的digdeeply.org,现在想统一起来,可是却发现wordpress里有很多上传附件的时候,链接已经是写死的,使用的当时的域名digdeeply.org,如果去一个一个的把他们替换过来,就不太现实了。
因为自己用过openresty中的lua,所以首先想到的就是使用一个content_by_lua来实现替换,在这个阶段把内容中字符串替换掉,不过有个细节问题就是这里lua拿到的都是nginx的一个一个的chunk,如果想要替换的字符串正好在两个chunk的分界处,那么这个就会被漏掉而不能替换。
然后在查一些资料的时候,突然发现,其实,nginx自己就有支持字符串替换的模块的: sub_filter , 这样就不用自己在lua写了,会更方便一些,那么先来看看sub_filter怎么用吧。

location / {
    sub_filter 'https://fukun.org'  'https://fukun.org';
    sub_filter 'https://fukun.org'  'https://fukun.org';
    sub_filter_once off;
}

用法很简单,只要使用 sub_filter ‘search’ ‘replacement’ ,把被替换和替换内容给出来就可以了,可以写多条规则,替换多次。 其它的几个参数稍微看下文档也能明白了:

语法: sub_filter_once on | off;
默认值: sub_filter_once on;
配置段: http, server, location 字符串替换一次还是多次替换,默认替换一次。如果全文中有多个查找到的字符串,希望全部替换的话,就开启这个选项。

语法: sub_filter_types mime-type …;
默认值: sub_filter_types text/html;
配置段: http, server, location 指定需要被替换的MIME类型,默认为“text/html”,如果制定为*,那么所有类型的内容都会被替换,我们其实一般来说,只希望替换text/html中的就可以了,因为如果是图片啥的,你想替换啥呢?

如何安装sub_filter功能模块http_sub_module

以上是sub_filter的用法,但是如果你已经安装好nginx服务了,而且之前没有安装sub_filter模块,那怎么办呢,总不能把nginx全部重新编译重装吧,那样会覆盖之前的nginx配置,还得再来配置一遍。
可以用另外一种方法来更新nginx,那就是只从新编译,而不安装。就是只执行 ./configure 和 make 方法,而不执行make install,这样就不会有问题了。然后把make编译好的nginx二进制文件拷贝到已安装的nginx sbin目录下覆盖之前的,覆盖之前请做好备份,并停止当前运行的nginx。
我是用的openresty,所以很多configure的参数不用自己加,第一次我编译的时候,因为需要http2和status模块, 只执行了:

./configure --prefix=/home/s/apps/openresty-1.9.7.1 --with-http_v2_module --with-http_ssl_module --with-luajit --with-http_stub_status_module

现在需要sub_filter功能,所以只要添加–with-http_sub_module就可以了。那么就是这么编译:

./configure --prefix=/home/s/apps/openresty-1.9.7.1 --with-http_v2_module --with-http_ssl_module --with-luajit --with-http_stub_status_module --with-http_sub_module

然后把build/nginx-xxx/objs目录下的nginx二进制文件复制走去用就可以了。

nginx下载apk文件变为zip的解决办法

nginx下载apk文件变为zip的解决办法

    include mime.types;
    types{application/vnd.android.package-archive apk;}
    location ~* .(apk)$ {
   		 add_header Content-Type application/vnd.android.package-archive;
	}

nginx referer限制

语法:valid_referers [none|blocked|server_names]
使用字段:server, location
这个指令在referer头的基础上为 $invalid_referer 变量赋值,其值为0或1。
可以使用这个指令来实现防盗链功能,如果valid_referers列表中没有Referer头的值, $invalid_referer将被设置为1。
参数可以使如下形式:
none 意为不存在的Referer头(表示空的,也就是直接访问,比如直接在浏览器打开一个图片)
blocked 意为根据防火墙伪装Referer头,如:“Referer: XXXXXXX”。
server_names 为一个或多个服务器的列表,0.5.33版本以后可以在名称中使用“*”通配符。

举例:

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

Read more…

lua技能点:根据特殊字符分割字符串;使用Redis进行hmget

最近的谈谈项目使用了部分lua,用于一些请求量超高的请求,在nginx层面挡回去,省的调用大量php进程。
由于以前没用过lua,开发过程中遇到了一些小技能点,记录一下。

1.lua字符串分割函数

 --字符串分割函数,按|分割
 function lua_string_split(str, split_char)
     local sub_str_tab = {};
     for mu_id in string.gmatch(str, "(%d+)|*") do
         table.insert(sub_str_tab, mu_id)
     end
     return sub_str_tab;
 end
 --字符串分割函数END

2.使用lua的reids 的 批量获取方法。 比如:hmget
先将所有的field整合到一个Table, 比如: field_table里。

redis:hmget(key,  unpack(field_table))

Read more…

nginx if判断 and 使用

360图片搜索遇到这么一个场景,pc端的搜索结果页和频道列表页,在移动端访问时,检测到是移动版的ua,会自动跳转到对应的移动端结果页或频道列表页。
现在,pc端新上了一个频道,服饰频道,但是移动端还没开发。当在移动端访问pc版的服饰频道页时,会跳转到移动端的频道页,但是没有服饰频道,所以跳转到了默认的美女频道,体验不好。
所以,就需要做一个处理,当发现是服饰频道时,不跳转,保持pc端的展示。

之前的nginx配置是判断ua后,就决定是否跳转。

if ($http_user_agent ~* "^((.*android.*)|(.*Mobile Safari.*)|(.*windows phone os.*)|acer|zte|lenovo|moto|samu|nokia|sony|kindle|240x320|mobile|mmp|ucweb|midp|pocket|psp|symbian|smartphone|treo    |up.browser|up.link|vodafone|wap)") {
         rewrite "^/$"              http://m.image.so.com/ permanent;
         rewrite "^/([z|i])$"       http://m.image.so.com/$1 permanent;
     }

现在需要对频道页判断,而由于频道页是通过参数传入,而不是在path里边,所以使用rewrite规则无法解决。
其实就可以使用$request_uri判断就可以了。本来是打算在if里通过 and 或者 && 解决,结果发现不支持。
查了资料后,发现一个方便实现的方法,通过两次判断,设置变量的形式。
如下:

    if ($http_user_agent ~* "^((.*android.*)|(.*Mobile Safari.*)|(.*windows phone os.*)|acer|zte|lenovo|moto|samu|nokia|sony|kindle|240x320|mobile|mmp|ucweb|midp|pocket|psp|symbian|smartphone|treo    |up.browser|up.link|vodafone|wap)") {
         set $mobile 1;
     }
     if ($request_uri ~* ".*ch=fushi.*"){
         set $mobile 0;
     }
     if ($mobile = 1){
         rewrite "^/$"              http://m.image.so.com/ permanent;
         rewrite "^/([z|i])$"       http://m.image.so.com/$1 permanent;
     }

Read more…

Cannot assign requested address 出现原因及解决方案

今天在压测时,会偶尔遇到错误,错误提示: Cannot assign requested address ,看了下,大致上是由于客户端频繁的连服务器,由于每次连接都在很短的时间内结束,导致很多的TIME_WAIT,以至于用光了可用的端 口号,所以新的连接没办法绑定端口,即“Cannot assign requested address”。是客户端的问题不是服务器端的问题。通过netstat,的确看到很多TIME_WAIT状态的连接。

解决办法是需要做一下内核参数优化:(需要root权限)
sysctl -w net.ipv4.tcp_timestamps=1 开启对于TCP时间戳的支持,若该项设置为0,则下面一项设置不起作用
sysctl -w net.ipv4.tcp_tw_recycle=1 表示开启TCP连接中TIME-WAIT sockets的快速回收

改完还不能解决问题,需要修改tcp_max_tw_buckets
sudo sh -c “echo ‘5000’> /proc/sys/net/ipv4/tcp_max_tw_buckets”

nginx client intended to send too large body 却没有触发error_page 413

在做以图搜图时,需要用户上传图片,同时需要对用户上传图片做大小限制。
如果文件全部上传到服务器了,可以用$_FILES变量来获取文件信息,判断文件大小,来决定是否拒绝用户的请求。
不过当文件过大时,会直接出发nginx的413 Request Entity Too Large 错误,如何友好的来提示用户呢。
这里就可以用到nginx的error_page用法了,直接指定413的错误跳转到一个指定页面,在指定页面内友好提示即可。

client_max_body_size 3M; #限定nginx允许上传的文件大小

#错误提示页面,指定哪个错误跳转到哪个页面,url可以是一个Action的路径
error_page 404 /404.html;
error_page 413 /stu?a=error413;
error_page 500 502 503 504 /50x.html;

这样设置了之后,就可以使用了,当上传的文件过大时,会直接被nginx拦截到413错误,进而重定向到友好的错误提示页。
不过在一次bug修复之后,发现再提交过大的文件时,没有出现这个提示页面了,而是等了很长时间之后出现了一个500错误,而且500错误也没有被拦截到50x的错误页面,很是奇怪。排查了下nginx日志,出现的错误提示就是 client intended to send too large body: 4224579 bytes.

排查了一会之后,再测试环境下打开错误输出,发现原来是413重定向后的Action在视图里有php语法错误。修复之后,就又恢复正常啦。

client intended to send too large body

client intended to send too large body

Nginx Gzip 压缩配置 IE6禁用

gzip(GNU-ZIP)是一种压缩技术。经过gzip压缩后页面大小可以变为原来的30%甚至更小,这样,用户浏览页面的时候速度会块得多。gzip的压缩页面需要浏览器和服务器双方都支持,实际上就是服务器端压缩,传到浏览器后浏览器解压并解析。

Nginx的压缩输出有一组gzip压缩指令来实现。相关指令位于http{….}两个大括号之间。

gzip on;
//该指令用于开启或关闭gzip模块(on/off)

gzip_min_length 1k;
//设置允许压缩的页面最小字节数,页面字节数从header头得content-length中进行获取。默认值是0,不管页面多大都压缩。建议设置成大于1k的字节数,小于1k可能会越压越大。

gzip_buffers 4 16k;
//设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。4 16k代表以16k为单位,安装原始数据大小以16k为单位的4倍申请内存。

gzip_http_version 1.1;
//识别http的协议版本(1.0/1.1)

gzip_comp_level 2;
//gzip压缩比,1压缩比最小处理速度最快,9压缩比最大但处理速度最慢(传输快但比较消耗cpu)

gzip_types text/plain application/x-javascript text/css application/xml
//匹配mime类型进行压缩,无论是否指定,”text/html”类型总是会被压缩的。
gzip_vary on;
//和http头有关系,加个vary头,给代理服务器用的,有的浏览器支持压缩,有的不支持,所以避免浪费不支持的也压缩,所以根据客户端的HTTP头来判断,是否需要压缩.

同时由于IE6不支持gizp解压缩,所以在IE6下要关闭gzip压缩功能。使用
gzip_disable “MSIE [1-6].”;

nginx 配置gzip段如下:
gzip on;
gzip_min_length 1k;
gzip_buffers 16 64k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;
gzip_disable “MSIE [1-6].”;

http请求头信息:If-Modified-Since

If-Modified-Since是标准的HTTP请求头标签,在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。
在你的请求中发送一个 If-Modified-Since 头信息,它包含了上一次从服务器连同数据所获得的日期。如果数据从那时起没有改变,服务器将返回一个特殊的 HTTP 状态代码 304,这意味着 “从上一次请求后这个数据没有改变”。
当服务器发送状态编码 304 时,不再重新发送数据。您仅仅获得了这个状态代码。所以当数据没有更新时,你不需要一次又一次地下载相同的数据;服务器假定你有本地的缓存数据。

前端机超时3s ,9s

这几天遇到一个很奇怪的问题,搜索的前端机经常会有一些超时的请求,以为是业务哪块效率不行导致的。后来就写了一个php,只做 echo 1 操作,结果在大量curl时,还是会出先超时的情况,而且仔细分析后,出现的超时要么是比正常时间多3秒整,要么是比正常时间多9s整,只有这两种可能,没有其它的超时时间,这就更奇怪了,。查了一下资料,3s和9s的超时,是网络问题引起的。
Mysterious 3 and 9 second delays calling connect()

If a client tries to
establish a TCP connection to a server but the server does not
respond, then the client tries again after 3 seconds, then once again
after 6 more seconds. The number of retry attempts is configurable by
changing tcp_syn_retries ("sysctl net.ipv4.tcp_syn_retries," "man 7
tcp" for description).

20131126174519-backlog-3s-nginx

nginx php 3s 9s timeout delay

nginx php 3s 9s timeout delay

再底层的东西也咱现在也不了解啊。。还得慢慢学啊,可是问题还得解决啊。。
于是把问题抛给了op的同事,,最后解决了。。
解决办法:
修改 /usr/local/nginx/conf.d/default.conf 的 backlog=8192 后,超时3秒、9秒的问题得到验证解决。
修改了nginx的默认的backlog参数。
具体原因是啥,现在也不懂,记录下来,以后慢慢参悟吧。