OpenResty应用总结 技术部 - kim
讲什么? • Lua 介绍 • Openresty 介绍 • 重构 Infov 的过程分享
什么是 Lua? • 轻量级脚本语言 • 最小、最快、最简单 • 嵌入式
Lua 的特性 • Functional 函数式 • Table 数组、哈希表、集合、对象 • Closure 闭包 • Coroutine 协程
著名的 Lua 项目 • 魔兽世界、愤怒的小鸟、Photoshop、仙剑 奇侠传五、淘宝内部 • 56.com (未来)
什么是 OpenResty? • 最快的 Web 应用开发框架
• OpenResty (也称为 ngx_openresty)是一个全功能的 Web 应用服务器, 它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的 大多数依赖项。由章亦春于2011年底发表。 • OpenResty 通过汇聚各种设计精良的 Nginx 模块,从而将 Nginx 有效的 变成一个强大的 Web 应用服务器,这样,Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种C以及Lua 模块,快速构造出足以胜任 10K+ 并发连接响应的超高性能Web 应用系统。 • OpenResty 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利 用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远 程后端诸如 MySQL,PostgreSQL,Memcached 以及 Redis 等都进行一 致的高性能响应。
性能对比 • PHP-Fpm:速度慢、吞吐量低、短连接 • OpenResty:执行快、大吞吐量、连接池 • 春哥做的 benchmark: https://github.com/agentzh/mysql-driverbenchmark
安装 • 下载解压: wget http://openresty.org/download/ngx_openresty-1.4.3.9.tar.gz tar -xzf ngx_openresty-1.4.3.9.tar.gz ./configure --help • 安装 mysql 连接开发包: yum install libdrizzle-devel • 安装: ./configure --prefix=/home/openresty/ --with-http_iconv_module --withhttp_postgres_module --with-http_drizzle_module --with-luajit --withpcre=/home/pcre-8.33 gmake && gmake install • 编辑 conf 配置 vi /home/openresty/nginx/conf/nginx.conf
Hello world - 1 location /helloworld { echo "hello world!"; }
Hello world - 2 location = /helloworld { content_by_lua ' ngx.say("hello world!") '; }
Hello world - 3 location = /helloworld { content_by_lua_file 'hello_world.lua'; } --hello_world.lua ngx.say("hello world!") ngx.flush(true)
更复杂例子 # http://localhost/test?name=kim&class=A location /test { echo "uri = $uri"; echo "request_uri = $request_uri"; set_unescape_uri $name $arg_name; set_unescape_uri $class $arg_class; echo "name: $name"; echo "class: $class"; }
无阻塞IO location /nonblocking { content_by_lua ' local res = ngx.location.capture("/query") if res.status == 200 then ngx.print(res.body) end'; }
并发子请求 location = /nonblock-multi { content_by_lua ' local res1, res2, res3 = ngx.location.capture_multi{ {"/memc"}, {"/mysql"}, {"/postgres"} } ngx.say(res1.body, res2.body, res3.body) '; }
重构 info.v.56.com
为什么? • 前端核心接口 • PHP 性能瓶颈 • 缓存和数据库压力
开发 • 公共模块:/lib • 实例代码:/lib/http.lua
一般 debug 方法 • 直接抛出错误:error(“抛出个error!”) • assert(io.read("*number"), "invalid input") • Lua提供了错误处理函数pcall: r, msg = pcall(foo) • 还可以用 xpcall
以上,我们一般不用。。。
实际 debug 方法 • tailf /home/openresty/nginx/logs/error.log
具体配置 • 新网段 + 3台新机器 • 高可用性:lvs + keepalive • 高可用 Cache:twemproxy + kt(12个) • 高性能:openresty • 实时性:缓存耦合(人气、评论),缓存更新 (直连 + httpsqs)
切换原则 • 完全保留原PHP的所有读写功能,即线上功 能不受影响
Nginx配置技巧 • ~ 为区分大小写的匹配。 • ~* 不区分大小写的匹配。 • !~ 和 !~* 意为“不匹配的”。
多重 if 判断 • # 仅当 uri 匹配 /?ids=xxxx ,即只读时才跳转至 lua: set $flag 0; if ($arg_ids ~* "(w)+") { set $flag "${flag}1"; } if ($request_uri !~ ".php") { set $flag "${flag}2"; } if ($arg_dy !~ "c") { set $flag "${flag}3"; } if ($flag = "0123") { content_by_lua_file 'vinfo.lua'; } fastcgi_pass 10.11.80.159:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php; include fastcgi_params; expires off;
Infov当前的conf location ~ .php { fastcgi_pass unix:/home/php/php-fastcgi.sock; fastcgi_index index.php; include fastcgi_params; expires off; } location / { if ($request_uri ~* "luzhi") { rewrite ^/(.*)$ /index.php?$query_string last; } content_by_lua_file '/diska/htdocs/openresty/vinfo.lua'; } location /api { content_by_lua_file '/diska/htdocs/openresty/vapi.lua'; }
切换过程 • 对每个应用,通过改 hosts 来灰度切换,有 问题立刻发现立刻修复 • 播放页和vxml 最后切换
遇到的问题
Json输出问题 • PHP json_encode :会把 utf-8 强制 escape 成 unicode,除非是新版 php5.4 以上,可通 过指定 JSON_UNESCAPED_UNICODE 参数来 禁掉。 • Lua cjson :默认并不会对 utf-8 做 escape 处 理,输出的就是 utf-8
Table类型
lua 的 table 数据结构: typedef union TKey { struct { TValuefields; struct Node *next; /* for chaining */ } nk; TValue tvk; } TKey; typedef struct Node { TValue i_val; TKey i_key; } Node; typedef struct Table { CommonHeader; lu_byte flags; // 元表标记 lu_byte lsizenode; /* log2 of size of `node' array */ // 哈希部分大小 struct Table *metatable; // 元表 TValue *array; /* array part */ // 数组部分 Node *node; // 哈希部分 Node *lastfree; /* any free position is before this position */ // 第一个空闲位置指针 GCObject *gclist; int sizearray; /* size of `array' array */ // 数组部分大小 } Table;
• 数组部分:速度极快,内存比哈希省一半 • Hash部分:链状发散表,本身即无序(比 如用PHP接收到来自Lua的输出的时候,不 能简单地“认为”它是有序的!)
文件vs模块 • 为什么要把配置文件写成模块 config.lua ?
测试环境问题 • 测试环境和正式环境最好还是分开,特别 是不要在一个 server 配置里面
dns解析问题 • 可以通过安装和配置 nginx 本地 dns 服务来 添加 resolver,从而使 sock 能通过 Nginx core‘s dynamic resolver 来解析域名。 • 但是目前实际中还是直接使用 ip 来解析, 未来可以对 hosts 进行单独解析,这样就能 使用域名了。
Bug问题 • Error 级别的都是要尽量修复的 • Emerg 级别的是必须修复的 • 举例 gsub 引发的血案。。。
入口不能太“窄” • vi /etc/sysctl.conf • net.netfilter.nf_conntrack_max = 655360 • net.netfilter.nf_conntrack_tcp_timeout_establ ished = 180
Timeout不能太短 • 无论是连接数据库、缓存还是Webservice, 超时时间太短反而不能充分发挥OpenResty 的优势
现状与展望
价值 • 1. 代码量 • 2. 开发效率 • 3. 性能提升
站内应用 • • • • • • • 用户信息接口 短消息提醒接口 头部消息接口 视频观看记录接口 视频信息接口 专辑信息接口 等等中间层。。。
缺少的 • 1. Session • 2. Http/Smtp/Rpc/… • 3. Html template • 等等。。。
场景 • 数据层 • 缓存层
资料 • 官网:http://openresty.org/ • nginx模块手册: https://github.com/chaoslawful/lua-nginxmodule • lua手册: http://www.lua.org/manual/5.2/manual.html
谢谢!

OpenResty/Lua Practical Experience