nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

JBoss EAP 6.2でAtmosphereのnativeを動かす

JBoss / WildFly (全部俺) Advent Calendar 2013の25日目です。

Atmosphere Frameworkという非同期アプリケーションフレームワークがあります。作者はGrizzlyの作者でもあるjfarcandさんです。

AtmosphereをJBoss EAP 6.2.0のNativeコネクタで提供されるAsyncサポートと組み合わせて利用するセットアップががなかなかWeb上に情報のないものだったので書いておきます。

JBoss EAP 6.2.0はNativeコネクタを追加インストール(要サブスクリプション、もしくはコミュニティ版JBoss Web Native Connectorsでがんばる)してwebサブシステムのnative="false"をtrueに変更。

OS側依存もインストールしておきます。

$ sudo yum install apr openssl zlib-devel

これでネイティブコネクタ(APR)が有効化されます。

この時点でAtmosphereのサンプルのchatのwarをデプロイすると怒られます。

WARN  [org.atmosphere.cpr.AtmosphereFramework] (http-thread-pool-threads - 10) Failed using comet support: org.atmosphere.container.JBossWebCometSupport, error: JBoss failed to detect this is a Comet application because the APR Connector is not enabled. 
Make sure atmosphere-compat-jboss.jar is not under your WEB-INF/lib and You must use the atmosphere-native-runtime dependency in order to use native Comet Support
there is no context.xml under WEB-INF Is the NIO or APR Connector enabled?
ERROR [org.atmosphere.cpr.AtmosphereFramework] (http-thread-pool-threads - 10) If you have more than one Connector enabled, make sure they both use the same protocol, e.g NIO/APR or HTTP for all. If not, org.atmosphere.container.BlockingIOCometSupport will be used and cannot be changed.
WARN  [org.atmosphere.cpr.AtmosphereFramework] (http-thread-pool-threads - 10) Using org.atmosphere.container.BlockingIOCometSupport

エラーメッセージがあまり親切ではないのですが、これはAtmosphereのセットアップの問題で、コンテナ依存のAsyncサポートを使う場合はドキュメントにある通りatmosphere-runtime.jarを使ってはいけません。atmosphere-runtime-nativeと、実際に利用するコンテナ以外のスタブjarを利用します。

    <dependency>
      <groupId>org.atmosphere</groupId>
      <artifactId>atmosphere-runtime-native</artifactId>
      <version>2.0.5</version>
    </dependency>
    <dependency>
      <groupId>org.atmosphere</groupId>
      <artifactId>atmosphere-compat-tomcat</artifactId>
      <version>2.0.1</version>
    </dependency>
    <dependency>
      <groupId>org.atmosphere</groupId>
      <artifactId>atmosphere-compat-tomcat7</artifactId>
      <version>2.0.1</version>
    </dependency>

chatのwarのlibを上の通りに差し替えてデプロイするとnativeサポートが有効になります。

INFO  [org.atmosphere.cpr.AtmosphereFramework] (ServerService Thread Pool -- 49) Atmosphere is using async support: org.atmosphere.container.JBossWebCometSupport running under container: JBoss Web/7.2.2.Final-redhat-1
INFO  [org.atmosphere.cpr.AtmosphereFramework] (ServerService Thread Pool -- 49) Atmosphere Framework 2.0.5 started.
INFO  [org.atmosphere.cpr.AtmosphereServlet] (ServerService Thread Pool -- 49) AtmosphereServlet with native support for Tomcat 6/7 and JBossWeb Installed.

JBoss製品のアップデート通知メーリングリスト

JBoss / WildFly (全部俺) Advent Calendar 2013の21日目です。Red Hatの提供しているエンタープライズ版のおはなしですが、エンタープライズ版をまだ使ったことはないのだけど雰囲気やおおまかな情報を知りたいという需要は結構あるようなので紹介してみます。

Red Hatの提供しているエンタープライズ版のJBoss製品のアップデートやセキュリティパッチのリリースなどを通知するJBoss Watch Listというメーリングリストがあります。リリース通知以外は流れないため、流量は少なめです。このリストは特にサブスクリプション購入者に限定するものではなく、オープンなものであるので誰でも購読できます。

