この記事は主に開発者向けの内容です。
この記事は2014年1月に作成したもので、最新の情報ではありません。特にTwitPaneおよびTwitter4Jは2014年3月15日現在、SPDYだけでなくHTTP/2.0にも対応しています。
Twitter4J を次世代 HTTP の SPDY に対応させてみました。
拙作の Twitter クライアント TwitPane では2013年12月リリースの Ver.3.3.1 より SPDY に対応しました。
本ページでは TwitPane で利用してみた結果も踏まえて、Twitter4J の SPDY 対応方法についてまとめています(作者のブログ で随時書いていた内容のまとめ+αになります)。
Twitter4J-spdy-support
- SPDY対応版(twitter4j-spdy-support):
導入方法
Twitter4J を導入済みのアプリに対して、下記2つの jar をクラスパスに(Androidならlibsに)追加してください。
- twitter4j-spdy-support-3.0.5-SNAPSHOT.jar
- okhttp-1.2.1-jar-with-dependencies.jar
- http://square.github.io/okhttp/ からダウンロードできます
あとは twitter4j が自動的に(この jar に含まれる) "twitter4j.internal.http.alternative.HttpClientImpl" を見つけて使ってくれます。
対応環境
- Twitter4J および OkHttp の対応環境に依存します
- 但し、Android の場合 SPDY は Android 4.1 以降で対応します(Android 4.0 以前は HTTP/1.1 でアクセスします)
- Java の場合は npn-boot が必要です(後述)
詳細な使い方とTips
SPDY通信の確認方法
Twitter4J の SPDY 拡張(twitter4j-spdy-support)を利用しても本当に SPDY 通信してるか不安ですよね。
一応下記のようなコードで Twitter オブジェクト(twitter) から SPDY のコネクションプール数を取得できます(リフレクションを利用しているので Twitter4J Ver.3.0.5 以降の版では動作しなくなる可能性があります)。
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);
final HttpClientWrapper wrapper = (HttpClientWrapper) f1.get(twitter);
final Field f2 = HttpClientWrapper.class.getDeclaredField("http");
f2.setAccessible(true);
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);
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)";
}
final String lastRequestTransport = http.getLastRequestTransport();
if (lastRequestTransport != null) {
message += "\nOkHttp-Selected-Transport:[" + http.getLastRequestTransport() + "]";
}
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
Log.d(TAG, message);
SPDYをオフにする設定
twitter4j.internal.http.alternative.HttpClientImpl.sPreferSpdy を false に設定することで SPDY 通信を強制的にオフにすることが出来ます。
OkHttp 単体を利用する場合の SPDY 通信の確認方法
Twitter4J ではなく OkHttp 自体を使う場合は HttpURLConnection からレスポンスヘッダーを見ると下記のようなカスタムヘッダーが取れます。
OkHttp-Received-Millis:[1388254484917]
OkHttp-Response-Source:[NETWORK 200]
OkHttp-Selected-Transport:[http/1.1]
OkHttp-Sent-Millis:[1388254484817]
SPDY 通信をしている場合は OkHttp-Selected-Transport が spdy/3 などになります。
Java(非Android)で使うときの設定
Java/Windows環境ではJVMにnpn-bootが必要です。
Jetty/Feature/NPN - Eclipsepedia からダウンロードできます。
java -Xbootclasspath/p:<path_to_npn_boot_jar> ...
簡易ベンチマーク(Androidの場合)
並列実行パターン
showStatus を 100ms ずつずらして11回リクエストした結果です。
GalaxyNexus SC-04D/Android 4.2:
| 環境 | 試行1回目(平均) | 2回目 |
|---|---|---|
| no SPDY, 3G | 2095ms | 1980ms |
| SPDY, 3G | 1088ms(48%高速) | 1398ms(29%高速) |
| no SPDY, WiFi | 654ms | 606ms |
| SPDY, WiFi | 527ms(19%高速) | 505ms(17%高速) |
測定に使ったコード: https://gist.github.com/takke/8143276
シーケンシャル実行パターン
| 環境 | 平均所要時間 |
|---|---|
| no SPDY, 3G | 1409ms |
| SPDY, 3G | 780ms(45%高速) |
| no SPDY, WiFi | 689ms |
| SPDY, WiFi | 452ms(34%高速) |
Android版Twitter4Jの注意
Android/Dalvik 環境で Twitter4J を利用する場合、とあるバグ の回避のために HTTP/1.1 の KeepAlive が無効になります。そのため、通信のたびに再接続するというオーバーヘッドがあります。
SPDY 版ではこのロスを回避できるため、比較的大きな改善になっています。
まとめ
TwitterのAPIはいずれも重く、最短でも300ms~500ms程度はかかるので、SPDY による高速性は相対的に低く感じられます。
ただ、iOS向けのTwitter公式アプリでは SPDY がデフォルトで有効になっており、通信遅延を最大30%削減できるとされています。
TwitPaneのようにタブベースで複数のリクエストを同時に投げるシチュエーションでは、1本の TCP セッションを共有できるSPDYの恩恵は多少なりとも得られると考えています。