2010年3月15日月曜日

バーチャルホストの設定

1つのサーバで2つ以上ドメインを使いたいと思うことがあると思います!

そんな時に使うのがApacheのバーチャルホストです。

使い方は簡単で、下記の記述をhttpd.confに記述します。
NameVirtualHost *:80
<VirtualHost *:80>
ServerName xxxxx
DocumentRoot "/opt/xxxx"
</VirtualHost>

<VirtualHost *:80>
ServerName hogehoge
DocumentRoot "/opt/hoge"
</VirtualHost>


ドメインがServerNameに一致する場合、その中に記述されている内容が適用されます。
今回で言うと、ServerNameによってDocumentRootが変わります。

詳しくは
http://httpd.apache.org/docs/2.2/ja/vhosts/examples.html
を見るといいかもしれません。

2010年3月14日日曜日

ThreadPoolExecutor について(第2部:指定出来るいろいろな機能)

実際に使用したときに、私が悩んだ項目であるコンストラクタ引数と、タスク登録について紹介したいと思います。

■コンストラクタ引数について
ThreadPoolExecutorには下記の4つのコンストラクタがあります。

1.ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
2.ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue
workQueue, RejectedExecutionHandler handler)
3.ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue
workQueue, ThreadFactory threadFactory)
4.ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue
workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)


コンストラクタ引数のうち、私が特に悩んだRejectedExecutionHandlerについて説明したいと思います。

この値(RejectedExecutionHandler)が何かと言うと、JavaDoc的には下記の様に記述されています。
executor がシャットダウンしている場合、または executor が最大スレッド数とワークキュー容量の両方で有限の境界を使用し、かつ飽和状態である場合、execute(java.lang.Runnable) メソッドで送信された新しいタスクは拒否されます。どちらの場合も、execute メソッドは、RejectedExecutionHandler の RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) メソッドを呼び出します。

要するに、タスク(スレッドで処理されるもの)が処理できなくなった(あふれた)場合に呼び出される処理ということです。
そして、この処理はデフォルトで4種類のポリシーが用意されています。(独自で用意することもできますが)

このポリシーの選択が重要になります!

このポリシーの選択を間違えると、処理して欲しいハズの処理が飛ばされたり、無視されたり、例外を投げられたりすることになります!

なので、このポリシーの選択は慎重に行ってください。

ポリシーは下記の4つが存在します。
ThreadPoolExecutor.AbortPolicy
 RejectedExecutionException をスローする拒否されたタスクのハンドラです。
ThreadPoolExecutor.CallerRunsPolicy
 executor がシャットダウンしていない場合に、execute メソッドの呼び出しで拒否されたタスクを直接実行する、拒否されたタスクのハンドラです。
ThreadPoolExecutor.DiscardOldestPolicy
 executor がシャットダウンしていない場合に、もっとも古い未処理の要求を破棄して execute を再試行する、拒否されたタスクのハンドラです。
ThreadPoolExecutor.DiscardPolicy
 拒否されたタスクを通知なしで破棄する拒否されたタスクのハンドラです。

下の2つ(DiscardOldestPolicy,DiscardPolicy)はタスクを破棄されてしまうので、それで問題ない場合は使用しましょう!
私が使用した時の要件では、絶対にタスクを破棄されてしまっては困るので上の2つ(AbortPolicy,CallerRunsPolicy)のどちらかを使う必要がありました。

私が考えた、この2つのポリシーのメリット・デメリットをまとめます
・CallerRunsPolicy
 メリット
  タスクがあふれた場合は、親スレッドが処理を行うので効率よくタスクを処理してくれる。
 デメリット
  スレッドごとにトランザクションを管理している場合に、親スレッドのトランザクションが中断されてしまう可能性がある。
  (親スレッドは、mainの最初に開始し、mainの最後にかならずトランザクションを終了させたい場合に、親がタスクの処理を行うとタスクの開始時にトランザクションを張ろうとしてネストしたトランザクションになってしまいます。
   また、タスクの終了時にトランザクションを閉じるので、mainの最後でトランザクションを終了させることができなくなってしまいます)

・AbortPolicy
 メリット
  親スレッドはタスクを処理しないので、CallerRunsPolicyのデメリットが発生しない。
  (この点はDiscardOldestPolicy,DiscardPolicyでも同じことが言えます)
  デメリットの項目とカブるが、タスクが溢れるようなロジックを作っていた場合に、例外が発生するため、間違に気付ける。
 デメリット
  タスクがあふれた場合は、例外が発生してしまう。
  タスクをあふれさせないように、ロジックで制御する必要がある

