关于openresty:Nginx-快速集成免费-WAF

OpenResty 是一个基于 Nginx 和 LuaJIT 的全功能 Web 应用服务器,它提供了一种弱小而灵便的形式来构建和扩大 Web 应用服务器,同时放弃了 Nginx 的高性能和可靠性。OpenResty 是 APISIX、Kong、Ingress Nginx 等网关类产品的根底,因而 OpenResty 及其衍生产品非常适合作为 WAF 防护的对立入口。 本次应用的收费WAF次要用了雷池社区版,挂一个官网链接(感兴趣的敌人能够尝试一下):https://waf-ce.chaitin.cn/ 。 本文讲述了如何利用收费的长亭雷池 WAF 社区版,通过 lua-resty-t1k 插件为 OpenResty 减少平安防护,实现转发与检测服务拆散的平安架构。 根底版根底版以 OpenResty 与长亭雷池 WAF 社区版装置在同一台 Host 为例。 第一步 – 装置雷池长亭雷池 WAF 社区版有多种装置形式,具体装置形式能够参考长亭雷池 WAF 社区版官网文档:https://waf-ce.chaitin.cn/posts/guide_install 如果之前曾经装置了长亭雷池 WAF 社区版,确保版本 >= 2.0.0。长亭雷池 WAF 社区版治理页面左下角显示了以后版本。 第二步 – 配置 OpenResty咱们以 OpenResty 官网镜像 alpine-fat 为例,介绍如何开启长亭雷池 WAF 防护: 进入长亭雷池 WAF 社区版装置目录,确认当前目录下存在 resources/detecotr 目录,启动 OpenResty。 docker run -d --name openresty -v $(pwd)/resources/detector:/opt/detector openresty/openresty:alpine-fat进入 OpenResty 容器,应用 luarocks 装置 lua-resty-t1k 插件: ...

July 3, 2023 · 2 min · jiezi

关于openresty:在-OpenResty-Edge-中配置分布式-gRPC-代理

明天我将演示如何在 OpenResty Edge 中设置一个 gRPC 反向代理和负载均衡器。 gRPC 样本服务器和样本服务咱们筹备了一个 gRPC 样本服务器。该服务器的 IP 地址以 .166 结尾。监听的端口是 8080。这个是样本 gRPC 服务的 protobuf 定义文件。1cat hello_world.proto这个服务依据 name 参数返回一个欢送信息。咱们能够应用 grpcurl 命令行工具来查看这个服务的输入。这个工具能够发送 gRPC 申请,而后以 JSON 格局显示收到的响应。因为咱们的 gRPC 服务器不反对 TLS 加密,这里要应用 “-plaintext” 选项。 将 “world” 作为 name 参数的值。输出下面提到的 gRPC 服务器的地址。最初,输出 gRPC 服务的名称。发送申请。不出所料,返回了 “hello world”。将 gRPC 服务器作为上游应用接下来,咱们将应用 OpenResty Edge 前面的这个 gRPC 服务器作为上游。像平常一样,让咱们进入 OpenResty Edge 的 Admin Web 控制台。这就是咱们控制台的样本部署。每个用户都有本人的本地部署。咱们能够持续应用之前的示例利用,test-edge.com。进入该利用。在利用中启用 HTTP 2gRPC 应用 HTTP 2 进行传输。HTTP 2 在 OpenResty Edge 中是默认启用的。这里我将展现如何在 Edge 的应用程序设置中启用 HTTP 2。抉择 “Enabled” 选项。保留咱们的改变。而后咱们去 SSL 页面,确保 SSL 证书曾经配置得当。能够看到咱们在以前的教程中配置的证书。为 gRPC 服务器创立上游跳转到 “Upstreams” 页面。为咱们的后端服务器创立一个新的上游。咱们给这个上游起个名字,“grpc backend”。如果 gRPC 后端服务器启用了 TLS 加密性能,咱们能够在这里抉择 HTTPS。在 gRPC 后端服务器的主机字段中填入后面提到的 IP 地址。填写端口号:8080。单击保留这个上游。咱们能够看到这个新的 “grpc backend” 上游曾经胜利被创立。启用 gRPC 代理当初让咱们创立一个新的页面规定来理论应用这个上游。创立一个新的页面规定。增加一个匹配 URI 前缀的规定条件。 ...

June 30, 2023 · 1 min · jiezi

关于openresty:优化超大-Nginx-配置导致的内存碎片

咱们最近应用 OpenResty XRay 帮忙一个销售 CDN 和流量网关服务的企业客户优化了他们的 OpenResty/Nginx 服务器的内存应用。这个客户在他们的 OpenResty/Nginx 配置文件中定义了许多虚构服务器和 URI location。OpenResty XRay 在客户的生产环境中主动进行了大部分剖析,基于剖析后果给出的计划让 nginx 过程的内存占用缩小了大概 30%。 和咱们的 OpenResty Edge 的 nginx worker 过程相比显示, 进一步的优化将会持续缩小约 90%。 OpenResty XRay 是一个动静追踪产品,它能够主动剖析正在运行中的应用程序,以排除性能问题、行为问题和安全漏洞,并提供可行的倡议。在底层实现上,OpenResty XRay 由咱们的 Y 语言驱动,能够在不同环境下反对多种不同的运行时,如 Stap+, eBPF+, GDB 和 ODB。 挑战这个 CDN 供应商应用一个超大的“nginx.conf”配置文件来为他们的 OpenResty 服务器中的近万个虚拟主机服务。每个 nginx 主过程在启动后占用了好几个 G 的内存,在一次或屡次 HUP reload 后,内存简直翻倍。从上面 OpenResty XRay 生成的图中能够看出,最大内存占用约为 4.60GB。 咱们能够从 OpenResty XRay 的利用层面内存应用明细表中看到,Glibc 分配器占用了大部分常驻内存,有 4.55GB。 而 OpenResty XRay 发现 Nginx cycle pool 占用了大量的内存: ...

February 17, 2023 · 2 min · jiezi

关于openresty:openresty-redis工具类luarestyredis

个性应用连接池连贯只须要一次认证装置opm install openresty/lua-resty-redis代码 redis.lua-- redis客户端 local redis = require("resty/redis")local config = { host = "127.0.0.1", port = 6379, password = "", db_index = 0, max_idle_time = 30000, database = 0, pool_size = 100, timeout = 5000,}local _M = {}function _M.new() local instance = { host = config.host or "127.0.0.1", port = config.port or 6379, password = config.password or "", timeout = config.timeout or 5000, database = config.database or 0, max_idle_time = config.max_idle_time or 60000, pool_size = config.pool_size or 100 } setmetatable(instance, {__index = _M}) return instanceendfunction _M:exec(func) local red = redis:new() -- 为后续操作设置超时(以毫秒为单位)爱护,包含connect办法。 red:set_timeout(self.timeout) -- 建设连贯 local ok, err = red:connect(self.host, self.port) if not ok then ngx.log(ngx.ERR, "Redis: ", "Cannot connect, host: " .. self.host .. ", port: " .. self.port) return nil, err end if self.password ~= "" then -- 如果连贯来自于连接池中,get_reused_times() 永远返回一个非零的值 -- 只有新的连贯才会进行受权 local count, err = red:get_reused_times() if count == 0 then ok, err = red:auth(self.password) if not ok then red:close() return ok, err end end end if self.database ~= 0 then red:select(self.database) end -- 执行业务逻辑 local res, err = func(red) -- 将连贯放回连接池 local ok, err = red:set_keepalive(self.max_idle_time, self.pool_size) if not ok then red:close() end return res, errendreturn _M理论应用local redis = require("redis")local red = redis.new()local v, err = red:exec(function(red) return red:get("wechat:login_key")end)-- print(v, type(v), err, type(err))-- v 不存在时是UserData类型if not v or v == ngx.null then -- 解决key不存在状况end-- 解决存在状况参考文章https://segmentfault.com/a/11...https://github.com/openresty/...https://xiaorui.cc/archives/4784https://cloud.tencent.com/dev... ...

November 9, 2022 · 2 min · jiezi

关于openresty:openresty微信公众平台开发

