INTRO

В интервью японца Tokuhiro Matsuno прозвучала следующая фраза:
«Некоторыми приложениями нашей компании пользуются более миллиона человек. Все это работает на Amon2


Я не нашел на русском языке никакой инфы об этом веб фреймворке. И решил попробовать, что это за Amon2, чисто для себя, и, может быть, кому-то это будет интересно.



Установка.


Под руку попалась виртуалка с Debian GNU/Linux 7.3 (wheezy)
Поехали…

# curl -L http://cpanmin.us | perl - Amon2
................
Building and testing Amon2-6.00 ... OK
Successfully installed Amon2-6.00
78 distributions installed
 Successfully installed Amon2-6.00

Все! Установка завершена.

Из коробки мы получили:


Создание каркаса



adduser dotcloud
su - dotcloud
amon2-setup.pl BBS


Все идет хорошо, пока ...
— Running flavor: Basic — [main] Loading asset: jQuery
[main] Loading asset: Bootstrap
[main] Loading asset: ES5Shim
[main] Loading asset: MicroTemplateJS
[main] Loading asset: StrftimeJS
[main] Loading asset: SprintfJS
[main] Loading asset: MicroLocationJS
[main] Loading asset: MicroDispatcherJS
[main] Loading asset: XSRFTokenJS
[Flavor::Basic] writing tmpl/index.tx
[Flavor::Basic] writing tmpl/include/layout.tx
[Flavor::Basic] writing tmpl/include/pager.tx
[Flavor::Basic] writing lib/BBS.pm
[Flavor::Basic] writing lib/BBS/Web.pm
[Flavor::Basic] writing lib/BBS/Web/Plugin/Session.pm
[Flavor::Basic] writing lib/BBS/Web/Dispatcher.pm
[Flavor::Basic] writing lib/BBS/Web/View.pm
[Flavor::Basic] writing lib/BBS/Web/ViewFunctions.pm
[Flavor::Basic] writing lib/BBS/DB.pm
[Flavor::Basic] writing lib/BBS/DB/Schema.pm
[Flavor::Basic] writing lib/BBS/DB/Row.pm
[Flavor::Basic] writing script/bbs-server
[Flavor::Basic] writing Build.PL
[Flavor::Basic] writing minil.toml
[Flavor::Basic] writing builder/MyBuilder.pm
[Flavor::Basic] writing cpanfile
[Flavor::Basic] writing static//js/jquery-2.0.3.min.js
[Flavor::Basic] writing static//bootstrap/fonts/glyphicons-halflings-regular.eot
[Flavor::Basic] writing static//bootstrap/css/bootstrap-theme.min.css
[Flavor::Basic] writing static//bootstrap/fonts/glyphicons-halflings-regular.woff
[Flavor::Basic] writing static//bootstrap/fonts/glyphicons-halflings-regular.ttf
[Flavor::Basic] writing static//bootstrap/css/bootstrap.min.css
[Flavor::Basic] writing static//bootstrap/js/bootstrap.min.js
[Flavor::Basic] writing static//bootstrap/fonts/glyphicons-halflings-regular.svg
[Flavor::Basic] writing static//bootstrap/js/bootstrap.js
[Flavor::Basic] writing static//bootstrap/css/bootstrap.css
[Flavor::Basic] writing static//bootstrap/css/bootstrap-theme.css
[Flavor::Basic] writing static//js/es5-shim.min.js
[Flavor::Basic] writing static//js/micro_template.js
[Flavor::Basic] writing static//js/strftime.js
[Flavor::Basic] writing static//js/sprintf-0.7-beta1.js
[Flavor::Basic] writing static//js/micro-location.js
[Flavor::Basic] writing static//js/micro_dispatcher.js
[Flavor::Basic] writing static//js/xsrf-token.js
[Flavor::Basic] writing static/img/.gitignore
[Flavor::Basic] writing static/robots.txt
[Flavor::Basic] writing static/js/main.js
[Flavor::Basic] writing static/css/main.css
[Flavor::Basic] writing db/.gitignore
[Flavor::Basic] writing config/development.pl
[Flavor::Basic] writing config/production.pl
[Flavor::Basic] writing config/test.pl
[Flavor::Basic] writing sql/mysql.sql
[Flavor::Basic] writing sql/sqlite.sql
[Flavor::Basic] writing t/Util.pm
[Flavor::Basic] writing t/00_compile.t
[Flavor::Basic] writing t/01_root.t
[Flavor::Basic] writing t/02_mech.t
[Flavor::Basic] writing t/03_assets.t
[Flavor::Basic] writing t/06_jshint.t
[Flavor::Basic] writing xt/01_pod.t
[Flavor::Basic] writing xt/02_perlcritic.t
[Flavor::Basic] writing .gitignore
[Flavor::Basic] writing .proverc
[Flavor::Basic] writing static/500.html
[Flavor::Basic] writing static/504.html
[Flavor::Basic] writing static/503.html
[Flavor::Basic] writing static/502.html
[Flavor::Basic] writing static/404.html
There is no git command.


