【问题标题】:How many routes does cowboy support?牛仔支持多少条路线?
【发布时间】:2014-01-19 17:27:54
【问题描述】:

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

也就是说:我有几千篇博客文章,每篇文章都可以通过几个名称访问,例如规范 URL(例如 /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...

即:将所有可能的路径放入路由器中,指向同一个handler,每个都有post的ID。

问题是:这合理吗?牛仔支持多少条路线?

【问题讨论】:

  • Cowboy 路线非常有效,您可以为几乎任何东西创建模式,但我想 nginx 对于这种情况仍然会更好。如果你能举几个例子说明你的网址是什么样的,也许我们可以更好地帮助你:)

标签: erlang cowboy


【解决方案1】:

你可以这样做,但没有必要。 Cowboy 在路由中有非常有效的模式匹配语法。让我们以您在示例中给出的路线为例

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

第一个 url 有一个可选的附加路径。在牛仔中,您可以将这两条路线表示为

"/:first/[:second]"

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

first 和 second 都是参数化的,它们可以采用任何值。 :second 周围的方括号表示这是可选的。上述模式将匹配您提供的两条路线。

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

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

cowboy_req:binding(first,Req)

如果是您的第一个网址,这将返回 {&lt;&lt;"canonical"&gt;&gt;,Req}

注意参数是atom。使用参数和可选参数,您应该能够匹配您的整个 url 集合。

阅读更多关于路由here

更多解释

据我了解,您有数千篇不同的博客文章,而且它们的网址并不一致。我建议不要动态创建路由,而是找到一致的 url 模式并将它们分组到一个路由中。回退在牛仔中自动发生。如果它与模式不匹配,它会查找另一个,依此类推。

例如

\:a\:b

将匹配

\hello\man,hello\world,\hello\slash\

不会匹配hello\man\world

\:a\:b\[:c]

将匹配\hello\man,hello\world,hello\man\world

路由数量没有硬性限制。您可以根据需要拥有任意数量。

【讨论】:

  • 别名没有单一、清晰的形式,它们可能包含斜杠(参见另一个问题)。我想要的是能够在数据库中查找任意路径并将其映射到帖子,或者 - 发现它不存在 - 沿着路线走得更远。
  • 尾部斜线不必在模式上保持一致。我提供的模式将匹配或不匹配斜线。基本上你应该做的是在尽可能多的 url 中找出一个共同的模式并创建路由。然后为下一组创建另一个。跌倒自动发生。如果一条路线不匹配,则检查下一条路线,依此类推。
  • 不尾随斜杠 -- 包含 斜杠 -- 使用 /:everything/2014/01/02/whatever 不匹配,因为它试图打破斜杠。
【解决方案2】:

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

不,我不认为生成的 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;
...

【讨论】:

    猜你喜欢
    • 2020-09-28
    • 2017-06-22
    • 2016-08-02
    • 2013-05-30
    • 2013-06-08
    • 2016-07-19
    • 2014-07-17
    • 2019-05-09
    • 1970-01-01
    相关资源
    最近更新 更多