首页 数据库 mysql教程 通过Nginx/Lua给Redis的PIPELINING减肥

通过Nginx/Lua给Redis的PIPELINING减肥

Jun 07, 2016 pm 04:29 PM
lua nginx pipelining redis 通过

某手机应用市场项目,其中请求量最大的功能是查询升级接口,具体点来说:客户端会不定期的把手机中应用名称及其应用版本发送到服务端,服务端通过比较版本来判断客户端的应用是否需要升级,如果需要就返回若干项相关信息。通常,一台手机里会装几十个到上百

某手机应用市场项目,其中请求量最大的功能是查询升级接口,具体点来说:客户端会不定期的把手机中应用名称及其应用版本发送到服务端,服务端通过比较版本来判断客户端的应用是否需要升级,如果需要就返回若干项相关信息。通常,一台手机里会装几十个到上百个应用不等,当海量客户端一起请求时,服务端的压力可想而知。

客户端请求的数据格式如下所示,可以采用GET或POST方法:

packages=foo|1&packages=bar|2&packages=<name>|<version>&...</version></name>
登录后复制

服务端选择Lua作为编程语言,同时利用了Redis的PIPELINING机制批量查询数据:

local redis = require "resty.redis"
local cjson = require "cjson"
local config = require "config"
ngx.header["Content-Type"] = "application/json; charset=utf-8"
local args = ngx.req.get_uri_args(1000)
if ngx.var.request_method == "POST" then
    ngx.req.read_body()
    for key, val in pairs(ngx.req.get_post_args(1000)) do
        args[key] = val
    end
end
if type(args["packages"]) == "string" then
    args["packages"] = {args["packages"]}
end
if type(args["packages"]) ~= "table" then
    ngx.exit(ngx.HTTP_BAD_REQUEST)
end
local cache = redis.new()
local res, err = cache:connect(config.host, config.port)
if not res then
    ngx.log(ngx.ERR, "error: ", err)
    ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end
cache:init_pipeline()
local packages = {}
for _, val in ipairs(args["packages"]) do
    if type(val) == "string" then
        local name, version = string.match(val, "([^|]+)|([0-9]+)")
        if name and version then
            packages[name] = tonumber(version)
            cache:hget(name, "all")
        end
    end
end
local res, err = cache:commit_pipeline()
if not res then
    ngx.log(ngx.ERR, "error: ", err)
    ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end
local data = {}
for _, val in ipairs(res) do
    if type(val) == "string" then
        val = cjson.decode(val)
        if packages[val["name"]] 
<p>补充:应用的数据作为HASH保存在Redis里,不过由于HGETALL非常耗费CPU,所以做了些处理:冗余保存了一个名为「all」的字段,用来保存原始数据的JSON编码,如此一来,就把复杂度为O(N)的HGETALL操作转化成了复杂度为O(1)的HGET操作,从而提升了效率。详细介绍请参考:《记Redis那坑人的HGETALL》。</p>
<p>如上代码平稳运行了一段时间,但随着访问量的增加,开始暴露问题:Redis时不时出现卡住的现象,究其原因就是单线程的Redis无法承载过大的PIPELINING请求。通常我们都是采用多实例的方法来规避Redis单线程的性能瓶颈问题,但在本例中,由于PIPELING很大,不是随便冗余几个实例就能解决问题,同时系统也没有太多的内存可用。</p>
<p>最后我们想到的办法是利用Nginx/Lua给Redis的PIPELINING减肥,具体一点来说:当客户端查询升级接口时,虽然会把多至上百个应用的信息同时发送到服务端,但其中真正升级的应用却很少,如果我们能把那些不升级的应用过滤掉,只查询升级的应用,无疑将大大提升系统的性能,形象一点说:当一个胖子请求经过过滤后,就变成了一个瘦子请求。实际操作时,我们可以把应用的版本缓存到Nginx/Lua共享内存里,客户端请求先在Nginx/Lua共享内存过滤一次,然后再判断是否需要查询Redis。</p>
<p>为了使用共享内存,需要在Nginx配置文件中声明:</p>
<pre class="brush:php;toolbar:false">lua_shared_dict versions 100m;
登录后复制

改良后的代码如下所示,注意其中共享内存的查询和设置部分的代码:

local redis = require "resty.redis"
local cjson = require "cjson"
local config = require "config"
local versions = ngx.shared.versions;
ngx.header["Content-Type"] = "application/json; charset=utf-8"
local args = ngx.req.get_uri_args(1000)
if ngx.var.request_method == "POST" then
    ngx.req.read_body()
    for key, val in pairs(ngx.req.get_post_args(1000)) do
        args[key] = val
    end
end
if type(args["packages"]) == "string" then
    args["packages"] = {args["packages"]}
end
if type(args["packages"]) ~= "table" then
    ngx.exit(ngx.HTTP_BAD_REQUEST)
end
local cache = redis.new()
local res, err = cache:connect(config.host, config.port)
if not res then
    ngx.log(ngx.ERR, "error: ", err)
    ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end
