[scheme optimization] 1. OpenResty combined with Lua and Redis to realize the strategy of high request concurrency

catalog

1, Overall architecture description

1.1 scheme v1.0

1.2 scheme v1.0 optimization

1.3 scheme comparison

2, Scenario 2.0 deployment

2.1 OpenResty installation

2.2 Lua scripting

2.3 configure OpenResty

3, Testing

1, Overall architecture description

1.1 scheme v1.0

Before optimizing the old design framework, take a look at common query request processing

  

The steps are as follows:

  • ① After request to Nginx or restApi service, request data from Redis
  • ② ③ Redis checks whether there is data in the cache. If there is data, it will directly enter the process ⑥. If not, it will enter the process ④
  • ④ ⑤ query the database data and update the data to Redis so as not to visit the database next time
  • ⑥ Return data to user

 

1.2 scheme v1.0 optimization

In v1.0, there are a lot of simple requests, such as querying user / group information in IM software, querying commodity information in mall projects, etc. these simple but frequent queries will undoubtedly cause certain pressure on services. So is there any way to optimize.

After the optimization of v1.0, the scheme v2.0 combines OpenResty, Lua and redis to realize the two-level cache. With the high concurrency of Nginx, these requests need not be serviced by rest API, but directly query and operate redis and mysql through Lua script to reduce the service pressure.

  

The steps are as follows:

  • ① ② after the request to Nginx, Nginx queries Nginx cache first. If there is no data in the cache, call the corresponding Lua script
  • ③ ④ Lua script queries Redis. If there is data in Redis cache, it will directly return and update Nginx cache. If there is no data, it will call Lua script to query database
  • ⑤ Query database data and update Redis cache

be careful:

Scheme v2.0 adopts a level by level caching method:

  • The first access: there is no cache in nginx and redis. The data is found in the database and stored in redis cache
  • Second access: there is no cache in nginx, the data is found in redis and stored in nginx cache
  • Third visit: data is found out from nginx

This is done for the following reasons:

  • nginx cache expiration time is less than redis cache expiration time, which is conducive to reducing the risk of redis avalanche
  • If cache is set for nginx and redis at the same time, the data access is nginx. When the database changes, it is impossible to achieve rapid response without accessing the database. The solution is to use Canal to realize the data synchronization between database and redis, set the effective time of nginx to be shorter, so as to prevent a large number of concurrent requests to the database and get the latest data at the same time

 

1.3 scheme comparison

According to different project requirements and actual plans, choose which one to use.

  Scheme v1.0 Scenario v2.0
flexibility Access to redis and database is controlled by restApi, which can handle complex requests. But if there is any modification, the service must be restarted

Using the high concurrency of Nginx to process a large number of simple requests, and modifying scripts does not need to restart the back-end services, as long as Nginx is reloaded.

But it can only handle simple requests, and needs to have a special identification method, such as adding a specific API

Concurrency high It must be higher than v1.0, but it should be used according to the actual situation rather than deployed to show off the technology
Deployment difficulty simple Medium. Need to learn OpenResty framework and Lua language
Applicable scenarios General scenario

It is more suitable for projects with a large number of simple query requests, such as warehouse management system, mall system, etc.

At the same time, this scheme can also deal with permission control, such as refusing the request without token directly in Nginx, or carrying out token verification.

 

2, Scenario 2.0 deployment

2.1 OpenResty installation

Installation process: https://blog.csdn.net/qq_34416331/article/details/106421783

 

2.2 Lua scripting

Basic usage of Lua: https://blog.csdn.net/qq_34416331/article/details/106419100

Lua scripting:

# Create a folder to store lua scripts, and customize the name
mkdir /usr/local/lua_conf

# Enter folder
cd /usr/local/lua_conf

# Create lua script
vim read_conf.lua
ngx.header.content_type="application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args();
local id = uri_args["id"];
-- load nginx Cache module
local cache_ngx = ngx.shared.dis_cache;
-- according to ID Get local cache data
local contentCache = cache_ngx:get('content_cache_'..id);

-- obtain IP Information, can be deleted
-- [[
local headers=ngx.header;
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
ngx.say(ip)
ngx.say(contentCache)
]]--

