使用 mod_mruby 的 Web 应用程序
这是 mod_mruby ngx_mruby 2014 年度圣诞日历的第 15 天(12/15)的投稿。
使用 mod_mruby 的 web 应用
“起初的契机是,在ISUCON上使用mod_mruby的想法,为了这个原因,我考虑了一个练习,去找出可以加速和找出瓶颈点的方法。
然而,实际上我没能及时参加ISUCON预选赛,所以这次是为了这个Advent Calendar而创作的。”
你开发了哪种类型的应用程序?
为了储存到某种存储设备,并处理大量访问的 Web 应用情景,我们开发了一个缩短 URL 的应用。该应用可以将长 URL 转换为短 URL,让用户不需要输入长 URL。
此外,我认为通过这种方式,进行功能扩展和验证会更加简单。例如,可以按用户管理缩短 URL,需要对用户进行身份验证,添加访问控制,或者考虑生成高效的缩短 URL 哈希值等等。因此,我们可以随后对功能进行扩展并进行验证。
准备
本次使用的 mrbgems 包括 mruby-redis 和 mruby-json。
由于 mruby-redis 没有我们这次需要的哈希相关的 API,所以我们稍微修改了一下。
設計 (shè jì)
我們將實現從輸入表單輸入URL並生成短縮URL的過程,以及當訪問短縮URL時,重定向到原始URL的過程。
Apache配置
# オリジナルの URL から、短縮 URL を生成する
<Location /api/v1/shorten>
mrubyHandlerMiddle /home/vagrant/opt/apache/mruby/shorten.rb
</Location>
# 短縮 URL から、オリジナルの URL にリダイレクトさせる
<Location /go>
mrubyHandlerMiddle /home/vagrant/opt/apache/mruby/go_original_url.rb
</Location>
输入表单
在这里使用jQuery将输入的URL发送到/api/v1/shorten,并显示生成的URL。Web API以JSON格式进行交互。
<!DOCTYPE html>
<html>
<head>
<title>Shorten URL</title>
</head>
<body>
<h1>Shorten URL</h1>
<input type="text" id="url" />
<button id="shorten-url">shrten</button>
=
<span id="result"></span>
</body>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script type="text/javascript">
<!--
$(function () {
$("#shorten-url").on("click", function () {
var url = $("#url").val();
if (url === "") {
alert("Please enter the URL");
return;
}
$.ajax({
type: "POST",
contentType: 'application/json',
url: "/api/v1/shorten",
data: JSON.stringify({ url: url }),
}).done(function(res) {
$("#result").text(res.short_url);
});
});
})
//-->
</script>
</html>
生成短链接
解析通过输入表单发送的 JSON 请求,生成缩短的 URL,并返回该 URL。
生成短网址的方法是生成一个顺序号,并直接使用它。此外,我们还固定指定了接收短网址请求的服务器的网址。
error_res = JSON::stringify({})
req = Apache::Request.new
req.content_type = "application/json"
unless req.method == "POST"
Apache.rputs(error_res)
Apache::return(Apache::OK)
end
post_data = JSON::parse(req.body)
url = post_data["url"]
# Redis config
host = "127.0.0.1"
port = 6379
database = 8
redis = Redis.new host, port
redis.select database
request_num = "request_number"
unless redis.exists?(request_num)
redis.set request_num, "0"
end
# generate a key
next_num = redis.incr(request_num)
url_key = "#{next_num}"
redis.hset "shorten_urls", url_key, url
Apache.rputs JSON::stringify({"url" => url, "short_url" => "http://localhost/go/#{url_key}"})
将缩短的URL重定向到原始URL。
从请求的URL中提取哈希值,然后从Redis中获取原始的URL并进行重定向。
error_res = JSON::stringify({})
req = Apache::Request.new
unless req.path_info.index('/') == 0
req.content_type = "application/json"
Apache.rputs(error_res)
Apache::return(Apache::OK)
end
url_key = req.path_info[1..-1]
# Redis config
host = "127.0.0.1"
port = 6379
database = 8
redis = Redis.new host, port
redis.select database
unless redis.hexists?("shorten_urls", url_key)
req.content_type = "application/json"
Apache.rputs(error_res)
Apache::return(Apache::OK)
end
original_url = redis.hget "shorten_urls", url_key
Apache.rputs(original_url)
req.headers_out["Location"] = original_url
Apache::return(Apache::HTTP_MOVED_PERMANENTLY)
总结
我计划研究一下给予系统压力时可以处理多少个请求,以及需要注意哪些方面,还有与MySQL的比较,以及与ngx_mruby的比较。对于这些我这次没做到的部分,我们将在今后进行。(很抱歉…有些实现采取了简化措施)
这次我用 mruby 制作了一个 Web 应用程序,我觉得用 mruby 来实现 Web API 非常容易且好用。如果在 mod_mruby 和 ngx_mruby 上共同构建一个通用的系统,我们就能根据应用的特性和环境进行选择和切换了。
也许不太常用 mod_mruby 来创建网页应用,但对于像没有 HTML 生成的 Web API 来说,可能还是有用的。而且,随着 mrbgems 越来越多,我觉得它的可能性会进一步扩展。
如果有模板引擎,可能会更容易地显示从数据库获取的值。
明天是 hkusu 先生的「使用 mod_mruby 进行静态文件的反向代理」!