この章では、OWLCS にデプロイするアプリケーションを開発するための要件とベスト プラクティスについて説明します。以下の節で構成されています。
典型的なプロダクション環境では、SIP アプリケーションはエンジン層クラスタを形成する OWLCS インスタンスのクラスタにデプロイされます。SIP データ層内のサーバのクラスタは、アクティブな呼び出しについての呼状態のレプリケートされたインメモリ データベースを提供します。この環境でアプリケーションを確実に機能させるには、以下の節に書かれているプログラミングの作法と慣行に従い、デプロイ後のアプリケーションの複数のコピーがクラスタ環境で期待どおりに動作するようにしてください。
旧バージョンの OWLCS のアプリケーションから移植しようとしている場合、以下で述べる規約や制約の多くは馴染みがない可能性があります。その理由は、2.0 および 2.1 のバージョンの WebLogic SIP Server 実装ではクラスタ化をサポートしていなかったからです。アプリケーションを移植したら徹底的なテストとプロファイリングを行って問題を洗い出し、新しい環境で満足な結果が得られるようにしてください。
OWLCS はマルチスレッド アプリケーション サーバであり、ホストしているモジュールについてリソースの割り当て、同時実行性、およびスレッドの同期化を慎重に管理します。OWLCS アーキテクチャを最大限に生かすには、SIP サーブレット および Java EE API の仕様に従ってアプリケーションのモジュールを構築してください。
SIP サーブレットなどのサーバサイド モジュール内に新たなスレッドを作成するようなアプリケーション設計は避けてください。
SIP サーブレット コンテナは、SIP サーブレットの do
xxx メソッドを呼び出す時、関連する呼状態が自動的にロックされます。doxxx
メソッドが追加のスレッドを生成したり、制御を返す前に別の呼び出し状態にアクセスしたりすると、シナリオのデッドロックやセッション データの更新の喪失が発生する恐れがあります。
独自のスレッドを作成するアプリケーションはあまり効率がよくありません。JVM のスレッドは、慎重に割り当てる必要のある限られたリソースです。サーバの負荷が増大すると、アプリケーションが停止するか、OWLCS のパフォーマンスが低下することがあります。デッドロックやスレッド不足などの問題は、アプリケーションに重い負荷がかかるまで明らかにならない可能性があります。
マルチスレッド モジュールは複雑なため、デバッグが困難です。アプリケーションが生成するスレッドと WebLogic Server スレッドとの対話は、特に予測や分析が難しい部分です。
「setAttribute() を使用して「No-Call」Scope のセッション データを変更する」に説明されているように WlssSipApplicationSession.doAction()
メソッドは、生成する Java スレッドに対し同期化しません。doAction()
内に作成したスレッドは同じ WlssSipApplicationSession
に他の doAction()
を実行します。同様に、着信した SIP メッセージを処理する際にコンテナがメイン スレッドを自動的にロックするため、異なる wlssSipApplicationSession
にアクセスするために doAction()
を使用するメインスレッドは、デッドロックを引き起こす場合があります。「setAttribute() を使用して「No-Call」Scope のセッション データを変更する」では、デッドロック状況の可能性について説明します。
警告 : アプリケーションによるスレッドの生成が必要な場合、デッドロックが起こらないよう、慎重にセッション データへの同時アクセスを管理しなければなりません。少なくとも、SIP サーブレットのサービス メソッド内にスレッドを生成することは避けてください。代わりに、サービス メソッドの外で別々のスレッド プールを保持してください。そして、すべてのセッション データへのアクセスを同期するように注意してください。 |
SIP および HTTP サーブレットは、メソッドが呼び出されるとき呼状態がロックされた状態になるので、SIP メソッドの本体でスレッドをブロックしないようにする必要があります。たとえば、サーブレット メソッドが SIP サーブレット コンテナに制御を返す前にデータの取得や書き込みを積極的に待機するような設計は避けてください。
アプリケーションを複数のエンジン層サーバ (レプリケートされた OWLCS コンフィグレーション内) にデプロイする場合は、すべてのアプリケーション データをセッション属性としてセッションに格納する必要があります。レプリケートされたコンフィグレーションでは、エンジン層サーバはキャッシュされた情報を保持しないので、すべてのアプリケーションを SIP データ層サーバにあるセッション属性からデシリアライズする必要があります。
SIP アプリケーション呼状態のインメモリ レプリケーションをサポートするためには、SIP サーブレット セッションに格納されるオブジェクトをすべてシリアライズ可能にする必要があります。オブジェクトがシリアライズ可能とみなされるには、オブジェクトのすべてのフィールドがシリアライズ可能か一時的なフィールドである必要があります。サーブレットでシリアライズ可能なオブジェクトとシリアライズ不可能なオブジェクトを組み合わせて使用した場合、OWLCS はシリアライズ不可能なオブジェクトのセッション ステートをレプリケートすることができません。
SIP サーブレット コンテナは、SIP サーブレットの doxxx
メソッドを呼び出す時、関連する呼び出し状態が自動的にロックされます。しかし、アプリケーションは、「No-Call」 Scope のセッション データを変更する場合もあります。No-call scope は、通常の doxxx
メソッドの範囲外で呼び出し状態データが変更されたコンテキストを参照します。たとえば、HTTP サーブレットが SIP セッション データを変更しようとする場合、またはサーブレットを呼び出す前にコンテナがロックした呼状態以外の呼状態を SIP サーブレットが変更しようとする場合、データは no-call scope に変更されます。
アプリケーションは常に、SIP セッションの setAttribute
メソッドを使用して no-call scope の属性を変更する必要があります。同様に、セッション オブジェクトから属性を削除するには、removeAttribute
を使用してください。セッションデータを更新するために setAttribute/removeAttribute
が使用されるたびに、SIP サーブレット コンテナは関連する呼状態のロックを取得および解除します。(メソッドは、更新するオブジェクトを待ち行列化させ、直ちに制御を
返します。)これにより、1 回に 1 つのアプリケーションだけがデータを変更し、クラスタの SIP データ層ノード間で変更がレプリケートされることが確実になります。
その他の set メソッドを使ってセッションの内部でオブジェクトを変更した場合、OWLCS はその変更をレプリケートできません。
OWLCS コンテナは、setAttribute
を呼び出した後に加えられた変更を呼状態に維持しません。たとえば、次のコード サンプルでは、setAttribute
を呼び出すと呼状態が直ちに変更されますが、続いて modifyState()
を呼び出すと、呼状態が変更されません。
Foo foo = new Foo(..); appSession.setAttribute("name", foo); // これは呼状態を保持します。 foo.modifyState(); // この変更は保持されません。
その代わりに、次のように呼状態属性の値を変更してから、setAttribute
を呼び出すようにしてください。
Foo foo = new Foo(..); foo.modifyState(); appSession.setAttribute("name", foo);
また、SIP サーブレット コンテナには、それぞれの setAttribute
の呼び出しに対して呼状態がロックされるという点にも留意してください。たとえば、HTTP サーブレットで次のコードを実行する時、SIP サーブレット コンテナは呼状態のロックを 2 回ずつ取得して解除します。
appSess.setAttribute("foo1", "bar2"); appSess.setAttribute("foo2", "bar2");
このようにロックすると、1 つのスレッドだけが常に呼状態を変更することが確実になります。しかし、他のプロセスは順次更新の間に呼状態を変更することができます。次のコードは、no-call 状態を行なった場合、スレッド セーフと見なされません。
Integer oldValue = appSession.getAttribute("counter"); Integer newValue = incrementCounter(oldValue); appSession.setAttribute("counter", newValue);
上記のコードをスレッドセーフ にするには、wlssAppSession.doAction
メソッドを使用して囲む必要があります。これにより、呼状態へのすべての変更が、次のように単一のトランザクション ロック内で行われます。
wlssAppSession.doAction(new WlssAction() { public Object run() throws Exception { Integer oldValue = appSession.getAttribute("counter"); Integer newValue = incrementCounter(oldValue); appSession.setAttribute("counter", newValue); return null; } });
最後に、呼び出しの呼び出し状態は doInvite()
などの「do
SipMethod」で呼をロックした場合、デッドロック状況を回避するようにしてください。do
SipMethod の手順が実行されると、OWLCS コンテナは、すでに呼び出し状態をロックしていることに注意してください。アプリケーション コードがメッソド内からの現在の呼状態にアクセスしようとすると、(たとえば、データ構造や属性に保存されているセッションへのアクセス)、ロックの発注結果はデッドロックになります。
例 4-1 は、デッドロックとなる例を示しています。コンテナによって callAppSession
に関連付けられた呼のコードが実行されると、ロックの順が逆転して getApplicationSession(callId)
とのセッションを取得しようとするとデッドロックの原因になります。
例 4-1 デッドロックを引き起こすセッション アクセス
WlssSipApplicationSession confAppSession = (WlssSipApplicationSession) appSession; confAppSession.doAction(new WlssAction() { // confAppSession is locked public Object run() throws Exception { String callIds = confAppSession.getAttribute("callIds"); for (each callId in callIds) { callAppSess = Session.getApplicationSession(callId); // callAppSession is locked attributeStr += callAppSess.getAttribute("someattrib"); } confAppSession.setAttribute("attrib", attributeStr); } }
com.bea.wcp.sip.WlssAction
インタフェースの使用については、節 6.3.1「SipApplicationSession の変更」を参照してください。
SIP サーブレットが doInvite()
、doAck()
、doNotify()
などの SIP リクエスト メソッド内で send()
メソッドを呼び出す場合は、OWLCS コンテナによってすべての send()
呼び出しがバッファされ、SIP リクエスト メソッドの復帰後にそれらが順に転送されるという点に注意してください。send()
呼び出しが即座に転送されることを前提にアプリケーションを設計しないでください。
警告 : リクエストや応答は制御が SIP サーブレット コンテナに返るまで送信されないので、send() を呼び出した後でアプリケーションが待機したりスリープしたりしないようにする必要があります。 |
SIP サーブレットをクラスタ環境にデプロイすることを前提として設計およびプログラムした場合は、アプリケーションをエンジン層サーバのクラスタにデプロイするときに、サーブレットのデプロイメント記述子に distributable
(配布可能) マーカー要素を入れる必要があります。distributable
要素を省略すると、OWLCS はそのサーブレットをエンジン層サーバのクラスタにデプロイしません。sip.xml で distributable
をマークした場合は、WAR ファイルの web.xml でも同様にマークする必要があります。
distributable
要素は必須ではなく、単一の組み合わせ層 (レプリケートされない) OWLCS インスタンスにデプロイする場合は無視されます。
SIP サーブレット 1.1 仕様は、SIP セッションがアクティブ化されるかパッシベーションされる場合アプリケーションにコールバックを提供する SipApplicationSessionActivationListener
について説明します。レプリケートされた OWLCS デプロイメントのみにコールバックが発生することに注意してください。単一サーバ デプロイメントは SIP データ層を使用しません。そのため SIP セッションはパッシベーションされません。
レプリケートされたデプロイメント OWLCS は、セッションに対して SIP メッセージが処理される前後に SIP Session を何度もアクティブ化し、パッシベーションします。(RDBMS ベースの永続性がコンフィグレーションされない場合でも、通常、どのレプリケートされたデプロイメントにおいてもこれは発生します。)アクティブ化およびパッシベーションのこの定数サイクルは、頻繁なコールバックになるため、アプリケーションで SipApplicationSessionActivationListener
の使用を控えます。
JSR289 アプリケーションでは、コンテナでのセッションの削除がより「インテリジェント」に行われます。たとえば、セッションまたは sipappsession に対し、invalidate() を明示的に呼び出す必要はありません。
ただし、セッションで setExpirs() が使用され、アプリケーションが JRS289 型である場合は、セッションで setInvalidateWhenReat(false) を呼び出さない限り、この呼び出しの効果はありません。
他の Java EE API を使用したアプリケーションをデプロイする場合は、それらの API の基本的なクラスタ化ガイドラインに従ってください。たとえば EJB をデプロイする場合は、すべてのメソッドを多重呼び出し不変として設計し、デプロイメント記述子の中で EJB ホームをクラスタ対応として指定してください。詳細については、「Clustering Best Practices」を参照してください。