Skip to content

Instantly share code, notes, and snippets.

@mendelgusmao
Created April 11, 2012 01:53
Show Gist options
  • Star 40 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save mendelgusmao/2356310 to your computer and use it in GitHub Desktop.
Save mendelgusmao/2356310 to your computer and use it in GitHub Desktop.
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
Copy link

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