Hatena::ブログ(Diary)

某ソフト作者の開発日記 このページをアンテナに追加 RSSフィード

2013-12-29

Twitter4J+SPDYをJava(非Android)で使うときの設定

※この記事のまとめは Twitter4JのSPDY対応について – TwitPane をご参照ください。

昨日の記事Twitter4J+SPDYSPDY通信しているか確認できると書いたのですが、非Android環境というか、Eclipseやコンソール上のjavaコマンドから実行するとどうにもSPDY通信してくれないので困っていました。

その後いろいろと調査した結果、どうやらJVMにnpn-bootというものが必要らしいです。


Jetty/Feature/NPN - Eclipsepedia

To enable NPN support, you need to start the JVM with:

java -Xbootclasspath/p:<path_to_npn_boot_jar> ...

肝心の npn_boot_jar も上記リンクからダウンロードできます(「もうjettyのバージョンに合わせてないのでファイル名のバージョンではなく日付に注目してね」って書いてあるので注意)。

NPNの仕組みはJVMが標準でサポートしていないということなのかな。

むしろAndroid/Dalvik VM*1が標準でサポートしているところがさすがGoogle製といったところですかね。

D:\(略)>java -version
java version "1.7.0_04"
Java(TM) SE Runtime Environment (build 1.7.0_04-b20)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)


D:\(略)>"C:\Program Files\Java\jdk1.7.0_04\bin\java" -Xms30m -Xmx30m -Xbootclasspath/p:..\npn-boot-8.1.2.v20120308.jar -classpath (中略) twitter4j.examples.tweets.ShowStatusBenchmark
start
 [1357ms]
 [160ms]
 [176ms]
average:[564ms]
rate limit:[159/180], [687sec]
SPDY : [1/1]

たぶん java で SPDY やってる人には常識なんでしょうけど、ググラビリティが低くて2時間も悩んでしまったのでどなたかの参考になれば。


Java/WindowsだとSPDY使うと3%〜5%ほど速いっぽい

※この記事のまとめは Twitter4JのSPDY対応について – TwitPane をご参照ください。

http://gyazo.com/954576b109ac8b2deb5fa448048dd3eb.png

http://gyazo.com/f92ee3e47b22aff8e26a0a2a92153b1e.png

*1:4.1以降

2013-12-28

Twitter4J+SPDYでSPDY通信してるかどうかの判定方法

※この記事のまとめは Twitter4JのSPDY対応について – TwitPane をご参照ください。

昨日の記事で公開したTwitter4JのSPDY拡張(twitter4j-spdy-support)ですが、本当にSPDY通信してるか心配ですよね。

色々調べてみたところ、一応下記のようなコードで Twitter オブジェクト(twitter)から SPDY のコネクションプール数を取得できることが分かりました。


String message = "SPDY : ";
if (!twitter4j.internal.http.alternative.HttpClientImpl.sPreferSpdy) {
    message += "Disabled";
} else {
    message += "Enabled(";
    try {
        final Class<?> clazz = Class.forName("twitter4j.TwitterBaseImpl");
        final Field f1 = clazz.getDeclaredField("http");
        f1.setAccessible(true);
        
        // wrapper = twitter.http
        final HttpClientWrapper wrapper = (HttpClientWrapper) f1.get(twitter);
        final Field f2 = HttpClientWrapper.class.getDeclaredField("http");
        f2.setAccessible(true);
        
        // http = wrapper.http
        final twitter4j.internal.http.alternative.HttpClientImpl http = 
                (twitter4j.internal.http.alternative.HttpClientImpl) f2.get(wrapper);
        final Field f3 = http.getClass().getDeclaredField("client");
        f3.setAccessible(true);
        
        // client = http.client
        final OkHttpClient client = (OkHttpClient) f3.get(http);
        
        if (client != null) {
            final ConnectionPool p = client.getConnectionPool();
            message += "SPDY[" + p.getSpdyConnectionCount() + "], ";
            message += "HTTP[" + p.getHttpConnectionCount() + "])";
        } else {
            message += "no connections yet)";
        }
    } catch (Exception e) {
        Log.e(TAG, e.getMessage(), e);
    }
}
Log.d(TAG, message);