我的项目源码https://github.com/helloJiu/o... openresty源码装置(ubuntu为例)apt install gcc libpcre3-dev libssl-dev perl make build-essential zlib1g-devwget https://openresty.org/download/openresty-1.19.9.1.tar.gztar -zxvf openresty-1.19.9.1.tar.gzcd openresty-1.19.9.1/./configuremake && make install装置luarockswget https://luarocks.github.io/luarocks/releases/luarocks-2.4.3.tar.gztar -xzvf luarocks-2.4.3.tar.gzcd luarocks-2.4.3/./configure --prefix=/usr/local/openresty/luajit --with-lua=/usr/local/openresty/luajit/ --lua-suffix=jit --with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1make && make install配置环境变量vim /etc/profileexport PATH=$PATH:/usr/local/openresty/bin:/usr/local/openresty/luajit/binsource /etc/profile# 设置lua软链到luajitln -s /usr/local/openresty/luajit/bin/luajit luamv lua /usr/bin/装置lapishttps://leafo.net/lapis//usr/local/openresty/luajit/bin/luarocks install lapis装置redis依赖包和http-client依赖包以及其余依赖opm install lua-resty-stringopm install openresty/lua-resty-redisopm install ledgetech/lua-resty-http微信公众平台筹备测试号申请https://mp.weixin.qq.com/debu... 内网穿透工具https://www.cpolar.com/ cpolar.exe http 8123配置# 测试号信息appID xxxappsecret xxx#接口配置信息批改内网穿透失去的地址 如https://444aece.r6.cpolar.top/wechat/accept# 验证Token 对应配置里的wechat.verifyTokenhelloworld配置app/config/config.lua -- 微信相干配置 wechat = { appId = "xxx", --公众号id appSecret = "xxx", -- 公众号秘钥 verifyToken = "helloworld", -- 验证Token }, -- redis相干配置 redis = { host = "127.0.0.1", port = 6379, password = "", db_index = 0, max_idle_time = 30000, database = 0, pool_size = 100, timeout = 5000, },启动我的项目lapis server压力测试## autocannon压测命令须要应用npm装置autocannon -c 100 -d 30 -p 2 -t 2 http://127.0.0.1:8123/wechat/checkLogin?scene=NHAK5ElJqz73YHaYhltG## 运行后果Running 30s test @ http://10.254.39.195:8123/wechat/checkLogin?scene=NHAK5ElJqz73YHaYhltG100 connections with 2 pipelining factor┌─────────┬───────┬────────┬────────┬────────┬───────────┬───────────┬─────────┐│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │├─────────┼───────┼────────┼────────┼────────┼───────────┼───────────┼─────────┤│ Latency │ 12 ms │ 314 ms │ 652 ms │ 701 ms │ 316.26 ms │ 186.86 ms │ 3094 ms │└─────────┴───────┴────────┴────────┴────────┴───────────┴───────────┴─────────┘┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤│ Req/Sec │ 7259 │ 7259 │ 8807 │ 9207 │ 8714.94 │ 436.3 │ 7258 │├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤│ Bytes/Sec │ 1.58 MB │ 1.58 MB │ 1.92 MB │ 2.01 MB │ 1.9 MB │ 95.1 kB │ 1.58 MB │└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘Req/Bytes counts sampled once per second.# of samples: 30267k requests in 30.03s, 57 MB read55 errors (0 timeouts)## QPS大略8700+

November 8, 2022 · 2 min · jiezi

关于openresty:在openresty上基于是lock和redis快速搭建高性能long-polling推送服务

为啥须要?在理论开发中咱们常常会遇到须要长时间期待后盾事件的状况,例如较为常见的扫码登录性能,二维码界面需期待后盾扫码登录胜利的事件,再如导入导出等须要较长时间能力解决实现的工作,此时须要把工作放到后盾由异步工作进行解决,实现后再给前台界面推送实现事件,以上需要咱们须要用长连贯能力实现推送,但长连贯推送状态治理简单,且须要部署独立零碎,零碎流程简单且横向程度扩大艰难,此时抉择更简略long polling期待是一个更好的抉择,http申请间接期待返回,显然逻辑更简略,可用性可维护性也会更高。 openresty是一个构建在nginx上的高性能能零碎,个别状况下咱们也须要在本身服务前部署nginx作为网关,那么抉择openresty来构建一个高性能的long polling服务显然是一个好抉择。slock是高性能的状态及原子操作数据库,redis则是高性能的内存缓存数据库,应用下边nginx配置文件即可疾速基于slock和redis构建一个高性能高可用long polling服务。同时构建的此long polling服务是一个通用服务,即可用于扫码登录这样的需要实现状态推送,也可用于像音讯零碎、私信零碎等的音讯推送。 slock我的项目地址:https://github.com/snower/slock slock简介可看:https://segmentfault.com/a/11... 疾速配置构建首先需在装置好的openresty服务中装置slock的lua client包。 我的项目地址:https://github.com/snower/slo... 装置形式即把slock-lua-nginx中slock.lua复制到openresty目录中的lualib/中,而后增加以下nginx配置文件批改相干参数即可。 init_worker_by_lua_block { local slock = require "slock" slock:connect("server1", "127.0.0.1", 5658)}server { listen 8081; default_type application/json; location /poll/event { content_by_lua_block { local cjson = require "cjson" local slock = require "slock" local slock_client = slock:get("server1") local default_type = ngx.var.arg_default_type or "clear" local wait_type = ngx.var.arg_wait_type or "" local event_key = ngx.var.arg_event or "" local wait_timeout = tonumber(ngx.var.arg_timeout) or 60 local sendResult = function(err_code, err_message) ngx.say(cjson.encode({ err_code = err_code, err_message = err_message, })) end if event_key == "" then return sendResult(400, "event key is empty") end local event = nil if default_type == "set" then event = slock_client:newDefaultSetEvent(event_key, 5, wait_timeout * 2) else event = slock_client:newDefaultClearEvent(event_key, 5, wait_timeout * 2) end if wait_type == "reset" then local ok, err = event:waitAndTimeoutRetryClear(wait_timeout) if not ok then return sendResult(504, "wait event timeout") end return sendResult(0, "succed") end local ok, err = event:wait(wait_timeout) if not ok then return sendResult(504, "wait event timeout") end return sendResult(0, "succed") } } location /poll/message { content_by_lua_block { local cjson = require "cjson" local redis = require "resty.redis" local slock = require "slock" local redis_client = redis:new() local slock_client = slock:get("server1") local default_type = ngx.var.arg_default_type or "clear" local wait_type = ngx.var.arg_wait_type or "" local event_key = ngx.var.arg_event or "" local wait_timeout = tonumber(ngx.var.arg_timeout) or 60 local sendResult = function(err_code, err_message, data) ngx.say(cjson.encode({ err_code = err_code, err_message = err_message, data = data, })) end if event_key == "" then return sendResult(400, "event key is empty") end redis_client:set_timeouts(5000, wait_timeout * 500, wait_timeout * 500) local ok, err = redis_client:connect("10.10.10.251", 6379) if not ok then return sendResult(502, "redis connect fail") end local message, err = redis_client:lpop(event_key) if err ~= nil then return sendResult(500, "redis lpop fail") end if message ~= ngx.null then redis_client:set_keepalive(7200000, 16) return sendResult(0, "", message) end local event = nil if default_type == "set" then event = slock_client:newDefaultSetEvent(event_key, 5, wait_timeout * 2) else event = slock_client:newDefaultClearEvent(event_key, 5, wait_timeout * 2) end if wait_type == "reset" then local ok, err = event:waitAndTimeoutRetryClear(wait_timeout) if not ok then return sendResult(504, "wait timeout") end local message, err = redis_client:lpop(event_key) if err ~= nil then return sendResult(500, "redis lpop fail") end redis_client:set_keepalive(7200000, 16) return sendResult(0, "succed", message) end local ok, err = event:wait(wait_timeout) if not ok then return sendResult(504, "wait timeout") end local message, err = redis_client:lpop(event_key) if err ~= nil then return sendResult(500, "redis lpop fail") end redis_client:set_keepalive(7200000, 16) return sendResult(0, "succed", message) } }}/poll/event 接口只期待事件触发,不返回数据。 ...

December 28, 2021 · 3 min · jiezi

关于openresty:centos7-源码安装openresty1193

1、下载openresty-1.19.3.1 源码包cd /usr/local/wget https://openresty.org/download/openresty-1.19.3.1.tar.gz2、下载openssl prce zlibtar -xf openresty-1.19.3.1.tar.gz cd openresty-1.19.3.1/bundlewget https://www.openssl.org/source/openssl-1.0.2k.tar.gzwget https://jaist.dl.sourceforge.net/project/libpng/zlib/1.2.11/zlib-1.2.11.tar.gzwget http://download.zhufunin.com/pcre-8.42.tar.gz 或者wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.42.tar.gzwget https://www.openssl.org/source/openssl-1.1.1k.tar.gzwget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz 3、解压、倡议找一个目录,放在一起,我的是 /home/openrestyyum -y install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl perl-devel perl-ExtUtils-Embedopenresty-1.19.3.1/bundle目录里寄存着nginx外围和很多第三方模块,比方有咱们须要的Lua和LuaJIT。3.1、装置LuaJITcd bundle/LuaJIT-2.1-20201027/ make clean && make && make install ln -sf luajit-2.1.0-beta3 /usr/local/bin/luajit 3.2 编译装置[root@t0-xxx-visit-web01 openresty-1.19.3.1]# ./configure --prefix=/usr/local/openresty-1.19.3.1/openresty --with-openssl=./bundle/openssl-1.1.1k --with-pcre=./bundle/pcre-8.42 --with-luajit --with-http_realip_module --with-zlib=./bundle/zlib-1.2.11 --add-module=./bundle/ngx_cache_purge-2.3 --add-module=./bundle/nginx-sticky-module --with-select_module --with-poll_module --with-file-aio --with-http_ssl_module --with-http_gzip_static_module --with-http_secure_link_module --with-http_sub_module --with-http_stub_status_module --with-http_perl_module --with-stream_ssl_preread_module --with-pcre-jit --with-stream --with-stream_ssl_module --with-http_v2_module --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --with-http_addition_module --with-http_auth_request_module --with-http_random_index_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-threads[root@t0-xxx-visit-web01 openresty-1.19.3.1]# gmake && gmake install–with* 装置一些内置/集成的模块 ...

November 5, 2021 · 1 min · jiezi

关于openresty:飞书-Lua-实现企业级组织架构登录认证

