Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
high performance URL shortener on steroids using nginx, redis and lua
# based on http://uberblo.gs/2011/06/high-performance-url-shortening-with-redis-backed-nginx
# using code from http://stackoverflow.com/questions/3554315/lua-base-converter
# "database scheme"
# database 0: id ~> url
# database 1: id ~> hits
# database 2: id ~> [{referer|user_agent}]
# database 3: id ~> hits (when id is not found)
# database 4: id ~> [{referer|user_agent}] (when id is not found)
# database 5: key "count" storing the number of shortened urls; the id is generated by (this number + 1) converted to base 62
server {
location ~ ^/retrieve$ {
internal;
redis2_query get $request_uri;
redis2_pass 127.0.0.1:6379;
}
location ~ ^/count$ {
internal;
redis2_query select 5;
redis2_query get count;
redis2_pass 127.0.0.1:6379;
}
location ~ ^/history$ {
internal;
redis2_query select 1;
redis2_query incr $request_uri;
redis2_query select 2;
redis2_query rpush $request_uri "$http_referer|$http_user_agent";
redis2_pass 127.0.0.1:6379;
}
location ~ ^/notfound$ {
internal;
redis2_query select 3;
redis2_query incr $request_uri;
redis2_query select 4;
redis2_query rpush $request_uri "$http_referer|$http_user_agent";
redis2_pass 127.0.0.1:6379;
}
location ~ ^/store$ {
internal;
redis2_query select 0;
set_unescape_uri $id $arg_id;
set_unescape_uri $url $arg_url;
redis2_query set $id $url;
redis2_query select 5;
redis2_query incr count;
redis2_pass 127.0.0.1:6379;
}
location ~ ^/shorten/(?<url>.*)$ {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
add_header Content-Type text/plain;
content_by_lua '
local parser = require "redis.parser"
local res = ngx.location.capture("/count")
local results, type = parser.parse_replies(res.body, 2)
basen = function(n, b)
n = math.floor(n)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
local sign = ""
if n < 0 then
sign = "-"
n = -n
end
repeat
local d = (n % b) + 1
n = math.floor(n / b)
table.insert(t, 1, digits:sub(d, d))
until n == 0
return sign .. table.concat(t,"")
end
local id = basen(results[2][1] + 1, 62)
ngx.location.capture("/store",
{ args = { id = "/"..tostring(id), url = ngx.var.url } }
)
ngx.say("http://" .. ngx.var.host .. "/" .. id)
';
}
location / {
content_by_lua '
local parser = require "redis.parser";
local res = ngx.location.capture("/retrieve");
local href, type = parser.parse_reply(res.body);
if type == parser.ERROR_REPLY or href == nil then
ngx.location.capture("/notfound");
ngx.exit(ngx.HTTP_NOT_FOUND);
else
ngx.location.capture("/history");
ngx.redirect(href);
end
';
}
}
@ebuildy

This comment has been minimized.

Copy link

@ebuildy ebuildy commented Jan 20, 2015

Got an error I could not resolve (yet..):

lua entry thread aborted: runtime error: content_by_lua(nginx.conf:129):24: attempt to perform arithmetic on field '?' (a nil value)

Any idea?

Thanks,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment