zl程序教程

您现在的位置是:首页 >  大数据

当前栏目

一次openresty http.lua 性能调优之旅

性能HTTP 一次 调优 lua OpenResty
2023-09-11 14:17:37 时间

最近要用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)

209 -- TODO check nreqt.max_body_size !!

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

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 end

 body = body .. data

 end```

假设按照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 调整为一个合适的值即可。


用Go语言写HTTP中间件 在web开发过程中,中间件一般是指应用程序中封装原始信息,添加额外功能的组件。不知道为什么,中间件通常是一种不太受欢迎的概念。但我认为它棒极了。
01.Nginx基础Http原理 1.Http协议概述HTTP全称HyperText Transfer Protocol中文名为超文本传输协议1.什么是超文本?包含有超链接(Link)和各种多媒体元素标记的文本。这些超文本文件彼此链接,形成网状(Web),因此又被称为网页(Web Page)。这些链接使用URL表示。最常见的超文本格式是超文本标记语言HTML。html文件- 包含各种各样的元素(URL链接)- 形成web page简称web页面。2.那什么是URL,URL简称统一资源定位符。那URL的组成部分是由协议, 域名:端口, 路径和文件名3.那超文本传输http协议是什么?是一种按照URL指示,将超文本文档从一台主机(
1.HTTP的核心模块. 这些HTTP模块会在编译Nginx时自动编译进来,除非使用configure命令禁止编译这些模块. (1)alias指令. 该指令用于在URL和文件系统路径之间实现映射.