Pixel Pedals of Tomakomai

北海道苫小牧市出身の初老の日常

Hokkaido.pmでuWSGIについてLTした

Hokkaido.pm でuWSGIについて話してきました。uWSGIは Nginx や Cherokee でも標準対応がされ初めており、今後の発展が期待されるアプリケーションサーバです。スライドは以下です。

デモがメインだったので、デモの手順についても書いておきます。

まず、uWSGI はデフォルトではhttpではなくuwsgiプロトコルを喋るので、uwsgiプロトコルを喋れるフロントエンドを立ち上げます。Plack::App::uWSGI は、いちいちnginxとかをセットアップするのが面倒だったので自分で書いたPSGIサーバで動かせるuwsgiのフロントエンドで、githubにだけ上げてます。uWSGIにpsgiプラグインを実行させるためのmodifier1である"5"は、現状ではfastrouterには指定しなくていいようです。

# frontend.psgi

use strict;
use warnings;
use Plack::Builder;
use File::Basename;
use Plack::App::uWSGI;
use Plack::App::Directory;

my $psgi = Plack::App::uWSGI->new(
    pass => "localhost:4000",
    param => {UWSGI_FASTROUTER_KEY => "psgi_cluster"},
#    modifier1 => "5",
)->to_app;

my $wsgi = Plack::App::uWSGI->new(
    pass => "localhost:4000",
    param => {UWSGI_FASTROUTER_KEY => "wsgi_cluster"},
)->to_app;

builder {
    mount '/static' => Plack::App::Directory->new(
        root => dirname(__FILE__) . '/static'
    )->to_app;
    mount '/psgi' => $psgi;
    mount '/wsgi' => $wsgi;
};
$ plackup -Ilib ../frontend.psgi

次に、ロードバランサであるfastrouterを立ち上げます。以下の起動方法ではルータを4000番で立ち上げているので、WEBサービスのクライアントは4000番に接続してuwsgiでやりとりすることになります。また、4001番に立っているのがサブスクリプションサーバとなり、ワーカからこいつへ通知することでfastrouterが新しいノードを認識するという仕組みです。

$ ./uwsgi --plugins fastrouter --fastrouter :4000 --fastrouter-subscription-server :4001

後はワーカを立ち上げれば終了です。今回はPSGIWSGIのワーカを3つずつ立ち上げました。PSGIのワーカは以下のような感じです。4001番のサブスクリプションサーバに対して、自分が「psgi_cluster」という名前のサービスについてmodifier1が"5"のuwsgiリクエストを処理できることを通知しています。-M を省略すると動作が安定しないみたいなのですが、不具合なのか仕様なのかちょっとまだ追えてません。

# app.psgi

use strict;
use warnings;
use utf8;
use Encode qw/encode_utf8/;
use Text::Xslate;

my $tpl = do { local $/; <DATA> };

sub { [
    200, ['Content-Type' => 'text/html; charset=UTF8'],
    [encode_utf8(Text::Xslate->new->render_string($tpl, {pid => $$}))]
] };

__END__
<img src="/static/hokkaidopm.gif">
<div style="font-size:100px;">PID: <: $pid :></div>
$ ./uwsgi --plugins psgi -s :4002 --psgi ../app.psgi -M --subscribe-to 127.0.0.1:4001:psgi_cluster:5
$ ./uwsgi --plugins psgi -s :4003 --psgi ../app.psgi -M --subscribe-to 127.0.0.1:4001:psgi_cluster:5
$ ./uwsgi --plugins psgi -s :4004 --psgi ../app.psgi -M --subscribe-to 127.0.0.1:4001:psgi_cluster:5

WSGIのワーカもPSGIと同様です。

# app.wsgi
# -*- coding: utf-8 -*-

import os
from jinja2 import Template

tpl = '''
<img src="/static/python.png">
<div style="font-size:100px;">PID: {{pid}}</div>
'''

def application(environ, start_response):
    response_headers = [('Content-type','text/html;charset=UTF-8')]
    start_response('200 OK', response_headers)
    return [Template(tpl).render(pid=os.getpid()).encode("UTF-8")]
$ ./uwsgi --plugins python -s :4005 --file ../app.wsgi -M --subscribe-to 127.0.0.1:4001:wsgi_cluster
$ ./uwsgi --plugins python -s :4006 --file ../app.wsgi -M --subscribe-to 127.0.0.1:4001:wsgi_cluster
$ ./uwsgi --plugins python -s :4007 --file ../app.wsgi -M --subscribe-to 127.0.0.1:4001:wsgi_cluster