Setup script was done! You are ready to run the skelton.

You need to install the dependencies by:

    > carton install

And then, run your application server:

    > carton exec perl -Ilib script/bbs-server

--------------------------------------------------------------


Вроде все ок, но не хватает carton и рекомендуется ввести еще несколько команд.
Ставим его
cpan Carton  # От рута
carton install #  Надо делать в папке BBS! Не от рута!

Получаем следующую структуру файлов и директорий:

builder  Build.PL  config  cpanfile  cpanfile.snapshot  db  lib  local  minil.toml  script  sql  static  t  tmpl  xt


Создание веб приложения



cd db
vim sqlite.sql


Файл sqlite.sql со следующим содержанием:

CREATE TABLE IF NOT EXISTS member (
    id           INTEGER NOT NULL PRIMARY KEY,
    name         VARCHAR(255)
);

CREATE TABLE IF NOT EXISTS sessions (
    id           CHAR(72) PRIMARY KEY,
    session_data TEXT
);

CREATE TABLE IF NOT EXISTS entry (
    entry_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    body varchar(255) not null
);



Создаем базу:

sqlite3 development.db < sqlite.sql  


Далее

cd config/
vim  development.pl


Втыкаем в него вот такой конфиг:

use File::Spec;
use File::Basename qw(dirname);
my $basedir = File::Spec->rel2abs(File::Spec->catdir(dirname(__FILE__), '..'));
my $dbpath = File::Spec->catfile($basedir, 'db', 'development.db');
+{
    'DBI' => [
        "dbi:SQLite:dbname=$dbpath", '', '',
        +{
            sqlite_unicode => 1,
        }
    ],
};


Пытаемся для теста запустить веб сервер.

 perl -Ilib script/bbs-server
Can't locate Teng/Schema/Declare.pm in @INC (you may need to install the Teng::Schema::Declare module) (@INC contains: script/../lib lib /etc/perl /usr/local/lib/perl/5.18.1 /usr/local/share/perl/5.18.1 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.18 /usr/share/perl/5.18 /usr/local/lib/site_perl .) at script/../lib/BBS/DB/Schema.pm line 6.
BEGIN failed--compilation aborted at script/../lib/BBS/DB/Schema.pm line 6.
Compilation failed in require at script/../lib/BBS.pm line 7.
BEGIN failed--compilation aborted at script/../lib/BBS.pm line 7.
Compilation failed in require at /usr/share/perl/5.18/parent.pm line 20.
BEGIN failed--compilation aborted at script/../lib/BBS/Web.pm line 5.
Compilation failed in require at script/bbs-server line 9.
BEGIN failed--compilation aborted at script/bbs-server line 9.


И обламываемся.
Хорошо, ставим
cpan Teng::Schema::Declare
cpan Module::Functions
cpan Plack::Middleware::ReverseProxy
cpan Plack::Handler::Starlet


Наконец-то увидели заветное:

dotcloud@debian:~/BBS$ perl -Ilib script/bbs-server
BBS: http://127.0.0.1:5000/


Если зайти по этому адресу, то можно увидеть вот такую неземную красотищу:



Теперь осталось из этого хозяйства сделать ВВS — доску объявлений (бабку бибисиху — знаете такую?)

vim lib/BBS/DB/Schema.pm

И постим туда следующее:


package BBS::DB::Schema;
use strict;
use warnings;
use utf8;

use Teng::Schema::Declare;

base_row_class 'BBS::DB::Row';

table {
    name 'sessions';
    pk 'id';
    columns qw(session_data);
};

table {
    name 'entry';
    pk 'entry_id';
    columns qw(entry_id body);
};

1;


Далее:

vim lib/BBS/Web/Dispatcher.pm


package BBS::Web::Dispatcher;
use strict;
use warnings;

use Amon2::Web::Dispatcher::Lite;

any '/' => sub {
    my ($c) = @_;

    my @entries = $c->db->search(
        entry => {
        }, {
            order_by => 'entry_id DESC',
            limit    => 10,
        }
    );
    return $c->render( "index.tx" => { entries => \@entries, } );
};  

post '/post' => sub {
    my ($c) = @_;

    if (my $body = $c->req->param('body')) {
        $c->db->insert(
            entry => +{
                body => $body,
            }
        );
    }
    return $c->redirect('/');
};

1;


И правим темплейт

vim tmpl/index.tx


: cascade "include/layout.tx"

: override content -> {

<form method="post" action="<: uri_for('/post') :>">
    <input type="text" name="body" />
    <input type="submit" value="Send" />
</form>

<ul>
    <: for $entries -> $entry { :>
    <li><: $entry.entry_id :>. <: $entry.body :></li>
    <: } :>
</ul>

: }


Запускаем:

perl -Ilib script/bbs-server


И при необходимости доустанавливаем следующие пакеты

cpan Router::Simple
cpan Router::Simple::Sinatraish 
cpan DBD::SQLite


УРА!
Бабка бибисиха — работает!



Осталось все это задеплоить на nginx

Устанавливаем nginx в качестве фронтэнда



Сначала делаем init файл, и чтобы запускался не от рута


vim /etc/init.d/plackup 


#!/bin/sh

PORT=5000
WORKERS=4
AMON_DIR="/home/dotcloud/BBS"
AMON_APP="$AMON_DIR/script/bbs-server"
AMON_USER="dotcloud"
AMON_MODE="development"
website="cc_Website"
plackup="/usr/local/bin/plackup "
PID=$AMON_DIR/logs/plackup.$website.pid
plackup_args="-E $AMON_MODE -p $PORT -s Starman --pid=$PID --workers $WORKERS -D"

U=`id -un`
if [ $U = root ]; then
       WRPERM=`find $AMON_DIR/ ! -user $AMON_USER | wc -l`
       if [ "$WRPERM" != 0 ]; then
               echo Fixing file ownership on $AMON_DIR
               chown -R $AMON_USER.nginx $AMON_DIR
       fi
       cd /
       su $AMON_USER -s /bin/sh  $0 "$@"
       exit
elif [ $U != $AMON_USER ]; then
       echo "Should be run under $AMON_USER or root"
       exit 1
fi


lockfile=$AMON_DIR/logs/plackup.$website

start() {
    [ -x $plackup ] || exit 5
    [ -f $AMON_APP ] || exit 6
    echo -n $"Starting $website: "
    $plackup $plackup_args -a $AMON_APP 2>&1 > /dev/null
    retval=$?
    if [ $retval -eq 0 ]; then
        echo OK
        touch $lockfile
    else
        failure $"Unable to start"
    fi
    echo
    return $retval
}

stop() {
    echo -n $"Stopping $website: OK"
    if [ -f $PID ]; then
        kill `cat $PID` > /dev/null
        retval=$?
        [ $retval -eq 0 ] && rm -f $lockfile
        echo
        return $retval
    fi
    failure $"pid $PID not found"
    echo
    return 1
}

restart() {
    stop
    start
}

case "$1" in
    start)
        $1
        ;;
    stop)
        $1
        ;;
    restart)
        $1
        ;;
    *)
        echo $"Usage: $0 {start|stop|restart}"
        exit 2
esac


Добавляем конфиг в nginx

vim /etc/nginx/conf.d/virtual.conf


upstream amon {
        server 127.0.0.1:5000;
}

server {
    listen          *:80; 
    
    location / {
                try_files /empty  @backend;
    }

  
    location @backend {
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_pass http://amon;
    }


}


Все готово!

Заключение



В качестве заключения я приведу опять-таки цитату из того самого интервью, с которого все началось:
Расскажи об Amon2. Чем он отличается от других Perl-фреймворков для веб?
Amon2 очень простой, надежный веб-фреймворк общего назначения.

Отличия от Mojolicious

Mojolicious неплох, и мне нравится сам подход. К сожалению, там не сохраняется обратная совместимость. В Amon2 же наоборот. Мне кажется, что ломать обратную совместимость можно только с изменением названия. Когда я решу что-либо серьезно изменить, я выпущу Amon3.

Отличия от Catalyst

Catalyst зависит от Moose, а Amon2 — нет. Это потому, что я хочу, чтобы мои приложения загружались быстро.

Отличия от Dancer

Практически нет никаких отличий между Amon2 и Dancer, включая Dancer2.



Автор скромняга, конечно.

А теперь мое имхо:
  1. Фреймворк и правда очень легковесный
  2. Написан японцем, с их тщательностью и перфекционизмом. Это ощущается в коде.
  3. Miyagawa, который написал plackup, с ним корешится, что называется, два сапога пара.
    Ведь используется и рекомендуется именно такая связка plackup + Amon2
  4. Надеюсь, что фрейворк, который юзают «более миллиона человек» по заявлению автора, не будет брошен просто так, а будет развиваться дальше.
  5. Фрейворк — относительно новый и современный, но уже есть громадное кол-во модулей на cpan
  6. В целом оставил приятное впечатление.

Комментарии (21)


  1. bo883
    07.01.2014 20:10

    ждал очередное творение на node а тут бах! перловый фреймворк, неожиданно, приятно, интересно, но…


    1. Vertex
      07.01.2014 20:10

      Вот с таким же непонятным каким-то чувством «но», я тоже завис. Нахлынули старые воспоминания. Вобщем, надо будет повтыкать… Топикстартеру — спасибо!


  1. igordata
    07.01.2014 20:10

    > Надеюсь, что фрейворк, который юзают «более миллиона человек» по заявлению автора
    «Юзает» тут подразумевает, что некие разработчики используют указанный фреймворк в своей разработческой деятельности?

    > Некоторыми приложениями нашей компании пользуются более миллиона человек. Все это работает на Amon2
    Если я правильно понял, то тут написано, что продуктами пользуется большое число человек. А продукты написаны на Amon2.
    А сколько разработчиков, которые им пользуются, из этой реплики не ясно.


    1. pcdesign Автор
      07.01.2014 20:10

      Это вы лучше у Tokuhiro Matsuno спросите :)



      Как еще можно трактовать эту цитату:

      Я пишу серверную часть на Perl. Некоторыми приложениями нашей компании пользуются более миллиона человек. Все это работает на Amon2.


      Почему-то, вспоминается фильм «Lost in Translation»


      1. igordata
        07.01.2014 20:10

        Я спрашивал у вас про ваше слово «юзают».


      1. lair
        07.01.2014 20:10

        Эту цитату можно трактовать как «у приложения, которое написано на Amon2, более миллиона пользователей». И в этом случае забросить фреймворк ничего не стоит — достаточно просто переехать на новый, сложность чего зависит только от сложности приложения.


        1. pcdesign Автор
          07.01.2014 20:10

          Я и не спорю.
          Все может умереть, любой проект. И амон2 в том числе.

          В данном случае этот продукт используется крупной японской корпорацией.
          Нельзя вот так просто взять и просто куда-то переехать. (здесь должна быть картинка, сами знаете кого)
          Поэтому есть определенный шанс, на мой взгляд, что у амон2 — есть будущее.

          И не подумайте, что я тут защищаю или как-то продвигаю его.
          Просто делюсь своими мыслями.


          1. lair
            07.01.2014 20:10

            Вот как раз у продукта, используемого (одной?) крупной корпорацией шансы на «переезд» выше — сменился менеджмент, сменился продукт.


            1. pcdesign Автор
              07.01.2014 20:10

              Рассмотрим другой случай — делал фреймоворк энтузиаст.
              Просто ради прикола.
              Энтузиазм — пропал.
              Фреймворк — умер.

              Здесь человек работает в крупной компании. И свой же собственный продукт сразу же и внедряет. Не забываем про трудоголизм японцев.
              Я не знаю, что лучше или хуже. Я не психолог и не маркетолог.


              1. lair
                07.01.2014 20:10

                А одинаково. Человек работает в крупной компании, а потом больше не работает. По произвольной причине. Судьба фреймворка непредсказуема.


                1. pcdesign Автор
                  07.01.2014 20:10

                  Может я ошибаюсь. Но мне кажется, что не одинаково.
                  Одно дело теории, а другое дело вот он реальный продакшен с миллионами юзеров.


  1. knutov
    07.01.2014 20:10

    Tatsuhiko Miyagawa — девушка? Seriously?


    1. pcdesign Автор
      07.01.2014 20:10

      Сорри, попутал. Думал о своем, о девичьем :)


  1. anonymous
    07.01.2014 20:10


  1. bubuq
    07.01.2014 20:10

    В интервью японца Tokuhiro Matsuno <...>


    Корректнее:

    В интервью японца Мацуно Токухиро (徳廣松野) <...>


    1. pcdesign Автор
      07.01.2014 20:10

      Да, верно, как вы сказали. На cpan он себя именно так и называет MATSUNO★Tokuhiro.

      Я не знаю, почему в интервью Tokuhiro Matsuno.


      1. bubuq
        07.01.2014 20:10

        Потому что интервью переведено с английского языка людьми, которые не умеют или не хотят правильно русифицировать японские собственные, и оставляют латиницу. По-русски принято писать имя-фамилия, даже если в родном языке носителя имени порядок фамилия-имя.


  1. stalkerg
    07.01.2014 20:10

    Японцы в плане интернета как обычно «впереди планеты всей»… :(


  1. Defined
    07.01.2014 20:10

    Спасибо за статью.

    Использовал этот фреймворк, нужно было сделать небольшое отдельное приложение по выводу статистики, хотел сначала Dancer, т.к. неплохо им владею, но захотелось что-нибудь поверх PSGI/Plack, в целом оставил приятное впечатление, ну и Plack радует конечно =))

    Еще советую обратить внимание вот на этот минималистичный фреймворк: Kelp, так же поверх PSGI/Plack и с минимальным кол-ом кода.


  1. pcdesign Автор
    07.01.2014 20:10

    Это вам спасибо за Kelp!

    Попробовал
    # cpanm Kelp 
    --> Working on Kelp
    Fetching http://www.cpan.org/authors/id/M/MI/MINIMAL/Kelp-0.457.tar.gz ... OK
    Configuring Kelp-0.457 ... OK
    Building and testing Kelp-0.457 ... OK
    Successfully installed Kelp-0.457
    1 distribution installed
    # 
    # 
    # adduser Kelp
    # su - Kelp
    $ 
    $ 
    $ Kelp MyApp
    Creating folder: ./log
    Writing file: ./log/keep
    Creating folder: ./lib
    Writing file: ./lib//MyApp.pm
    Creating folder: ./conf
    Writing file: ./conf/deployment.pl
    Writing file: ./app.psgi
    Writing file: ./conf/test.pl
    Creating folder: ./t
    Writing file: ./t/main.t
    Writing file: ./conf/config.pl
    Writing file: ./conf/development.pl
    Creating folder: ./views
    Writing file: ./views/home.tt
    $ 
    $ 
    $ 
    
    $ /usr/local/bin/plackup  -Ilib -E deployment -s Twiggy -a app.psgi -p 5000
    
    
    # curl   http://127.0.0.1:5000/home  
    Hello, world!
    # curl   http://127.0.0.1:5000/config
    {"modules":["Template","JSON","Logger"],"app_url":"http://localhost:5000","modules_init":{"JSON":{"allow_blessed":1,"pretty":0,"convert_blessed":1,"utf8":1},"Template":{"encoding":"utf8","paths":["/home/Kelp/views","/home/Kelp/../views"]},"Routes":{"base":"MyApp"},"Logger":{"outputs":[["File","name","error","filename","log/error.log","min_level","error","mode",">>","newline",1,"binmode",":encoding(UTF-8)"]]}},"middleware":[],"charset":"UTF-8","middleware_init":{}}
    




    Буду использовать.


    1. Defined
      07.01.2014 20:10

      так быстро =)
      удачи вам

      у меня вот щас есть небольшая задачка, для себя, вот разрываюсь между амоном и келпом =)