飞书是字节跳动旗下一款企业级协同办公软件,本文将介绍如何基于飞书开放平台的身份验证能力,应用 Lua 实现企业级组织架构的登录认证网关。 登录流程让咱们首先看一下飞书第三方网站免登的整体流程: 第一步: 网页后端发现用户未登录,申请身份验证;第二步: 用户登录后,开放平台生成登录预受权码,302跳转至重定向地址;第三步: 网页后端调用获取登录用户身份校验登录预受权码合法性,获取到用户身份;第四步: 如需其余用户信息,网页后端可调用获取用户信息(身份验证)。 Lua 实现飞书接口局部实现获取利用的 access_tokenfunction _M:get_app_access_token() local url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/" local body = { app_id = self.app_id, app_secret = self.app_secret } local res, err = http_post(url, body, nil) if not res then return nil, err end if res.status ~= 200 then return nil, res.body end local data = json.decode(res.body) if data["code"] ~= 0 then return nil, res.body end return data["tenant_access_token"]end通过回调 code 获取登录用户信息function _M:get_login_user(code) local app_access_token, err = self:get_app_access_token() if not app_access_token then return nil, "get app_access_token failed: " .. err end local url = "https://open.feishu.cn/open-apis/authen/v1/access_token" local headers = { Authorization = "Bearer " .. app_access_token } local body = { grant_type = "authorization_code", code = code } ngx.log(ngx.ERR, json.encode(body)) local res, err = http_post(url, body, headers) if not res then return nil, err end local data = json.decode(res.body) if data["code"] ~= 0 then return nil, res.body end return data["data"]end获取用户详细信息获取登录用户信息时无奈获取到用户的部门信息,故这里须要应用登录用户信息中的 open_id 获取用户的详细信息,同时 user_access_token 也是来自于获取到的登录用户信息。 ...

August 13, 2021 · 3 min · jiezi

关于openresty:得物技术初探OpenResty

简介Nginx 的高性能是业界公认的,近年来在寰球服务器市场上的占比份额也在逐年减少,在国内出名互联网公司也有宽泛的利用,阿里还基于Nginx进行扩大打造了驰名的Tengine。而OpenResty是由国人章亦春基于Nginx和LuaJIT打造的动静web平台,LuaJIT是Lua编程语言的即时编译器。Lua是一种弱小、动静、轻量级的编程语言。该语言的设计目标是为了嵌入应用程序中,从而为应用程序提供灵便的扩大和定制性能,OpenResty就是通过应用Lua来扩大Nginx来实现的可扩大Web平台。目前OpenResty 大多用在 API 网关的开发中,当然也能够用来代替Nginx,用于反向代理和负载平衡的场景。 OpenResty 的架构组成 如前所述,OpenResty 底层是基于Nginx 和 LuaJIT 的,所以 OpenResty 继承了 Nginx 的多过程架构, 每一个 Worker 过程都是 fork Master 过程而失去的, 其实, Master 过程中的 LuaJIT 虚拟机也会一起 fork 过去。在同一个 Worker 内的所有协程,都会共享这个 LuaJIT 虚拟机,Lua 代码的执行也是在这个虚拟机中实现的。而在同一个工夫点上,每个 Worker 过程只能解决一个用户的申请,也就是只有一个协程在运行。 Nginx因为 Nginx 解决申请采纳的是事件驱动模型,所以每一个 Worker过程最好独占一个CPU。实际中咱们往往把 Worker 过程的数量配置成与CPU核数雷同,此外把每一个 Worker 过程与某一个CPU核绑定在一起,这样能够更好的应用每一个CPU核上的CPU缓存,缩小缓存生效的命中率,进而进步申请解决的性能。 LuaJIT其实 OpenResty 最后默认应用的是规范Lua,从 1.5.8.1 版本开始才默认应用 LuaJIT,背地的起因是因为 LuaJIT 相比规范Lua有很大的性能劣势。 首先,LuaJIT 的运行时环境除了一个汇编实现的 Lua 解释器外,还有一个能够间接生成机器代码的 JIT 编译器。开始的时候,LuaJIT 和规范 Lua 一样,Lua 代码被编译为字节码,字节码被 LuaJIT 的解释器解释执行。但不同的是,LuaJIT 的解释器会在执行字节码的同时,记录一些运行时的统计信息,比方每个 Lua 函数调用入口的理论运行次数,还有每个 Lua 循环的理论执行次数。当这些次数超过某个随机的阈值时,便认为对应的 Lua 函数入口或者对应的 Lua 循环足够热,这时便会触发 JIT 编译器开始工作。JIT 编译器会从热函数的入口或者热循环的某个地位开始,尝试编译对应的 Lua 代码门路。编译的过程,是把 LuaJIT 字节码先转换成 LuaJIT 本人定义的两头码(IR),而后再生成指标机器的机器码。这个过程跟Java中JIT编译器工作原理相似,其实它们都是为了进步程序运行效率而采取的同一类优化伎俩,正所谓底层技术都是相通的,能够类比学习。 ...

August 13, 2021 · 2 min · jiezi

关于openresty:Lua-级别-CPU-火焰图简介

在 OpenResty 或 Nginx 服务器中运行 Lua 代码现在曾经变得越来越常见,因为人们心愿他们的非阻塞的 Web 服务器可能兼具超高的性能和很大的灵活性。有些人应用 Lua 实现一些非常简单的工作,比方检查和批改某些申请头和响应体数据,而有些人则利用Lua 创立非常复杂的 Web 利用、 CDN 软件和 API 网关等等。Lua 以简略、内存占用小和运行效率高而著称,尤其是在应用 LuaJIT 这样的的即时编译器 (JIT) 的时候。但有些时候,在 OpenResty 或 Nginx 服务器上运行的 Lua 代码也会耗费过多的 CPU 资源。通常这是因为程序员的编程谬误,比方调用了一些低廉的 C/C++ 库代码,或者其余起因。 要想在一个在线的 OpenResty 或 Nginx 服务器中疾速地定位所有的 CPU 性能瓶颈,最好的办法是应用 OpenResty XRay 产品提供的 Lua 语言级别 CPU 火焰图的采样工具。这个工具不 须要对 OpenResty 或 Nginx 的指标过程做任何批改,也不会对生产环境中的过程产生任何可发觉的影响。 本文将解释什么是火焰图,以及什么是 Lua 级别的 CPU 火焰图,会交叉应用多个玲珑且独立的 Lua 代码实例来做演示。咱们将利用 OpenResty XRay 来生成这些示例的火焰图来进行解说和剖析。咱们抉择小例子的起因是,它们更容易预测和验证各种性能剖析的后果。雷同的分析方法和工具也实用于那些最简单的 Lua 利用。过来这几年,咱们应用这种技术和可视化形式,胜利地帮忙了许多领有忙碌网站或利用的企业客户。 什么是火焰图火焰图是由 Brendan Gregg 创造的一种可视化办法,用于展现某一种系统资源或性能指标,是如何定量散布在目标软件里所有的代码门路上的。 ...

October 8, 2020 · 5 min · jiezi

关于openresty:Introduction-to-LuaLand-CPU-Flame-Graphs

Lua code running inside OpenResty or Nginx servers is very common nowadays since people want both performance and flexibility out of their nonblocking web servers. Some people use Lua for very simple tasks like modifying and checking certain request headers and response bodies while other people use Lua to build very complicated web applications, CDN software, and API gateways. Lua is known for its simplicity, small memory footprint, and efficiency, especially when using Just-in-Time (JIT) compilers like LuaJIT. But still some times the Lua code running atop OpenResty or Nginx servers may consume too much CPU resources due to the programmer's coding mistakes, calling out to some expensive C/C++ library code, or some other reasons. ...

September 2, 2020 · 16 min · jiezi

关于openresty:Memory-Fragmentation-in-OpenResty-and-Nginx-Shared-Memory-Zones

Memory fragmentation is a common problem in computer systems though many clever algorithms have emerged to tackle it. Memory fragmentation wastes free memory blocks scattered in a memory region and these free blocks cannot be merged as a whole to serve future requests for large memory blocks or cannot be returned to the operating system for other use 1. This could lead to a phenomenon of memory leaks since the total memory needed to fulfill more and more memory requests for large blocks would grow indefinitely. This kind of indefinite memory usage growth is usually not considered memory leaks in common perception since unused memory blocks are indeed released and marked free but they are just cannot be reused (for larger memory block requests) nor can be returned to the operating system. ...

August 25, 2020 · 9 min · jiezi

关于openresty:OpenResty-和-Nginx-的共享内存区是如何消耗物理内存的

