一次openresty http.lua 性能调优之旅
记一次openresty http.lua 性能调优之旅
1 背景
最近要用Nginx lua进行http 数据交互,因此想到了resty/http.lua,因此开启一段性能调优之旅。
2 发送HTTP GET请求代码
local ok, status, headers, code, body = hc:request {url = uri,method = "GET", }
很简单的一段代码,利用http.lua request 函数发送http get 请求并返回body及相关信息。
3 性能表现及现象
在get 小文件的时候性能表现正常,符合预期,但是get 大文件的时候非常慢,在内网环境下GET 1个 1M左右的Object 竟然需要1s+,这性能实在不能忍,而且随着文件增大性能急剧下降。开始怀疑是不是http server 的原因,用wget 试了一下,发现很快,排除server的原因。百思不得其解后开始分析http.lua 代码
4 http.lua 分析
这是Lua 读取http body 代码,可以看出这里有个fetch_size参数,从代码上看直观含义是一次从底层网络读上来数据块的大小
161 local function read_body_data(sock, size, fetch_size, callback)
162 local p_size = fetch_size
163 while size and size > 0 do
164 if size < p_size then
165 p_size = size
166 end
167 local data, err, partial = sock:receive(p_size)
168 if not err then
169 if data then
170 callback(data) --这里有个callback,下面看看是啥
171 end
172 elseif err == "closed" then
173 if partial then
174 callback(partial)
175 end
176 return 1 -- 'closed'
177 else
178 return nil, err
179 end
180 size = size - p_size
181 end
182 return 1
183 end
看下fetch size 设置值是多少
nreqt.fetch_size = reqt.fetch_size or 16*1024
默认为16K
再看一下function read_body_data 在哪里调用的,参数callback 传又是什么
185 local function receivebody(sock, headers, nreqt)
186 local t = headers["transfer-encoding"] -- shortcut
187 local body = ''
188 local callback = nreqt.body_callback
189 if not callback then
190 local function bc(data, chunked_header, ...)
191 if chunked_header then return end
192 body = body .. data
193 end
194 callback = bc
195 end
196 if t and t ~= "identity" then
197 -- chunked
198 while true do
199 local chunk_header = sock:receiveuntil("\r\n")
200 local data, err, partial = chunk_header()
201 if not data then
202 return nil,err
203 else
204 if data == "0" then
205 return body -- end of chunk
206 else
207 local length = tonumber(data, 16)
208
209 -- TODO check nreqt.max_body_size !!
210
211 local ok, err = read_body_data(sock,length, nreqt.fetch_size, callback)
212 if err then
213 return nil,err
214 end
215 end
216 end
217 end
218 elseif headers["content-length"] ~= nil and tonumber(headers["content-length"]) >= 0 then
219 -- content length
220 local length = tonumber(headers["content-length"])
221 if length > nreqt.max_body_size then
222 ngx.log(ngx.INFO, 'content-length > nreqt.max_body_size !! Tail it !')
223 length = nreqt.max_body_size
224 end
225
226 local ok, err = read_body_data(sock,length, nreqt.fetch_size, callback)
227 if not ok then
228 return nil,err
229 end
230 else
231 -- connection close
232 local ok, err = read_body_data(sock,nreqt.max_body_size, nreqt.fetch_size, callback)
233 if not ok then
234 return nil,err
235 end
236 end
237 return body
238 end
这里可以看到我们的程序中没有传callback 进去,callback 默认是
190 local function bc(data, chunked_header, ...)
191 if chunked_header then return end
192 body = body .. data -- 注意这里会对每次接收到的body 进行拼接
193 end
194 callback = bc
分析到这里问题已经很明显了
fetch_size 是一次sock:receive 调用读上来的body 的size,每次读出来fetch_size 的body 后会回调默认callback 对body 进行拼接,如果文件size 很大而fetch size 很小就会造成因字符串拼接造成的CPU资源消耗及内存消耗。而我们的场景是需要缓存所有body后处理,所以一次读出越多body越好。
默认Callback是
if chunked_header then return endbody = body .. dataend```
假设按照fetch size默认值16k 来算,get 1MB 文件光string 拼接就要进行64次,所以一次性接收所有body性能最佳,fetch_size 设置为1GB。(大家都知道字符串拼接需要额外内存分配会消耗大量CPU)### 5 结论fetch_size 设置太小导致大文件body 拼接次数过多导致,从我的场景来看要缓存所有body后才能进行下一步因此fetch_size 设置越大越好修正后代码为:
url = uri,fetch_size = 1024*1024*1024,method = "GET", }```
注意:如果你的业务场景是需要流式处理或者转发这个值只需要将fetch_size 调整为一个合适的值即可。
一次openresty http.lua 性能调优之旅相关推荐
- IOS性能调优系列:使用Time Profiler发现性能瓶颈
硬广:<IOS性能调优系列>第五篇,预计会有二十多篇,持续更新,欢迎关注. 之前四篇都是关注于内存方面,分析了内存泄漏.僵尸对象.内存分配,本篇介绍Time Profiler工具的使用,开 ...
- 由浅入深解读Redis高级能力及性能调优
摘要 本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍.之后概览Redis提供的高级能力,并在部署.维护.性能调优等多个方面进行更深入的介绍和 ...
- Redis 宝典 | 基础、高级特性与性能调优
转载:Redis 宝典 | 基础.高级特性与性能调优 本文由 DevOpsDays 本文由简书作者kelgon供稿,高效运维社区致力于陪伴您的职业生涯,与您一起愉快的成长. 作者:kelgon 链接: ...
- Redis 基础、高级特性与性能调优 | 高薪必备
来源:http://c7.gg/fxqAK 本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍.之后概览Redis提供的高级能力,并在部署.维护 ...
- redis scan 效率太慢_Redis 基础、高级特性与性能调优(下)
数据淘汰机制 Redis提供了5种数据淘汰策略: volatile-lru:使用LRU算法进行数据淘汰(淘汰上次使用时间最早的,且使用次数最少的key),只淘汰设定了有效期的key allkeys-l ...
- 程序员精进之路:性能调优利器--火焰图
作者:厉辉,腾讯 CSIG 后台开发工程师 本文主要分享火焰图使用技巧,介绍 systemtap 的原理机制,如何使用火焰图快速定位性能问题原因,同时加深对 systemtap 的理解. 让我们回想一 ...
- 参数调优为什么要采样_程序员精进之路:性能调优利器--火焰图
本文主要分享火焰图使用技巧,介绍 systemtap 的原理机制,如何使用火焰图快速定位性能问题原因,同时加深对 systemtap 的理解. 让我们回想一下,曾经作为编程新手的我们是如何调优程序的? ...
- 高薪必备|Redis 基础、高级特性与性能调优
点击▲关注 "数据和云" 给公众号标星置顶 更多精彩 第一时间直达 本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍. ...
- redis decr 防止超卖_Redis基础、高级特性与性能调优——一篇文章搞定
本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍.之后概览Redis提供的高级能力,并在部署.维护.性能调优等多个方面进行更深入的介绍和指导. ...
最新文章
- 破解石碑(区间动规)
- IDEA切换git分支
- Loadrunner通过ssh连接linux进行hadoop基准测试
- java发送html模板
- 关于安卓手机在微信浏览器中无法调起相机的原因
- ubuntu16.04 kinetic外接Intel Realsense D435i配置教程
- 局部坐标系和全局坐标系
- Redis签到功能设计与实现
- Spring Boot(5)---第一个Spring Boot应用程序
- Docker教程:dokcer的配置和命令
- java视频通话_Java使用WebSocket和WebRTC视频通话
- 影音先锋 android下载地址,影音先锋app官方普通下载-影音先锋 安卓版v5.8.2-PC6安卓网...
- 虚假信息成物联网“毒瘤”
- Linux 经典书籍推荐
- docker安装php拓展
- 八种排序java实现
- 海洋cms index.php被修改,海洋CMS(SEACMS)新版本V6.55补丁仍可被绕过执行任意代码...
- Java汉字输入练习
- java并发包线程池原理分析锁的深度化
- mac软件全屏时候最顶上任务栏保留_Mac菜单栏上必备的十款应用程序
热门文章
- java制作管理系统视频_阶段1:手把手快速做一个Java swing mysql学生信息管理系统附带完整源码及视频开发教程【猿来入此自营】...
- 高平二中2021高考成绩查询,录取信息
- xshell 无法定位输入点_linux基础知识个人总结
- 深圳职业技术学院计算机专业组代码,深圳职业技术学院代码是多少 有哪些优势专业...
- 低压抽屉柜常见故障处理方法_传真机常见故障如何处理 传真机常见故障处理方法【详解】...
- linux怎样删除定时任务,Linux(CentOS)使用定时任务删除Tomcat日志文件
- python字典包含指定键_python-字典中所有值的总和,其中包含键中的项
- Spring MVC访问页面拦截js和css文件解决方法( No mapping found for HTTP request with URI [/Recruit/js/popper1.15.0.)
- pdo mysql ascii_跟bWAPP学WEB安全(PHP代码)--SQL注入的一些技巧
- java 支付宝转账_Java 支付宝支付,退款,单笔转账到支付宝账户(支付宝订单退款)...