概要
java.net.http.HttpClient
を利用してHTTP通信を行う実装を行った際、処理が集中すると IOException: Too many open files
というエラーが発生してしまうことがあります。
それほど大した負荷でもないのに、この例外が発生してしまった場合、以下の2つに問題がある可能性が考えられます。
- OSの設定
- HttpClientのインスタンスの持ち方
それぞれ、どのように解消すれば良いか、以下に記述します。
1. OSの設定を見直す
Linux OSにおいて、ファイルディスクリプタの上限が低く抑えられている可能性があります。この上限値を増やす、または取り払うことで問題が解消されるかもしれません。
詳細は他記事に委ねます。
2. HttpClientインスタンスをキャッシュする
HttpClientは、デフォルト実装をHttpClient.newBuilder().build()
によって生成することが出来ますが、通信の度に生成するのではなく、シングルトンとして使い回す方法が適切な様です。
インスタンスを使い回して問題ないことはドキュメントに記載があります。
HttpClientを使用して「リクエスト」を送信し、「レスポンス」を取得できます。 builderを介して HttpClientが作成されます。 ビルダーを使用して、クライアントごとに次のような状態を構成できます: 優先プロトコル・バージョン(HTTP/1.1またはHTTP/2)、リダイレクトの有無、プロキシ、認証プロバイダなどがあります。一度ビルドされると、HttpClientは不変になり、複数のリクエストの送信に使用できます。
ここで「不変になる」ということは、HttpClient自体がステートレスであり、通信の都度インスタンス生成する方針でも問題ないように見えます。
しかしながら、HttpClientのデフォルト実装を見てみると、コネクションをプールしている実装であることが分かります。
private final ConnectionPool connections;
実際にローカルPCで動作検証したところ、ソケット利用数に明確な差が現れました(スレッド数は1)
- HttpClientインスタンスを使い回す場合、ソケット利用数が30程度で上限を迎える
- HttpClientインスタンスを都度生成する場合、ソケット利用数が250程度まで上昇
この挙動を確認する限り、やはりシングルトンとして使い回す方法が適切であると結論付けられます。