HaskellでServer::Starterを使う

  • 7
    Like
  • 0
    Comment

Server::Starter を Haskell で利用するためのライブラリを書きました。 warp で動かすWEBアプリでも、 start_server コマンドを使った hot-deploy が可能になります。とりあえず github にのみ上げました。

https://github.com/hiratara/hs-server-starter

以下でサンプルプログラムまで含めてビルドできます。

$ git clone git@github.com:hiratara/hs-server-starter.git
$ cd hs-server-starter/
$ stack build --flag hs-server-starter:example

使い方

https://github.com/hiratara/hs-server-starter/blob/master/app/server-starter-warp-example.hs を見てください。

Network.ServerStarter.Socketimport して、 listenAll を呼び出すと listen 状態の Socket が返ってきます。この SocketServer::Starter の管理下にある接続であり、これを利用することでホットデプロイが可能になります。 warp で使うのであれば、 runSettingsSocket 関数に渡すことでこの Socket に対して accept させられます。

使用例: PerlのWebアプリをHaskellのWebアプリに無停止で差し替える

普通はWebアプリの新しい版をアップロードして、無停止で新しい版に差し替えれるよってことをやるのですが、今回はあえて変な例でやってみます。 start_server はここでは Perl で実装されたオリジナルのものを利用するものとします。

Server::Starter は Perl と cpanm コマンドの環境を整えた上で cpanm Server::Starter で導入できます。また今回の例の中で使うため、 cpanm Starlet でついでに Perl のWEBアプリケーションサーバである Starlet も導入しておきます。

app.sh を以下の内容で作ります。このシェルは Starlet で動く簡単なアプリを立ち上げるものです。

#!/bin/sh
exec plackup -s Starlet -e 'sub { [200, [], ["Hello, perl.\n"]] }'

start_server を使ってこのアプリを起動します。

$ start_server --port 12345 -- ./app.sh
start_server (pid:29812) starting now...
starting new worker 29827
...

http://localhost:12345 へアクセスすると、ページが見えるはずです。

$ curl http://localhost:12345
Hello, perl.

Server::Starter を使うと、システムを停止することなくオンラインのままこのアプリを warp で動く Haskell の実装に差し替えることができます。 app.sh を以下のように編集します。

#!/bin/sh
exec stack exec server-starter-warp-example

ここではまだ、 http://localhost:12345Starlet で動くPerlのアプリがレスポンスを返しています。ここで、サーバを差し替えるために start_server のプロセスに -HUP シグナルを送ります。PIDは start_server の起動直後のログでわかります。

$ kill -HUP 29812

もう一回 http://localhost:12345 へアクセスしてみると、サーバが切り替わっていることがわかります。

$ curl http://localhost:12345
Hello, Web!

簡単ですね!

ただし、Haskellではファイルディスクリプタを O_NONBLOCK にする必要があるため、今回のようにブロッキングなファイルディスクリプタと共用させるとトラブルの元ですので十分注意が必要です1

golang実装

start_server コマンドには golang の実装 もあり、 go get github.com/lestrrat/go-server-starter/cmd/start_server でインストールできます。当然 golang 版の start_server コマンドを使っても同じことができます。

Perl版とgolang版はコマンド名が完全に同じであるため、両方インストールして挙動を見るときはどちらの実装を使っているか気をつけて検証してください。


  1. ブロックしない accept で待つのでPerl側のWebアプリがCPU使い潰したり。深追いしてないけどgolang版だと問題起きなそうだった。