将来的にJBoss製品使うかもしれないなー、という場合にサブスクライブしておくとアップデートの頻度などの雰囲気がつかめて良いかもしれません。アーカイブも公開されているので、必要になったらそちらを見る、というのでも良いとは思いますが、個人的にはリリース通知はプッシュで欲しい派です。

サブスクライブ手順などはJBoss EAP 6.2のインストールガイドのパッチの章にも記述があります。

JavaのCPUプロファイリング

JBoss / WildFly (全部俺) Advent Calendar 2013の19日目です。Java一般なトピック寄りで。

自分で主に利用するJavaのプロファイラはYourKitなのですが、YourKitを購入していない場合で性能問題を解析するときとかに何が使えるかなぁと思って調べてみました。

$ uname -a
Linux localhost.localdomain 3.11.10-301.fc20.x86_64 #1 SMP Thu Dec 5 14:01:17 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
$ java -version
java version "1.7.0_45"
OpenJDK Runtime Environment (fedora-2.4.3.0.fc20-x86_64 u45-b15)
OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)

FedoraのOpenJDKを使う場合はdebuginfoを事前にインストールします。入っていないとlibjvm.soのシンボルが見えません。

$ sudo debuginfo-install java-1.7.0-openjdk

まずは普通に起動してみます。1.3秒です。

$ ./standalone.sh
15:54:07,063 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: JBoss EAP 6.2.0.GA (AS 7.3.0.Final-redhat-14) started in 1305ms - Started 129 of 186 services (56 services are passive or on-demand)

hprof

ふつうのJavaのhprofです。-agentlib:hprof=cpu=samplesをJava VMオプションに追加します。結果はjava.hprof.txtに出力されます。

$ vi ./standalone.conf
$ ./standalone.sh
17:05:55,614 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: JBoss EAP 6.2.0.GA (AS 7.3.0.Final-redhat-14) started in 1580ms - Started 129 of 186 services (56 services are passive or on-demand)
$ grep -A11 "^CPU SAMPLES BEGIN" java.hprof.txt 
CPU SAMPLES BEGIN (total = 1349) Tue Dec 17 17:07:31 2013
rank   self  accum   count trace method
   1 45.14% 45.14%     609 300838 sun.nio.ch.EPollArrayWrapper.epollWait
   2 18.46% 63.60%     249 300299 java.lang.ClassLoader.defineClass1
   3  7.64% 71.24%     103 301073 java.net.PlainSocketImpl.socketAccept
   4  1.63% 72.87%      22 300300 java.util.zip.Inflater.inflateBytes
   5  0.67% 73.54%       9 301142 java.io.FileDescriptor.sync
   6  0.44% 73.98%       6 300071 java.lang.ClassLoader.findBootstrapClass
   7  0.44% 74.43%       6 300358 java.util.zip.ZipFile.open
   8  0.30% 74.72%       4 300722 java.util.zip.ZipFile.getNextEntry
   9  0.30% 75.02%       4 300751 java.util.zip.ZipFile.getEntry
  10  0.30% 75.32%       4 300954 sun.misc.Unsafe.unpark

多少のオーバヘッドがあり、起動時間が増加しています。hprofではJavaメソッドレベルでのプロファイル結果となり、ネイティブ側の詳細は計測できません。

cpu=timesは遅すぎて使い物になりませんし、JBoss EAP 6を計測対象にした場合はcpu=timesのプロファイルではクラスロードの挙動が変わってしまい、クラスローディングに失敗してまともに起動しなくなると思います。

perf

perfはLinuxの性能解析ツールです。

$ sudo yum install perf -y
$ perf record ./standalone.sh
15:56:35,771 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: JBoss EAP 6.2.0.GA (AS 7.3.0.Final-redhat-14) started in 1339ms - Started 129 of 186 services (56 services are passive or on-demand)
$ perf report

perfではJavaメソッドは見えません。ネイティブコードやJava VMのコードなどJavaメソッド以外の部分のパフォーマンスは見られます。

パフォーマンス劣化はほぼありません。

oprofile-jit

