开发者社区> sunrainchy> 正文

一次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,
+关注继续查看

记一次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 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 调整为一个合适的值即可。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
OpenResty与Lua实现高并发请求处理
OpenResty与Lua实现高并发请求处理
277 0
用Go语言写HTTP中间件
在web开发过程中,中间件一般是指应用程序中封装原始信息,添加额外功能的组件。不知道为什么,中间件通常是一种不太受欢迎的概念。但我认为它棒极了。
173 0
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指示,将超文本文档从一台主机(
148 0
php rpc框架有哪些
什么是RPC框架 ? 如果用一句话概括RPC就是:远程调用框架(Remote Procedure Call) 那什么是 远程调用 ? 通常我们调用一个php中的方法,比如这样一个函数方法: localAdd(10, 20),localAdd方法的具体实现要么是用户自己定义的,要么是php库函数中自带的,也就说在localAdd方法的代码实现在本地,它是一个本地调用! 远程调用 意思就是:被调用方法的具体实现 不在 程序运行本地,而是在别的某个远程地方。
1929 0
【swoole入门】如何快速创建一个web服务器
Swoole是一种PHP高级Web开发框架,可以提升网站的开发效率。本篇文章中小编将介绍如何用swoole创建web服务器,感兴趣的朋友可以来学习一下。http_server.php 0.0.0.0 表示监听所有IP地址,一台服务器可能同时有多个IP,如127.0.0.1本地回环IP、192.168.1.100局域网IP、210.127.20.2 外网IP,这里也可以单独指定监听一个IP。
744 0
php runtime 中 http web 中 rewrite 浅解和方案
本文针对函数计算的 php runtime 的web运用开发, 提供一个简单实现url rewrite的方案
1980 0
基于 Nginx 的 HTTPS 性能优化实践
随着相关浏览器对HTTP协议的“不安全”、红色页面警告等严格措施的出台,以及向 iOS 应用的 ATS 要求和微信、支付宝小程序强制 HTTPS 需求,以及在合规方面如等级保护对传输安全性的要求都在推动 HTTPS 的发展。
4535 0
动手做webserver的核心之http解析
简介         webserver往小里说核心功能就是socket管理、url处理、http协议处理、业务dll管理等;下面简介绍一下http协议:超文本传输协议(HTTP)是一种通信协议,当时就是为web传输设计的一个基于tcp的协议;基于这个字面上理解,可以简单的点说就是用tcp来传输文本、数据的一种编解码格式。
1477 0
Nginx http模块(学习笔记二十一)
1.HTTP的核心模块. 这些HTTP模块会在编译Nginx时自动编译进来,除非使用configure命令禁止编译这些模块. (1)alias指令. 该指令用于在URL和文件系统路径之间实现映射.
923 0
基于hi-nginx的web开发(python篇)——动态路由和请求方法
hi.py 的提供的路由装饰器接受两个参数,第一个参数指定动态路由的正则模式,第二个参数指定同意的http请求方法列表。 比如: 1 @app.route(r"^/client/?$",['GET','POST']) 2 def client(req,res,param): 3 res.
1074 0
第四章:nginx重要模块+HTTPS+Lua详解
官方模块* nginx -V 显示的信息就是加载的模块信息 Module(1)ngx_http_stub_status_module 本机状态 该ngx_http_stub_status_module模块提供对基本状态信息的访问。
1143 0
+关注
sunrainchy
Aliyun OSS 研发,专注存储高性能
文章
问答
视频
相关电子书
更多
PHP运行机制初探
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载