cache:init_pipeline()
local packages = {}
for _, val in ipairs(args["packages"]) do
    if type(val) == "string" then
        local name, version = string.match(val, "([^|]+)|([0-9]+)")
        if name and version then
            version = tonumber(version)
            if not versions[name] or versions[name] > version then
                packages[name] = version
                cache:hget(name, "all")
            end
        end
    end
end
local res, err = cache:commit_pipeline()
if not res then
    ngx.log(ngx.ERR, "error: ", err)
    ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end
local data = {}
for _, val in ipairs(res) do
    if type(val) == "string" then
        val = cjson.decode(val)
        if packages[val["name"]] 
<p>或许会有人会质疑:本质上请求量并没有减少啊,只是从Redis转嫁到了Nginx而已,这样就能提升性能么?问题的关键在于Redis是单线程的,而Nginx通过worker_processes指令,可以充分发挥多核CPU的能力,所以性能总体是提升的。</p>
<p>补充:代码里在设置共享内存过期时间的时候,没有采用固定值,而是采用了一个随机数的方式,之所以这样设计,是为了避免大量数据同时过期,系统性能出现抖动。</p>
<p>…</p>
<p>当然,随着访问量的增加,本文的方案可能还会出现问题,到时候怎么办呢?其实类似查询升级之类的功能,就不应该设计成同步的形式,如果能改成异步的方式,那么多数问题就都不存在了,不过这个就不多言了,现在的方案刚刚好够用。</p>
    <p class="copyright">
        原文地址:通过Nginx/Lua给Redis的PIPELINING减肥, 感谢原作者分享。
    </p>
    
    


登录后复制
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

docker容器名称怎么查 docker容器名称怎么查 Apr 15, 2025 pm 12:21 PM

可以通过以下步骤查询 Docker 容器名称:列出所有容器(docker ps)。筛选容器列表(使用 grep 命令)。获取容器名称(位于 "NAMES" 列中)。

nginx在windows中怎么配置 nginx在windows中怎么配置 Apr 14, 2025 pm 12:57 PM

如何在 Windows 中配置 Nginx?安装 Nginx 并创建虚拟主机配置。修改主配置文件并包含虚拟主机配置。启动或重新加载 Nginx。测试配置并查看网站。选择性启用 SSL 并配置 SSL 证书。选择性设置防火墙允许 80 和 443 端口流量。

centos redis如何配置Lua脚本执行时间 centos redis如何配置Lua脚本执行时间 Apr 14, 2025 pm 02:12 PM

在CentOS系统上,您可以通过修改Redis配置文件或使用Redis命令来限制Lua脚本的执行时间,从而防止恶意脚本占用过多资源。方法一:修改Redis配置文件定位Redis配置文件:Redis配置文件通常位于/etc/redis/redis.conf。编辑配置文件:使用文本编辑器(例如vi或nano)打开配置文件:sudovi/etc/redis/redis.conf设置Lua脚本执行时间限制:在配置文件中添加或修改以下行,设置Lua脚本的最大执行时间(单位:毫秒)

docker怎么启动容器 docker怎么启动容器 Apr 15, 2025 pm 12:27 PM

Docker 容器启动步骤:拉取容器镜像:运行 "docker pull [镜像名称]"。创建容器:使用 "docker create [选项] [镜像名称] [命令和参数]"。启动容器:执行 "docker start [容器名称或 ID]"。检查容器状态:通过 "docker ps" 验证容器是否正在运行。

怎么查看nginx是否启动 怎么查看nginx是否启动 Apr 14, 2025 pm 01:03 PM

确认 Nginx 是否启动的方法:1. 使用命令行:systemctl status nginx(Linux/Unix)、netstat -ano | findstr 80(Windows);2. 检查端口 80 是否开放;3. 查看系统日志中 Nginx 启动消息;4. 使用第三方工具,如 Nagios、Zabbix、Icinga。

docker怎么创建容器 docker怎么创建容器 Apr 15, 2025 pm 12:18 PM

在 Docker 中创建容器: 1. 拉取镜像: docker pull [镜像名] 2. 创建容器: docker run [选项] [镜像名] [命令] 3. 启动容器: docker start [容器名]

nginx怎么启动 nginx怎么启动 Apr 14, 2025 pm 01:06 PM

问题:如何启动 Nginx?答案:安装 Nginx启动 Nginx验证 Nginx 是否已启动探索其他启动选项自动启动 Nginx

centos redis如何配置慢查询日志 centos redis如何配置慢查询日志 Apr 14, 2025 pm 04:54 PM

在CentOS系统上启用Redis慢查询日志,提升性能诊断效率。以下步骤将指导您完成配置:第一步:定位并编辑Redis配置文件首先,找到Redis配置文件,通常位于/etc/redis/redis.conf。使用以下命令打开配置文件:sudovi/etc/redis/redis.conf第二步:调整慢查询日志参数在配置文件中,找到并修改以下参数:#慢查询阈值(毫秒)slowlog-log-slower-than10000#慢查询日志最大条目数slowlog-max-len

See all articles