読者です 読者をやめる 読者になる 読者になる

ローグウェーブソフトウェアのブログ

開発をシンプルに 安全で高品質のコードを 素早くお客様のもとへ

ActiveMQの Pluggable storage lockers - CodeBuzzから

OpenLogic プログラミング 記事紹介

ActiveMQJava用の非同期型メッセージキューイングサービスです。ローグウェーブのOSSアーキテクト Justin Reockが書いたActiveMQ Pluggable storage lockersについての解説と設定のサンプルをご紹介します。前回の記事と合わせてご覧ください。

blog.roguewave.jp

ActiveMQのPluggable storage lockers

blog.klocwork.com

ActiveMQは、ブローカーのインスタンスが常にオンラインで、メッセージのトラフィックを処理できるようにするためにいくつかの高可用性(HA、high availability)の仕組みをサポートしています。そのうちよく使用される2つのモデルでは、アクティブおよびパッシブのブローカーインスタンスに対してLevelDBとKahaDBのようなデータストアを提供するためにネットワーク上でファイルシステムを共有しています。

こうしたフェイルオーバーの仕組みが成り立つには、LevelDBやKahaDBのディレクトリ上のファイルに対してOSレベルの「ロック」が取得可能かつ維持可能である必要があります。

f:id:RWSJapan:20160816142057p:plain

ロックを最初に取得したブローカーインスタンスはアクティブインスタンス、またはマスターと呼ばれます。別のインスタンス(パッシブもしくはスレーブ)は定期的にそのファイルをチェックしてロックを取得可能かどうか調べます。ロック可能になったらマスターがロックを解放したと判断し、自身をマスターモードに切り替えます。

従来のモデルの問題点

この仕組みはエレガントですが、マスター不在という状況が起こり得るという問題点があります。つまりスレーブが自分がロック可能であることに気づかなかったり、更に悪いことにmaster-master configurationによってインデクスやジャーナルが壊れ、メッセージが完全に失われてしまうことが起こるのです。

こういった問題の多くはActiveMQのコントロールの及ぶ範囲の外で生じます。たとえばあまり最適化されていないNFSファイルストレージでは、高負荷時にロックしているデータがstaleになり、フェイルオーバー時にマスター不在のダウンタイムが発生してしまいます。CIFS/SMBネットワーク・ソリューションの共有違反も同様の問題を起こします。OSの仮想ファイルシステム(VFS)に不正確なロック情報を伝えるSANソリューションもmaster-masterのシナリオを引き起こします。実に多くのファイルシステム共有ソリューションが利用可能ですが、全ての状況に対応できるようなロックのソリューションを実現するのはActiveMQコミュニティにとって非常に困難です。

Pluggable storage lockersの導入

こうしたHAソリューションの多くの問題がOSレベルの不正確なファイルロックに起因しているため、ActiveMQコミュニティはブローカーのバージョン5.7からpluggable torage lockerのコンセプトを導入しました。これによりユーザーは、OSレベルのファイルシステムのロックではなく行レベルのJDBCデータベースのロックを使って、いくつかの共有ロックの手段を利用できるようになりました。最初に挙げられているDatabase Lockerというソリューションは、ロックを保持するためにブローカーとデータベースとの間に永続的な接続を使うのですが、上記のコミュニティサイトで説明されているように、このやり方はマスターブローカーがクラッシュしたりデータベースへの接続を失った時などに効率的ではないことが判明したため、これ以上ここでは触れません。

代わりにここではLease Database Lockerについて詳しく解説したいと思います。このアプローチは、データベースへの永続的接続を要求するのではなくマスターブローカーが定期的にデータベースの行をリース(借り受け)し、一定の期間ごとに更新します。この期間は編集可能です。もしマスターがそのリースを更新しなかった場合、リースは失効し、スレーブがそのロックを取得して、つまり新しいマスターになることができます。このアプローチは品質の悪いネットワーク上でもうまく機能し、リースを取得できなくなったマスターノードを強制的に落とします。

このソリューションは非常に簡単に実装でき、しかも私たちが調べたところActiveMQの共有ファイルシステムのHAモデルとして非常に安定し信頼のおけるソリューションでした。

最初に使用しているデータベースを編集する必要があります。このソリューションはどんなJDBC Compliant なデータベースとも互換であり、わたしたちはPostgres、 MySQL/MariaDBOracleMicrosoft SQL Serverでテストしました。まず私たちの例では新しいデータベースユーザーとしてactivemqというユーザを作成し、パスワードactivemqを与えました。そして新しくactivemqというデータベースを作成し、このユーザーにロックとread/writeパーミッションを付与し、activemq_lockというテーブルを作成しました。スキーマは以下です。

f:id:RWSJapan:20160815154519p:plain

ここでこのテーブルに以下のように一行挿入します。この行に対してActiveMQがロックを行うため、この過程を飛ばしてはいけません。