-- if nginx No corresponding cache information in
if contentCache == "" or contentCache == nil then

    -- obtain redis modular
    local redis = require("resty.redis");
    local red = redis:new()
    red:set_timeout(2000)
    red:connect("192.168.47.142", 6379)
    local rescontent=red:get("content_"..id);

    -- if redis The module also does not have this information
    if ngx.null == rescontent then
        -- Get data from database
        local cjson = require("cjson");
        local mysql = require("resty.mysql");
        local db = mysql:new();
        db:set_timeout(2000)
        local props = {
            host = "192.168.47.142",
            port = 3306,
            database = "changgou_content",
            user = "root",
            password = "123456"
        }
        local res = db:connect(props);
        local select_sql = "select url,pic from tb_content where status ='1' and category_id="..id.." order by sort_order";
        res = db:query(select_sql);
        local responsejson = cjson.encode(res);

        -- Store to redis in
        red:set("content_"..id,responsejson);

        -- Return data
        ngx.say(responsejson);
        -- Close database connection
        db:close()
    else
        -- if redis If there is a cache in, set to nginx Cache, and return
        -- 2*60 Presentation settings nginx The cache time is 2 minutes, which should be modified according to the actual situation
        cache_ngx:set('content_cache_'..id, rescontent, 2*60);
        ngx.say(rescontent)
    end
    -- close redis connect
    red:close()
else
-- if nginx If there is corresponding information in, return
    ngx.say(contentCache)
end

 

2.3 configure OpenResty

For OpenResty to use Lua scripts, you only need to configure the configuration file of Nginx.

### modify Nginx configuration file ###
# Enter the Nginx directory of OpenResty
cd /usr/local/openresty/nginx/conf

# Edit profile
vim nginx.conf

  

 lua_ shared_ The purpose of dict is to declare a shared memory area name to act as the shared storage based on Lua dictionary. In short, the size of lua script cache space when nginx is running

  

To configure the interface to be accessed in http:

  

Restart nginx

# Reload nginx
/usr/local/openresty/nginx/sbin/nginx -s reload

 

3, Testing

Modify read_content.lua

ngx.header.content_type="application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args();
local id = uri_args["id"];
-- load nginx Cache module
local cache_ngx = ngx.shared.dis_cache;
-- according to ID Get local cache data
local contentCache = cache_ngx:get('content_cache_'..id);

--[[
local headers=ngx.header;
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
ngx.say(ip)
ngx.say(contentCache)
]]--

if contentCache==nil then
  ngx.say("nginx Cache is empty")
else 
  ngx.say("from nginx Data found in cache")
end

-- if nginx No corresponding cache information in
if contentCache == "" or contentCache == nil then

    -- obtain redis modular
    local redis = require("resty.redis");
    local red = redis:new()
    red:set_timeout(2000)
    red:connect("192.168.47.142", 6379)
    local rescontent=red:get("content_"..id);

    if ngx.null == rescontent then
       ngx.say("redis Empty")
    else
       ngx.say("nginx Empty, from redis Data found in")
    end
    -- if redis The module also does not have this information
    if ngx.null == rescontent then
        -- Get data from database
        local cjson = require("cjson");
        local mysql = require("resty.mysql");
        local db = mysql:new();
        db:set_timeout(2000)
        local props = {
            host = "192.168.47.142",
            port = 3306,
            database = "changgou_content",
            user = "root",
            password = "123456"
        }
        local res = db:connect(props);
        local select_sql = "select url,pic from tb_content where status ='1' and category_id="..id.." order by sort_order";
        res = db:query(select_sql);
        local responsejson = cjson.encode(res);

        -- Store to redis in
        red:set("content_"..id,responsejson);

        -- Return data
        ngx.say(responsejson);
        -- Close database connection
        db:close()
    else
        -- if redis If there is a cache in, set to nginx Cache, and return
        cache_ngx:set('content_cache_'..id, rescontent, 30);
    end
    -- close redis connect
    red:close()
else
-- if nginx If there is corresponding information in, return
    ngx.say(contentCache)
end

Clean up redis

visit:

First visit

Second visit:

Third visit:

Tags: Nginx Redis Database MySQL

Posted on Mon, 01 Jun 2020 05:00:28 -0400 by gabriel kent