Twitter.getHomeTimeline() などを呼び出したあとに ConnectionPool.getSpdyConnectionCount() が1以上であれば正しくSPDY通信していると判定できます。

ちなみに TwitterFactory.getInstance() で Twitter オブジェクトを作った単位でプールが共有されます。通信のたびに TwitterFactory.getInstance() していると SPDY の意味がないので注意が必要です。

あと上の例のように twitter4j.internal.http.alternative.HttpClientImpl.sPreferSpdy を false に設定することで SPDY 通信を強制的にオフにすることが出来ます。これで、SPDY通信をオプション化することができます(拙作のTwitPaneでSPDY切替をするためにこの仕組みを入れました)。

ちなみに OkHttp なら・・・

Twitter4J ではなく OkHttp 自体を使う場合は (OkHttpClient.open で取得した) HttpURLConnection でレスポンスヘッダー見れば下記のようなカスタムヘッダーが取れるので判定できます。

OkHttp-Received-Millis:[1388254484917]
OkHttp-Response-Source:[NETWORK 200]
OkHttp-Selected-Transport:[http/1.1]
OkHttp-Sent-Millis:[1388254484817]

2013-12-27

Twitter4J を SPDY 対応してみた

※この記事のまとめは Twitter4JのSPDY対応について – TwitPane をご参照ください。

Twitter4Jを次世代HTTPSPDYに対応してみた。

OkHttpというSPDY&HTTP/2.0対応の超便利ライブラリがあったのでそれを使わせてもらっただけ。


でも後述の通りベンチマーク取ってみたら単独スレッドでの通信では速くないというかむしろ遅くなるんで、あくまでも実験的な実装ということで。

Android/Dalvikだと TFJ-296 対応で強制的に http.keepAlive=false されていてコネクションプールが機能していませんでしたorz

⇒OkHttpのコネクションプールを別途設定することで高速化しました! (詳細は後ほど)


導入方法

下記2つのjarをクラスパス上に(Androidならlibsに)配置するだけ。

あとはtwitter4jが自動的に"twitter4j.internal.http.alternative.HttpClientImpl"を見つけて使ってくれる感じ。Twitter4Jよくできてる。


簡易ベンチマーク(並列実行)

拙作のTwitterクライアントTwitPaneに入れてベンチマーク取ってみた。

showStatusを100msずつずらして11回*1リクエストした結果。SPDY版がいい感じで速いです。

GalaxyNexus SC-04D/Android 4.2

環境 試行1回目(平均所要時間)2回目
no SPDY, 3G 2095ms1980ms
SPDY, 3G 1088ms(48%高速)1398ms(29%高速)
no SPDY, WiFi/B-Flets654ms606ms
SPDY, WiFi/B-Flets 527ms(19%高速)505ms(17%高速)

うまく並列化させると20%くらいは速くなりそうな感じですかね。

http://gyazo.com/8eff90cd42f8a821c862505053f5b6a8.png

測定に使ったコード: https://gist.github.com/takke/8143276


簡易ベンチマーク(シーケンシャル実行)

先ほどのshowStatusのコードを単純に11回繰り返し実行するパターンもやってみました。

環境 平均所要時間
no SPDY, 3G 1409ms
SPDY, 3G 780ms(45%高速)
no SPDY, WiFi/B-Flets689ms
SPDY, WiFi/B-Flets 452ms(34%高速)

http://gyazo.com/bbca733346713b35a200bc5e725e4a16.png

シーケンシャルに取得するパターンでも高速化されました。

これは使ってみたいと思える改善っぷりですね。素晴らしいです。

*1:10回のつもりだったけどベンチマーク用に10回繰り返したあとに1回本来の処理が走ってた