最近1周,陆陆续续的花了10个小时 ,将我的 私人项目 从 jvm 迁移到 openresty 上。
至于为什么要迁移, 主要是最近发现 vps 的内存不够用了,看一下下面的数据
迁移之前
1 2 3 %CPU %MEM ... 0.0 55.9 349:51.88 java -Xmx299m -Xms299m -server -jar xxx.jar 0.0 10.2 133:00.58 ./src/redis-server 127.0.0.1:6379
benchmark
1 2 3 4 5 6 7 root@vultr:~/wrk-4.0.2# ./wrk -t2 -c 20 -d30s 'http://localhost:9000/p/p/1' Running 30s test @ http://localhost:9000/p/p/1 2 threads and 20 connections ^C Thread Stats Avg Stdev Max +/- Stdev Latency 321.11ms 370.56ms 1.84s 87.80% Req/Sec 12.13 7.15 30.00 58.89% 134 requests in 11.22s, 238.29KB read
迁移之后
1 2 3 4 5 %CPU %MEM ... 0.0 0.5 0:10.83 nginx: worker process 0.3 0.5 0:10.88 nginx: worker process 0.0 0.4 0:00.00 nginx: master process ./ 0.0 10.2 133:00.58 ./src/redis-server 127.0.0.1:6379
benchmark
1 2 3 4 5 6 7 root@vultr:~/wrk-4.0.2# ./wrk -t2 -c 20 -d30s 'http://lelouchcrgallery.tk/p/1' Running 30s test @ http://lelouchcrgallery.tk/p/1 2 threads and 20 connections ^C Thread Stats Avg Stdev Max +/- Stdev Latency 78.30ms 106.42ms 355.13ms 79.17% Req/Sec 2.94k 1.94k 5.62k 47.16% 57459 requests in 10.71s, 177.11MB read
结果就是 资源耗费下降了,性能上去了,而且上去了不是一点点 (不过这里不是在黑 java,毕竟 以前为了图方便且没有什么性能要求没用nio, 用netty 的话 性能肯定会提上去)
把原本在java层做的 业务全利用 redis 的script 放到 redis-server 里面做了。
我是根据 Openresty 最佳实践 这本书边写边看的, 但还是遇到了不少坑,下面记录一下
module 由于lua 的module 能cache 所以尽量将 一些 immutable 的变量放到module 里面,避免重复申请
1 2 3 4 5 6 7 8 9 10 11 12 13 14 local _M = {}local ran_arr = { 0.5 , 0.6 , 0.625 , 0.7 , 0.8 , 0.85 , 0.9 , 0.95 , 1 , 1.05 , 1.1 , 1.15 , 1.2 , 1.25 , 1.3 , 1.35 ,1.4 , 1.5 , 1.75 , 2 , 2.5 , 3 }local ran_arr_len = #ran_arrmath .randomseed(os .time())function _M.ran_ratio(self, ...) local args = {...} return ran_arr[math .random(ran_arr_len)] end return _M
这样,ran_arr 数组 一个nginx 进程只会有一份
局部变量 最佳实践 里面很多篇幅都提到 没有加local 的变量就是全局变量,容易坑,但是有的时候不得不使用全局变量
在openresty 里面可以用三种全局变量
比如有一个非常大的script, 我不想每次都eval, 我想script load 后 evalsha,减少请求大小。
所以我不得不找一个全局变量来存 script_sha
由于 进程间 共享变量要用 lua_shared_dict
实现,所以建议放在 init_by_lua
中实现 参考
1 2 3 4 5 6 7 8 9 10 11 12 13 lua_shared_dict dogs 1m; init_by_lua ' local dogs = ngx.shared.dogs; dogs:set("Tom", 50) ' server { location = /api { content_by_lua ' local dogs = ngx.shared.dogs; ngx.say(dogs:get("Tom")) ' } }
不过由于我的vps 只有一个核,所以就一个 进程,所以 我直接用了 进程内的变量,也就是lua vm 的全局变量
1 2 3 4 5 6 7 8 9 10 11 12 api_script_sha = nil function _M.api_script_sha(...) local arg = {...} local red = arg[1 ] if not api_script_sha then ngx.log(ngx.WARN, "start api_script_sha : " ) api_script_sha, err = red:script("load" , api_script) ngx.log(ngx.WARN, "end api_script_sha : " , api_script_sha) end return api_script_sha end
lua_code_cache 在开发时, 可以关闭 lua_code_cache
选项,方便调试。
不过由于开启了这个选项, 所以,相当于每次请求都会初始化 lua vm,并且 reload 所有 script。
所以,当你需要调试 lua vm 级别的 global variable 的时候,还是要把它开启
调试 lua table 不能直接 print log,可以用 cjson.safe
print
1 print ("--> res type:" .. type (res) .. ' -- ' .. cjson_safe.encode(res))
random lua 的random 比较坑爹,即使用 math.randomseed (os.time()) 也是没秒变化一次
但是更坑爹的事 redis 的 lua script 并不包括 os see 并且不抛错
所以根据文档,在 redis里面 用redis 的time 来做seed (能精确到ms 级别)
1 2 local _time = redis.call('time' ) math .randomseed(tonumber (_time[2 ]))
开发环境 开发环境 建议直接用类似虚拟机的环境比如docker,以便节省不必要的时间花在不同环境下.
reference https://moonbingbing.gitbooks.io/openresty-best-practices
http://redis.io/commands/eval#available-libraries