oprofileはLinuxの定番の性能解析ツールですが、oprofile-jitを使うとJITコンパイルされたJavaメソッドもプロファイルできます。インタプリタ実行されているJavaメソッドはプロファイルできないので、起動処理やライフタイムの短いアプリケーションのプロファイルには向きません(Thanks to @YaSuenag)。Java VMオプションに-agentlib:jvmti_oprofileを指定して有効化します。データは実行ディレクトリのperf.dataに保存されます。

$ sudo yum install oprofile-jit -y
$ vi ./standalone.conf
$ operf ./standalone.sh
16:25:22,161 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: JBoss EAP 6.2.0.GA (AS 7.3.0.Final-redhat-14) started in 1750ms - Started 129 of 186 services (56 services are passive or on-demand)
$ opreport -l | head
Using /home/nekop/usr/local/jboss-eap-6.2.0/bin/oprofile_data/samples/ for samples directory.
warning: /no-vmlinux could not be found.
warning: [vdso] (tgid:25791 range:0x7fff2a5fe000-0x7fff2a5fffff) could not be found.
CPU: Intel Sandy Bridge microarchitecture, speed 3.4e+06 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (No unit mask) count 100000
samples  %        image name               app name                 symbol name
15978    13.5321  25791.jo                 java                     Interpreter
9328      7.9001  no-vmlinux               java                     /no-vmlinux
5271      4.4641  libjvm.so                java                     SymbolTable::lookup_only(char const*, int, unsigned int&)
3310      2.8033  libz.so.1.2.8            java                     inflate_fast
1332      1.1281  libjvm.so                java                     Symbol::equals(char const*, int) const
1224      1.0366  libz.so.1.2.8            java                     inflate
1136      0.9621  libjvm.so                java                     ClassFileParser::parse_method(constantPoolHandle, bool, AccessFlags*, typeArrayHandle*, typeArrayHandle*, typeArrayHandle*, Thread*)
$ opreport -l | head -n 50 | grep java.lang
Using /home/nekop/usr/local/jboss-eap-6.2.0/bin/oprofile_data/samples/ for samples directory.
warning: /no-vmlinux could not be found.
warning: [vdso] (tgid:25791 range:0x7fff2a5fe000-0x7fff2a5fffff) could not be found.
434       0.3676  25791.jo                 java                     boolean java.lang.String.equals(java.lang.Object)
303       0.2566  25791.jo                 java                     int java.lang.String.hashCode()

ネイティブに加えてJITコンパイルされたJavaメソッドも見えるのが良いですね。JITコンパイルされる前のインタプリタ実行はInterpreterというエントリとして見えます。

起動時間を見ると元に比べ130-140%くらいに増加しており、少しオーバヘッドがあります。operfコマンドに関係なくJava VMオプションに-agentlib:jvmti_oprofileが追加されているだけでもオーバヘッドが発生するので、プロファイル以外では削除しましょう。

NetBeans Profiler / VisualVM

これら2つはモノは一緒です。NetBeansからトラブルシューティング関連機能を抜き出したのがVisualVMです。

VisualVMでは起動時からアタッチすることができないので、起動からプロファイルしたい場合はデバッガを併用しなければなりません。NetBeans Profilerなら大丈夫でしょう。

そしてNetBeans Profilerを使ってみようと思ってNetBeans 7.4をダウンロードしたんですが起動しないのでポイしました。

$ ./netbeans 
Xlib:  extension "XInputExtension" missing on display ":0".

Eclipse TPTP

めんどくさくなってきたのでもういいよね。

まとめ

YourKit買いましょう。YourKitであればパフォーマンス劣化の少ないサンプリング実行するだけでもコールスタックの閲覧、メモリの推移、スレッドの推移やCPUの状況など幅広く拾ってくれてスナップショットにまとめてくれて解析超簡単。

独自機能とポータビリティのジレンマ

JBoss / WildFly (全部俺) Advent Calendar 2013の18日目です。

Tomcat, Jetty, WildFly, GlassFish, Resin, WebSphere, WebLogic, Cosminexus, Interstage, WebOTXなどいろいろなJavaアプリケーションサーバがありますが、仕様に準拠するための機能の他に、それぞれ独自の機能を持っていたりします。JBossでも昨日紹介したsarのような独自の機能があります。