INSERT INTO activemq_lock(ID) VALUES (1);

データベースのセットアップが終わったらLease Database Lockerを使用するようにactivemq.xmlを編集して、Spring JDBCコネクションのbeanを作成します。

永続的なアダプタの設定

persistence adapter configuration を以下のように設定します。

<persistenceAdapter>
    <levelDB directory="/tmp/activemq-jdbc-locker-data" lockKeepAlivePeriod="5000">
        <locker>
            <lease-database-locker lockAcquireSleepInterval="10000" dataSource="#postgres-ds">
                <statements>
                    <statements lockTableName="activemq_lock"/>
                </statements>
            </lease-database-locker>
        </locker>
    </levelDB>
 </persistenceAdapter>

この例では典型的なLevelDB の永続的ストア設定をカスタムロッカーのlease-database-lockerを使うように拡張しました。ブローカーに行レベルのリースを5秒ごとに行うよう設定し、スレーブインスタンスが10秒ごとにロックの取得を試みるように設定しています。また、このロッカーがSpringデータソースの「postgres-ds」を使うよう指定しています。また、LevelDBにアクセスするための場所をdirectoryパラメータとして指定しています。

これは、LevelDBを共有するためにネットワークマウントを使用しているからで、この部分はオリジナルのモデルから変わっていません。今回は単にロックのメカニズムをJDBCに置き換えただけですが、依然としてブローカーはどちらも共有NFS経由で永続化ストアにアクセスする必要があります。

f:id:RWSJapan:20160815173202p:plain

JDBCコネクションの設定

次のステップはActiveMQにデータベースへの接続方法を指示します。そのためには標準的なJDBC connection spring beanを作成します。

<bean id="postgres-ds" class="org.postgresql.ds.PGPoolingDataSource" destroy-method="close">
    <property name="serverName" value="localhost"/>
    <property name="databaseName" value="activemq"/>
    <property name="portNumber" value="0"/>
    <property name="user" value="activemq"/>
    <property name="password" value="activemq"/>
    <property name="dataSourceName" value="postgres"/>
    <property name="initialConnections" value="1"/>
    <property name="maxConnections" value="10"/>
  </bean>

ここではPostgresのデータベースに接続し、上記 lease-database-locker の設定で与えた識別子をbeanに指定しています。どちらの設定もマスターとスレーブで共通でなければなりません。また、JDBCのドライバ .jarファイルをActiveMQの/libディレクトリにコピーして設定内で指定したクラスへのアクセスを与えなければなりません。具体的な方法は使用しているデータベースによって異なります。

着火

これで設定は終わり。ブローカーに着火して何が起こるかを見てみましょう。アクティブブローカーのログにいくつかの出力があることに気づかれるでしょう。

INFO | amq-master, becoming master with lease expiry Mon Jun 27 15:27:01 EDT 2016 on dataSource: org.postgresql.ds.PGPoolingDataSource@600b90df

一方スレーブインスタンスは定期的にスタンプを発行しています。

INFO | amq-slave failed to acquire lease. Sleeping for 10000 milli(s) before trying again...<br />
INFO | amq-slave Lease held by amq-master till Mon Jun 27 15:29:23 EDT 2016

ボーナスとして、どのブローカーがマスターモードなのか知ることができ、また、以下のようなactivemq_lockへの簡単なクエリによってフェイルオーバーのシナリオを監視することができます。

SELECT * FROM activemq_lock

f:id:RWSJapan:20160815165211p:plain

lock行のbroker_nameの値は現在のマスターインスタンスのブローカー名に対応しています。

時間について

非常に大事なことですが、マスターとスレーブノードはNTPのような時刻同期ソリューションに基いて同期しています。割り当てられたマスターブローカーは、システムが生成したタイムスタンプをリース用に使用します。もしマスターとスレーブインスタンスの間で時刻のずれが生じたら、私たちの例では5秒のずれで、スレーブインスタンスはその時点でマスターがもうリースを更新していないと判断して、自分がマスターになろうと試みます。これはmaster-masterの悪夢であり、ジャーナルは破壊されメッセージは失われます。

結論

ActiveMQが採用している伝統的なネットワーク共有型ファイルシステムのHAモデルはOSレベルのファイルシステムロックで使われていますが、そこには多くの問題が避けられません。そうした問題はActiveMQ自体のせいではなく、ネットワークファイル共有の実装に問題があるのです。Lease Database Lockerはこのロックを提供する非常に定評があるソリューションで、侵襲性が少なく、簡単に設定変更ができます。私たちは安定した高可用性のメッセージング実装としてこのモデルを使用するようお客様に勧めています。

Justin Reock

編集後記

いかがでしょうか?著者のJustinについて以下の記事で人物紹介や得意分野等を紹介していますのでご一読ください。

blog.roguewave.jp

ローグウェーブ セールスエンジニア 柄澤(からさわ)