Nginx+Lua脚本控制负载均衡

有时候会根据不同客户需求,提供特有的私有环境,但是接口入口都是统一的情况下,如何将用户请求转发到特定的机器上呢?

如果可以根据用户请求的参数进行判断,然后nginx转根据对应参数转发到对应服务器就可以啦。

Nginx本身是可以获取到用户请求参数的,就像这样:

比如我要获取https://api.example.com?id=test

location / {
        echo "appId: $arg_id";
}

通过上述配置是可以获取到Get请求中Url所携带的参数,但是Post请求是无法获取的,但是现在大部分api都是Post请求。而且为了更灵活的判断用户参数,再进行相应转发,我们就需要用到Lua脚本啦。

Lua脚本配置

Lua 是一个由标准 C 语言 开发的、开源的、可扩展的轻量级的弱类型的解释型脚本语言

OpenResty集成了Lua脚本,直接上如何配置。

1.设置缓存数据的脚本 cache.lua
-- 获取内存数据
local ups_cache = nginx.shared.ups_cache

function set_cache(key, value)
  local success, err = my_cache:set(key, calue)
  if not success then  
        ngx.log(ngx.ERR, "failed to set cache: ", err)  
        return nil  
    end  
    return true  
end  

function get_cache(key)
      return ups_cache:get(key)
end

function get_ups()
      ngx.req.read_body() -- 确保读取整个请求体  
    local body_data = ngx.req.get_body_data()  
    if not body_data then  
        ngx.status = ngx.HTTP_BAD_REQUEST  
        ngx.say("Failed to read request body")  
        return nil  
    end  
    local json_data = cjson.decode(body_data)  
    if not json_data then  
        ngx.status = ngx.HTTP_BAD_REQUEST  
        ngx.say("Invalid JSON data in request body")  
        return nil  
    end  
         local appId = json_data.appId
      return get_cache(appId)
end

return {
    set_cache = set_cache;
    get_cache = get_cache;
      get_ups = get_ups;
}
2.根据缓存数据获取ups,(get_ups.lua)
local cacheUtils= require "cache"

local upsValue = cacheUtils.get_ups();
if upsValue then 
      ngx.var.my_ups = upsValue
end

Nginx配置

1. 指定内存区域缓存配置数据
http {
     lua_shared_dict ups_cache 128k;
}
2.通过接口传输配置数据到Nginx
server {
        listen 80;
        server_name api.xxx.com;
        
        location /updateCache {
            content_by_lua_block {  
          -- 引入json工具
          local cjson = require "cjson"  
          -- 引入cache.lua
          local cache = require "cache"  
  
          -- 读取请求体中的 JSON 数据  
          ngx.req.read_body() -- 确保读取整个请求体  
          local json_data = ngx.req.get_body_data()  
          if not json_data then  
              ngx.status = ngx.HTTP_BAD_REQUEST  
              ngx.say("Failed to read request body")  
              return  
          end  
  
          -- 解析 JSON 数据  
          local data, err = cjson.decode(json_data)  
          if not data then  
              ngx.status = ngx.HTTP_BAD_REQUEST  
              ngx.say("Invalid JSON data")  
              return  
          end  
                
          local key = data.key  
          local value = cjson.encode(data.value) -- 将 Value 编码为 JSON 字符串存入缓存  

          -- 将数据存入缓存  
          if cache.set_cache(key, value) then  
              ngx.status = ngx.HTTP_OK  
              ngx.say("Data cached successfully")  
          else  
              ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR  
              ngx.say("Failed to cache data")  
          end  
        }  
        }
}
3.业务接口配置
upstream ups_normal {
        server 192.168.1.1:8080;
}

upstream ups_test {
        server 192.168.1.2:8080;
}

server {
        #省略
        ...
        
    location /v1 {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_buffering off;
        
        #设置默认转发的upstream
        set $my_ups 'ups_normal';
        access_by_lua_file "/opt/get_ups.lua";
        proxy_pass http://$my_ups
    }
}

这种方式可以很灵活的通过业务端进行负载均衡的控制,如果觉得Nginx的共享内存不稳定,容易造成配置数据的丢失,还可以对数据的缓存做进一步的处理,比如将数据缓存到redis中,再持久化到数据库中。定时将数据同步至Nginx缓存中,避免redis或者数据库对于接口性能的影响。

最后修改:2025 年 05 月 12 日
如果觉得我的文章对你有用,请随意赞赏