この記事は、2015 Clojure Advent Calender の17日目の記事です。
去年にひきつづき Advent Calenderので苦戦してしまった。今回は、当初の予定は間に合わず、ちょっと
頭をよぎったREPLについて書くことにしました。しかも、遅刻。
運用中のシステムのREPLにアクセスする
最近「REPL駆動開発」なんていう言葉も出てきて、REPLが一般的になりつつあります。
clojureで開発をするときにREPLを使うというのは普通のことですし、僕の場合、ちょっとしたデータの解析をするとか、Project Eulerを解くとか、そんなときはjarにせずに、REPLで結果を出してしまってそれで終りということもよくあります。
さて、そんな便利なREPLですが、たいていの場合は開発時にしか使っていないだろうと思います。
ところが、Lisp界隈には運用中のシステムに直接接続してそれを書き換えてしまうという文化があります。そしてその入口もREPLです。 clojureもLispの一員ですので、同じことができます。
運用中のシステムにREPLで接続して、開発中と同じように、新しい関数を定義したり、再定義したり、Varを追加したりなどいろいろなことができて、それが、すぐさま環境に反映されて利用することができるわけです。
たとえば、僕は、職場の開発チーム用にちょっとした便利ツールをWeb経由で提供しているのですが、運用しているWebサーバーは別チーム管理なので、あまり頻繁に差し替えとかお願いしにくかったりします。
ですが、REPLを組み込んであるので、機能変更や追加などはちょいとREPLにつないでうらうらっとやってしまうのです。 また、DBのメンテナンスなども、わざわざそのためのWebインターフェースを書くのも面倒だったりするので、REPLから直接やってしまいます。
だだ、運用しているイメージの上だけの変更ですので、サーバーがリスタートしたりなどすると、その変更は消えてしまうので、イメージを書き換えておくことは必要ですけどね。
また、1.8の新しい機能に、socket serverがあります。用途はいろいろありそうですが、REPLに使うのが一般的でしょう。 言語の基本機能として、ソケットがサポートされたことで、便利になるかもしれませんね。(今でもたいして面倒ではないですけど)
nREPL
nREPLのnはnetworkのnで、ネットワーク経由でREPLにアクセスできるようにするためのモジュールです。
clojureのREPLをネットワーク経由で利用するための機能を持っています。また、ミドルウェアを組み込んで、機能を追加することができるようにもなっています。
nREPLの情報
nREPLのgithubサイトはここです。
README.mdを読むと使いかたがわかるのですが、プロセスのなかにREPLを組み込むためのモジュールの説明だけあって、使うことが目的の場合ちょっと難しいです。
使う視点に立った場合、使い方ベースで記述してある開発環境側での説明の方がわかりやすいですね。僕はcider派なので、ciderのページを参照してます。他のIDEとかツールの場合もそれぞれの場所に書いてあると思います。
これらを読めば使いかた判ってしまうのですが、軽く説明しときます。
やってみよう
それほど難しいことではなくて、モジュールを追加して、サーバーを起動するだけです。
-
project.cljのdependenciesにnreplのモジュールを追加する。
[org.clojure/tools.nrepl "0.2.11"]
-
nsのreqireにnreplモジュールを追加するを追加する。
[clojure.tools.nrepl.server :as nrepl]
- nreplサーバーを起動する。
(defonce nrepl-server (nrepl/start-server :port 7888))
これで、起動しているマシンの7888ポートで、replが接続を待ってくれます。
これだと素のままなので、ciderを使う場合はciderのミドルウェアをハンドラとして指定しておくといろいろ便利な機能が使えます。
(defonce nrepl-server (nrepl/start-server :port 7888 :handler cider-nrepl-handler))
起動するポイントはどこでもいいのですが、webサーバー系であればinit関数などの起動時に呼ばれる関数内に入れればいいでしょう。
また、常時起動しておくのは怖いということであれば、起動ルートを作ってそこに入れればよいでしょう。
停止するのは、
(nrepl/stop-server nrepl-server)
とします。
この例のようにdefonce
にしてしまうとと、2度目の起動ができません。atom
にするなどしましょう。
接続する
お使いの環境で、REPLに接続すればいいのですが、ciderの場合は、cider-connect
とすると、ホストとポートを聞いてくれるので、必要な情報を入力します。
稼動しているシステムのソースを開いていれば、後は、通常のREPLの使いかたとまったく同じ。いろいろできます。
おしまい
稼動中のシステムにREPLで接続して動作を変えてしまおう、っていう話は、Lisp界隈では普通のことですが、Clojureを含めてよそではあまり聞いたことが無いので、書いてみました。
Common LispのCLOSなどは、クラスを再定義すると、そのクラスの生成済みのインスタンスまで変更できてしまうという驚きの機能まで言語仕様として入っていたりします。
本番稼動中のシステムを変更するっていうのは、現実的にはなかなか難しいことではありますが、サービスを停止することなく、改善/修正できるというのは、それはそれで価値のあることだと思います。
では、良いクリスマスになりますように。