独自の機能を考えたときに、常につきまとうのがポータビリティの問題です。不用意な独自機能はポータビリティを阻害しますし、JBossオープンソースでありポータビリティマターな信条の人が多いので当然これを嫌います。

他のアプリケーションサーバの独自機能の流量制御に依存している、デプロイメント機能にロックインされていて移行できない、というような悲しい話がたまにあるのですが、このような部分は後で困らないようにポータブルに作っておいたほうが良いです。例えば以流量制御については以前書きました。他にもアプリケーションサーバ内で実装されているバージョニングやローリングアップデート依存なんかもポータビリティ問題でよく耳にしますが、アプリケーションサーバの外に枠組み作ったほうがポータブルで安全で柔軟だと思います。

また、独自機能というのは一見ファンシーに見えますが、あとあと問題を引き起す地雷となる可能性があります。

yamadamnさんがJavaアプリケーションサーバでThreadLocal利用時の注意点というエントリを書いていて、TomcatのThreadLocalリークを防ぐ独自機能に触れてWebLogicにも欲しい、とおっしゃっていますが、この機能の存在、そしてデフォルトで有効になっているのは個人的には悪だと思います。本来これはアプリケーションで解放漏れしているのが問題なので、コンテナがその問題を無理やりどうにかしてしまうことで、元の問題がいつまで経っても修正されないという不幸がうまれます。そしていつまでもアプリが修正されないまま他のアプリケーションサーバに移行しようとしたとたんにメモリリークがもりもり発覚する、という不幸も待っています。もちろんアプリとは関係ない運用者には役立つ機能だと思うので、デフォルトはThreadLocalリークの警告のみにして解放機能はOFF、設定でONにできる、という状態のほうが望ましいのではないかと思います。TomcatではThreadLocalリークの最終的な対応としてアプリケーションのundeploy時にスレッドプールを破棄する、ということもしているみたいなのですが、これも本来あるべき姿ではないのでもんにょりします。意味論は置いといてundeployなんてそんなにしないから影響もほとんどないだろうし実装としてアリだという判断も納得できるのですが、クソの上にクソを重ねる感もあります。ジレンマですね。

スレッドがプールされる環境で動作するアプリケーションでは、アプリケーションで利用したThreadLocalはアプリケーションの終了時に参照解放しましょう。以上。

さておき、アプリケーションサーバ実装としては魅力も出さないといけないわけですし、独自機能というのはてっとりばやい手段なのですが、JBoss的にはこの部分でユーザがロックインされてしまうようなものはなるべく避けたい、という思いもあるので難しいところです。アプリケーションサーバとしてのコアの成熟、拡張性、オープンであること、管理運用性の向上というあたりでがんばっています。

JBoss EAP / WildFlyで起動時の処理

JBoss / WildFly (全部俺) Advent Calendar 2013の17日目です。

起動時や停止時になにか処理を実行したいという要件はチラホラあります。そういうときに使える拡張ポイントとなるコンポーネントを見ていきます。

標準で利用できるのは上の三つです。JBoss EAPWildFlyではこれに加えて下のふたつ、sarと呼ばれるMBeanをパッケージングしたものや、ふつうのPOJOインスタンス化してメソッドを実行することもできます。

sarやpojoについてはas7-hello-serviceというのをサンプルとして以前作っています。両方とも基本的に以下の4つのライフサイクルメソッドを定義したクラスを作成すればOKです。

  • create
  • start
  • stop
  • destroy

sarの場合はMBeanインタフェースと合わせてMETA-INF/jboss-service.xmlを含めてjarにパッケージングします。MBeanServerに登録されるので、運用系のツールを書くのにとても便利です。sarはJBoss独自のデプロイメント拡張ですが、Singleton EJBを使ったMBean登録処理などに簡単に変換できるので、ポータビリティ的にも問題になりにくい拡張です。

