使用 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格式进行交互。

shorten_url.png
<!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 进行静态文件的反向代理」!

广告
将在 10 秒后关闭
bannerAds