牛仔支持多少条路线?

时间:2014-01-02 09:39:50

标签: erlang cowboy

我正在寻找一种方法,在Cowboy中,将任意路径(存储在数据库中)映射到特定的博客帖子。

那就是:我有几千篇博文,可以通过几个名称访问,例如规范网址(例如/post/42),一些别名(例如/2013/11/25/erlang-rocks),历史位置(例如/path-on-old-blog/12345)等

我知道我可以简单地使用全能路线:

{ "/[...]", catch_all_handler, [] },

...然后查找数据库中的路径,但我正在考虑从数据库创建路由,如下所示:

Posts = posts:all(),
Paths = [get_handlers_for_post(P) || P <- Posts],
Routes = lists:flatten(Paths),

get_handler_for_post(P) ->
    % Generate a list of paths with IDs from the database.
    % Return something that looks like this:
    %  [{"/canonical/1", post_handler, [1]},
    %   {"/first-alias", post_handler, [1]}].
% TODO: code goes here...

即:将所有可能的路径放在路由器中,指向同一个处理程序,每个路径都有帖子的ID。

问题是:这是明智的吗?牛仔支持多少条路线?

2 个答案:

答案 0 :(得分:3)

你可以做到,但没有必要。牛仔在路由中具有非常有效的模式匹配语法。让我们以你的例子中给出的路线为例

[{"/canonical/1", post_handler, [1]},
 {"/first-alias", post_handler, [1]}].

第一个网址有一个额外的路径是可选的。在牛仔中,您可以将这两条路线表示为

"/:first/[:second]"

这匹配/canonical/1以及/first-alias

第一个和第二个都是参数化的,它们可以取任何值。 :second周围的方括号表示这是可选的。上述模式将匹配您提供的两个路径。

那么你如何在路由处理程序中实际访问这些参数呢?

真的很简单。 Cowboy在cowboy_req模块中提供了一个绑定方法,您可以从那里访问您的网址参数

cowboy_req:binding(first,Req)

如果是您的第一个网址,则会返回{<<"canonical">>,Req}

请注意,参数是atom。使用参数和可选参数,您应该能够匹配整个网址集。

详细了解路由here

更多解释

据我了解,您有数千个不同的博客帖子,而且他们的网址不一致。我建议而不是创建路由动态地找到一致url的模式并将它们分组到路由中。回落在牛仔中自动发生。如果它在模式上不匹配,它会看向另一个,依此类推。

例如

\:a\:b

将匹配

\hello\manhello\world\hello\slash\

hello\man\world不匹配。

\:a\:b\[:c]

将匹配\hello\manhello\worldhello\man\world

路线数量没有硬性限制。你可以拥有你需要的任意数量。

答案 1 :(得分:0)

很久以前问的,还是很有趣。

不,我不认为生成的 Cowboy 路由规则是在大量非结构化路径上进行查找的有效方法。

cowboy_router:compile/1 生成的调度规则是元组、列表和二进制的结构,如下所示:

[{'_',[],
      [{[<<"canonical">>,<<"1">>],[],post_handler,[1]},
       {[<<"first-alias">>],[],post_handler,[1]}]}]

路由是这种结构中的线性搜索。它会被复制到每个请求处理程序进程中,因此如果它非常大,则每个请求的复制都会产生很大的开销。

在 Cowboy 的最新版本中,路线可以存储在 persistent_term 中,从而消除了复制。尽管如此,它仍然是一个线性搜索。

对于大量非结构化路径,我相信 ETS 表查找会更有效,因为它是作为哈希表实现的。

我想提到的另一个选项,因为您正在考虑代码生成,是生成一个 Erlang 模块,其中包含一个执行查找的函数。这消除了复制,并且可以从模式匹配的编译器优化中受益。

%% Generated module
-module(blog_path_aliases).
-export([lookup/1]).
lookup(<<"/2013/11/25/erlang-rocks">>) -> 42;
lookup(<<"/path-on-old-blog/12345">>) -> 42;
lookup(<<"/some-other/path">>) -> 123;
...