pojoMETA-INF/jboss-beans.xmlと一緒にパッケージングします。こちらはライフサイクルメソッド名を変更できます。sarのJMX経由のような外部から操作するインタフェースはpojoにはありません。こちらも独自要素はデプロイメントディスクリプタであるjboss-beans.xmlしかないので、sarと同じように標準にすぐ移行できます。

アプリがデプロイされる前後、アプリ内ではなく外で処理を行いたいという場合はデプロイメント同士のデプロイ順序制御を使えばデプロイメントの依存を定義できます。ここはポータビリティ的にはグレーなので、なるべくアプリに閉じた構成のほうが良いです。

全部入りEJBをGlassFish 4.0で動かす

JBoss / WildFly (全部俺) Advent Calendar 2013の15日目です。

ちょっと脇道にそれて全部入りEJBGlassFish 4.0で動かしてみます。デプロイするモノは一緒です。

GlassFishのクライアントはInitialContextを引数なしでnewしてOKということになっているのですが、Thread context class loaderからgetResource("jndi.properties")するとglassfish-naming.jarが持っているようです。これはjndi.propertiesがクラスパス上他にも存在していたりするとハマりそうですね。

jar:file:/home/nekop/tmp/glassfish4/glassfish/modules/glassfish-naming.jar!/jndi.properties

その中身をチラっと追うとデフォルトでRMI-IIOP使うようです。RMI-IIOPなのでJBoss EAPと同じようにパフォーマンスを見るとやはりそれほど速くはないですね。良い子はJAX-RS使いましょう。

 14.860000   1.970000  16.830000 ( 11.275000)

JAX-WSは問題なく動きます。

JAX-RSアノテーションがインタフェースに定義されているのが関連しているのか、実行時に以下のようにインタフェースにコンストラクタが無いというエラーになり正常に実行できません。デプロイ時は問題なさそうです。自分のてきとーに書いたコードが悪いのか、GlassFishの問題なのかの切り分けはしていません。

[2013-12-16T16:48:22.547+0900] [glassfish 4.0] [WARNING] [] [javax.enterprise.web] [tid: _ThreadID=23 _ThreadName=http-listener-1(5)] [timeMillis: 1387180102547] [levelValue: 900] [[
  StandardWrapperValve[com.github.nekop.examples.HelloApplication]: Servlet.service() for servlet com.github.nekop.examples.HelloApplication threw exception
java.lang.NoSuchMethodException: Could not find a suitable constructor in com.github.nekop.examples.HelloLocal class.
	at org.glassfish.jersey.internal.inject.JerseyClassAnalyzer.getConstructor(JerseyClassAnalyzer.java:182)
	at org.jvnet.hk2.internal.Utilities.getConstructor(Utilities.java:155)
	at org.jvnet.hk2.internal.Utilities.justCreate(Utilities.java:817)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.create(ServiceLocatorImpl.java:772)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:862)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:855)
	at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:173)

ここまで確認して一旦終わりにします。

JBoss EAP 6.2全部入りEJBでネットワークトラフィックを見る

JBoss / WildFly (全部俺) Advent Calendar 2013の14日目です。

もはや誰得情報になってきていますが全部入りEJBのリモート呼び出しのトラフィックをキャプチャして見ています。

取得は以下のコマンドでEJBを各インタフェースで呼び出してCtrl-cで停止します。

$ sudo tcpdump -s 65535 -i lo -w lo.tcpdump

閲覧はWiresharkで。中身貼っても面白くはないと思うのでコネクション上のペイロードサイズだけまとめます。

  • native protocol
    • 805 bytes (sent 379 bytes, received 426 bytes)
  • RMI-IIOP
    • lookupとEJBメソッドコールで2つコネクション使う、合計 1707 bytes
    • 722 bytes (sent 417 bytes, received 305 bytes)
    • 985 bytes (sent 618 bytes, received 367 bytes)
  • JAX-WS
    • WSDLの取得、実際のリクエストで2つコネクション使う、合計 3459 bytes
    • 2597 bytes (sent 182 bytes, received 2415 bytes)
    • 862 bytes (sent 498 bytes, received 364 bytes)
  • JAX-RS
    • 363 bytes (sent 224 bytes, received 139 bytes)

JAX-RSが一番ペイロードが小さい。JAX-RSステキ。