OpenResty as redirect server for logging sent to Kafka
Purpose
A redirect server for logging is used to trace various urls user clicked. The redirect server will log any query parameters in url and redirect to url parameter finally. The logging should be doing asynchronously to minimize redirect time.
OpenResty is a combination of Nginx and Lua. I won’t explain that here, you could google it to know more.
Environment
MacOS
Docker Desktop
Download lua-resty-kafka for lua kafka operation
Lua to get query paramters and send to kafka
Encode query parameters using cjson as message
Send message to kafka
Download lua-resty-kafka
1 2 3 4 5
mkdir openresty cd openresty wget https://github.com/doujiang24/lua-resty-kafka/archive/v0.07.zip unzip v0.07.zip mkdir logs
Directory layout
We will mount docker host files(nginx.conf) and directory(logs, lua-resty-kafka-0.07) to container
1 2 3 4 5 6 7 8 9
(base) kevin.luo➜~/dev/openresty» ls -al total 32 drwxr-xr-x 8 kevin.luo staff 256 Nov 6 12:59 . drwxr-xr-x@ 24 kevin.luo staff 768 Nov 5 20:10 .. drwxr-xr-x 5 kevin.luo staff 160 Nov 6 10:47 logs drwxr-xr-x@ 9 kevin.luo staff 288 Aug 31 08:48 lua-resty-kafka-0.07 -rw-r--r-- 1 kevin.luo staff 4490 Nov 6 12:59 nginx.conf -rwxr-xr-x 1 kevin.luo staff 874 Nov 6 11:19 run.sh -rwxr-xr-x 1 kevin.luo staff 58 Nov 5 20:29 stop.sh
lua_package_path "/usr/local/openresty/lualib/resty/kafka/?.lua;;"; # without resolver setting, it will fail in DNS lookup # either 8.8.8.8 or using container's resolver # resolver 8.8.8.8 resolver local=on ipv6=off; resolver_timeout 5s;
server { listen 8089; server_name localhost;
location / { root html; index index.html index.htm; }
# Make sure Lua is working location /hello { default_type text/html; content_by_lua ' ngx.say("Lua: hello world!") '; }
# The default 1by1 gif location /empty_gif { empty_gif; }
location /rd { default_type text/html; content_by_lua ' local cjson = require "cjson" local producer = require "resty.kafka.producer" -- local client = require "resty.kafka.client" local broker_list = { { host = "192.168.0.16", port = 9092}, { host = "192.168.0.17", port = 9092}, { host = "192.168.0.18", port = 9092} } -- Parsing request args local params_json = {} local args, err = ngx.req.get_uri_args() if err == "truncated" then -- one can choose to ignore or reject the current request here end for key, val in pairs(args) do params_json[key] = val end ngx.log(ngx.ERR, "url: ", params_json["d"]) local topic = "redirect-server" local params_message = cjson.encode(params_json) -- Set producer async local bp = producer:new(broker_list, { producer_type = "async" }) -- The second parameter of send method is to control kafka routing. -- When it is nill, it will write message to same partition -- With designated key,it will write message to hash of key of partition local ok, err = bp:send(topic, nil, params_message) -- For debugging ngx.log(ngx.ERR, "Message in json: ", params_message) if not ok then ngx.log(ngx.ERR, "kafka send err:", err) return end if params_json["url"] ~= nil then return ngx.redirect(params_json["url"], 301) end return ngx.exec("/empty_gif") ';
} } }
Testing
Check you logs/error.log file, there should not have any kafka send err: