This article focuses on how to Ngx_lua non-blocking communication with memcached and Redis on the backend.
1. Memcached
Access to the memcached in Nginx requires the support of the module, where Httpmemcmodule, which can be used with the back-end memcached for non-blocking communication. We know that the memcached is officially available, this module only supports get operations, and MEMC supports most memcached commands.
The MEMC module is passed as an entry variable as a parameter, and all variables prefixed with $MEMC_ are MEMC entry variables. Memc_pass points to the back-end of memcached Server.
Configuration:
[Plain] View plaincopyprint?
- #使用HttpMemcModule
- Location =/MEMC {
- Set $MEMC _cmd $arg _cmd;
- Set $MEMC _key $arg _key;
- Set $MEMC _value $arg _val;
- Set $MEMC _exptime $arg _exptime;
- Memc_pass ' 127.0.0.1:11211 ';
- }
Output:
[Plain] View plaincopyprint?
- $ Curl ' Http://localhost/memc?cmd=set&key=foo&val=Hello '
- $ STORED
- $ Curl ' Http://localhost/memc?cmd=get&key=foo '
- $ Hello
This enables memcached access, and below is a look at how to access memcached in Lua.
Configuration:
[Plain] View plaincopyprint?
- #在Lua中访问Memcached
- Location =/MEMC {
- Internal #只能内部访问
- Set $MEMC _cmd get;
- Set $MEMC _key $arg _key;
- Memc_pass ' 127.0.0.1:11211 ';
- }
- Location =/LUA_MEMC {
- Content_by_lua '
- Local res = ngx.location.capture ("/memc", {
- args = {key = Ngx.var.arg_key}
- })
- if res.status = =
- Ngx.say (Res.body)
- End
- ';
- }
Output:
[Plain] View plaincopyprint?
- $ Curl ' Http://localhost/lua_memc?key=foo '
- $ Hello
access to memcached through Lua is achieved primarily through the use of a similar function call in a child request. First, a MEMC location is defined for communication over the backend memcached, which is equivalent to memcached storage. Because the entire MEMC module is non-blocking, the ngx.location.capture is also non-blocking, so the entire operation is non-blocking.
2. Redis
Access to Redis requires httpredis2module support, and it can also be non-blocking with Redis. However, the response of Redis2 is the native response of Redis, so when used in Lua, this response needs to be resolved. The luaredismodule can be used to build the native request of Redis and resolve the native response of Redis.
Configuration:
[Plain] View plaincopyprint?
- #在Lua中访问Redis
- Location =/redis {
- Internal #只能内部访问
- Redis2_query get $arg _key;
- Redis2_pass ' 127.0.0.1:6379 ';
- }
- Location =/lua_redis {#需要LuaRedisParser
- Content_by_lua '
- Local parser = require ("Redis.parser")
- Local res = ngx.location.capture ("/redis", {
- args = {key = Ngx.var.arg_key}
- })
- if res.status = =
- Reply = parser.parse_reply (res.body)
- Ngx.say (Reply)
- End
- ';
- }
Output:
[Plain] View plaincopyprint?
- $ Curl ' Http://localhost/lua_redis?key=foo '
- $ Hello
similar to accessing memcached, you need to provide a Redis storage dedicated to querying Redis, and then calling Redis through child requests.
3. Redis Pipeline
when you actually access Redis, it is possible to query multiple keys at the same time. We can take ngx.location.capture_multi by sending multiple sub-requests to redis storage, and then parsing the response content. However, there is a limitation that the Nginx kernel specifies that no more than 50 child requests can be initiated at a time, so this scheme is no longer applicable when the number of keys is more than 50 o'clock.
Fortunately, Redis provides the pipeline mechanism to execute multiple commands in a single connection, which reduces the round-trip delay of executing commands multiple times. After the client sends multiple commands through pipeline, Redis sequentially receives the commands and executes them, then prints out the results of the command in order. Using pipeline in Lua requires a native request query for Redis using the redis2_raw_queries of the Redis2 module.
Configuration:
[Plain] View plaincopyprint?
- #在Lua中访问Redis
- Location =/redis {
- Internal #只能内部访问
- Redis2_raw_queries $args $echo _request_body;
- Redis2_pass ' 127.0.0.1:6379 ';
- }
- Location =/pipeline {
- Content_by_lua ' Conf/pipeline.lua ';
- }
Pipeline.lua
[Plain] View plaincopyprint?
- --Conf/pipeline.lua file
- Local parser = require (' Redis.parser ')
- Local reqs = {
- {' Get ', ' one '}, {' Get ', ' one '}
- }
- --Constructs native Redis query, get one\r\nget two\r\n
- Local raw_reqs = {}
- For I, req in Ipairs (reqs) do
- Table.insert (Raw_reqs, Parser.build_query (req))
- End
- Local res = Ngx.location.capture ('/redis? '). #reqs, {BODY = Table.concat (Raw_reqs, ')})
- If Res.status and Res.body then
- --parsing the native response of Redis
- Local replies = Parser.parse_replies (Res.body, #reqs)
- For I, reply in Ipairs (replies) do
- Ngx.say (Reply[1])
- End
- End
Output:
[Plain] View plaincopyprint?
- $ Curl ' http://localhost/pipeline '
- $ first
- Second
4. Connection Pool
In the previous example of accessing Redis and memcached, each time a request is processed, a connection is established with the backend server and then the connection is freed after the request is processed. In this process, there will be 3 handshake, timewait and other overhead, which is not tolerated for high concurrency applications. The connection pool is introduced here to eliminate this overhead.
Connection pooling requires the support of the Httpupstreamkeepalivemodule module.
Configuration:
[Plain] View plaincopyprint?
- HTTP {
- # need Httpupstreamkeepalivemodule
- Upstream Redis_pool {
- Server 127.0.0.1:6379;
- # can accommodate 1024 connected pools
- KeepAlive;
- }
- server {
- Location =/redis {
- ...
- Redis2_pass Redis_pool;
- }
- }
- }
This module provides the keepalive instruction, and its context is upstream. We know that upstream use Nginx to do reverse proxy, the actual upstream refers to "upstream", this "upstream" can be Redis, memcached or MySQL and other servers. Upstream can define a virtual server cluster, and these backend servers can enjoy load balancing. KeepAlive 1024 defines the size of the connection pool, and when the number of connections exceeds this size, subsequent connections are automatically degraded to short connections. The use of connection pooling is simple, just replace the original IP and port number.
It has been measured that, in the absence of a connection pool, access to memcached (using the previous MEMC module), RPS is 20000. After using the connection pool, RPS went all the way to 140000. In fact, such a large increase may not be achieved, but basically 100-200% improvement is still possible.
5. Summary
Here's a summary of the memcached and Redis visits.
1. Nginx provides a powerful programming model, location equivalent function, sub-request equivalent function call, and location can also send themselves sub-request, so as to form a recursive model, so adopt this model to implement complex business logic.
2. Nginx IO operation must be non-blocking, if the nginx blocking, it will greatly reduce the performance of Nginx. Therefore, in Lua it is necessary to send these IO operations to the Nginx event model through Ngx.location.capture.
3. Use connection pooling whenever possible when you need to use a TCP connection. This eliminates the overhead of creating and releasing connections in large numbers.
The above describes the use of Ngx_lua to build high-concurrency applications, including aspects of the content, I hope that the PHP tutorial interested in a friend helpful.