热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用ngx_lua构建高并发应用

:本篇文章主要介绍了使用ngx_lua构建高并发应用,对于PHP教程有兴趣的同学可以参考一下。
这篇文章主要着重讨论一下如何通过ngx_lua同后端的memcached、redis进行非阻塞通信。

1. Memcached

在Nginx中访问Memcached需要模块的支持,这里选用HttpMemcModule,这个模块可以与后端的Memcached进行非阻塞的通信。我们知道官方提供了Memcached,这个模块只支持get操作,而Memc支持大部分Memcached的命令。

Memc模块采用入口变量作为参数进行传递,所有以$memc_为前缀的变量都是Memc的入口变量。memc_pass指向后端的Memcached Server。

配置:

[plain] view plaincopyprint?

  1. #使用HttpMemcModule
  2. location = /memc {
  3. set $memc_cmd $arg_cmd;
  4. set $memc_key $arg_key;
  5. set $memc_value $arg_val;
  6. set $memc_exptime $arg_exptime;
  7. memc_pass '127.0.0.1:11211';
  8. }

输出:

[plain] view plaincopyprint?

  1. $ curl 'http://localhost/memc?cmd=set&key=foo&val=Hello'
  2. $ STORED
  3. $ curl 'http://localhost/memc?cmd=get&key=foo'
  4. $ Hello

这就实现了memcached的访问,下面看一下如何在lua中访问memcached。

配置:

[plain] view plaincopyprint?

  1. #在Lua中访问Memcached
  2. location = /memc {
  3. internal; #只能内部访问
  4. set $memc_cmd get;
  5. set $memc_key $arg_key;
  6. memc_pass '127.0.0.1:11211';
  7. }
  8. location = /lua_memc {
  9. content_by_lua '
  10. local res = ngx.location.capture("/memc", {
  11. args = { key = ngx.var.arg_key }
  12. })
  13. if res.status == 200 then
  14. ngx.say(res.body)
  15. end
  16. ';
  17. }

输出:

[plain] view plaincopyprint?

  1. $ curl 'http://localhost/lua_memc?key=foo'
  2. $ Hello

通过lua访问memcached,主要是通过子请求采用一种类似函数调用的方式实现。首先,定义了一个memc location用于通过后端memcached通信,就相当于memcached storage。由于整个Memc模块时非阻塞的,ngx.location.capture也是非阻塞的,所以整个操作非阻塞。

2. Redis

访问redis需要HttpRedis2Module的支持,它也可以同redis进行非阻塞通行。不过,redis2的响应是redis的原生响应,所以在lua中使用时,需要解析这个响应。可以采用LuaRedisModule,这个模块可以构建redis的原生请求,并解析redis的原生响应。

配置:

[plain] view plaincopyprint?

  1. #在Lua中访问Redis
  2. location = /redis {
  3. internal; #只能内部访问
  4. redis2_query get $arg_key;
  5. redis2_pass '127.0.0.1:6379';
  6. }
  7. location = /lua_redis { #需要LuaRedisParser
  8. content_by_lua '
  9. local parser = require("redis.parser")
  10. local res = ngx.location.capture("/redis", {
  11. args = { key = ngx.var.arg_key }
  12. })
  13. if res.status == 200 then
  14. reply = parser.parse_reply(res.body)
  15. ngx.say(reply)
  16. end
  17. ';
  18. }

输出:

[plain] view plaincopyprint?

  1. $ curl 'http://localhost/lua_redis?key=foo'
  2. $ Hello

和访问memcached类似,需要提供一个redis storage专门用于查询redis,然后通过子请求去调用redis。

3. Redis Pipeline

在实际访问redis时,有可能需要同时查询多个key的情况。我们可以采用ngx.location.capture_multi通过发送多个子请求给redis storage,然后在解析响应内容。但是,这会有个限制,Nginx内核规定一次可以发起的子请求的个数不能超过50个,所以在key个数多于50时,这种方案不再适用。

幸好redis提供pipeline机制,可以在一次连接中执行多个命令,这样可以减少多次执行命令的往返时延。客户端在通过pipeline发送多个命令后,redis顺序接收这些命令并执行,然后按照顺序把命令的结果输出出去。在lua中使用pipeline需要用到redis2模块的redis2_raw_queries进行redis的原生请求查询。

配置:

[plain] view plaincopyprint?

  1. #在Lua中访问Redis
  2. location = /redis {
  3. internal; #只能内部访问
  4. redis2_raw_queries $args $echo_request_body;
  5. redis2_pass '127.0.0.1:6379';
  6. }
  7. location = /pipeline {
  8. content_by_lua 'conf/pipeline.lua';
  9. }

pipeline.lua

