标签为 "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…

Unknown: Input variables exceeded 1000;nginx在post大量字段时部分字段被丢弃

今天有运营同事反馈在使用一个内部的运营工具时,有些操作失败,后来抓包发现,post到服务端的数据是正常的,在服务端接受到的数据却并不完整,有缺失。很是奇怪。看了下nginx的error log,发现问题所在了,原来是php有设置最大接受变量个数。

2014/04/02 18:06:33 [error] 23623#0: *1115 FastCGI sent in stderr: "PHP message: PHP Warning:  Unknown: Input variables exceeded 1000. To increase the limit change max_input_vars in php.ini. in Unknown on line 0" while reading response header from upstream, client: 10.18.120.25,

所以,需要做的就是修改下php.ini中的设置:max_input_vars ,默认没有开启,默认值是1000,修改为自己合适的值,就可以了。

linux下vim实现php远程调试环境搭建:vim DBGp+xdebug+xdebug helper实现方便的远程开启Debugging, profiling & tracing

首先是必需组件的安装。
有三个,vim的DBGp插件,php的xdebug扩展,Chrome浏览器的xdebug helper扩展。

一: vim 安装 dbgp client 插件
下载 DBGp client

$ cd ~/.vim/
$ wget http://www.vim.org/scripts/download_script.php?src_id=7285 -O debugger.zip
$ unzip debugger.zip

二:PHP安装xdebug扩展
具体安装方法就不讲了,网上例子很多.官方安装说明.
我把我的配置信息贴出来:
sudo vim /home/s/apps/php-5.2.6/etc/include/xdebug.ini

[Xdebug]
zend_extension="/home/s/apps/php-5.2.6/lib/php/extensions/no-debug-non-zts-20060613/xdebug.so"
xdebug.profiler_enable=0    ;开启profile
xdebug.profiler_enable_trigger=1    ;使用触发器开启profile
xdebug.profiler_output_dir="/home/s/logs/xdebugLog"
xdebug.auto_trace=0     ;开启trace
xdebug.trace_enable_trigger=1   ;使用触发器开启trace
xdebug.trace_output_dir="/home/s/logs/xdebugLog"
xdebug.cli_color=1
xdebug.collect_params=2
xdebug.collect_return=on

;remote debug   ;开启远程调试
xdebug.remote_enable=1
xdebug.remote_autostart=Off
xdebug.remote_port=9000	;远程端口,不是你web服务的端口,如果没有冲突,一般不用修改。

其中需要注意的是有注释的地方,不自动开启profile和trace,因为这样每次请求都会保存相应信息,会把磁盘爆掉的。把触发器打开,这样,只有在请求的时候带特定信息的时候,才会记录相应的性能信息。触发器的实现机制是:在请求时,如果GET/POST/COOKIE信息中包含特定的变量,会开启对应的debug,profile或trace,所以我们可以通过在web中访问页面时,添加对应的变量来开启相应的功能。如果每次都自己拼接这个,也挺麻烦,所以会介绍后边的xdebug helper的chrome扩展,想开启任何一个功能的时候,点击一下扩展就可以了。也就是说,这个扩展不是调试必须的,但是会让你感觉用起来很方便。

三:chrome扩展xdebug helper
地址:https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc
如果在 chrome://apps里搜索xdebug helper搜索不到,记得把你的语言改为 English.

xdebug-helper-introduction20140226142342

xdebug-helper-introduction20140226142342


xdebug helper两处配置要注意,
IDE Key要注意选择Other, 值为1,然后保存。
第二个是domain白名单,如果不希望每个页面都出调试的debug icon,就把开发时的域名填进来,比如localhost神马的。其实icon很小,也不太碍事,可以不用设置。

开始使用
如果不要debug调试,只是需要记录profile和trace信息,看一下性能问题,那么很简单,直接在chrome访问对应的url,会在url右侧看到一个xdebg helper 的小icon,点击后,选择 profile或trace,再刷新,就可以在预先设置的目录见到对应的文件了。
cachegrind* 是profile的文件,trace* 是trace的文件。
在windows下使用WinCacheGrind可以方便的查看profile的信息。

xdebug_helper_debug_profile_trace

xdebug_helper_debug_profile_trace

xdebug_output_file_trace_profile

xdebug_output_file_trace_profile

winCacheGrind_output

winCacheGrind_output

最后,是如何开启远程调试.
用vim编辑程序文件,在需要中断的地方使用:Bp命令打上断点。然后按F5执行监听。

这个时候,vim会提示 waiting for a new connection on port 9000 for 5 seconds… ,所以这个时候你需要在5s内向服务端发起带中断的请求,在前边的url地址,我们把xdebug helper的debug选择上,刷新页面,vim就会进入中断了。右侧有相关提示,我们就可以按GDB的方式逐步调试啦。如果没有安装xdebug helper,也无所谓,自己手动在url上拼接一个参数: &XDEBUG_SESSION_START=1 也可以开启远程调试的。

xdebug_remote_debug_how

xdebug_remote_debug_how

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].”;