Openresty的同步输出与流式响应
Openresty的同步输出与流式响应
默认情况下, ngx.say和ngx.print都是异步输出的,先来看一个例子:
location /test {
content_by_lua_block {
ngx.say("hello")
ngx.sleep(3)
ngx.say("the world")
}
}
执行测试,可以发现首先, /test 响应内容是在触发请求 3s 后一起接收到响应体,第一个ngx.say好像是被“绕过”,先执行sleep,然后和最后一个ngx.say的内容一起输出。
location /test {
content_by_lua_block {
ngx.say("hello")
ngx.flush() -- 显式的向客户端刷新响应输出
ngx.sleep(3)
ngx.say("the world")
}
}
首先输出"hello",然后停顿3秒,最后输出"the world"——正如我们想象的那样。ngx.flush执行显示的输出,前一个ngx.say被“阻塞”住,执行完输出后方往下执行。
再看一个例子:
server {
listen 80;
lua_code_cache off;
location /test {
content_by_lua_block {
ngx.say(string.rep("hello", 4000))
ngx.sleep(3)
ngx.say("the world")
}
}
}
这个例子和第一个例子相比,唯一不同就是ngx.say输出内容长了不少,我们发现浏览器先收到所有的hello,接着又收到了"the world" 。然而如果我们把4000改为小一点的值如2000(不同配置这个相对大小或有不同),那么仍然会出现先停顿3s,然后所有"hello"连同最后"the world"一起输出的情况。
通过以上三个例子,我们可以得出下面的结论:
ngx.say和ngx.print的同步和异步
nginx有个输出缓冲(system send buffer),如16k。ngx.say和ngx.print默认是向这个输出缓冲写入数据,如果没有显示的调用ngx.flush,那么在content阶段结束后输出缓冲会写入客户端;
如果没有ngx.flush也没有到结束阶段,但如果输出缓冲区满了,那么也会输出到客户端;
因此ngx.say和ngx.print的默认向客户端的输出都是异步的,非实时性的,改变这一行为的是ngx.flush,可以做到同步和实时输出。这在流式输出,比如下载大文件时非常有用。
ngx.flush的同步和异步
lua-nginx也提到了ngx.flush的同步和异步。某一个ngx.say或者ngx.print调用后,这部分输出内容会写到输出缓冲区,同步的方式ngx.flush(true)会等到内容全部写到缓冲区再输出到客户端,而异步的方式ngx.flush()会将内容一边写到缓冲区,而缓冲区则一边将这些内容输出到客户端。
openresty和nginx流式输出的比较
流式输出,或者大文件的下载,nginx的upstream模块已经做得非常好,可以通过proxy_buffering|proxy_buffer_size|proxy_buffers 等指令精细调控,而且这些指令的默认值已经做了妥善处理。我们来看看这些指令以及默认值:
proxy_buffering on;
proxy_buffer_size 4k|8k;
proxy_buffers 8 4k|8k;
proxy_busy_buffers_size 8k|16k;
proxy_temp_path proxy_temp;
- proxy_buffering on表示内存做整体缓冲,内存不够时多余的存在由proxy_temp_path指定的临时文件中,off表示每次从上游接收proxy_buffer_size响应的内容然后直接输出给客户端,不会试图缓冲整个响应
- proxy_buffer_size和proxy_buffers都是指定内存缓冲区的大小,proxy_buffer_size通常缓冲响应头,proxy_buffers缓冲响应内容,默认为一页的大小,proxy_buffers还可以指定这样的缓冲区的个数
- proxy_busy_buffers_size nginx在试图缓冲整个响应过程中,可以让缓冲区proxy_busy_buffers_size大小的已经写满的部分先行发送给客户端。于此同时,缓冲区的另外部分可以继续读。如果内存缓冲区不够用了,还可以写在文件缓冲区
- proxy_temp_path 使用文件作为接受上游请求的缓冲区buffer,当内存缓冲区不够用时启用
openresty的怎么做到过大响应的输出呢? 《OpenResty 最佳实践》 提到了两种情况:
- 输出内容本身体积很大,例如超过 2G 的文件下载
- 输出内容本身是由各种碎片拼凑的,碎片数量庞大
前面一种情况非常常见,后面一种情况比如上游已经开启Chunked的传输方式,而且每片chunk非常小。笔者就遇到了一个上游服务器通过Chunked分片传输日志,而为了节省上游服务器的内存将每片设置为一行日志,一般也就几百字节,这就太“碎片”了,一般日志总在几十到几百M,这么算下来chunk数量多大10w+。笔者用了resty.http来实现文件的下载,文件总大小48M左右。
local http = require "resty.http"
local httpc = http.new()
httpc:set_timeout(6000)
httpc:connect(host, port)
local client_body_reader, err = httpc:get_client_body_reader()
local res, err = httpc:request({
version = 1.1,
method = ngx.var.request_method,
path = ngx.var.app_uri,
headers = headers,
query = ngx.var.args,
body = client_body_reader
})
if not res then
ngx.say("Failed to request ".. ngx.var.app_name .." server: ", err)
return
end
-- Response status
ngx.status = res.status
-- Response headers
for k, v in pairs(res.headers) do
if k ~= "Transfer-Encoding" then --必须删除上游Transfer-Encoding响应头
ngx.header[k] = v
end
end
-- Response body
local reader = res.body_reader
repeat
local chunk, err = reader(8192)
if err then
ngx.log(ngx.ERR, err)
break
end
if chunk then
ngx.print(chunk)
ngx.flush(true) -- 开启ngx.flush,实时输出
end
until not chunk
local ok, err = httpc:set_keepalive()
if not ok then
ngx.say("Failed to set keepalive: ", err)
return
end
多达10w+的"碎片"的频繁的调用ngx.pirnt()和ngx.flush(true),使得CPU不堪重负,出现了以下的问题:
- CPU轻轻松松冲到到100%,并保持在80%以上
- 由于CPU的高负荷,实际的下载速率受到显著的影响
- 并发下载及其缓慢。笔者开启到第三个下载连接时基本就没有反应了
这是开启了ngx.flush(true)的情况(ngx.flush()时差别不大),如果不开启flush同步模式,则情况会更糟糕。CPU几乎一直维持在100%左右:
可见,在碎片极多的流式传输上,以上官方所推荐的openresty使用方法效果也不佳。
于是,回到nginx的upstream模块,改content_by_lua_file为proxy_pass再做测试,典型的资源使用情况为:
无论是CPU还是内存占用都非常低,开启多个下载链接后并无显著提升,偶尔串升到30%但迅速下降到不超过10%。
因此结论是,涉及到大输出或者碎片化响应的情况,最好还是采用nginx自带的upstream方式,简单方便,精确控制。而openresty提供的几种方式,无论是异步的ngx.say/ngx.print还是同步的ngx.flush,实现效果都不理想。
Openresty的同步输出与流式响应的更多相关文章
- Django的视图流式响应机制
Django的视图流式响应机制 Django的响应类型:一次性响应和流式响应. 一次性响应,顾名思义,将响应内容一次性反馈给用户.HttpResponse类及子类和JsonResponse类属于一次性 ...
- 飘城旅游网pc,流式,响应式布局
相关视频教程http://pan.baidu.com/s/1o77wirK 我的源码链接:http://pan.baidu.com/s/1czTsKI
- 基于grpc的流式方式实现双向通讯(python)
grpc介绍 grpc是谷歌开源的一套基于rpc实现的通讯框架(官网有更完整的定义).在搞懂grpc之前,首先要弄懂rpc是什么.下面是自己理解的rpc定义,若有不对,望指出: rpc官方称为 远程过 ...
- Go gRPC教程-服务端流式RPC(三)
前言 上一篇介绍了简单模式RPC,当数据量大或者需要不断传输数据时候,我们应该使用流式RPC,它允许我们边处理边传输数据.本篇先介绍服务端流式RPC. 服务端流式RPC:客户端发送请求到服务器,拿到一 ...
- CSS3与页面布局学习笔记(四)——页面布局大全(负边距、双飞翼、多栏、弹性、流式、瀑布流、响应式布局)
一.负边距与浮动布局 1.1.负边距 所谓的负边距就是margin取负值的情况,如margin:-100px,margin:-100%.当一个元素与另一个元素margin取负值时将拉近距离.常见的功能 ...
- EasyDSS直播服务器如何帮助用户解决OBS不能同时同步输出多路直播流到直播平台、CDN平台的限制
最近有用户突然寻求帮助,大概的意思就是说: 他需要同步将桌面的直播同时RTMP发布到:斗鱼.熊猫TV等等多个平台,但是OBS又只能同时采集并发布推流直播到单一个平台,而且有时候在4G或者网络比较差的情 ...
- 浅谈静态布局、流式布局,rem布局,弹性布局、响应式布局
静态布局: 特点:没有兼容性问题 PC:居中布局,所有样式使用绝对宽度/高度(px),设计一个Layout,在屏幕宽高有调整时,使用横向和竖向的滚动条来查阅被遮掩部分:移动设备:另外建立移动网站,单独 ...
- 流式布局&固定宽度&响应式&rem
我们现在在切页面布局的使用常用的单位是px,这是一个绝对单位,web app的屏幕适配有很多中做法,例如:流式布局.限死宽度,还有就是通过响应式来做,但是这些方案都不是最佳的解决方法. 1.流式布局: ...
- HttpURLConnection的流式输出的缺陷和解决方法
转自:http://www.mzone.cc/article/198.html 最近在用applet写文件上传控件的时候发现使用URLConnection来对服务器进行流式输出时的一些问题.我们通常要 ...
随机推荐
- safari 浏览器 input textarea select 等不能响应用户输入
解决办法 -webkit-user-select:auto; /*webkit浏览器*/ user-select:auto; -o-user-select:auto; -ms-user-select: ...
- 了解Scala反射
本篇文章主要让大家理解什么是Scala的反射, 以及反射的分类, 反射的一些术语概念和一些简单的反射例子. 什么是反射 我们知道, Scala是基于JVM的语言, Scala编译器会将Scala代码编 ...
- 漫谈“采样”(sampling)
越学越懵了,计算机中是怎么进行采样的,用了这么久的 rand() 函数,到现在才知道是怎么做的. 从均匀分布中采样 计算机中通过线性同余发生器(linear congruential gene ...
- Python中os和sys模块中常用的方法
os模块 os模块:该模块提供了一些方便使用操作系统相关功能的函数 os.remove() 删除文件 os.rename() 重命名文件 os.walk() 文件目录遍历器 os.chdir() 改变 ...
- 金三银四,今年Python就业前,看看这篇文章找找感觉
Python就业行情和前景分析之一爬取数据 最近Python大热,就想要分析一下相关的市场需求,看一下Python到底集中在哪些城市,企业对Python工程师的一些需求到底是怎样的,基于此,爬取了国内 ...
- 【java线程】的wait、sleep、join、yied
1.概述 使用Thread相对来说比较简单,没有什么成本,但是通常来说,我们使用线程基本就是覆写run方法,然后调用线程对象的start函数启动线程. 对于面试人员来说,这些都不是面试官会问到的问题, ...
- 带着新人看java虚拟机02
上一节是把大概的流程给过了一遍,但是还有很多地方没有说到,后续的慢慢会涉及到,敬请期待! 这次我们说说垃圾收集器,又名gc,顾名思义,就是收集垃圾的容器,那什么是垃圾呢?在我们这里指的就是堆中那些没人 ...
- 如何使用JS来开发室内地图商场停车场车位管理系统
在线体验到室内地图的功能后,手机对室内地图加载一个字,要显示“快”,目前微信和电脑都可以打开室内地图的要求是3秒内打开,能有定位导航的功能最好,这样方便找到要去的地方. 对于经常逛商场的MM来说,哪里 ...
- 用系统为centos6的主机,搭建PXE服务器,实现批量安装centos6,7系统
1. iptables -F setenforce 0 临时关掉selinux,清掉防火墙 永久生效更改配置文件:vim /etc/sysconfig/selinux chkconfig iptabl ...
- express+handlebars 快速搭建网站前后台
最近在重构公司网站,原来网站使用PHP,前后端不分离,添加与更新网站内容仍使用原始方法,先出布局再把调好的布局给PHP后端开发,花时间长,维护不易.因此决定将网站前后端分离,核心功能含网站下单及CRM ...