Intro to Lua and Openresty, Part 5: Redis
In Part 4 of this series, we used envvars to tell our Lua webapp how to connect to Postgres. This next post will demonstrate how to interact with redis (connect/read/write).
For this example, we will use the resty.redis
lua module.
If you are following along in the code, we are here.
Connecting and Writing to Redis with Lua
To demonstrate the basics, this example will:
- connect to redis
- fail with an error message if lua is unable to connect to redis
- grab/process POST data
- encode the POST data as JSON and write it to the
queue
table in redis withlpush
- clear the connection
- respond to the user with a JSON message that confirms what was saved to redis
We should then be able to send in some test messages and then cat the keys on the queue
table.
worker_processes 1;
env REDIS_HOST;
error_log error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8000;
charset utf-8;
charset_types application/json;
default_type application/json;
location / {
content_by_lua '
local cjson = require "cjson"
local redis = require "resty.redis"
local r = redis:new()
local ok, err = r:connect(os.getenv("REDIS_HOST"), 6379)
if not ok then
ngx.say(cjson.encode({status = "error", msg = "failed to connect: " .. err}))
return
end
local get, post, files = require "resty.reqargs"()
assert(r:lpush("queue", cjson.encode(post)))
r = nil
ngx.status = ngx.HTTP_OK
ngx.say(cjson.encode({status = "saved", msg=post}))
return ngx.exit(ngx.HTTP_OK)
';
}
}
}
The lua application will now use the REDIS_HOST
envvar when connecting to the redis host, so we need to include that in our Dockerfile
. We are using the resty.redis
lua module, so we also need to install that with luarocks
:
FROM openresty/openresty:alpine-fat
EXPOSE 8000
ENV REDIS_HOST 127.0.0.1
RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-reqargs
ADD nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' > /etc/nsswitch.conf
Makefile
We won’t use Postgres for this example, but our Makefile
is similar to the last few examples:
build:
docker build --tag=app:6 --rm=true .
# add "-v `pwd`:/usr/local/openresty/nginx/conf/" to the app for local dev
run:
docker run -d --name redis --net host -p 127.0.0.1:6379:6379 redis:alpine
docker run -d --name app --net host -p 127.0.0.1:8000:8000 app:6
clean:
docker stop redis || true
docker stop app || true
docker rm redis || true
docker rm app || true
reload:
docker exec -it app /usr/local/openresty/nginx/sbin/nginx -s reload
logs:
docker exec -it app tail -f /usr/local/openresty/nginx/error.log
cat-posts:
docker exec -it redis redis-cli -c LRANGE queue 0 -1
app-shell:
docker exec -it app /bin/sh
redis-shell:
docker exec -it redis redis-cli
test:
curl -H "Content-Type: application/json" -X POST -d '{"username":"xyz","password":"xyz"}' localhost:8000/
This gives us a number of make targets which are very helpful when developing and debugging unexpected issues.
Build / Test / Run
Use make build
to create the Docker image:
ᐅ make build
docker build --tag=app:6 --rm=true ./
Sending build context to Docker daemon 16.9 kB
Step 1 : FROM openresty/openresty:alpine-fat
---> 366babf2b04d
Step 2 : EXPOSE 8000
---> Using cache
---> 35a8c6e42825
Step 3 : ENV REDIS_HOST 127.0.0.1
---> Using cache
---> 43fc68284411
Step 4 : RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-reqargs
---> Using cache
---> e17bae3848b8
Step 5 : ADD nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
---> Using cache
---> 306d00c38cee
Step 6 : RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' > /etc/nsswitch.conf
---> Using cache
---> 9ae83b74b72a
Successfully built 9ae83b74b72a
Run:
ᐅ make run
docker run -d --name redis --net host -p 127.0.0.1:6379:6379 redis:6
399c0c7f815d0e4ab7660b7e2852fb64f6f82ec42586c232a70bc11f5b577d2c
docker run -d --name app --net host -p 127.0.0.1:8000:8000 app:6
39e71e2333452730960e60c09afe4a6ad1bc8f8a4d625f49c2f3859ad1c0634f
Test:
ᐅ make test
curl -H "Content-Type: application/json" -X POST -d '{"username":"xyz","password":"xyz"}' localhost:8000/
{"status":"saved","msg":{"password":"xyz","username":"xyz"}}
I repeated that test 4 times, let’s see what is in redis:
ᐅ make cat-posts
docker exec -it redis redis-cli -c LRANGE queue 0 -1
1) "{\"password\":\"xyz\",\"username\":\"xyz\"}"
2) "{\"password\":\"xyz\",\"username\":\"xyz\"}"
3) "{\"password\":\"xyz\",\"username\":\"xyz\"}"
4) "{\"password\":\"xyz\",\"username\":\"xyz\"}"
Yay!
Then teardown the running containers with make clean
.
Time Tracking: since last check: 1 hour; total: 7 hours