Разработка приложений 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 { ... };
}
например
load_site_names
возвращает список сайтов, таких какhttp://example.com
,http://some.other.site.com
...- каждый виртуальный хост будет использовать один и тот же
Some::PsgiFramework::MyApp
- просто их конфиг отличается
Мне нужно именно это - нужно разработать одно простое веб-приложение, которое должно быть развернуто для разных сайтов (с небольшим трафиком) и не требовать установки разных серверов PSGI для каждого сайта.
Тем не менее, автор самого Plack говорит (в Plack:: Request)
Обратите внимание, что этот модуль предназначен для разработчиков промежуточного программного обеспечения Plack и разработчиков инфраструктуры веб-приложений, а не для разработчиков приложений (конечных пользователей).
Написание вашего веб-приложения напрямую с использованием Plack:: Request, безусловно, возможно, но не рекомендуется: это похоже на то, как это делается с Apache:: Request от mod_perl: пока слишком низкий уровень.
Если вы пишете веб-приложение, а не фреймворк, вам рекомендуется использовать одну из фреймворков веб-приложений, поддерживающих PSGI ( http://plackperl.org/), или посмотреть такие модули, как HTTP::Engine обеспечить более высокий уровень API запросов и ответов поверх PSGI.
И это проблема.
Я проверил множество различных основанных на PSGI фреймворков в MetaCPAN. И каждый AFAIK основан на одиночном коде, например, не позволяет писать приложения, которые можно было бы многократно использовать (монтировать) для разных сайтов в одном и том же месте. app.psgi
,
Итак, вопросы:
- Я что-то пропустил в MetaCPAN (или в документах), и здесь существует любая (облегченная) веб-инфраструктура, которая позволяет разрабатывать приложения, многократно устанавливаемые в
app.psgi
? - или я вынужден развиваться
Just Another My Own PSGI Framework
? (Если честно, я не проверил катализатор - так как он слишком тяжелый) - или просто плохо разбираетесь в "креплении"?
2 ответа
Построение диспетчера в Плаке
Существует альтернатива 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 { ... }
Slash-приложение с реальным именем хоста. Может быть, это панель управления или что-то в этом роде. Так что подключите его к фактическому URL.
$host_map->map( "example.org" => sub { ... } );
Веб-фреймворк для этого
Синглтон на самом деле не проблема здесь. Кажется, невозможно заставить 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
,
Тем не менее, можно просто определить одно и то же приложение в цикле, снова и снова. Вы могли бы, вероятно, переместить обработчик маршрута, используя код ref, такой как 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 будет внутренне отслеживать все приложения, которые вы создали здесь программно. Но я понятия не имею, насколько это эффективно.
Я считаю, что цитируемая документация больше предназначена для Plack::Request, а не для Plack::Builder.
Вполне допустимо монтировать различные приложения (например, приложение Dancer/Catalyst/Mojolicious/homegrown), используя Plack:: Builder, и это действительно довольно часто.