この章では、Oracle WebLogic Server SIP Containerにデプロイするアプリケーションを開発するための要件とベスト・プラクティスについて説明します。内容は次のとおりです。
典型的な本番環境では、SIPアプリケーションはエンジン層クラスタを形成するOracle WebLogic Server SIP Containerインスタンスのクラスタにデプロイされます。SIPデータ層内のサーバーのクラスタは、アクティブな呼出しについての呼出し状態のレプリケートされたインメモリー・データベースを提供します。この環境でアプリケーションを確実に機能させるには、次の項に説明されているプログラミングの作法と慣行に従い、デプロイ後のアプリケーションの複数のコピーがクラスタ環境で期待どおりに動作するようにしてください。
旧バージョンのOracle WebLogic Server SIP Containerのアプリケーションを移植しようとしている場合、次に説明する規約や制約の多くは馴染みのないものかもしれません。というのも、以前のWebLogic Server実装ではクラスタリングをサポートしていなかったからです。アプリケーションを移植したら徹底的なテストとプロファイリングを行って問題を洗い出し、新しい環境で満足な結果が得られるようにしてください。
Oracle WebLogic Server SIP Containerはマルチ・スレッド・アプリケーション・サーバーであり、ホストしているモジュールについてリソースの割当て、同時実行性およびスレッドの同期化を慎重に管理します。Oracle WebLogic Server SIP Containerアーキテクチャを最大限に生かすには、SIPサーブレットおよびJava EE APIの仕様に従ってアプリケーションのモジュールを構築してください。
SIPサーブレットなどのサーバー側モジュール内に新たなスレッドを作成するようなアプリケーション設計は避けてください。
SIPサーブレット・コンテナは、SIPサーブレットのdo
xxxメソッドを呼び出すとき、関連する呼出し状態を自動的にロックします。doxxx
メソッドが追加のスレッドを生成したり、制御を返す前に別の呼出し状態にアクセスしたりすると、シナリオのデッドロックやセッション・データの更新の喪失が発生する恐れがあります。
独自のスレッドを作成するアプリケーションはあまり効率がよくありません。JVMのスレッドは、慎重に割り当てる必要のある限られたリソースです。サーバーの負荷が増大すると、アプリケーションが停止するか、Oracle WebLogic Server SIP Containerのパフォーマンスが低下することがあります。デッドロックやスレッド不足などの問題は、アプリケーションに重い負荷がかかるまで明らかにならない可能性があります。
マルチスレッド・モジュールは複雑なため、デバッグが困難になります。アプリケーションが生成したスレッドとWebLogic Serverスレッドの間の対話の場合、特に予測や分析が難しくなります。
「setAttribute()を使用して「No-Call」Scopeのセッション・データを変更する」に説明されているようにWlssSipApplicationSession.doAction()
メソッドは、生成するJavaスレッドに対し同期化しません。doAction()
内に作成したスレッドは同じWlssSipApplicationSession
に他のdoAction()
を実行します。同様に、着信したSIPメッセージを処理する際にコンテナがメイン・スレッドを自動的にロックするため、異なるwlssSipApplicationSession
にアクセスするためにdoAction()
を使用するメイン・スレッドは、デッドロックを引き起こす場合があります。「setAttribute()を使用して「No-Call」Scopeのセッション・データを変更する」では、デッドロック状況の可能性について説明します。
警告: アプリケーションによるスレッドの生成が必要な場合、デッドロックが起こらないよう、慎重にセッション・データへの同時アクセスを管理しなければなりません。少なくとも、SIPサーブレットのサービス・メソッド内にスレッドを生成することは避けてください。かわりに、サービス・メソッドの外で別々のスレッド・プールを保持してください。そして、すべてのセッション・データへのアクセスを同期するように注意してください。 |
SIPおよびHTTPサーブレットは、メソッドが呼び出されるとき呼出し状態がロックされた状態になるので、SIPメソッドの本体でスレッドをブロックしないようにする必要があります。たとえば、サーブレット・メソッドがSIPサーブレット・コンテナに制御を返す前にデータの取得や書込みを積極的に待機するような設計は避けてください。
アプリケーションを複数のエンジン層サーバー(レプリケートされたOracle WebLogic Server SIP Container構成内)にデプロイする場合は、すべてのアプリケーション・データをセッション属性としてセッションに格納する必要があります。レプリケートされた構成では、エンジン層サーバーはキャッシュされた情報を保持しないので、すべてのアプリケーションをSIPデータ層サーバーにあるセッション属性からデシリアライズする必要があります。
SIPアプリケーション呼出し状態のインメモリー・レプリケーションをサポートするためには、SIPサーブレット・セッションに格納されるオブジェクトをすべてシリアライズ可能にする必要があります。オブジェクトがシリアライズ可能と見なされるには、オブジェクトのすべてのフィールドがシリアライズ可能か一時的なフィールドである必要があります。サーブレットでシリアライズ可能なオブジェクトとシリアライズ不可能なオブジェクトが組み合せて使用した場合、Oracle WebLogic Server SIP Containerはシリアライズ不可能なオブジェクトのセッション状態をレプリケートすることができません。
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メソッドを使ってセッションの内部でオブジェクトを変更した場合、Oracle WebLogic Server SIP Containerはその変更をレプリケートできません。
Oracle WebLogic Server SIP Containerコンテナは、setattribute
を呼び出した後に加えられた変更を呼出し状態に維持しません。たとえば、次のコード・サンプルでは、setAttribute
を呼び出すと呼出し状態が直ちに変更されますが、続いてmodifyState()
を呼び出すと、呼出し状態が変更されません。
Foo foo = new Foo(..); appSession.setAttribute("name", foo); // This persists the call state. foo.modifyState(); // This change is not persisted.
そのかわりに、次のように呼出し状態属性の値を変更してから、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の手順が実行されると、Oracle WebLogic Server SIP Containerコンテナは、すでに呼出し状態をロックしていることに注意してください。アプリケーション・コードがメソッド内からの現在の呼出し状態にアクセスしようとすると、(たとえば、データ構造や属性に保存されているセッションへのアクセス)、ロックの発注結果はデッドロックになります。
例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()
メソッドを呼び出す場合は、Oracle WebLogic Server SIP Containerコンテナによってすべてのsend()
呼出しがバッファされ、SIPリクエスト・メソッドの復帰後にそれらが順に転送されるという点に注意してください。send()
呼出しが即座に転送されることを前提にアプリケーションを設計しないでください。
警告: リクエストやレスポンスは制御がSIPサーブレット・コンテナに返るまで送信されないので、send() を呼び出した後でアプリケーションが待機したりスリープしたりしないようにする必要があります。 |
SIPサーブレットをクラスタ環境にデプロイすることを前提として設計およびプログラムした場合は、アプリケーションをエンジン層サーバーのクラスタにデプロイするときに、サーブレットのデプロイメント記述子にdistributable
マーカー要素を入れる必要があります。distributable
要素を省略すると、Oracle WebLogic Server SIP Containerはそのサーブレットをエンジン層サーバーのクラスタにデプロイしません。sip.xmlでdistributable
をマークした場合は、WARファイルのweb.xmlでもマークする必要があります。
distributable
要素は必須ではなく、単一の組合せ層(レプリケートされない)Oracle WebLogic Server SIP Containerインスタンスにデプロイする場合は無視されます。
SIPサーブレット1.1仕様は、SIPセッションがアクティブ化されるかパッシブ化される場合アプリケーションにコールバックを提供するSipApplicationSessionActivationListener
について説明します。レプリケートされたOracle WebLogic Server SIP Containerデプロイメントのみにコールバックが発生することに注意してください。単一サーバー・デプロイメントはSIPデータ層を使用しません。そのためSIPセッションはパッシブ化されません。
レプリケートされたデプロイメントOracle WebLogic Server SIP Containerは、セッションに対してSIPメッセージが処理される前後にSIPセッションを何度もアクティブ化し、パッシブ化します。(RDBMSベースの永続性が構成されない場合でも、通常、どのレプリケートされたデプロイメントにおいてもこれは発生します。)アクティブ化およびパッシブ化のこの定数サイクルは、頻繁なコールバックになるため、アプリケーションでSipApplicationSessionActivationListener
の使用を控えます。
JSR289アプリケーションでは、コンテナでのセッションの削除がよりインテリジェントに行われます。たとえば、セッションまたはsipappsessionに対し、invalidate()を明示的に呼び出す必要はありません。
ただし、セッションでsetExpirs()が使用され、アプリケーションがJRS289型である場合は、セッションでsetInvalidateWhenReat(false)を呼び出さないかぎり、この呼出しの効果はありません。
他のJava EE APIを使用したアプリケーションをデプロイする場合は、それらのAPIの基本的なクラスタリング・ガイドラインに従ってください。たとえばEJBをデプロイする場合は、すべてのメソッドを多重呼出し不変として設計し、デプロイメント記述子の中でEJBホームをクラスタ化可能として指定してください。