CallerRunsPolicy、AbortPolicyをこのように分類し、最終的には両方とも使用しました。
トランザクションを考える必要がない場所ではCallerRunsPolicyを使い、そうでない場合はAbortPolicyを使いました。

■タスク登録方法
ThreadPoolExecutorには、タスクとして扱える形式が2つ存在します。
1つ目がRunnableインタフェースを実装する方法、2つ目がCallableインタフェースを実装する方法です。
RunnableインタフェースとCallableインタフェースの簡単な違いは、Callableインタフェースの場合は戻り値を受け取れるということです。
使用用途によって分けるのもいいでしょう!
ただ、RunnableとCallableはタスクの登録方法が若干違うので設計するときは注意しましょう!

・Runnable
1.コンストラクタ引数として渡す、BlockingQueueに格納しておく
2.executeメソッドに引数として渡す
3.submitメソッドに引数として渡す

・Callable
1.submitメソッドに引数として渡す


Runnableの方がいろいろな方法でタスク登録できます。
使用する状況によって分けてみましょう!


こんな感じでThreadPoolExecutorにはいろいろな設定ができるので、
使用用途にあった使い方を吟味してしようしてください。

2010年3月6日土曜日

ThreadPoolExecutor について(第1部:基本的な使い方)

本当に基本的な使い方を紹介します。

■前提条件
スレッド数:3
スレッド消失時間:500ms
スレッド実行される処理(タスク)はRunnableインタフェースを実装させます。
(Callableインタフェースでもできます(タスクの登録方法がRunnableと若干異なります))

■実ソース

package sample.thread.basic;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SampleMain {

public static void main(final String[] args) {
new SampleMain().go();
}

public void go() {
System.out.println("処理開始");
final BlockingQueue<Runnable%gt; queue = new LinkedBlockingQueue]<Runnable>();
// queueに事前に積んでおくこともできる
for (int i = 0; i < 50; i++) {
queue.add(new TestRunnable());
}
final ThreadPoolExecutor tpe = new ThreadPoolExecutor(3, 3, 500, TimeUnit.MILLISECONDS, queue, new ThreadPoolExecutor.CallerRunsPolicy());
try {
// shutdown()を呼ぶ前であれば、タスクを後から追加することもできる
for (int i = 0; i < 50; i++) {
tpe.execute(new TestRunnable());
}
} finally {
// 必ずshutdownを実行する。実行しないとスレッドが開放されないので処理が一生
System.out.println("ShutDown実行。");
tpe.shutdown();
}
while (true) {
if (tpe.isTerminated()) {
break;
}
try {
System.out.println("タスク終了待ち 1000ms.");
Thread.currentThread();
Thread.sleep(1000);
} catch (InterruptedException ignore) {
}
}
System.out.println("処理終了");
}

/**
* スレッド実行されるタスク。
*/
private static class TestRunnable implements Runnable {

private static volatile int count = 0;

@Override
public void run() {
System.out.println(count++);
}
}
}


■気をつける点
必ずshutdown()メソッドを呼ぶ必要があります!
これを忘れるとMainスレッドが終了してもスレッドだけプーリングされている状態となり、処理が終わりません。
shutdown()メソッドを実行してもすぐに処理が終了するわけではありません。
すべてのスレッドを正常に処理し終えるまでスレッドは開放されません。
すべてのタスクの処理が終わるのを待ちたいのであれば、終了を待つ必要があります。

ThreadPoolExecutor.terminated()をオーバーライドして、終了処理を書くこともできます。


こんな簡単な記述で、スレッドをプーリングすることができます。
次回は、ThreadPoolExecutorをインスタンス化する際に指定できる、いろいろな機能について触れたいと思います。

2010年3月3日水曜日

ThreadPoolExecutorについて(第0部)

業務でこのクラスを使用する機会があったので、ちょっとまとめておこうと思います。

今回の業務の制約はだいたい下記の通りです。
1.Javaでバッチを作成する必要があった
2.性能を追求する必要があった
3.本番環境のCPUの数が8個あった(もちろんメモリもたくさんありました)

このような条件があったので、スレッドを使用することにしました。
そして、Javaでスレッドを使用するならプーリングして使った方が効率がいいので、プーリング機能を提供しているThreadPoolExecutorを使うことになりました。

今回は、このThreadPoolExecutorを使って実現できたこと、はまったことなどを紹介できたらと思います。

一応、全3部構成(今回を入れると4部ですが)で紹介しようと思います。
構成は下記のような感じで進めようと思います。

第0部
 使ったきっかけ
第1部
 基本的な使い方
第2部
 指定出来るいろいろな機能

では、今日はこの目次で終わります。