OpenResty 和 Nginx 服务器通常会配置共享内存区,用于贮存在所有工作过程之间共享的数据。例如,Nginx 规范模块 ngx_http_limit_req 和 ngx_http_limit_conn 应用共享内存区贮存状态数据,以限度所有工作过程中的用户申请速率和用户申请的并发度。OpenResty 的 ngx_lua 模块通过 lua_shared_dict,向用户 Lua 代码提供基于共享内存的数据字典存储。 本文通过几个简略和独立的例子,探讨这些共享内存区如何应用物理内存资源(或 RAM)。咱们还会探讨共享内存的使用率对系统层面的过程内存指标的影响,例如在 ps 等零碎工具的后果中的 VSZ 和 RSS 等指标。 与本博客网站 中的简直所有技术类文章相似,咱们应用 OpenResty XRay 这款动静追踪产品对未经批改的 OpenResty 或 Nginx 服务器和利用的外部进行深度剖析和可视化出现。因为 OpenResty XRay 是一个非侵入性的剖析平台,所以咱们不须要对 OpenResty 或 Nginx 的指标过程做任何批改 -- 不须要代码注入,也不须要在指标过程中加载非凡插件或模块。这样能够保障咱们通过 OpenResty XRay 剖析工具所看到的指标过程外部状态,与没有观察者时的状态是完全一致的。 咱们将在少数示例中应用 ngx_lua 模块的 lua_shared_dict,因为该模块能够应用自定义的 Lua 代码进行编程。咱们在这些示例中展现的行为和问题,也同样实用于所有规范 Nginx 模块和第三方模块中的其余共享内存区。 Slab 与内存页Nginx 及其模块通常应用 Nginx 外围里的 slab 分配器 来治理共享内存区内的空间。这个slab 分配器专门用于在固定大小的内存区内调配和开释较小的内存块。 在 slab 的根底之上,共享内存区会引入更高层面的数据结构,例如红黑树和链表等等。 slab 可能小至几个字节,也可能大至逾越多个内存页。 ...

August 12, 2020 · 3 min · jiezi

关于openresty:CVE202011724OpenResty-HTTP-request-smuggling-漏洞