[plain] view plaincopyprint?

  1. -- conf/pipeline.lua file
  2. local parser = require(‘redis.parser’)
  3. local reqs = {
  4. {‘get’, ‘one’}, {‘get’, ‘two’}
  5. }
  6. -- 构造原生的redis查询,get one\r\nget two\r\n
  7. local raw_reqs = {}
  8. for i, req in ipairs(reqs) do
  9. table.insert(raw_reqs, parser.build_query(req))
  10. end
  11. local res = ngx.location.capture(‘/redis?’..#reqs, { body = table.concat(raw_reqs, ‘’) })
  12. if res.status and res.body then
  13. -- 解析redis的原生响应
  14. local replies = parser.parse_replies(res.body, #reqs)
  15. for i, reply in ipairs(replies) do
  16. ngx.say(reply[1])
  17. end
  18. end

输出:

[plain] view plaincopyprint?

  1. $ curl 'http://localhost/pipeline'
  2. $ first
  3. second

4. Connection Pool

前面访问redis和memcached的例子中,在每次处理一个请求时,都会和后端的server建立连接,然后在请求处理完之后这个连接就会被释放。这个过程中,会有3次握手、timewait等一些开销,这对于高并发的应用是不可容忍的。这里引入connection pool来消除这个开销。

连接池需要HttpUpstreamKeepaliveModule模块的支持。

配置:

[plain] view plaincopyprint?

  1. http {
  2. # 需要HttpUpstreamKeepaliveModule
  3. upstream redis_pool {
  4. server 127.0.0.1:6379;
  5. # 可以容纳1024个连接的连接池
  6. keepalive 1024 single;
  7. }
  8. server {
  9. location = /redis {
  10. redis2_pass redis_pool;
  11. }
  12. }
  13. }

这个模块提供keepalive指令,它的context是upstream。我们知道upstream在使用Nginx做反向代理时使用,实际upstream是指“上游”,这个“上游”可以是redis、memcached或是mysql等一些server。upstream可以定义一个虚拟server集群,并且这些后端的server可以享受负载均衡。keepalive 1024就是定义连接池的大小,当连接数超过这个大小后,后续的连接自动退化为短连接。连接池的使用很简单,直接替换掉原来的ip和端口号即可。

有人曾经测过,在没有使用连接池的情况下,访问memcached(使用之前的Memc模块),rps为20000。在使用连接池之后,rps一路飙到140000。在实际情况下,这么大的提升可能达不到,但是基本上100-200%的提高还是可以的。

5. 小结

这里对memcached、redis的访问做个小结。

1. Nginx提供了强大的编程模型,location相当于函数,子请求相当于函数调用,并且location还可以向自己发送子请求,这样构成一个递归的模型,所以采用这种模型实现复杂的业务逻辑。

2. Nginx的IO操作必须是非阻塞的,如果Nginx在那阻着,则会大大降低Nginx的性能。所以在Lua中必须通过ngx.location.capture发出子请求将这些IO操作委托给Nginx的事件模型。

3. 在需要使用tcp连接时,尽量使用连接池。这样可以消除大量的建立、释放连接的开销。

以上就介绍了使用ngx_lua构建高并发应用,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

推荐阅读
  • Openresty+Lua+Redis灰度发布
    Openresty+Lua+Redis灰度发布灰度发布,简单来说,就是根据各种条件,让一部分用户使用旧版本,另一部分用户使用新版本。百度百科中解释:灰度发布是指在黑与白之间,能够平 ... [详细]
  • luarestyqlesswebUI界面运行
    lua-resty-qless-web是lua-resty-qless的web管理界面以及lua-resty-template模版引擎开发的,里面实现了一个简单的路由功能备注:de ... [详细]
  • springcloud:微服务涉及哪些技术、有哪些核心组件(二)
    0.引言上一期我们介绍了什么是微服务,微服务的基础概念,那么本期我们来介绍一下微服务涉及的技术点以及所需要的组件1.微服务涉及哪些技术1.1、基础 ... [详细]
  • 本文介绍了在无法联网的情况下,通过下载rpm包离线安装zip和unzip的方法。详细介绍了如何搜索并下载合适的rpm包,以及如何使用rpm命令进行安装。 ... [详细]
  • 有意向可以发简历到邮箱内推.简历直达组内Leader.能做同事的话,内推奖励全给你. ... [详细]
  • Ansibleplaybook roles安装redis实例(学习笔记二十九)
    1、相关redis参数:2、templatesredis.conf配置相关参数:daemonizeyespidfilevarrunredis_{{red ... [详细]
  • 大厂首发!思源笔记docker
    JVMRedisJVM面试内存模型以及分区,需要详细到每个区放什么?GC的两种判定方法GC的三种收集方法:标记清除、标记整理、复制算法的 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了python面试题——数据库和缓存(46题)相关的知识,希望对你有一定的参考价值。1、列举常见的关系型数据库和非关系型都有那些? ... [详细]
  • 数据库基本介绍
    1、数据库基本知识概念:数据库:database(DB),是一种存储数据的仓库数据库是根据数据结构组织、存储和 ... [详细]
  • 架构师必读:日均500万数据,如何进行数据存储选型?
    点击上方关注我,选择“置顶或者星标”作者:麦田里的老农来源:https:zhuanlan.zhihu.comp37964096小编公司有一 ... [详细]
  • 实战项目memcached+tomcat+session+nginx在工作中的应用和配置
    环境介绍:公司根据实际需要搭建一个购物网站,当用户购物时可以将不同商品,放到同一个购物车中进行同时付款。环境的搭建:外网用户IP地址:1.1.1.1主机名:fanxiaohui用户 ... [详细]
  • 对于WEB应用集群的技术实现而言,最大的难点就是如何能在集群中的多个节点之间保持数据的一致性,会话(Session)信息是这 ... [详细]
  • php还能用多少年(php还行吗)
    导读:很多朋友问到关于php还能用多少年的相关问题,本文编程笔记就来为大家做个详细解答,供大家参考,希望对大家有所帮助!一起来看看吧!本文目录一览: ... [详细]
  • 爬虫框架Scrapy(三)
    正文共:2957字6图预计阅读时间:8分钟每日分享Bethetypeofpersonyouwanttomeet.努力变成理想的模样。小闫笔记࿱ ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了redis调优的实战经验相关的知识,希望对你有一定的参考价值。本文根据redis的inf ... [详细]
author-avatar
泡沫茱_617
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有