Nginx-Perl配置使用入门


原生的Nginx并不支持动态语句的解析,它通过改良过的cgi方式来实现对后端动态语言的支持,像fastcgi、scgi、uwsgi或直接代理到后端的动态解析服务器。对于内嵌式的Nginx-Perl支持,需要在其安装时指定相关的编译选项(虽然发行版本中提供有nginx-full、nginx-extras等包声称支持Perl,但测试后并不那么好,推荐使用编译方法)。
通过在内嵌Perl的作用下,就可以在nginx层面上做一些在后端才能做的事情,但这种使用方法可能影响nginx的处理速度,同时在控制服务器时,只能通过重启来实现,而不能像reload等柔和的方式控制服务器了。
在configure时指定相关的编译选项:
--with-http_stub_status_module
--with-http_perl_module
--with-http_addition_module
--with-http_realip_module
--with-http_sub_module
nginx中使用perl有两种方法,一种是直接在配置文件中写,还有一种是把perl脚本写在外部文件中,下面主要介绍一下第二种用法。
假设nginx的根目录为/opt/nginx,perl脚本存放的目录为其根目录下的perl/lib下,使用了perl模块后,尽量避免进行reload操作(kill -HUP pid 会造成使用perl模块让nginx内存泄露这个风险)。使用perl的代码越短越好,尽量降低nginx的性能消耗。
#位于http配置中
perl_modules /opt/nginx/lib;
perl_require Helloworld.pm;
#位于server配置中
location /nphandler {
perl Helloworld::handler;
}
location /nprocess {
perl Helloworld::process;
}
location /nparam {
perl Helloworld::param;
}
location /npost {
perl Helloworld::phandler;
}
这样将把所有来自http://server_name/the_location/下的请求交由Helloworld.pm脚本中定义的handler方法来处理。
该模包内容如下:
package Helloworld;
use strict;
use warnings;
use nginx;
use Time::Piece;
use Data::Dumper;
no warnings 'uninitialized';
sub handler {
my $r = shift;
my $ua = $r->header_in("User-Agent");
$r->send_http_header("text/html");
return OK if $r->header_only;
$r->print("hello,$ua\n<br/>");
$r->print($r->remote_addr);
$r->rflush;
if (-f $r->filename or -d _) {
$r->print($r->uri, " exists!\n");
}
return OK;
}
sub process {
my $r = shift;
$r->send_http_header('text/html; charset=utf-8');
my @arr = split('/', $r->uri);
my $user = $arr[2];
if (!$user || ($user eq "")){
$user = "Anonymous";
}
$r->print('Hello, You name is : <b>'.$user.'</b>');
$r->rflush();
return;
}
sub param {
my $r = shift;
$r->send_http_header("text/html");
#取得参数的 Map
my $param = $r->args;
#获得具体参数并解析
my %params;
if(length($param)>0){
my @pairs = split(/&/, $param);
foreach my $pair (@pairs){
my ($name, $value) = split(/=/, $pair);
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$params{$name} = $value;
}
}
#回显
$r->print('Hello: '.$params{user});
return OK;
}
##handle post
sub phandler {
my $r = shift;
if($r->request_method ne "POST"){
#如果不是post请求直接过掉不处理
return 403;
}
if($r->has_request_body(\&postme)){
#存在request body内容,则对body数据进行处理
return OK;
}
return 400;
}
sub postme {
my $r = shift;
my $url = '/def.html';
if($r->request_body eq 'isfreeoa=1'){
#可对body内容进行字符串的相关处理
$url = '/index.html';
}
#根据相关的post数据进行相关的内部重定向
$r->send_http_header;
#$r->print($url);
#$r->internal_redirect($url);
#$r->rflush;
$r->print("request_body: \"", $r->request_body, "\"<br/>");
$r->print("request_body_file: \"", $r->request_body_file, "\"<br/>\n");
return OK;
}
1;
__END__
在浏览器中返回访问都的浏览器Tag及ip地址:
http://172.18.0.2:2012/nphandler
hello,Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0 Iceweasel/28.0
172.18.0.2
http://172.18.0.2:2012/nparam?user=freeoa&password=loveus
Hello: freeoa
http://172.18.0.2:2012/nprocess/freeoa
Hello, You name is : freeoa
http://172.18.0.2:2012/npost
可以用curl来进行post测试,
$ curl -d "isfreeoa=1" http://172.18.0.2:2012/npost
request_body: "isfreeoa=1"<br/>request_body_file: ""<br/>
$ curl "http://172.18.0.2:2012/npost?isfreeoa=1"
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.6.2</center>
</body>
</html>
内置变量参考
Nginx原生的内置全局变量,可以访问这里。
当使用 use nginx 时,会有如下的对象可以调用,可以看到上面 shift 一个对象到 $r 上,然后就可以用 $r 调用那些对象了:
$r->args – 请求的参数
$r->discard_request_body – 这个参数是让 Nginx 放弃 request 的 body 的内容
$r->filename – 返回合适的请求文件的名字
$r->has_request_body(function) – 如果没有请求主体,返回0,但是如果请求主体存在,那么建立传递的函数并返回1,在程序的最后,nginx将调用指定的处理器
$r->header_in(header) – 查找请求头的信息
$r->header_only – 如果我们只要返回一个响应的头
$r->header_out(header, value) – 设置响应的头
$r->internal_redirect(uri) – 使内部重定向到指定的URI,重定向仅在完成perl脚本后发生.可以使用 header_out(Location….的方法来让浏览器自己重定向
$r->print(args, …) – 发送数据给客户端
$r->request_body – 得到客户端提交过来的内容(body 的参数,可能需要修改 nginx 的 client_body_buffer_size. )
$r->request_body_file —给客户的 body 存成文件,并返回文件名
$r->request_method — 得到请求 HTTP method
$r->remote_addr – 得到客户端的 IP 地址
$r->rflush – 立即传送数据给客户端
$r->sendfile(file [, displacement [, length ] ) – 传送给客户端指定文件的内容,可选的参数表明只传送数据的偏移量与长度,精确的传递仅在perl脚本执行完毕后生效
$r->send_http_header(type) – 添加一个回应的 http 头的信息
$r->sleep(milliseconds, handler) – 设置为请求在指定的时间使用指定的处理方法和停止处理,在此期间nginx将继续处理其他的请求,超过指定的时间后,nginx将运行安装的处理方法,注意你需要为处理方法通过一个reference,在处理器间转发数据你可以使用$r->variable().
$r->status(code) – 设置 http 的响应码
$r->unescape(text) – 使用 http 方法加密内容如%XX
$r->uri – 得到请求的 URL
$r->variable(name[, value]) – 设置变量的值
Nginx.conf 内置变量示例
server{
set $CONTEXT_PATH '/perl'; # uri的访问路径,如:http://np.freeoa.net/perl
set $APPLICATION_PATH '/opt/nginx/'; #板块文件路径,在该路径下需要放置一个lib目录,内置处理模块
server_name localhost;
location /perl { # 此处需与$CONTEXT_PATH保持一致
perl FreeOA::exec; #调用对应的处理方法
}
}
通过在内嵌Perl的作用下,就可以在nginx层面上做一些在后端才能做的事情,但这种使用方法可能影响nginx的处理速度,同时在控制服务器时,只能通过重启来实现,而不能像reload等柔和的方式控制服务器了。
在configure时指定相关的编译选项:
--with-http_stub_status_module
--with-http_perl_module
--with-http_addition_module
--with-http_realip_module
--with-http_sub_module
nginx中使用perl有两种方法,一种是直接在配置文件中写,还有一种是把perl脚本写在外部文件中,下面主要介绍一下第二种用法。
假设nginx的根目录为/opt/nginx,perl脚本存放的目录为其根目录下的perl/lib下,使用了perl模块后,尽量避免进行reload操作(kill -HUP pid 会造成使用perl模块让nginx内存泄露这个风险)。使用perl的代码越短越好,尽量降低nginx的性能消耗。
#位于http配置中
perl_modules /opt/nginx/lib;
perl_require Helloworld.pm;
#位于server配置中
location /nphandler {
perl Helloworld::handler;
}
location /nprocess {
perl Helloworld::process;
}
location /nparam {
perl Helloworld::param;
}
location /npost {
perl Helloworld::phandler;
}
这样将把所有来自http://server_name/the_location/下的请求交由Helloworld.pm脚本中定义的handler方法来处理。
该模包内容如下:
package Helloworld;
use strict;
use warnings;
use nginx;
use Time::Piece;
use Data::Dumper;
no warnings 'uninitialized';
sub handler {
my $r = shift;
my $ua = $r->header_in("User-Agent");
$r->send_http_header("text/html");
return OK if $r->header_only;
$r->print("hello,$ua\n<br/>");
$r->print($r->remote_addr);
$r->rflush;
if (-f $r->filename or -d _) {
$r->print($r->uri, " exists!\n");
}
return OK;
}
sub process {
my $r = shift;
$r->send_http_header('text/html; charset=utf-8');
my @arr = split('/', $r->uri);
my $user = $arr[2];
if (!$user || ($user eq "")){
$user = "Anonymous";
}
$r->print('Hello, You name is : <b>'.$user.'</b>');
$r->rflush();
return;
}
sub param {
my $r = shift;
$r->send_http_header("text/html");
#取得参数的 Map
my $param = $r->args;
#获得具体参数并解析
my %params;
if(length($param)>0){
my @pairs = split(/&/, $param);
foreach my $pair (@pairs){
my ($name, $value) = split(/=/, $pair);
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$params{$name} = $value;
}
}
#回显
$r->print('Hello: '.$params{user});
return OK;
}
##handle post
sub phandler {
my $r = shift;
if($r->request_method ne "POST"){
#如果不是post请求直接过掉不处理
return 403;
}
if($r->has_request_body(\&postme)){
#存在request body内容,则对body数据进行处理
return OK;
}
return 400;
}
sub postme {
my $r = shift;
my $url = '/def.html';
if($r->request_body eq 'isfreeoa=1'){
#可对body内容进行字符串的相关处理
$url = '/index.html';
}
#根据相关的post数据进行相关的内部重定向
$r->send_http_header;
#$r->print($url);
#$r->internal_redirect($url);
#$r->rflush;
$r->print("request_body: \"", $r->request_body, "\"<br/>");
$r->print("request_body_file: \"", $r->request_body_file, "\"<br/>\n");
return OK;
}
1;
__END__
在浏览器中返回访问都的浏览器Tag及ip地址:
http://172.18.0.2:2012/nphandler
hello,Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0 Iceweasel/28.0
172.18.0.2
http://172.18.0.2:2012/nparam?user=freeoa&password=loveus
Hello: freeoa
http://172.18.0.2:2012/nprocess/freeoa
Hello, You name is : freeoa
http://172.18.0.2:2012/npost
可以用curl来进行post测试,
$ curl -d "isfreeoa=1" http://172.18.0.2:2012/npost
request_body: "isfreeoa=1"<br/>request_body_file: ""<br/>
$ curl "http://172.18.0.2:2012/npost?isfreeoa=1"
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.6.2</center>
</body>
</html>
内置变量参考
Nginx原生的内置全局变量,可以访问这里。
当使用 use nginx 时,会有如下的对象可以调用,可以看到上面 shift 一个对象到 $r 上,然后就可以用 $r 调用那些对象了:
$r->args – 请求的参数
$r->discard_request_body – 这个参数是让 Nginx 放弃 request 的 body 的内容
$r->filename – 返回合适的请求文件的名字
$r->has_request_body(function) – 如果没有请求主体,返回0,但是如果请求主体存在,那么建立传递的函数并返回1,在程序的最后,nginx将调用指定的处理器
$r->header_in(header) – 查找请求头的信息
$r->header_only – 如果我们只要返回一个响应的头
$r->header_out(header, value) – 设置响应的头
$r->internal_redirect(uri) – 使内部重定向到指定的URI,重定向仅在完成perl脚本后发生.可以使用 header_out(Location….的方法来让浏览器自己重定向
$r->print(args, …) – 发送数据给客户端
$r->request_body – 得到客户端提交过来的内容(body 的参数,可能需要修改 nginx 的 client_body_buffer_size. )
$r->request_body_file —给客户的 body 存成文件,并返回文件名
$r->request_method — 得到请求 HTTP method
$r->remote_addr – 得到客户端的 IP 地址
$r->rflush – 立即传送数据给客户端
$r->sendfile(file [, displacement [, length ] ) – 传送给客户端指定文件的内容,可选的参数表明只传送数据的偏移量与长度,精确的传递仅在perl脚本执行完毕后生效
$r->send_http_header(type) – 添加一个回应的 http 头的信息
$r->sleep(milliseconds, handler) – 设置为请求在指定的时间使用指定的处理方法和停止处理,在此期间nginx将继续处理其他的请求,超过指定的时间后,nginx将运行安装的处理方法,注意你需要为处理方法通过一个reference,在处理器间转发数据你可以使用$r->variable().
$r->status(code) – 设置 http 的响应码
$r->unescape(text) – 使用 http 方法加密内容如%XX
$r->uri – 得到请求的 URL
$r->variable(name[, value]) – 设置变量的值
Nginx.conf 内置变量示例
server{
set $CONTEXT_PATH '/perl'; # uri的访问路径,如:http://np.freeoa.net/perl
set $APPLICATION_PATH '/opt/nginx/'; #板块文件路径,在该路径下需要放置一个lib目录,内置处理模块
server_name localhost;
location /perl { # 此处需与$CONTEXT_PATH保持一致
perl FreeOA::exec; #调用对应的处理方法
}
}