用OpenResty搭建高性能服务端
Posted xingxia
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用OpenResty搭建高性能服务端相关的知识,希望对你有一定的参考价值。
OpenResty 简介
OpenResty®
是一个基于 nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty 基于 Nginx
开发,可以简单认为是 Nginx
+ lua-nginx-module
的组合版。
官网:https://openresty.org/cn/ 官方文档:https://github.com/openresty/lua-nginx-module#version
高性能服务端两个重要要素:需要支持缓存,语言层面要支持异步非堵塞。
缓存速度上,内存 > SSD > 机械磁盘;本机 > 网络 ; 进程内 > 进程间 。异步非阻塞指的是事件驱动方式(事件完成后再通知)。
OpenResty 包含的技术:
-
Nginx:不仅仅是负载均衡+反向代理等功能,Nginx c module开发成本高。
-
LuaJIT:OpenResty用的是 LuaJIT,LuaJIT 是主打性能的Lua。
OpenResty
本质上是将 LuaJIT
的虚拟机嵌入到 Nginx的worker中,所以效率特别高,在性能上, OpenResty
接近或超过 Nginx c module:
OpenResty已经颠覆了高性能服务端的开发模式。
OpenResty
与市面上其他语言对比:
-
node.js:第一门将异步非阻塞特性放入自己语言中的,前端同学可以快速切入。但是 node.js 用回调(callback)实现异步非阻塞,代码写起来比较麻烦。
-
Python:3.4之后加入了异步的支持,比如异步io和aiohttp;3.5引入了协程。缺点是版本跨度大,因为很多人还是使用2.7。
-
Golang:最近几年非常火。缺点:代码写法上需要使用go关键字;线上热调试不方便(
SystemTap
提供了有限的支持)。
Hello World
OpenResty安装
以 CentOS 为例:
mkdir /opt && cd /opt # download openresty wget https://openresty.org/download/openresty-1.13.6.2.tar.gz tar zxvf openresty-1.13.6.2.tar.gz cd openresty-1.13.6.2 # configure ./configure --prefix=/usr/local/openresty -j4 make -j4 && make install
其中 源码包可以到 https://openresty.org/cn/download.html 该页面获取。 -j4
表示使用4核。 configure
那一步还可以指定各种参数:
./configure --prefix=/usr/local/openresty --with-luajit --without-http_redis2_module --with-http_iconv_module --with-http_postgres_module
使用 ./configure--help
查看更多的选项。
其它系统环境上安装可以参考 https://openresty.org/cn/installation.html 。
其实安装 OpenResty 和安装 Nginx 是类似的,因为 OpenResty 是基于 Nginx 开发的。
如果已经安装了 Nginx,又想使用 OpenResty 的功能,可以参考 《Nginx编译安装Lua》:https://www.cnblogs.com/52fhy/p/10164553.html 一文安装 lua-nginx-module
模块即可。
第一个程序
修改 /usr/local/openresty/nginx/conf/nginx.conf
:
worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { server { listen 8080; location /hello { default_type text/html; content_by_lua ‘ ngx.say("<p>hello, world</p>") ‘; } } }
把默认的 80
端口改为 8080
,新增 /hello
部分。
其中 content_by_lua
便是 OpenResty 提供的指令,在官方文档可以搜索到https://github.com/openresty/lua-nginx-module
现在我们启动OpenResty:
/usr/local/openresty/nginx/sbin/nginx
启动成功后,查看效果:
知识点: 1、 content_by_lua
:返回的内容使用 lua 代码。 2、 content_by_lua_file
:读取lua文件里的 lua 代码。 3、默认情况下,修改Lua代码,需要 reload OpenResty服务才会生效。可以修改 lua_code_cache
为 off
,作用域: http, server, location, location if。请勿在生产环境里开启。
测试1:使用 content_by_lua_file
cd /usr/local/openresty mkdir nginx/conf/lua vim nginx/conf/lua/hello.lua
内容为:
ngx.say("<p>hello, lua world</p>")
然后修改 nginx.conf:
location /hello { default_type text/html; content_by_lua_file conf/lua/hello.lua; }
重启 OpenResty:
./nginx/sbin/nginx -s reload
查看效果:
curl http://127.0.0.1:8080/hello
测试2:关闭 lua_code_cache
: 根据 lua_code_cache
作用域,我们可以在server块加上:
lua_code_cache off; location /hello { default_type text/html; content_by_lua_file conf/lua/hello.lua; }
重启:
./nginx/sbin/nginx -s reload
提示说 lua_code_cache
关闭后影响性能。我们再次修改 nginx/conf/lua/hello.lua
的代码,保存后就会生效,无需 reload server。
OpenResty 入门
这节使用 ngx_lua api完成一个小功能。
lua代码:
nginx/conf/lua/getrandomstring.lua
-- 实现随机字符串 local args = ngx.req.get_uri_args() local salt = args.salt if not salt then ngx.exit(ngx.HTTP_BAD_REQUEST) end local str = ngx.md5(ngx.time() .. salt) ngx.say(str)
修改 nginx.conf ,新增:
location /get_random_string { content_by_lua_file conf/lua/getrandomstring.lua; }
由于修改了 nginx.conf ,需要reload OpenResty 服务。然后,我们访问服务:
curl http://127.0.0.1:8080/get_random_string?salt=2
说明:
1、 ngx.req.get_uri_args()
用于获取URI请求参数。
2、 ngx.HTTP_BAD_REQUEST
为ngx常量,指的是400。代码里尽量使用常量。
3、 ngx.time()
用于获取时间戳,是带有缓存的。与Lua的日期库不同,不涉及系统调用。尽量使用Ngx给出的方法,以免发生性能问题。
4、 ngx.md5()
用于生成md5值。
5、如果代码里有语法错误,我们可以通过nginx 的 error.log里看到,默认文件是 nginx/logs/error.log
。
再次提醒大家,做 OpenResty 开发,lua-nginx-module 的文档是你的首选,Lua 语言的库都是同步阻塞的,用的时候要三思。也就是说,尽量使用 ngx_lua提供的api,而不是使用 Lua 本身的。例如 ngx.sleep()
与 lua提供的sleep,前者不会造成阻塞,后者是会阻塞的,详见:sleep · OpenResty最佳实践 。
ngx_lua API介绍
本节主要是带着大家简单的过一下常用的ngx_lua API。
ngx_lua 有60多个指令(Directive),140多个 API(截止到2019-3-26)。
指令 是 ngx_lua 提供给Nginx调用的方法,与 Nginx自带的 location
、 rewrite
等是一个级别的。指令有自己的作用域,例如: content_by_lua_file
只能作用于 location
和 locationif
里面:
API 是指ngx_lua基于lua代码实现的一系列方法或常量,遵循 lua的语法规则。只能在lua代码块或者lua文件里使用。
例如:
ngx.say("<p>hello, lua world</p>")
其中 content_by_lua
是指令,作用于 location
块; ngx.say()
是 ngx_lua 提供的API。
下面,我们使用 ngx_lua完成另外一个小功能:实现base64的解码并重新json编码输出。代码里会用到一些指令和API。
lua代码:
nginx/conf/lua/decode_info.lua
修改 nginx.conf ,新增:
location /decode_info { content_by_lua_file conf/lua/decode_info.lua; }
由于修改了 nginx.conf ,需要 reload OpenResty 服务。然后,我们访问服务
$ php -r "echo base64_encode(‘test‘);" dGVzdA== $ curl -XPOST -d "info=dGVzdA==" http://127.0.0.1:8080/decode_info { "user_agnet":"curl/7.19.7", "client_ip":"127.0.0.1", "info":"test" }
说明:
1、 require
是 lua 里面引入其他库的关键字。这里引入的 cjson。
2、当我们要读取 http里的post数据的时候,就需要使用 ngx.req.read_body()
。该API同步读取客户端请求主体而不阻塞Nginx事件循环。
3、 ngx.req.get_post_args()
用于获取post请求数据。
4、 ngx.var.remote_var
实际是获取的nginx里的变量 remote_var
。
也就是说, ngx.var.xxx
实际是获取的nginx里的变量 xxx
。例如:
nginx变量详见:[Alphabetical index of variables}(http://nginx.org/en/docs/varindex.html)。 ngx_lua ngx.var
API详见:ngx.var.VARIABLE。
5、 ngx.req.get_headers()
用于读取nginx的header参数。返回的是lua table。
6、 ngx.decode_base64()
用于 base64字符串解码。对应的编码API是 ngx.encode_base64()
。
连接数据库
连接数据库我们需要使用到ngx_lua的第三方库:
-
lua-resty-redis library based on ngx_lua cosocket.
-
lua-resty-mysql library based on ngx_lua cosocket.
这两个库都是基于cosocket实现的,特点是异步非阻塞。代码风格是同步的写法。更多第三方库详见:See Also 。
连接 MySQL
lua代码:
nginx/conf/lua/test_mysql.lua
以上是关于用OpenResty搭建高性能服务端的主要内容,如果未能解决你的问题,请参考以下文章