Perl在nginx里的应用

3,032 views

Published on

简述了脚本语言(主要是perl)在nginx中的应用,介绍了nginx_perl项目。

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,032
On SlideShare
0
From Embeds
0
Number of Embeds
95
Actions
Shares
0
Downloads
14
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Perl在nginx里的应用

  1. 1. Perl 与 Nginx 应用 —— jeff
  2. 2. 题目的由来 <ul><li>perl 是 OPSer 最熟悉的 script language </li></ul><ul><li>nginx 是 OPSer 最喜欢的 web server </li></ul><ul><li>相信我的 perl 和 nginx 水平都不是最好的,不过感谢 laird 的乱点鸳鸯谱,把它们凑在一起给我作讲题,大概或许可能仿佛应该想来我看过一些而大家没留意的吧 ^=^ </li></ul>
  3. 3. 提要 <ul><li>nginx 的主要功能 </li></ul><ul><li>nginx 原生 perl 模块介绍 </li></ul><ul><li>几个简单的例子 </li></ul><ul><li>nginx 第三方模块 (ngx_js/ngx_lua) 介绍 </li></ul><ul><li>nginx 增强版 perl 介绍 </li></ul><ul><li>总结 </li></ul>
  4. 4. nginx 的主要功能 <ul><li>nginx 主要分成 http 和 mail 两个部分,一般来说,我们的操作都是 http 部分的(奇怪的是 nginx-src 的 configure 选项里只有 --without-http 而没有 --without-mail ) </li></ul><ul><li>http 中,最常用的标准模块是静态文件发布的 core ,反向代理的 proxy ,上游集群的 upstream ,重定向的 rewrite 等 …… </li></ul><ul><li>其他常用官方模块有: fastcgi/flv/mp4/gzip_static/limit_conn/uwsgi 等 </li></ul>
  5. 5. nginx 与动态应用 <ul><li>nginx 的标准作用是静态文件发布( proxy_cache/proxy_store 的结果也是静态文件发布,区别在目录结构而已) </li></ul><ul><li>与动态程序结合,最通用的办法就是使用 proxy 和 upstream 模块。 </li></ul><ul><li>方便一点的,比如用 fastcgi 启动 perl/php 程序,或者支持 uwsgi 标准的 python/perl 程序等 …… </li></ul>
  6. 6. nginx 原生 perl 模块介绍 <ul><li>大家都知道, apache 除了 cgi 方式外,还有 mod_perl , nginx 也一样。 </li></ul><ul><li>在编译时,启用 --with-http_perl_module 选项即可。 </li></ul><ul><li>功能上,主要分两种,一种是简单的通过 perl_set 指令,返回一个 nginx.conf 的内部变量,完成后续响应;一种是通过 handler 替代一部分 serve 功能。 </li></ul><ul><li>指令的使用方法,见 nginx 的 wiki 。 </li></ul>
  7. 7. perl_set 举例 <ul><li>通过 perl_set 完成 url 大小写的改变。注意,这并不意味着 url 大小写不重要了,只是说在实际文件统一小写的情况下,不会对有大写的 url 返回 404 了 …… </li></ul><ul><li>nginx.conf: http { perl_set $new_uri ' sub { my $r = shift; return lc($r->uri); }; '; rewrite ^.*$ $new_uri last; }; </li></ul>
  8. 8. perl 举例 <ul><li>在文件下载服务时,根据文件大小的不同,将请求引向不同的服务器集群。进一步可以根据 ip ,根据 dir ,根据 cookie 等等 …… </li></ul><ul><li>nginx.conf: http { perl_module perl/lib; perl_require Redirect.pm; server { location / { perl Redirect::handler; }; }; }; </li></ul><ul><li>Redirect.pm: package Redirect; use nginx; sub handler { my $self = shift; my $webroot = '/www/dl.gamedomain.com/' return HTTP_NOT_ALLOWED unless $self->uri =~ m!^(/.+/)[^/]+$!; my $file = $webroot . $1 . $self->filename; my @filestat = stat($file) or return HTTP_NOT_FOUND; my $filesize = $filestat[7]; if ( $filesize < 8 * 1024 * 1024 ) { return OK; } else { $self->location('http://bigfile.cdndomain.com'.$self->uri); } }; 1 </li></ul>
  9. 9. 模拟 ngx_perl 的 ngx_js <ul><li>除了 Igor Sysoev 提供的 ngx_perl ,在第三方模块库里还有另外一个 ngx_javascript 模块,其实现效果甚至指令命名,都模仿了 ngx_perl 的形式。 </li></ul><ul><li>感谢微博上某位我不记得的童鞋当初告诉我这个模块 …… 可惜偶不会 js ,估计会 js 又想折腾 webserver 都去玩 node 去了 = =! </li></ul>
  10. 10. 突破 ngx_perl 的 ngx_lua <ul><li>agentzh 目前全力以赴的 OpenResty 项目主要就是以这个 ngx_lua 为中心,粘合一系列其他 ngx_*(memd/redis/mysql...) 的 asynchronous 模块完成一个全程 asynchronous 的 webservice 。 </li></ul><ul><li>agentzh 提到过两点:一是 openresty 的早期版本是 perl 的,后来因为性能废弃了;二是 ngx_perl 模块的 blocking 完全浪费了 nginx 优秀的 nonblocking 设计所以纯粹是个玩具。 </li></ul>
  11. 11. 突破 ngx_perl 的 ngx_lua <ul><li>在 ngx_perl 的 wiki 上,有这么两句话:一是不要试图使用 perl 来完成类似 dns 解析、 mysql 请求之类的需要长时间等待的任务,这会导致整个 worker 被阻塞;二是启用了 ngx_perl 的 server ,尽量避免使用 reload 命令,有可能会内存溢出。 </li></ul><ul><li>在 agentzh 的文档 ( 讲座 ?) 中,也曾提到, ngx_lua 模块最好用来做运算,把 IO 请求通过 subrequest 交给专门的模块来做。 </li></ul>
  12. 12. nginx 增强版 perl 介绍 <ul><li>由上推断,可以认为如果只是单纯的运算类的事情,使用 ngx_perl 完成是完全可以的,比如之前举例的 perl_set 的情况。 </li></ul><ul><li>那么,如果想完成一个复杂功能,只能靠 ngx_lua 了么? </li></ul><ul><li>感谢 CPAN ,我发现了 Nginx::Engine 模块 —— 前不久这个模块刚刚从 cpan 搬到 github ,名为 nginx_perl ,会在 release 后再搬回 cpan~~ </li></ul>
  13. 13. nginx 增强版 perl 介绍 <ul><li>在 Nginx::Engine 的介绍上,写的是 an asynchronous web framework based on nginx ;在 nginx_perl 的介绍上,则是 full-featured perl support for nginx 。 </li></ul><ul><li>通过 $r->print 发送内容的方式很类似原始的 CGI.pm ,所以如果是写网页,还是用 dancer 之类的 framework 比较方便。 </li></ul><ul><li>除了一些细节上的功能,比如 perl_init_worker/perl_eval 指令之外,最重要的几个地方: </li></ul>
  14. 14. nginx_perl 新增功能 <ul><li>1 、 $r->main_count_inc; 每个 async 的 handler 都要 increase 主进程的计数器以便 callback ; </li></ul><ul><li>2 、 ngx_timer $after, $repeat, sub {}; 一个定时器,在 $after 秒后 callback ,重复 $repeat 次; </li></ul><ul><li>3 、 ngx_connector $ip, $port, $timeout, sub {}; 异步链接 $ip:$port ,超时时间 $timeout ,成功的话,建立的链接将作为 $_[0] 返回;否则返回 $! 。 </li></ul>
  15. 15. nginx_perl 新增功能 <ul><li>4 、 ngx_reader $connection, $buf, $min, $max, $timeout, sub{}; 从 ngx_connector 返回的 $connection 中异步读取数据到 $buf ,规定 $buf 的长度在 $min 到 $max 之间。如果长度不足,返回 NGX_EOF ,也会存入 $! 中。 </li></ul><ul><li>5 、 ngx_writer $connection, $buf, $timeout, sub {}; 和 ngx_reader 相对的。 </li></ul>
  16. 16. nginx_perl 新增功能 <ul><li>ngx_ssl_handshaker $connection, sub {}; 如果启动了 ssl 加密,必须在 connection 之后使用 handshaker ,再创建 reader/writer 。 </li></ul><ul><li>ngx_resolver $host, $timeout, sub {}; 返回域名解析结果 ip 列表到 @_ 中;目前这个指令还是 nginx 原生 resolver 的封装,如果要使用的话,建议在本机配备 named/dnsmasq 等 dns 缓存 …… 这里是 TODO 中列的下一步重点改进。 </li></ul>
  17. 17. nginx_perl 新增功能 <ul><li>$r->take_connection(); </li></ul><ul><li>$r->give_connection(); 之前的 ngx_connector 指令,用来创建和其他 server 的链接,而这里的 takeover ,是用来获取 client 的链接,这样做 websocket 之类的就比较方便 …… 使用时同样要记得 inc ,更关键的是要使用 $r->finalize_request(NGX_DONE) 表示 EOF , return NGX_NOOP 表示 NGX_CLOSE 。 </li></ul>
  18. 18. nginx_perl 示例 <ul><li>源码中默认有 helloworld,self_sufficient,redis 和 Nginx::Util 四个示例。不过我还是自己试着写一个类似 proxy_pass 的例子,从中也发现一些 README 没有说明的细节: </li></ul><ul><li>package HelloWorld; </li></ul><ul><li>use Nginx; </li></ul><ul><li>use strict; </li></ul><ul><li># 用来在 nginx 启动的时候做的事情,这里单纯显示一下 </li></ul><ul><li>sub init_worker { </li></ul><ul><li>warn 'nginx_perl start [OK]'; </li></ul><ul><li>}; </li></ul><ul><li># 这里是 nginx 的 http 模块中调用的 handler , alexander 有计划改成 tcp 级别的 </li></ul><ul><li>sub handler { </li></ul><ul><li>my $r = shift; </li></ul><ul><li># 增加主循环的计数器 </li></ul><ul><li>$r->main_count_inc; </li></ul>
  19. 19. nginx_perl 示例 <ul><li># 使用非阻塞的连接器连接 127.0.0.1 的 80 端口, 10 秒超时 </li></ul><ul><li>ngx_connector '127.0.0.1', 80, 10, sub { </li></ul><ul><li># 如果连接出问题,会记录在 $! 中 </li></ul><ul><li>if ($!) { </li></ul><ul><li>$r->send_http_header('text/html'); </li></ul><ul><li>$r->print(&quot;Connection failed, $!n&quot;); </li></ul><ul><li>$r->send_special(NGX_HTTP_LAST); </li></ul><ul><li># 不管怎么处理这次连接,最后一定要记得用 $r->finalize_request() ,会 decrease 之前 $r->main_count_inc; 里加上的计数。 </li></ul><ul><li>$r->finalize_request(NGX_OK); </li></ul><ul><li>return NGX_CLOSE; </li></ul><ul><li>}; </li></ul><ul><li># 返回 $c 是建立的连接 </li></ul><ul><li>my $c = shift; </li></ul><ul><li>my $req_buf = &quot;GET /index.php HTTP/1.0x0dx0a&quot;. </li></ul><ul><li>&quot;Host: chenlinux.comx0dx0a&quot;. </li></ul><ul><li>&quot;Connection: closex0dx0a&quot;. </li></ul><ul><li>&quot;x0dx0a&quot;; </li></ul><ul><li># 这里记住定义 buffer 的时候不要搞成 undef 了,会报段错误的,不过俄国佬回信说他修复了 </li></ul><ul><li>my $res_buf = ''; </li></ul>
  20. 20. nginx_perl 示例 <ul><li># 非阻塞写入,超时 10 秒 </li></ul><ul><li>ngx_writer $c, $req_buf, 10, sub { </li></ul><ul><li>if ($!) { </li></ul><ul><li>...... </li></ul><ul><li>}; </li></ul><ul><li>$req_buf = ''; </li></ul><ul><li># 之前的 buffer 测试就是这里,如果加一个 warn ,就不会报错 …… 汗 </li></ul><ul><li>#warn &quot;$req_bufn$res_bufn&quot;; </li></ul><ul><li># 写入完成后,开始调用读取 </li></ul><ul><li>return NGX_READ; </li></ul><ul><li>}; </li></ul>
  21. 21. nginx_perl 示例 <ul><li># 读取到 buffer ,最短 0 字节,最长 8000 字节,超时 10 秒 </li></ul><ul><li>ngx_reader $c, $res_buf, 0, 8000, 10, sub { </li></ul><ul><li>if ($!) { </li></ul><ul><li>...... </li></ul><ul><li>} </li></ul><ul><li>$r->send_http_header('text/html'); </li></ul><ul><li>$r->print($res_buf); </li></ul><ul><li>$r->send_special(NGX_HTTP_LAST); </li></ul><ul><li>$r->finalize_request(NGX_OK); </li></ul><ul><li>return NGX_CLOSE; </li></ul><ul><li>}; </li></ul><ul><li># 这个是 connector 的语句,表示连接成功后调用写入 </li></ul><ul><li>return NGX_WRITE; </li></ul><ul><li>}; </li></ul><ul><li># 各个非阻塞调用完成后的返回, NGX_DONE 只能用在 http 的 handler 里,不能在 ngx_*r 里用,里面请用 NGX_CLOSE 。 </li></ul><ul><li>return NGX_DONE; </li></ul><ul><li>}; </li></ul><ul><li>1; </li></ul>
  22. 22. 总结 <ul><li>今天主要是介绍了一下在 nginx.conf 里能够使用的几个 script ,以及用 perl 完成的两个 cdn 功能的实验。 </li></ul><ul><li>关于 asynchronous 的应用,其实我还是门外围观者,感觉单纯 perl 也好 lua 也好,主要还是多多搭配各种模块的复杂运用。 </li></ul>

×