多站点感知PSGI应用程序开发

时间:2016-03-26 01:21:41

标签: perl plack psgi

Plack::Builder允许挂载多个主机,例如以下代码片段:

my @sites = load_site_names();
my $apps;
for my $site (@sites) {
    $apps->{$site} = Some::PsgiFramework::MyApp->new( config => get_config($site) );
}

use Plack::Builder;
builder {
    for my $site (@sites) {
        mount "$site" => $apps->{$site};
    }
    mount '/' => sub { ... };
}

e.g。

  • load_site_names会返回http://example.comhttp://some.other.site.com,...等网站的列表。
  • 每个“虚拟主机”都将使用相同的Some::PsgiFramework::MyApp
  • 只是他们的配置不同

我完全需要上述内容 - 需要开发一个简单的网络应用程序,该应用程序应该部署在不同(低流量)站点的数据库中,并且不希望为每个站点设置不同的PSGI服务器。

然而,Plack的作者自己说(在Plack::Request

  

请注意,此模块旨在供Plack中间件使用   开发人员和Web应用程序框架开发者而不是   应用程序开发人员(最终用户)。

     

使用Plack :: Request直接编写Web应用程序   当然可能但不推荐:就像这样做   mod_perl的Apache :: Request:但是级别太低了。

     

如果您正在编写Web应用程序,而不是框架,那么您就是   鼓励使用其中一个支持的Web应用程序框架   PSGI(http://plackperl.org/#frameworks),或者查看类似的模块   HTTP :: Engine在顶部提供更高级别的请求和响应API   PSGI。

这就是问题所在。

我在MetaCPAN中检查了许多不同的基于PSGI的框架。 AFAIK 每个都是基于单身的,例如不允许为同一app.psgi中的不同站点多次共享(挂载)的写入应用程序。

所以问题是:

  • 错过了MetaCPAN(或文档)中的某些内容,此处存在 任何(轻量级)Web框架,允许开发应用程序多次挂载{ {1}}?
  • 或者我被迫开发app.psgi? (老实说,我没有检查催化剂 - 因为它太重了)
  • 或者只是很难理解“装载”?

2 个答案:

答案 0 :(得分:0)

在Plack中构建调度程序

Plack :: App :: URLMap有一个名为Plack::App::HostMap的替代方法,它可以更快地执行查找,因为它在内部使用哈希,而不是数组。所以没有迭代进行。它只是进行哈希查找,而且它们在Perl中非常快。

权衡是现在你只能使用常量主机名。所以,如果你的列表是这样的:

example.org
example.com
example.de
example.am
example.cx

或者使用以下子域:

one.example.org
two.example.org
three.example.org
four.example.org
five.example.org
six.example.org

然后这是完美的。另一方面,我不确定它是否支持也具有常量路径部分的URL,例如http://foo.example.org/bar,其中有很多foo个,但它们共享相同的/bar安装应用程序的路径。该模块根本没有任何测试,我无法尝试。如果您查看更改,至少有一个人建议使用其他功能,因此作者以外的其他人正在使用它。

要使用它,您可以从Plack :: Builder切换到使用Plack :: App :: HostMap作为您调用方法的应用程序。

use Plack::App::HostMap;

# set up %apps (e.g. foo.example.org, bar.example.org)

my $host_map = Plack::App::HostMap->new;

for my $site (@sites) {
    $host_map->map( $site => $apps->{$site} );
}

你没有告诉我们/路线应该做什么,但基本上它也需要一个主机。如果您的服务器有很多主机名,那么所有主机名都会响应此请求。这就是你想要做的全部想法。但/的主机名是什么?因此,最好的办法是为sub { ... }斜杠应用程序添加一行 real 主机名。也许这是一个控制面板或什么的。所以把它连接到实际的URL。

 $host_map->map( "example.org" => sub { ... } );

使用

执行此操作的Web框架

单身人士在这里不是问题。似乎不可能让Dancer2加载不同的配置或环境。我没有尝试使用Mojo,Web :: Simple或Catalyst这个用例。

我确实尝试了很多D2,而我最接近的是在MyApp中使用/路由,以及这个PSGI应用。请注意,不起作用

use Plack::Builder;

my $builder = Plack::Builder->new;
foreach my $name (qw/development production/) {
    $builder->mount(
        "/$name" => builder {
            eval <<"APP";
package MyApp::$name {
    use Dancer2;
    use MyApp with => { environment => "$name" };
}
APP

            "MyApp::$name"->to_app;
        }
    );
}

$builder->to_app;

它使用由dancer2 -a MyApp生成的默认框架和未更改的环境文件。 Plack的调度工作正常,但是Dancer2感到困惑。

HTTP::Server::PSGI: Accepting connections at http://0:5000/
[MyApp::production:4896] core @2017-02-10 02:14:42> looking for get / in /home/julien/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Dancer2/Core/App.pm l. 35
[MyApp::production:4896] core @2017-02-10 02:14:42> Entering hook core.error.init in (eval 49) l. 1
[MyApp::production:4896] core @2017-02-10 02:14:42> Entering hook core.error.before in (eval 49) l. 1
[MyApp::production:4896] core @2017-02-10 02:14:42> Entering hook core.error.after in (eval 49) l. 1
127.0.0.1 - - [10/Feb/2017:02:14:42 +0100] "GET /production/ HTTP/1.1" 404 456 "-" "curl/7.47.0"
[MyApp::development:4896] core @2017-02-10 02:18:06> looking for get  in /home/julien/perl5/perlbrew/perls/perl-5.20.1/lib/site_perl/5.20.1/Dancer2/Core/App.pm l. 35
[MyApp::development:4896] core @2017-02-10 02:18:06> Entering hook core.error.init in (eval 49) l. 1
[MyApp::development:4896] core @2017-02-10 02:18:06> Entering hook core.error.before in (eval 49) l. 1
[MyApp::development:4896] core @2017-02-10 02:18:06> Entering hook core.error.after in (eval 49) l. 1
127.0.0.1 - - [10/Feb/2017:02:18:06 +0100] "GET /development HTTP/1.1" 404 457 "-" "curl/7.47.0"

我的想法是使用相同的包文件并将其子类化以通过with获取不同的配置。

但是,可以一遍又一遍地在循环中定义相同的应用程序。您可以移动路径处理程序,使用get '/' => \&main::get_slash之类的代码引用,其中sub get_slash不在eval中。

use Plack::Builder;

my $builder = Plack::Builder->new;
foreach my $name (qw/development production/) {
    $builder->mount(
        "/$name" => builder {
            eval <<"APP";
package MyApp::$name {
use Dancer2;
    use Data::Printer;

    set environment => "$name";

    get "/" => sub { np(config) }
}
APP

            "MyApp::$name"->to_app;
        }
    );
}

$builder->to_app;

字符串eval并不像它在这里看到的那样邪恶,因为该代码仅在启动时运行。 D2将在内部跟踪您在此处以编程方式创建的所有应用程序。但我不知道这是多么高效。

答案 1 :(得分:0)

我认为引用的文档更适用于Plack :: Request而不是Plack :: Builder。

使用Plack :: Builder安装各种应用程序(例如Dancer / Catalyst / Mojolicious / homegrown app)是完全可以接受的,这确实非常普遍。