OpenResty 最近公布的正式版本 1.17.8.2 修复了安全漏洞 CVE-2020-11724。这个破绽是一个 HTTP request smuggling 破绽,能够实现某种程度上的平安防护绕过。 0x01 什么是 HTTP request smugglingHTTP request smuggling 指利用两台 HTTP 服务器在解析 HTTP 协定的过程中的差别,来结构一个在两台服务器看来不同的申请。通常这样的做法能够用来绕过平安防护,比方发送申请 A 给 Web 防火墙,而后 Web 防火墙代理给后端应用服务器时,让后端服务器误以为是申请 B。 0x02 这次的破绽长什么样子先上一个 exploit: server { listen 1984; server_name 'localhost'; location /test1 { content_by_lua_block { local res = ngx.location.capture('/backend') ngx.print(res.body) } } location /app { content_by_lua_block { ngx.log(ngx.ERR, ngx.var.uri) } } location /backend { proxy_http_version 1.1; proxy_set_header Connection ""; proxy_pass http://backend/app/api; } location /t { content_by_lua_block { local sock = ngx.socket.tcp() sock:settimeout(500) assert(sock:connect("127.0.0.1", 1984)) local req = [[GET /test1 HTTP/1.1Host: fooTransfer-Encoding: chunkedContent-Length: 420GET /test1 HTTP/1.1Host: fooX: GET /app/admin HTTP/1.0]] local ok, err = sock:send(req) if not ok then ngx.say("send request failed: ", err) return end sock:close() } } }}为了便于复现,这里咱们把 OpenRestry 即当作客户端,也把它当作代理服务器和后端利用。其中 /t 用于作为客户端触发 HTTP 申请,/test1 则是作为代理服务器解决申请。/test1 通过 subrequest 申请了 /backend,而 /backend 作为代理拜访了后端利用 /app。 ...

July 25, 2020 · 2 min · jiezi

关于openresty:The-Wonderland-of-Dynamic-Tracing-Part-4-of-7

This is Part 4 of the series "The Wonderland of Dynamic Tracing" which consists of 7 parts. I will keep updating this series to reflect the state of art of the dynamic tracing world. The previous one, Part 3, introduced various real world use cases of SystemTap in production. This part will take a close look at Flame Graphs which were frequently mentioned in the previous part. ...

July 18, 2020 · 7 min · jiezi

The-Wonderland-of-Dynamic-Tracing-Part-3-of-7

This is Part 3 of the series "The Wonderland of Dynamic Tracing" which consists of 7 parts. I will keep updating this series to reflect the state of art of the dynamic tracing world. The previous one, Part 2, introduced DTrace and SystemTap, two famous dynamic tracing frameworks. This part will continue looking at real world applications of SystemTap. See also Part 1 and Part 4. ...

July 17, 2020 · 10 min · jiezi

The-Wonderland-of-Dynamic-Tracing-Part-2-of-7

This is the second part of the 7 part series "The Wonderland of Dynamic Tracing." This series will consist of updates on the state of art of the dynamic tracing world. The previous part, Part 1, introduced the basic concepts of dynamic tracing and covered its advantages. This part will continue looking at some of the most popular dynamic tracing frameworks in the open source world. ...

July 16, 2020 · 8 min · jiezi

The-Wonderland-of-Dynamic-Tracing-Part-1-of-7

This is the first part of the article "The Wonderland of Dynamic Tracing" which consists of 7 parts. I will keep updating this series to reflect the state of art of the dynamic tracing world. See also Part 2, Part 3, and Part 4. Dynamic TracingIt’s my great pleasure to share my thoughts on dynamic tracing —— a topic I have a lot of passion and excitement for. Let’s cut to the chase: what is dynamic tracing? ...

July 15, 2020 · 10 min · jiezi

The-OpenResty-10Year-Community-Report-OpenSource-Projects

Over the years, OpenResty’s open source community has grown bit by bit right before our eyes. Casting our memories back to 2014, before OpenResty Inc. became a full-fledged company and was instead a seedling idea, we looked back at your involvements in OpenResty. Remembering the last half-decade of the OpenResty Github family, we wanted to share some of OpenResty’s history, and celebrate OpenResty diving headfirst into a new decade. ...

July 14, 2020 · 3 min · jiezi

OpenResty-11781-released

OpenResty 1.17.8.1, based on the Nginx 1.17.8 core, has been officially released. For the full announcement with a list of detailed changes, highlights, and testing, see here.Because of the miscellaneous security updates, we recommend all OpenResty users to upgrade to this version.A brief overview of the most notable changes: The OpenSSL used by all our official OpenResty binaries has been upgraded to 1.1.1g.This is the latest version of the main line of 1.1.1 series.We have also added the official OpenResty binary repository for the following Linux distributions:Alpine 3.12, OpenSUSE 15.2, Ubuntu 14.04, and Fedora 28. In addition, we have dropped support for PUC-Rio Lua;from now on, only LuaJIT 2.x is supported.Furthermore, our final release tarball size is now 3.5MB, down from 4.7MB. ...

July 13, 2020 · 2 min · jiezi

OpenResty-11781-新版发布

基于 Nginx 1.17.8 外围的 OpenResty 1.17.8.1 正式公布。全副公布内容和具体批改、变更和测试相干的信息,参考这里。因为本版本蕴含很多安全更新,咱们倡议所有 OpenResty 用户降级到此版本。次要变更简介:所有官网 OpenResty 二进制应用的 OpenSSL 降级到了 1.1.1。这是是主线 OpenSSL 1.1.1 版本系列的最新版本。咱们还给下列 Linux 公布增加了官网的 OpenResty 二进制仓库:Alpine 3.12,OpenSUSE 15.2,Ubuntu 14.04,和 Fedora 28。另外咱们勾销了 PUC-Rio Lua 的反对;当初开始,只反对 LuaJIT 2.x。另外,咱们最初的 tar 包尺寸当初是 3.5MB,比之前的 4.7MB 小不少。 致谢向咱们所有开发人员,赞助者和贡献者示意特地的感激!同时感激 Thibault Charbonnier,Junlong Li 和 Lujia Zhai 在筹备此版本时提供的帮忙。 残缺 Changelog自最新(正式)的 1.17.8.1 的公布以来所有的变更历史,都能够浏览这个页面 。 反馈欢送给本版本提供反馈。欢送到 GitHub issues 创立新 issue 或者给咱们的邮件列表之一发送邮件,或者到咱们新建设的 bbs 探讨。 下一个版本下一个版本的 OpenResty 打算在十月初公布,只有 3 个月的工夫,将基于 Nginx 1.19.x 系列外围。向所有的支持者、贡献者和用户表示感谢! 其余对咱们新的商业产品:OpenResty XRay 感兴趣的用户,能够参阅这里获取更多信息。 ...

July 13, 2020 · 1 min · jiezi

OpenResty-Inc-锁定四百万融资领先流量管理软件同时发布实时诊断新品

OpenResty Inc. 在 2020 年 4 月 20 日完成了 Sphere 5200 领投的 A 轮融资———丝毫没有受到 Covid-19 疫情的影响。OpenResty Inc. 是一个开发针对 web 应用的性能、可靠性和安全性进行智能分析工具的公司。自 2017 年成立以来,已经获得了超过 400 万美元的资金。 总部位于旧金山湾区的 OpenResty 公司致力于研发可以不侵入用户代码的开源软件环境下的问题和 bug 分析工具,可透明地分析和追踪 web 应用。OpenResty 公司已经闻名于其强大可靠的网络流量管理软件 OpenResty Edge,此次投资令 OpenResty 公司可以投入更广泛和更细致的开源软件相关工具链,名为 OpenResty XRay 的工具的研发以及进一步充实网络流量管理软件领域的地位,简而言之,追求领域卓越。目前这两种软件都可以在 OpenResty 官网 https://www.openresty.com 申请试用。此次融资无疑将进一步扩大 OpenResty 的行业领先地位。 目前,该公司的 OpenResty 开源流量管理器在全球有着超过 4 百万次下载、在 GitHub 上有超过 3 万 5 千颗星的好评。OpenResty 的产品用于帮助开发人员和商业公司轻松构建可伸缩的 web 应用、web 服务和动态 web 网关。大量流行的互联网站点都是建立在 OpenResty 提供的可靠的软件基础上,无论是国际和中国,从传统门户网站到在线电商,从 CDN 服务商到音视频播客网站,大多数都采用了某种形式的开源 OpenResty。对于未来,OpenResty Inc. 目标是成为世界上第一批自动化编程的公司。 ...

June 3, 2020 · 1 min · jiezi

学习到实践六docker自定义nginxopenresty

前言为什么要使用openresty?官方说明:OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。所以。但openresty官方没有发布docker相关东西,所以以:结合openresty安装、参考docker-nginx官方的为原则编写。 1.依赖安装测试官方声明依赖:perl 5.6.1+, libpcre, libssl a.按nginx镜像测试# perl查找/ # apk search perl5perl-5.26.3-r0 #就一个可选/ # apk search libpcrelibpcre2-32-10.32-r1 #貌似最新版本了,32位的?pcre-dev-8.42-r1libpcre2-16-10.32-r1pcre2-dev-10.32-r1libpcre16-8.42-r1libpcre32-8.42-r1pcre2-10.32-r1pcre-8.42-r1libpcrecpp-8.42-r1/ # apk search libsslopenssl-dev-1.1.1b-r1libressl-dev-2.7.5-r0 #看起来最合适nss-3.41-r0libssl1.1-1.1.1b-r1dovecot-2.3.6-r0libressl2.7-libssl-2.7.5-r0/ # apk add perl-5.26.3-r0 libpcre2-32-10.32-r1 libressl-dev-2.7.5-r0# 完全报错,这个alpine依赖搞不了,官方https://pkgs.alpinelinux.org/包的搜索页捉襟见肘,不得不放弃“小而美”。b.容器测试参考官方选则debian,github上构建平台镜像的许多镜像选择stretch-slim精简版的,看下大小最新的只有55.3M,比较满意。环境安装测试: []:~/tmp/dk/openresty# docker run -itd --name df -v /root/tmp/dk/openresty:/tmp/host debian:stretch-slim[]:~/tmp/dk/openresty# docker exec df -it /bin/bash初始安装清华源,切换百度源(云本机),apt-get update超慢,后回归官源,后期卡死用网易源。问题:1、./configure: error: ngx_postgres addon was unable to detect version of the libpq library. apt-get libpq-dev -y2、./configure: error: the HTTP gzip module requires the zlib library. ...

June 29, 2019 · 2 min · jiezi

学完这些课快速助你提升Level

本文为大家推荐一些博主已经学习过的付费课程。请放心,博主不会随便推荐课程给大家。下面这些课程质量非常高,学完你的Redis、MySQL等技能必定提升一个Level! Tips: 通过本文配图里的海报购买课程,相比直接去官网购买可以享受一定的优惠。Redis 深度历险:核心原理与应用实践大型互联网企业 Redis 实践总结,结合实际问题深入讲解 Redis 内部机制。小册在内容结构上分为 Redis 基础应用、原理、集群、拓展学习和源码分析 5 个版块: Redis 基础应用:占据篇幅最长,这也是对读者最有价值的内容,可以直接应用到实际工作中。原理和集群版块:适合对技术有着极致追求的开发者,他们希望透过简单的技术表面看到精致的底层世界。拓展学习版块:作为最核心内容之外的补充部分,主要用于进一步扩展技术视野或者夯实基础,便于进阶学习,作者会尽可能的在拓展篇持续扩充更多知识点。源码分析版块:主要满足高阶用户深入探索 Redis 内部实现的强烈渴望,这类读者坚信读懂源码才是技术实力的真正体现。 MySQL 是怎样运行的:从根儿上理解 MySQL授人以鱼不如授人以渔,从根儿上理解 MySQL,让 MySQL 不再是一个黑盒。你会学到什么? MySQL 的一些基本概念;如何处理使用 MySQL 过程中的乱码问题;从根儿上理解 InnoDB 存储引擎是如何存储记录、数据页,以及由页作为节点组成的B+树索引的原理;理解 InnoDB 存储引擎的表空间概念,知道 InnoDB 是如何管理段、区、页这些玩意儿以及 InnoDB 的数据字典;理解 MySQL 是如何执行单表查询、如何执行连接查询;理解 MySQL 基于代价的优化和基于规则的优化到底是啥意思;知道如何查看自己写的查询语句是好是坏,学会使用optimizer tracer;理解为什么需要事务以及它的基本概念;redo 和 undo 日志的作用以及在 MySQL 中这些日志的细节;理解并发带来的各种问题以及 MySQL 中使用锁的各种细节;理解学习的快乐,希望各位看完本小册就像是读完一本小说一样畅快淋漓。 OpenResty从入门到实战pphh 开源书籍《OpenResty 最佳实践》的作者之一,温铭大佬又推出了实战课《OpenResty 从入门到实战》,推荐入手! 专栏目录: (未完待续)

June 4, 2019 · 1 min · jiezi

用OpenResty搭建高性能服务端笔记

概要《用OpenResty搭建高性能服务端》是OpenResty系列课程中的入门课程,主讲人:温铭老师。课程分为10个章节,侧重于OpenResty的基本概念和主要特点的介绍,包括它的指令、nginx_lua API、缓存、如何连接数据库、执行阶段等,并通过几个实际的操作和代码片段,告诉大家学习中如何搭建开发、测试环境,如何调试、查找和解决问题。视频播放地址:https://study.163.com/course/... 课程目录一览: 个人评价:评分满分。内容由浅入深,思路清晰,内容组织有序,容易上手,为初学者打开了一扇学习的大门。非常不错的分享。学完后需再配合 《OpenResty最佳实践》 + 官方文档 进行系统学习。 下面是学习笔记,内容主要是以老师的讲解为主,加上部分自己补充或理解的内容。 本文环境: $ uname -aLinux ba2f3eedf7df 4.4.111-boot2docker #1 SMP Thu Jan 11 16:25:31 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux$ cat /etc/redhat-release CentOS release 6.8 (Final)$ /usr/local/openresty/bin/openresty -vnginx version: openresty/1.13.6.2$ /usr/local/openresty/luajit/bin/luajit -vLuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/OpenResty 简介OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。OpenResty 基于Nginx开发,可以简单认为是 Nginx + lua-nginx-module的组合版。 官网:https://openresty.org/cn/官方文档:https://github.com/openresty/... ...

May 24, 2019 · 6 min · jiezi

OpenResty一些重要特性的整理

OpenResty (简称OR) 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的Lua Api,第三方模块以及常用的依赖项,基于这些能力,我们可以利用OR快速方便的搭建能够处理超高并发的,极具动态性和扩展的Web应用、Web服务和动态网关。 这篇文章选择OR中具有重要意义的一些模块、命令、Api和框架介绍如下: 基础的Http处理OR 作为一款 Web 服务器,提供了如: ngx.print() | ngx.say()ngx.req.get_headers()ngx.resp.get_headers()ngx.header.HEADERngx.exit()等函数和方法控制http请求的输入和输出,我们可以通过这些 api 灵活的控制 Web Server 中包括输入输出,转发到上游或者发起子请求在内的各个环节。 ngx.print同ngx.say(), 通过该 api 可以指定返回的请求的消息体,其中每一次函数的调用,都是在nginx的输出链上增加了一个结点,所以我们可以多次调用该函数而不用担心后者会覆盖前者,如: location =/hello { content_by_lua_block { ngx.print("Hello") ngx.print(" ") ngx.print("World!") }}GET /hello 将会得到如下结果======Hello World!请求头和响应头控制ngx.exit() 和 ngx.statusngx.exit,通过该 api可以指定请求响应体的 http status, 如: location =/error { content_by_lua_block { return ngx.exit(500) }}GET /error======<html><head><title>500 Internal Server Error</title></head><body bgcolor="white"><center><h1>500 Internal Server Error</h1></center><hr><center>openresty</center></body></html>当我们访问 /error 接口的时候,可以得到一个默认的 500 页面,如上所示,当然我们可以通过ngx.status + ngx.print 的组合方式得到自定义的错误页面,如: ...

May 22, 2019 · 2 min · jiezi

OpenResty从入门到实战-课程返现福利

系统掌握一款高性能开发利器 《 OpenResty 从入门到实战 》课程大纲可以说,掌握了 OpenResty,你就可以同时拥有脚本语言的开发效率和迭代速度,以及 NGINX C 模块的高并发和高性能优势。《OpenResty从入门到实战》 作者温铭,同时也是 OpenResty 软件基金会主席,《OpenResty 最佳实践》作者。 关注有课学微信公众号,回复暗号 OR 获取购买地址,购买成功后提交购买截图即可获得返现。

May 22, 2019 · 1 min · jiezi

如何应对-OpenResty-为支持-ARM64-引入的-break-change

本文不是关于新版 OpenResty 如何支持 ARM64 的,而是关于如何应对这一过程引入的 break change。 另外,如果你没用 OpenResty 自己的 LuaJIT 分支,那么可以直接关掉这个页面了,因为这些 break change 只有在使用了 OpenResty 自己的 LuaJIT 分支才会出现。 一切的根源在于,新版本的 OpenResty 把当前请求的 ngx_http_request_t 放到了 luaState 的 exdata 属性里面,不再使用 getfenv(0).__ngx_req 这种方式了。exdata 是 OpenResty 自己的 LuaJIT 分支加的属性,所以如果不用 OpenResty 自己的 LuaJIT 分支,依旧还是得走 getfenv(0).__ngx_req这种方式。同样被移除的还有 getfenv(0).__ngx_cycle 和给每个 main thread 准备的全局环境表。接下来我们谈谈如何应对这几个变化。 getfenv(0).__ngx_req新版 OpenResty 使用 resty.core.base 里面的 get_request() 代替了 getfenv(0).__ngx_req。在你的代码里,可以这么写: local base = require "resty.core.base"local get_request = base.get_requestif not get_request then get_request = function() return getfenv(0).__ngx_req endend...local r = get_request()但是! get_request() 并非 100% 兼容 getfenv(0).__ngx_req。前者返回的是一个 cdata,而后者返回的是一个 lightuserdata。cdata 和 lightuserdata 在语义上有些微妙的不同。当你用 lightuserdata 作为 table 的 hash key 时,如果 lightuserdata 指向的地址相同,那么 hash 值会相同。但如果是 cdata,即使是指向同一地址的指针类型的 cdata,由于计算 hash 时用的是 cdata 的地址,而非其内部的值,所以不同的 cdata 的 hash 值会不一样。举个例子: ...

May 3, 2019 · 2 min · jiezi

在 OpenSSL 1.1.1 下支持 OpenResty 的非阻塞 SSL session fetch

起如果你用 OpenResty 做过 SSL session reuse,你可能会用到其中的异步获取 SSL session 的特性,比如在 ssl_session_fetch_by_lua* 里面发起非阻塞的网络请求,从 memcache 或别的什么存储服务器上读取 SSL session。ssl_session_fetch_by_lua* 的实现原理,就是在 OpenSSL 的 session get callback 里执行 Lua 代码。然而 OpenSSL 的 session get callback 并不支持 yield,所以只靠原生 OpenSSL 是没办法支持在该阶段发起非阻塞的网络请求的。因此,Cloudflare 的工程师贡献了一个 OpenSSL 的补丁给 OpenResty,让 OpenSSL 的 session get callback 支持 yield。要想解释这个补丁的实现原理,我们得先说明下什么是“支持 yield”。在 OpenResty 的语境里,支持 yield 就是指某个函数(或者阶段)可以被中断,然后在合适的时间继续执行。这样的函数需要至少有三种返回结果:OK,ERROR 和 AGAIN。如果函数返回 AGAIN,表示它的工作被中断了,比如发起了一个网络请求,需要等待对端的响应。同时该函数还会把自己的状态保存到一个 ctx 里。当合适的机会来临时,它会被重新调用,读取 ctx 里面的状态继续执行下去,直到返回 OK 或 ERROR 为止。让 session get callback 支持 yield 的补丁最早是针对 OpenSSL 1.0 开发的。后来 OpenSSL 1.1.0 版本大改了握手流程,于是 Cloudflare 的工程师写了第二版补丁。之后 OpenSSL 1.1.0 的小版本又有些许小变化,我便帮着润色了下。针对 OpenSSL 1.1.0 版本的补丁,主要做了两个改动:在调用获取 session 的 callback 的时候,如果 callback 返回一个 magic 值,那么就保存当前的上下文,返回一个表示重试的错误码。在 server 处理 client 请求的状态机中,引入一个新的状态 READ_STATE_PROCESS 。这个状态里,OpenSSL 会尝试 session resumption。该过程有可能会返回一个表示重试的错误码,在这种情况下,如果重新调用 SSL_handshake, OpenSSL 会重新进入这一状态。补丁的代码可以在这里看到:https://github.com/openresty/…除了要给 OpenSSL 打补丁外,该功能还依赖于一个 Nginx 补丁。Nginx 的 ngx_ssl_handshake 只有在 SSL_ERROR_WANT_READ 和 SSL_ERROR_WANT_WRITE 下才会重试 SSL_handshake。 所以 OpenResty 给 Nginx 打了补丁,让它在 SSL_ERROR_PENDING_SESSION 下也能重试。SSL_ERROR_PENDING_SESSION 这个名字来源于 BoringSSL。BoringSSL 是 Google 和 Cloudflare 一起开发的,在这个 SSL 库里面,session fetch 是可以 yield 的。该特性的名字就叫做 PENDING SESSION。ssl_session_fetch_by_lua* 就是先在 Cloudflare 内部使用一段时间,然后再开源出来。自然,这个 Nginx 补丁也是针对 BoringSSL 设计的。为此,OpenSSL 补丁也给自己的错误类型起了个 SSL_ERROR_PENDING_SESSION 的别名。补丁的代码可以在这里看到:https://github.com/openresty/…OpenSSL 1.1.1 版本对握手流程又做了新的变化,变化之大,以致于现有的补丁没法再缝缝补补凑合用下去了。由于这次没有天降 Cloudflare 工程师来帮忙,我就接下了这一任务,开始针对新的握手流程修改当前的补丁。承看了下 OpenSSL 1.1.1 涉及的函数,最大的变化是它把 session resumption 从原来的 READ_STATE_BODY 向后挪到了 READ_STATE_POST_PROCESS。补丁里之所以要引入 READ_STATE_PROCESS 把 READ_STATE_BODY 劈成两半,是因为 READ_STATE_BODY 里包含了读取 TLS 数据块的这一不可重试的操作。但是 READ_STATE_POST_PROCESS 里就没这样的顾忌了。所以我们可以把 READ_STATE_PROCESS 这个阶段去掉。一番调整后,我发现 ssl_session_fetch_by_lua* 现在只要一 yield,就会报 Fatal 错误。一路追查下来发现,OpenSSL 有些函数的返回码改变了。如果照原来的错误码,会走到一个被 SSLfatal 挡路的地方。于是调整了下返回码。现在 ssl_session_fetch_by_lua* 已经能 yield 了,只是 resume 的时候会报 no cipher 的错误。可是我再三确认,握手时是有 cipher 传递的。我拿着 OpenSSL 1.1.0 和 OpenSSL 1.1.1 的源码,就 server 处理 ClientHello 的 post process 部分的流程对比了下,发现还有一处不同。OpenSSL 1.1.0 里面,关于 cipher 处理部分是在 session resumption 之后进行的。而 OpenSSL 1.1.1 为了遵循 TLS 1.3,把 cipher 的处理挪到 session resumption 之前完成。cipher 处理过程中会把 cipher 给消耗掉,导致重入 session resumption 逻辑的时候,报 no cipher 错误。解决方法也很简单,就是在处理 cipher 前把它保存起来,如果要 yield,就恢复它。这么改之后,OpenResty 所有关于 SSL session fetch 的测试在 OpenSSL 1.1.1 下终于能跑通了。补丁的代码可以在这里看到:https://github.com/spacewande…转看上去本文到这里应该完了,但是,还没有呢! 在开发 patch 的过程中,我发现有一个 CLIENT_HELLO_CB,好像在我每次改动的地方都能看到它。这种感觉,就像一路上发现有人一直跟你同路。这种情况下,我不由得对这个 CB 感兴趣起来。查了下发现,OpenSSL 1.1.1 起引入了一个 SSL_CTX_set_client_hello_cb 的函数。这个 callback 在 server 处理 ClientHello 时调用,而且支持 yield。这不就是打完 patch 之后的 session get callback 么! 当然,我们仍然需要通过 session get callback 来设置 session,但已经不需要要求 session get callback 支持 yield 了。我们可以在支持 yield 的 ClientHello callback 中发起非阻塞的网络请求,然后待到 session get callback 再把准备好的 session 丢出去。这么一来,就不需要给 OpenSSL 打 patch 了。合于是我着手修改 OpenResty 的代码,如果当前 OpenSSL 支持 ClientHello callback,就改由 ClientHello callback 执行 ssl_session_fetch_by_lua* 里的 Lua 代码。我们依然需要注册 session get callback,但如果 ClientHello callback 已经执行了 ssl_session_fetch_by_lua*,那么就该 callback 就只需要返回准备好的 session 了。ClientHello callback 还有一个作用,就是你可以在这里面修改 preferred cipher。在没有 ClientHello callback 的时代,你得自己造一个。其余的 callback 还是有覆盖不到的路径。当然,由于只在有 session id 的情况下才会执行 ssl_session_fetch_by_lua* 的逻辑,直接在该阶段里修改 preferred cipher 是行不通的。最后,还需要修改下 Nginx 的那个补丁,让它在 SSL_ERROR_WANT_CLIENT_HELLO_CB 下也重试。你可能会奇怪,为什么我会在开发完对 OpenSSL 1.1.1 的 patch 之后,还要另起炉灶开发 ClientHello callback?因为一直对 OpenSSL 修修补补也不是什么好办法。毕竟我不是专业的 OpenSSL 开发者,改 OpenSSL 可能会改出 bug。虽然目前还需要在 Nginx 端维护一个补丁,但是维护 Nginx 的补丁相对来说简单一些,毕竟 OpenResty 都维护了那么多 Nginx 的补丁,也不缺能改 Nginx 的人。 ...

April 13, 2019 · 2 min · jiezi

openresty/nginx升级http2

http2需要https支持,假设你已经有https证书从源码编译安装openresty解压:tar -xzvf openresty-VERSION.tar.gzcd openresty-VERSION/三连:./configuremakesudo make install此时安装的openresty配置http2会报错 the “http2” parameter requires ngx_http_v2_module重新编译:./configure –with-http_v2_module编译完成后make (不要make insall ,会覆盖安装)编译出的nginx在 openresty-1.13.6.2/build/nginx-1.13.6/objs/里我们之拥替换此二进制文件就好了nginx -s stop先停止nginx将openresty-1.13.6.2/build/nginx-1.13.6/objs/nginx 复制到/usr/local/openresty/nginx/sbin不先停止nginx的话会出现 cp: 无法创建普通文件’./nginx’: 文本文件忙然后将listen的http2配置上启动nginx应该就可以看到如果替换了新编译的nginx二进制文件还是使用http1.1,检查一下openssl版本和编译的模块,openssl版本需大于1.0.1才能支持http2

March 8, 2019 · 1 min · jiezi

如何编写正确且高效的 OpenResty 应用

本文内容,由我在 OpenResty Con 2018 上的同名演讲的演讲稿整理而来。PPT 可以在 这里 下载,因为内容比较多,我就不在这里一张张贴出来了。有些内容需要结合 PPT 才能理解,请多包涵。编写正确且高效的应用,最为关键是一系列软件工程上的实践,像测试、code review、灰度、监控、压测等等。不过由于这是 OpenResty 大会上的演讲,我会专注于讲讲 OpenResty 和 LuaJIT 的一些小细节,帮助各位听众避免线上踩坑。自我介绍按惯例,得先自我介绍下。spacewander,这个是我的 GitHub 昵称。我目前在 OpenResty Inc. 公司工作。第一部分先从 OpenResty 开始讲吧。init_by_luainit_by_lua 是 OpenResty 目前唯一运行在 master 进程里的阶段。它运行的时机非常靠前,就在 Nginx 刚解析完配置之后。这就意味着,只要运行 Nginx 可执行文件,init_by_lua* 里面的代码就会被调用。有些时候,我们运行 Nginx 可执行文件,并不是想启动它的服务。比如在调用 nginx -t 检查配置文件是否正确,或者在调用 nginx -s 控制当前的 master 进程的时候。如果你的代码里包含对本进程以外的资源的修改,这种意料之外的执行是不受欢迎的。那怎么避免呢?对于 -s,可以通过直接用 kill 发送信号的方式代替。对于 -t,略微复杂一点,可以通过 FFI 的方式获取当前 Nginx 进程的命令行参数,判断其中是否包含 -t 选项。PPT 上面包含了这么做的代码。如果 init_by_lua* 里面的代码执行时间过长,比如启动时会先从远程服务器加载数据,可能会带来另一个问题。大多数部署脚本里面,检测 Nginx 进程是否顺利启动,是通过查看 nginx.pid 这个文件实现的。由于 Nginx 是在执行完这一部分 Lua 代码后才会创建 nginx.pid 文件,如果执行时间过长,可能会在部署时造成误判。这时候可能需要恰当地增加查看 nginx.pid 的时间间隔。ngx.worker.id()要想区分不同 worker 进程,通常的做法是用 ngx.worker.id()。需要注意的是,有些时候多个 worker 进程可能会有同样的 id,比如在 reload 或者 binary upgrade 的时候。Nginx 在 reload 的时候,会有两组 worker 进程。新的 worker 们会接替老的 worker,但直到老 worker 退出之前,这两组 worker 是同时运行的。如果 shdict 的配置不变,这两组进程甚至有同样的共享内存空间,所以在用 worker id 作为 shdict key 的时候,这种边界情况需要考虑下。Nginx 在 binary upgrade 的时候,则会有两组 master+workers。这两组进程并不共享内存空间,所以用 worker id 作为 shdict key 时,不用关心这种情况。但是当你用 worker id 作为外部服务或者文件系统的 key 时,还是要注意下的。一种可能的解决方案是引入 parent pid 作为前缀,然后处理好 key 变化时,数据迁移的逻辑。shdict is a LRU cache如果 shdict 里面的数据超过了事先分配好的内存大小限制,OpenResty 会根据 LRU 算法,清除现有的数据。我发现许多情况下,我们会忽视这一事实,认为 shdict 里面的空间一定足以容纳所有数据。不过假若你是一个厌恶风险的人,可以考虑只用 safe_ 开头的一系列 API 来操作 shdict。这一类 API 在 shdict 不够空间时,会失败,而不是默默地挤掉当前的数据。update timeNginx 对时间的缓存非常激进,只有在开始新的一个事件循环时才会更新缓存的时间。对于服务器,这似乎足够了;但对于应用代码,一不小心可能就踩坑了。通俗易懂的说法,如果你的 Lua 代码不 yield,那么从头到尾获取到的时间都是缓存的结果。另外,如果多个请求同处一个事件循环里面,而其中一个请求产生了阻塞操作,那么执行剩下的请求时,缓存的时间跟真正的时间就会有明显的差别。所以,在进行了耗时操作之后,可能需要调用下 ngx.update_time。如果需要准确的时间戳,不应直接调用 ngx.now()。接下来让我们比较下几种 ngx.now() 的替代品。在上图中,我们以 os.time() 作为基准,比较下各方的性能。我们看到,ngx.update_time() + ngx.now() 的组合是最耗时的,因为 ngx.update_time() 除了要获取当前的时间外,还要更新一系列时间字符串。值得一提的是,我们自己实现 current_ms_time() 无论是性能还是时间的精准度都比 os.time() 要好,可见在 JIT 下,FFI 实现可以击败无法被 JIT 的内置函数。另外我们也会看到,resty.core版本的 ngx.now() 和 ngx.update_time() 相当地快。在本演讲的最后,我会解释为什么会这样。有限的 timer在 OpenResty 里面,timer 的个数是有限的。首先每个 timer 都是一个 fake request,在 Nginx 这边看来,每个 timer 其实都是一个请求。跟真实的 request 一样,它也会占据一个连接。所以你的 worker_connections 要足够大,即使 timer 并不会真的建立一个网络连接。另外,OpenResty 还有两个参数限制了 timer 的总数,lua_max_pending_timers 和 lua_max_running_timers,需要保证它们够大。另外如果启动 timer 时没有足够的内存,也是会失败的。如果可以的话,尽可能用 ngx.timer.every 来启动定期的 timer。用 ngx.timer.at 反复启动 timer 的话,一旦每次启动失败,那就真的失败了。既然每个 timer 都是一个请求,那么如果你每个网络请求都会启动一个甚至多个 timer,性能自然好不到哪儿去。最简单的优化办法是引入批处理,避免不断创建 timer,你也可以考虑下队列,甚至更为复杂的时间轮。不过要想复用 timer,还要面对额外的挑战……第一个挑战来自于 Nginx 的每请求内存池。只有在请求结束时,Nginx 才会释放这一内存池内所有的内存。而前面已经说了,每个 timer 在 Nginx 看来都是一个请求,所以某种意义上,一个 timer 就像是一个长连接,尤其当这个 timer 会一直运行到进程结束时。长时间运行的 timer 自然会带来内存的持续上涨,但其上涨的速度一般而言并不显著。原因有二:许多 OpenResty API 实际上只会分配 Lua 层面上的内存。而这部分内存是在 LuaJIT GC 管理下的。在引入了 lua-resty-core 后更是如此。cosocket 的 send/receive 操作,会有 buffer 复用机制,使得其内存占用不会无限制增加。另一个挑战来自于较为隐晦的地方。当前 entry thread 会把它所创建的每个协程,记录到一个链表里。而各种协程 API,大都需要访问这个链表。如果 timer 或者长连接持续大量地创建协程,会导致协程 API 变得越来越慢。就目前的情况,要想解决这个问题,需要对协程进行复用,避免无限制地创建协程。第二部分讲完 OpenResty,让我们看看 LuaJIT。不可变的 stringLua 跟其他大部分语言有一点不一样,就是它的字符串是不可变的。不变字符串自然有些优点,比如减低内存占用、比较字符串时可以直接比较它的内存地址等等。但是缺点也不少。在其他语言里面,当我们想修改一个字符串部分内容,比如大小写转换,我们可以直接改变对应的位置上的 byte。毕竟字符串通常就是一个字节数组(byte array)。但是这事要在 Lua 里面做,非得拷贝一个新字符串不可。而且由于要保证每种字符串都只有一个实例,lj_str_new 需要对实际的字符串内容做 hash,然后用它查找该内容是否已经创建了对应的实例。既然说到做 hash,那么自然得提到 hash 碰撞。对于那些 hash 值一样的字符串,LuaJIT 把它们存储在链表里。如果许多字符串有着一样的 hash 值,那么这个链表就会很长,原来 O(1) 的开销会退化为 O(n)。这就是所谓的 hash 碰撞。不幸的是,LuaJIT 的默认的字符串 hash 函数就有这样的问题。在网上你能找到一些相关的报告。OpenResty 自带的 LuaJIT 用硬件加速的 CRC32 函数替换了默认的字符串 hash 函数,降低了发生 hash 碰撞的风险。需要说明的是,只有在支持 SSE 4.2 指令集的 x64 平台上才会启用这一函数。即使 hash 碰撞的问题可以避免,lj_str_new 依然是一个既频繁又耗时的函数。最好的优化就是不做。比如如果只是想查看字符串里面的字符,可以用 string.byte 代替 string.sub。OpenResty 里面,也有许多 API 支持在 C 层面上完成字符串的拼接,无需调用 lj_str_new,比如 cosocket 的 send、ngx.say 和 ngx.log。它们接受多个参数,或者数组 table,在 C 层面上拼接成字符串。这里的数组 table 甚至可以是嵌套的。谁可以代替字节数组LuaJIT 缺乏字节数组,这是个痛点,尤其是在做协议转换的时候。一个通常的代替品是用数组 table。另一个是借助 FFI,申请一块名符其实的字节数组。这里有些操作数组 table 的方法。有两个需要解释下:table.new 是 LuaJIT 独有的方法,允许在创建 table 时指定大小,减少后面 resize 的成本。table.clone 是 OpenResty 自带的 LuaJIT 的方法,允许对一个 table 做浅复制。它内部调用了 lj_tab_dup 这个 LuaJIT 内部函数。buffer 复用前面讲到,我们可以给某些 API 传递 table 而不是 concat 之后的字符串。可能有人会怀疑,创建 table 开销不会比 concat 字符串大吗?其实这里的 table,是可以复用的,无需每次都创建。如果你的函数里面没有 yield,你可以直接拿个 local 变量,每次都复用这个变量。为了避免影响到其他函数,我们这里用了个 do block 把相关的变量都包起来。如果你的函数里面有 yield,你可以通过 lua-tablepool 这个库实现 table 的回收复用。避免在 table 中间存储 nil一个众所周知的事实:如果数组 table 中间有 nil,获取到的长度可能会不准。Lua 可能会把某个 nil 的位置作为这个 table 的结尾。不过较少为人所知的是,nil 也会影响 unpack 的结果。由于 unpack 返回的结果个数取决于 table 的长度,所以如果获取的长度不准,unpack 返回的结果数也会不准。如果我们 unpack 前面的 table,就只会返回第一个数 0. 另外,Lua 里常用的两种迭代数组的方式,for i in ipairs() 和 for i = 1, #table,在处理数组中的 nil 的方式上有所不同。前者每次迭代时都会检查当前元素是不是 nil,如果是的话结束迭代。尽可能不要把数组 table 中的某个元素置为 nil,应该用 ngx.null 作为占位符。有上限的 unpack既然提到了 unpack,顺便提下 unpack 也是有大小限制的。如果 unpack 的数组大小超过 8000,unpack 会抛异常。FFI buffer 作为 byte[]除了用 table,也可以考虑下用 FFI buffer 作为字节数组。FFI buffer 的好处在于内存占用少。坏处呢,一个是周边的 API 支持少,用起来不像 table 那么方便;另一个是,如果不能被 JIT 编译的话,FFI 操作很昂贵。当然 FFI buffer 也是可以复用的,复用方法跟 table 差不多。有兴趣的听众可以看看 lua-resty-core 的 get_string_buf 这个方法。LUAJIT_NUMMODELuaJIT 有一个编译选项 LUAJIT_NUMMODE,控制对 number 类型的处理方式。它的默认值为 1。当我们把它在编译时设置为 2 时,对于能够用 32 位整数表示的 number,LuaJIT 会用 int32 表示,而不是一概用 double 来表示。通常来说,设置 LUAJIT_NUMMODE=2 会让程序快一点,因为 CPU 更擅长对整数进行计算。但是也不一定,因为影响性能的因素非常复杂,具体问题需要具体分析。后面我会给大家看一个例子,LUAJIT_NUMMODE=2 会让程序更慢。JIT终于讲到重头戏,LuaJIT 的 JIT 编译。LuaJIT 采用 Tracing JIT 来记录并实时编译字节码。当某个循环或者函数调用足够热时,LuaJIT 会开始记录执行的字节码,进行优化后生成 IR,然后把 IR 编译成 mcode。你可以在上面两个文档中找到对 字节码 和 IR 的一些说明。你可以在 LuaJIT 代码中添加下面两行代码,把这一过程 dump 到指定文件中:local dump = require “jit.dump"dump.on(“abimsrtx”, filepath)让我们看一个实际的例子。这个例子是为了展示 JIT 过程而设计的,我们可以从 dump 输出中看到不少信息。从 Trace 2 的 bytecode 部分可以看到,Tracing JIT 在 tracing 的时候是跨函数的。从 Trace 2 的 IR 部分可以看到,string.rep 等操作被移到了 LOOP 以外,因为它的结果在整个循环中是不变的。在 IR 里面有一个有意思的输出:CALLXS [0x7f248ac41180]从对应的 base_encoding.lua 代码可以看出,这里其实是通过 FFI 调用了某个 so 里面的函数。在最终生成的 mcode 里面,我们也能找到对应的 call 0x7f248ac41180。为什么 FFI 在 JIT 下性能会比解释器模式下快很多呢?原因在于解释器模式下,LuaJIT FFI 需要实现 Lua 和 C 数据间的 marshal 和 unmarshal。而在 JIT 模式下,两者的交互都是汇编层面上的。我们可以看到,不少 IR 左边有个 >,这表示这个 IR 是作为 guard 存在的。Trace 是没有分支的,一旦发生 guard 不能满足的情况,会退出当前 trace 进入解释器模式。看下 LOOP 里面这个 NE 0069 这个 IR。结合上一个 IR,可以知道它的意思是判断 % 5 != 4。我们可以找到对应的 mcode:7f24b63bfeca mov ebx, eax7f24b63bfecc mov esi, 0x57f24b63bfed1 mov edi, ebp7f24b63bfed3 call 0x7f248c2e68a0 ->lj_vm_modi7f24b63bfed8 mov rdi, [rsp+0x8]7f24b63bfedd cmp eax, +0x047f24b63bfee0 jz 0x7f24b63b002c ->7我们可以看到,这里面插了个 jz 0x7f24b63b002c 的判断。也就是如果不符合 != 4 的条件,就会跳到 0x7f24b63b002c 这个地址,而不是继续执行下去。旁边有一个 ->7 的标记,表示退出时用 snapshot 7 里面的数据恢复解释器模式。snapshot 7 就在 NE 0069 的上面。需要解释下,snapshot 的输出和 IR 的输出是并行的,只是恰好在 NE 0069 上面,两者输出的位置并无因果性。再往下拉,我们会看到 TRACE 2 多次 exit 7。当另一个分支足够热时,会从原来的 TRACE 里面生成一个 side trace,也就是这里的 TRACE 3. 然后 TRACE 3 追踪到 unpack 这里的没了。因为 unpack 是 NYI 的,JIT 没法 tracing 下去。不过好在 LuaJIT 支持 stitch,可以绕过 NYI 语句,生成新的 TRACE 4.有点像下了高速,开了段路后又重上高速。side trace 有一个问题,就是它们在结束后,会跳回到 root trace 的开头。像 TRACE 4 的最后一个指令,就是跳到 TRACE 2 的开头。我们知道,TRACE 4 是从 LOOP 里面长出来的,然而 TRACE 4 结束后会跳到 TRACE 2 开头,也就是像 string.rep 这样的操作,每次在 TRACE 4 执行完之后都会再走一遍,哪怕它的结果在整个循环里是不变的。让我们看下第二个例子。这是段在 Lua 里面算 CRC32 的程序。然后改动了两行代码,用 FFI buffer 替换了 table,它的性能是原来的 2.5 倍。我会从 jit.dump 输出的角度解释为什么前后差别那么大。why_byte_level_slow 是 table 版本的 dump,而 crc32_ffi 是 FFI 版本的 dump。这两个 dump 的 TRACE 1,都是一样的字节码,但是两者 IR 的 LOOP 中间部分不一样。抛去相似的部分不谈,可以看出 table 版本多了个 ABC,也就是 array boundary check。然后比较下 mcode 对应部分,table 版本有 23 个指令,而 FFI 版本只有 17 个指令。但是 LOOP 部分从 23 个指令减少到 17 个跟 2.5 倍提升对不上。显然还有第二个因素在起作用。看下 table 版本的 dump,你会发现它的 TRACE 数量很多,而且相似。仔细看,你会发现,有些地方从 table 中加载的数据类型是 num,而有些地方是 int。比如 TRACE 1 的 ALOAD 是 num,而 TRACE 2 的 ALOAD 是 int。这个 dump 是在 LUAJIT_NUMMODE=2 的情况下生成的。前面提到,这种模式下,LuaJIT 会尽可能把数值当作 int32 处理。但是 CRC32 表里面,有些数字超过了 int32,只能作为 double 处理。由于这两种类型需要生成不同的 mcode,导致大量 side trace 的生成。在 FFI 版本里,由于我们指定 CRC32 表的类型为 unsigned int,就没有这个问题。why lua-resty-core is faster最后我们来看下为什么同样的函数, lua-resty-core 里面的版本会更快。这是同样一段使用了 ngx.re.find 的代码,在 CFunction 和 FFI+JIT 两个版本下生成的火焰图。我们可以看到,CFunction 版本的火焰图里面有大量 lua_xxxx 这样的函数的开销,而 FFI+JIT 版本里面,就没有这些函数。由于 JIT 时可以优化掉 FFI 调用的数据交换过程,所以当一个 API 在数据交换上耗费的比重越多,改写成 FFI 时带来的性能提升越大。比如 ngx.re.find (数据交换复杂)比如 ngx.time (C 部分的逻辑简单,大部分耗时在数据交换上)反之,如果一个 API 耗费在数据交换的比重小,则 FFI 化带来的提升就小,比如 ngx.md5。FFI 改造还能减少 stitch,这方面的提升需要结合具体上下文分析。 ...

December 29, 2018 · 4 min · jiezi