Nginx-Perl配置使用入门
2014-10-25 13:22:03 阿炯

原生的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; #调用对应的处理方法
}
}

参考文档

http://nginx.org/en/docs/http/ngx_http_perl_module.html