ExecutorServiceを利用して、並列処理を行うサンプル
今やっているアプリケーションの高速化を行うために並列処理を取り入れました。 やっている内容はレポートを作るのですが、APIで取得した100枚以上の画像をレポートに貼り付けるのですが、画像を取得する部分は並列化しています。
サンプルを作ったので、Githubにアップしています。 github.com
冬休みを利用して、解説を書きたいな。
Concurrency Utilities for Java EE(JSR-236)のシンプルなサンプル試してみる
Java EEで非同期処理を行う方法を調べていました。 やりたいことは、iOSやAndroidのPushメッセージのリクエストを受け付けて、リクエストの内容はデータベースに保存。非同期で、データベースの情報を元に、APNsやGCMにメッセージを送るという処理です。
選択肢としてはこんな感じかなと思います。
方式 | 検討したこと |
---|---|
Concurrency Utilities for Java EE | Concurrencyという名前が付いているだけあって、単なる非同期処理というよりは、並列処理に関するコントロールがいろいろできそうです。 |
Message Driven Bean | キューにメッセージがエンキューされたタイミングで、イベント・ドリブンで処理が動きます。JMSにメッセージがキューイングされるので、メッセージの永続化が可能。メッセージの永続化が可能なら、この方式かなと思います。今回は、Pushメッセージの内容はDBに永続化されていて、処理依頼のメッセージ自体のの永続性は不要だったので、JMSの環境構築の手間も考えて選択肢から外しました。 |
EJB Timer Service | 単純な非同期処理以外にも、cron的なスケジュールベースの処理起動ができるみたいです。今回の要件では、多重度なくて、並行処理はどっちでもいいので、こちらの方式でもいいかも。 |
このエントリではConcurrency Utilities for Java EEを使ってみたいと思います。 Executors.newSingleThreadExecutorを使ってみます。これは、アプリケーション・サーバーなどのコンテナからスレッドを借りてこれますが、スレッドの多重度は1つになります。 生成されたスレッドは、コンテナのコンテキストを持っています。
Executors.newSingleThreadExecutorを使ったサンプルコード
Webサービスでリクエストを受け付ける部分
GETメソッドで処理を受け付けて、後述する非同期処理を起動します。 リクエストごとにRequestIdを採番しています。
package sample; import javax.ws.rs.GET; import javax.ws.rs.Path; @Path("/task") public class TaskService { static int index = 0; @GET @Path("/") public void start() { System.out.println("TaskService.start is called. RequestId is " + index); TaskManager timer = TaskManager.getInstance(); timer.exec(index++); } }
非同期処理を行う部分
Executors.newSingleThreadExecutorを使って、処理を非同期で行います。 シングルトンで実装しています。
非同期処理部分で2秒間待合せ処理をしています。 パラメタで受け取ったリクエストIDと、スレッドを識別するためのスレッドIDをログに出力しています。
package sample; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TaskManager { static private TaskManager instace = null; private ExecutorService executor = null; private TaskManager() { executor = Executors.newSingleThreadExecutor(); } synchronized static public TaskManager getInstance() { if (instace == null) { instace = new TaskManager(); } return instace; } public void exec(int requestId) { Runnable runnable = new Runnable() { public void run() { System.out.println("RequestId " + requestId + ":ThreadId " + Thread.currentThread().getId() + " is running."); try { Thread.sleep(2000); } catch (InterruptedException e) {} System.out.println("RequestId " + requestId + ":ThreadId " + Thread.currentThread().getId() + " is finished."); } }; executor.execute(runnable); } }
実行時ログ
作ったWebサービスを呼び出して、ログを確認します。 Executors.newSingleThreadExecutorを使っていることで、リクエストが重なった時も1つのスレッドで順次処理されていることがわかります。
# 最初のリクエスト 01:00:46,566 INFO [stdout] (default task-2) TaskService.start is called. RequestId is 0 01:00:46,568 INFO [stdout] (pool-5-thread-1) RequestId 0:ThreadId 138 is running. 01:00:48,570 INFO [stdout] (pool-5-thread-1) RequestId 0:ThreadId 138 is finished. # リクエストを受け付けて、処理が不通に動きました。スレッドIDは138。 01:00:50,164 INFO [stdout] (default task-3) TaskService.start is called. RequestId is 1 01:00:50,164 INFO [stdout] (pool-5-thread-1) RequestId 1:ThreadId 138 is running. 01:00:52,169 INFO [stdout] (pool-5-thread-1) RequestId 1:ThreadId 138 is finished. # ここからリクエストを連続で投げました 01:00:53,444 INFO [stdout] (default task-4) TaskService.start is called. RequestId is 2 01:00:53,444 INFO [stdout] (pool-5-thread-1) RequestId 2:ThreadId 138 is running. 01:00:53,756 INFO [stdout] (default task-5) TaskService.start is called. RequestId is 3 01:00:54,028 INFO [stdout] (default task-6) TaskService.start is called. RequestId is 4 01:00:54,317 INFO [stdout] (default task-7) TaskService.start is called. RequestId is 5 # リクエストは受付られましたが、処理はキューイングされています 01:00:55,447 INFO [stdout] (pool-5-thread-1) RequestId 2:ThreadId 138 is finished. 01:00:55,447 INFO [stdout] (pool-5-thread-1) RequestId 3:ThreadId 138 is running. 01:00:57,448 INFO [stdout] (pool-5-thread-1) RequestId 3:ThreadId 138 is finished. 01:00:57,449 INFO [stdout] (pool-5-thread-1) RequestId 4:ThreadId 138 is running. 01:00:59,451 INFO [stdout] (pool-5-thread-1) RequestId 4:ThreadId 138 is finished. 01:00:59,451 INFO [stdout] (pool-5-thread-1) RequestId 5:ThreadId 138 is running. 01:01:01,452 INFO [stdout] (pool-5-thread-1) RequestId 5:ThreadId 138 is finished. # 同じスレッドIDで順次、処理が実行されていきました
サンプルコード
サンプルコードの全部はGithubで公開しています。
参考ドキュメント
こちらを参考にさせてもらいました。
WebLogic 12.1.2のCUIインストール
WebLogicをCUIのみで入れる必要があって、いろいろ調べました。
12.1.1はコンソールモードっていうのがあって、CUIだけでインストールできるのですが、12.1.2からはなくなりました。サーバー側のソフトウェアのインストーラーにX Window systemがいるのって、本当にナンセンスだと思います。
クラウド化が進んでいくと、サーバーはどこにあるかわからない状態になって、リモート接続してセットアップしないといけなくて、セキュリティバリバリだと、いろいろポートは開いてないし、Xで接続するだけでも面倒。
しかもWebLogicのインストールは、半年に1回くらいしかやらないから、毎回手順忘れるっていう。
コンソールモードの廃止について
http://www.slideshare.net/OracleMiddleJP/wlstudy-201412unknown-features http://docs.oracle.com/cd/E28613_01/web.1211/b65940/console_mode.htm
OSXでssh経由でXに接続する
ポートフォーワーディングして、ssh経由でXに接続するやりかた。 http://www.cyberciti.biz/faq/apple-osx-mountain-lion-mavericks-install-xquartz-server/
前準備
インストール先のディレクトリの作成
今回は、次の2つを事前に作成しました。
[root@demo02 opt]# chown weblogic:weblogic /opt/oracle/weblogic/ [root@demo02 opt]# chown weblogic:weblogic /opt/oracle/oraInventory/
orainst.locの作成
[weblogic@demo02 ~]$ cat /opt/oracle/oraInventory/oraInst.loc inventory_loc=/opt/oracle/oraInventory/ inst_group=weblogic
WebLogicのインストール
レスポンスファイルの作成
[weblogic@demo02 ~]$ cat media/sinstall.txt
[ENGINE] #DO NOT CHANGE THIS. Response File Version=1.0.0.0.0 [GENERIC] #The oracle home location. This can be an existing Oracle Home or a new Oracle Home ORACLE_HOME=/opt/oracle/weblogic/ #Set this variable value to the Installation Type selected. e.g. Fusion Middleware Infrastructure, Fusion Middleware Infrastructure With Examples. INSTALL_TYPE=WebLogic Server #Provide the My Oracle Support Username. If you wish to ignore Oracle Configuration Manager configuration provide empty string for user name. MYORACLESUPPORT_USERNAME= #Provide the My Oracle Support Password MYORACLESUPPORT_PASSWORD= #Set this to true if you wish to decline the security updates. Setting this to true and providing empty string for My Oracle Support username will ignore the Oracle Configuration Manager configuration DECLINE_SECURITY_UPDATES=true #Set this to true if My Oracle Support Password is specified SECURITY_UPDATES_VIA_MYORACLESUPPORT=false #Provide the Proxy Host PROXY_HOST= #Provide the Proxy Port PROXY_PORT= #Provide the Proxy Username PROXY_USER= #Provide the Proxy Password PROXY_PWD=<SECURE VALUE> #Type String (URL format) Indicates the OCM Repeater URL which should be of the format [scheme[Http/Https]]://[repeater host]:[repeater port] COLLECTOR_SUPPORTHUB_URL=
インストーラーの実行
[weblogic@demo02 media]$ java -jar wls_121200.jar -silent -responseFile /home/weblogic/media/sinstall.txt -invPtrLoc /opt/oracle/oraInventory/oraInst.loc ファイルを抽出しています....... Oracle Universal Installerを起動中です CPU速度が300MHzを超えていることを確認してください. 実際2500.114MHz 問題なし スワップ領域の確認中: 512MBを超えている必要があります. 実際4079612MB 問題なし このプラットフォームに64-bit JVMが必要かどうかを確認中. 実際 64 問題なし(64-bitは不要) 一時領域の確認中: 300MBを超えている必要があります. 実際39149MB 問題なし /tmp/OraInstall2016-02-19_03-54-05PMからOracle Universal Installerの起動を準備中 ログ: /opt/oracle/oraInventory//logs/install2016-02-19_03-54-05PM.log Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. レスポンス・ファイルの読取り中.. 予期した結果: enterprise-4,enterprise-5,enterprise-6,redhat-6,redhat-4,redhat-5,SuSE-10,SuSE-11の1つ 実際の結果: (不明なOSバージョン) チェックが完了しました。このチェックの全体的な結果: 失敗しました <<<< 問題: このOracleソフトウェアは現行のオペレーティング・システム上では認証されていません。 推奨: ソフトウェアを適切なプラットフォームにインストールしていることを確認してください。 警告: チェック: CertifiedVersionsに失敗しました。 予期した結果: 1.7 実際の結果: 1.7.0_71 チェックが完了しました。このチェックの全体的な結果: 問題なし CheckJDKVersionチェック: 成功しました。 データの検証中...... ファイルのコピー中... -----------20%----------40%----------60%----------80%--------100% WebLogic Server 12.1.2.0.0のinstallationが正常に完了しました。
ドメインの作成
ドメインの作成はWLSTを使ってやることができます。
こちらを参考にさせていただきました。 「一歩先に進むためのWLST活用技法 + お悩み相談室」 https://blogs.oracle.com/wlc/entry/wlstudy_20130723_2
WLSTを起動
http://montai-garage.sakura.ne.jp/getthreaddump/
[weblogic@demo02 bin]$ ./wlst.sh WebLogic Scripting Tool(WLST)を初期化しています... WebLogic Server Administration Scripting Shellへようこそ 使用可能なコマンドに関するヘルプを表示するには、help()と入力してください wls:/offline> myHome="/opt/oracle/weblogic" wls:/offline> wlHome=myHome + "/wlserver" wls:/offline> readTemplate(wlHome + "/common/templates/wls/wls.jar") wls:/offline/base_domain>setOption('ServerStartMode', 'prod') wls:/offline/base_domain>cd('/Servers/AdminServer') wls:/offline/base_domain/Server/AdminServer>set('ListenAddress', '0.0.0.0') wls:/offline/base_domain/Server/AdminServer>set('ListenPort', 7001) wls:/offline/base_domain/Server/AdminServer>cd('/Security/base_domain/User/weblogic') wls:/offline/base_domain/Security/base_domain/User/weblogic>cmo.setPassword('パスワード設定してね') wls:/offline/base_domain/Security/base_domain/User/weblogic>writeDomain(myHome + '/user_projects/domains/mydomain') wls:/offline/mydomain/Security/mydomain/User/weblogic>closeTemplate() wls:/offline>exit() WebLogic Scripting Toolを終了しています。
writeDomainで、すっごい時間掛かった。
インストール後の作業
起動WebLogicユーザーの設定
http://www.oracle.com/webfolder/technetwork/jp/obe/fmw/wls/12c/15-BootProp--4471/bootproperties.htm
ユーザーのプロパティの指定。 起動時にパスワードをいれなくていいように。
JBoss ASのデプロイプロセスを眺めた
WebLogic Serverの会社にいたので、JBossは全然触ったことがなかったのですが、最近、使ってます。今日は、デプロイのプロセスを眺めていたので、メモしておきます。
デプロイ
デプロイはこのパスにwarファイルなどを置くことで簡単にできます。 {JBOSS_HOME}/standalone/deployments/
その時、置いたwarファイルに対して、isdeployingというファイルができる。
AppPot_1.1.4_3_jboss.war
AppPot_1.1.4_3_jboss.war.isdeploying
しばらくまってると、deployedファイルになり、デプロイ完了。ファイル名で分かるというのはシンプルで面白いやり方ですね。
AppPot_1.1.4_3_jboss.war
AppPot_1.1.4_3_jboss.war.deployed
アンデプロイ
アンデプロイするときは、deployedファイルをundeployという名前に変えるだけ。 $ mv AppPot_1.1.4_2_jboss.war.deployed AppPot_1.1.4_2_jboss.war.undeploy
そうすると、undeployに加えて、undeployedファイルができて、実際にWebアプリのURLにアクセスしてみると404エラーが返ってくる
-rw-r--r-- 1 ncdc ncdc 13887481 6月 5 08:54 2014 AppPot_1.1.4_2_jboss.war
-rw-rw-r-- 1 jboss jboss 24 6月 5 08:54 2014 AppPot_1.1.4_2_jboss.war.undeploy
-rw-rw-r-- 1 jboss jboss 24 6月 5 10:08 2014 AppPot_1.1.4_2_jboss.war.undeployed
欲しいClassがどのjarファイルに入っているか探す
Enterprise Javaをやっていると必ずはまるJar地獄。 自分が使っているライブラリを、アプリケーションサーバーや、3rdパーティーのライブラリが使っていて、ClassNotFoundや、そんなメソッドないとか、そのメソッドはPublicじゃないとか訳分かんなくなるやつ。
そんな時はエラーになっているClassがクラスパスのどこにあるか探して、複数ある場合はどっちを使うべきかを設定してやればいいし、無ければクラスパスに追加してやればOK。
Classがどのjarに含まれているか調べないといけないけど、Googleで調べても有名で皆がはまったことのあるやつしか出てこない。ですので、作りました。5年くらい前に。最近もこれに助けられた。
JavaのpropertiesのUTF8、Native変換
いつも忘れてしまう。
符号化されているpropertiesファイルをもらったら、こちらで人が読めるように変換。
native2ascii -reverse MessagesBundle_ja.properties MessagesBundle_ja_native.properties
日本語の修正が終わったら、符号化する。
native2ascii MessagesBundle_ja_native.properties MessagesBundle_ja.properties
Oracleの公式マニュアルはこちら。
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/native2ascii.html