1. rejasupotaro

    Posted

    rejasupotaro
Changes in title
+OkHttpとCache-Control
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,91 @@
+# OkHttpとは
+
+[OkHttp](http://square.github.io/okhttp/) はSquareが開発しているHTTPクライアントのライブラリで、HTTP、HTTPS、HTTP/2、SPDY、WebSocketに対応していて、同期/非同期のAPIを提供しています。
+OkHttpはデフォルトでGZIPに対応していたり、コネクションプールやキャッシュの制御をしたり、シンプルで効率良く通信できることを売りにしています。また、HttpURLConnectionやApache HttpClientからマイグレーションしやすいように、それらと同じインタフェースを提供するモジュールも用意されています。
+
+今回はOkHttpとCache-Controlについて書きます。
+
+# Cache-Controlとは
+
+HTTP/1.1で定められているリソースをどのようにキャッシュするかの取り決めです。リクエストとレスポンスが取りうる値は以下のようになっています。
+
+```
+cache-request-directive =
+ "no-cache"
+ | "no-store"
+ | "max-age" "=" delta-seconds
+ | "max-stale" [ "=" delta-seconds ]
+ | "min-fresh" "=" delta-seconds
+ | "no-transform"
+ | "only-if-cached"
+ | cache-extension
+```
+
+```
+cache-response-directive =
+ "public"
+ | "private" [ "=" <"> 1#field-name <"> ]
+ | "no-cache" [ "=" <"> 1#field-name <"> ]
+ | "no-store"
+ | "no-transform"
+ | "must-revalidate"
+ | "proxy-revalidate"
+ | "max-age" "=" delta-seconds
+ | "s-maxage" "=" delta-seconds
+ | cache-extension
+```
+
+たくさんのディレクティブが出てきましたが、OkHttpではno-cache、no-store、max-age、only-if-cachedが重要な働きをしています。
+
+- no-store
+このヘッダは、サーバから返されてくるデータをキャッシュに保存しないという指示です。
+
+- no-cache
+キャッシュが保存されている場合でもサーバに対して、現在でもキャッシュが有効か問い合わせる指示です。
+
+- max-age
+キャッシュの有効な時間を表します。
+
+- only-if-cached
+キャッシュからのみデータを取得するという指示です。
+
+# OkHttpのCache-Controlの振る舞い
+
+## レスポンス
+
+Railsでは `expires_in` や `expires_now` でキャッシュをどう扱うかを指定します。
+
+```ruby
+expires_in(1.hour, public: true) # => Cache-Control: max-age=3600, public
+expires_now # => Cache-Control: no-cache
+```
+
+これらを受け取った場合にOkHttpがどういう挙動をするかを見てみます。
+
+[HttpEngine](https://github.com/square/okhttp/blob/master/okhttp%2Fsrc%2Fmain%2Fjava%2Fcom%2Fsquareup%2Fokhttp%2Finternal%2Fhttp%2FHttpEngine.java) というクラスがOkHttpのコアになっています。Cache-Controlヘッダはparseされて [CacheControl](https://github.com/square/okhttp/blob/master/okhttp%2Fsrc%2Fmain%2Fjava%2Fcom%2Fsquareup%2Fokhttp%2FCacheControl.java) クラスになり、[CacheStrategy](https://github.com/square/okhttp/blob/master/okhttp%2Fsrc%2Fmain%2Fjava%2Fcom%2Fsquareup%2Fokhttp%2Finternal%2Fhttp%2FCacheStrategy.java) というクラスから参照されて、実際にどのようにキャッシュするかが決められます。
+
+OkHttpではリクエストのurlをキーにして、ハッシュでレスポンスをキャッシュしています。キャッシュすると判定されたレスポンスはメモリとファイルにLRUで書き込まれます。細かい条件はありますが、大まかには
+
+- GETメソッド
+- リクエストとレスポンスが共にno-storeでない
+- max-ageが指定されている
+
+上記の場合でキャッシュが保存/利用されます。また、GET以外の操作が行われた場合にはキャッシュを破棄するということをしています。
+
+## リクエスト
+
+キャッシュを有効にするには `OkHttpClient#setCache` メソッドに [Cache](https://github.com/square/okhttp/blob/master/okhttp%2Fsrc%2Fmain%2Fjava%2Fcom%2Fsquareup%2Fokhttp%2FCache.java) クラスを渡します。そのときに書き込み先やキャッシュサイズの上限を指定することができます。
+
+```java
+File cacheDir = new File(context.getCacheDir(), CACHE_FILE_NAME);
+Cache cache = new Cache(cacheDir, MAX_CACHE_SIZE);
+okHttpClient.setCache(cache);
+```
+
+特に何も指定しないと、キャッシュが有効だった場合にはキャッシュのデータを返します。only-if-cachedを指定するとキャッシュしか見に行かなくなるので、オフライン時に指定するとよりバッテリーに優しくなります。
+キャッシュを見て欲しくない場合もあると思いますが、その場合にはno-cacheを付けるとネットワークからのみデータを取得するようになります。
+`Cache#getRequestCount` や `Cache#getNetworkCount` や `Cache#getHitCount` メソッドがあるので、実際にどれくらいキャッシュを見ているか確認することができます。
+
+# 所感
+
+Cache-Controlはお手軽だけどアプリだとprivateのデータもキャッシュしたいし、結局独自でキャッシュを持たないといけないなくなる気がして、もう一層キャッシュするレイヤーを挟むよりは、Cache-Controlを使わないで独自のキャッシュレイヤーを実装した方が良い?うーむ。