首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >实战:使用lua脚本在nginx层解决高并发访问问题

实战:使用lua脚本在nginx层解决高并发访问问题

原创
作者头像
小草飞上天
发布2024-12-25 23:28:39
发布2024-12-25 23:28:39
1.1K0
举报
文章被收录于专栏:java学习java学习

概要

公司产品最近出了一个需求,说需要进行ABtest进行自动切换页面投放,因为页面本身挺复杂,动态性比较大很多数据都在后台返回的。所以本来想着挺简单的需求,直接通过网关动态进行转发就ok~

很完美的事情,产品说,不行,得投放,并发量预估挺大的。那怎么办呢?于是乎我们就只能在nginx层通过lua脚本调用redis缓存进行动态的处理。完全避免了流量打到应用层。

那首先我们说一下什么是lua脚本呢?

lua脚本

lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

这是lua语言官方的介绍。简而言之就是:lua是脚本程序,很简单很小,就是为了扩展一些额外的功能点而存在的。

我们来学习一下lua

hello world

一般情况下,linux服务器本身就会安装lua 。所以这里就不介绍安装了,咱们直接来第一个hello world程序。

1、在linux任意目录文件下,新建一个hello.lua

2、在hello.lua中添加以下的代码

代码语言:txt
复制
print("hello world")

3、运行。对的就是这么简单。

lua数据类型和变量申明

lua的数据类型有以下几种。

如果想知道一个变量是什么数据类型的话。使用 type函数就可以。

代码语言:txt
复制
print(type("hello"))
//这个命令就会输出一个 String

print(type(1))
//这个命令就会输出一个 number

而在lua中申明一个变量只需要使用local关键字就可以:

代码语言:txt
复制
local str = "hello"

local num = 1

local arr = {"a","b","c"}

local obj = {"a":"1","b":"2"}

对于访问的话与其他一些开发语言类似:
数组:arr[0]  对象:obj.a

下面我们快速的说一下其他的逻辑判断和函数等。因为我们的重点并不在lua语言上。

循环的使用:

代码语言:txt
复制
local arr = {"a","b","c"}
for index,val in ipairs(arr) do
   print(index,val)
end

local obj = {"a"="1","b"="2"}
for index,val in pairs(obj) do
   print(index,val)
end

函数定义

代码语言:txt
复制
funtion eat(food){
    print("我爱吃",food)
}

条件控制

代码语言:txt
复制
local f = true;
if(f) then
   print("对")
else
   print("错")
end

以上就是基本的用法了。接下来进入主题了。在nginx中运行lua脚本读取redis缓存。

OpenResty

在nginx中运行lua脚本需要 ngx_http_lua_module 模块来实现

一般情况下,我们不自行安装,直接使用OpenResty框架。

什么是OpenResty呢?

官方说OpenResty是一个基于 Nginx的高性能 Web 平台,用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用

Web 服务和动态网关。

总结一下就是:包含nginx、额外支持lua脚本,集成大量lua库的一个框架。

我们安装一下OpenResty

安装开发库

代码语言:txt
复制
yum install -y pcre-devel openssl-devel gcc --skip-broken

安装OpenResty仓库

我们 CentOS 系统中添加 openresty 仓库,运行下面的命令就可以添加我们的仓库:

代码语言:txt
复制
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo

如果提示说命令不存在,则运行:

代码语言:txt
复制
yum install -y yum-utils

然后再重复上面的命令

完成上面操作后,执行安装命令

代码语言:txt
复制
yum install -y openresty

安装完成后的目录结构

默认情况下,OpenResty安装的目录是:/usr/local/openresty

下面目录中的luajit和lualib就是lua脚本的一些扩展库及我们自定义脚本的存放目录。

nginx目录则是完整的nginx服务。

配置环境变量

代码语言:txt
复制
vi /etc/profile

在这个文件中追加上环境配置

代码语言:txt
复制
export NGINX_HOME=/usr/local/openresty/nginx
export PATH=${NGINX_HOME}/sbin:$PATH

执行命令,使配置生效

代码语言:txt
复制
source /etc/profile

启动nginx

nginx目录下是完整的nginx服务。我们开始启动

代码语言:txt
复制
# 启动nginx (启动只需要这个就行) 注意nginx目录 在sbin目录下执行
nginx
# 重新加载配置(重启就用这个命令)
nginx -s reload
# 停止
nginx -s stop

修改基础版nginx配置,简化代码量

修改/usr/local/openresty/nginx/conf/nginx.conf文件,内容如下:

代码语言:txt
复制
#user  nobody;
worker_processes  1;
error_log  logs/error.log;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       8081;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

加载OpenResty的lua模块:

代码语言:txt
复制
#lua 模块
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
#c模块     
lua_package_cpath "/usr/local/openresty/lualib/?.so;;";

最后记得重启nginx

lua脚本读取redis

做好前面的准备工作后,我们写代码

下面定义了2个方法,一个读取,一个关闭 redis.

代码语言:txt
复制
local redis = require("resty.redis")
-- 初始化对象
local red = redis:new()
-- 设置超时时间
red.set_timeouts(1000)
-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
    end
    --得到的数据为空处理
    if resp == ngx.null then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
    end
    close_redis(red)
    return resp
end
-- 关闭redis连接的工具方法,其实是放入连接池
local function close_redis(red)
    local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    if not ok then
        ngx.log(ngx.ERR, "放入redis连接池失败: ", err)
    end
end

我们只需要调用read_redis方法就可以访问redis。

一般情况下我们获取到的是一个json的字串。如何解析json呢?

OpenResty提供了一个cjson的模块用来处理JSON的序列化和反序列化。

代码语言:txt
复制
local cjson = require "cjson"
local obj ={name='zhangsan',age=18}
local json = cjson.encode(obj)

local json1 ='{"name":"lisi","age": 18}'
local obj1=cjson.decode(json1);
print(obj1.name)

最后我们在说一下在lua中如何获取前端传递的参数呢?

在openResty中提供了一些方法来获取不同地方的参数。例如:

代码语言:txt
复制
local headers = ngx.req.get_headers()

local getParams = ngx.req.get_uri_args()

ngx.req.read_body()
local postParams = ngx.req.get_post_args()

local jsonBody = ngx.req.get_body_data()

总结

以上就是lua脚本在nginx层读取reis缓存的全部操作了。其实挺简单的。读取缓存并解析成功数据后,就可以进行abtest数据返回或者内部直接通过进行转发了。完全防止了流量打到应用层。直接做好缓存预热后,就可以解决大量的并发问题了

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概要
  • lua脚本
  • hello world
  • lua数据类型和变量申明
  • OpenResty
    • 安装开发库
    • 安装OpenResty仓库
    • 安装完成后的目录结构
    • 配置环境变量
    • 启动nginx
    • 修改基础版nginx配置,简化代码量
    • 加载OpenResty的lua模块:
  • lua脚本读取redis
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档