お久しぶりです、大谷です。
朝晩めっきり涼しくなり、少しずつ秋の気配を感じることができる日が増えてきました、いかがお過ごしですか。
シルバーウィークも近づいているので、どこか遊びに行こうかと妄想している日々が続いています。
さて、今回はSolrJの利用方法の注意点についてです。
よく、Solrを利用していて「Too many open files」が発生して困っていますという話を聞きます。(今年は3回くらいきいている)
この話を聞いたときに、毎回ハマるのですが、Solrが動作しているサーバ側で発生していると思い、
「optimize」していますか?とか「リクエスト数はどのくらいで負荷テストしていますか?」などという質問をしてしまいます。
よくよく話を聞いていく(ここまでに数回やり取りをする)と、実はSolrが動作しているサーバではなく、SolrJを利用しているクライアントアプリ側で
「Too many open files」と出力されているという話に行き着きます。そこで、いつもSolrJの利用方法に問題があることを思い出します。
では、なぜ、Solrへリクエストを投げているだけのクライアント側で「Too many open files」が発生してしまうのでしょう。
実は、SolrJのあるクラスをリクエストごとにインスタンス化していることが問題です。
「CommonsHttpSolrServer」がそのクラスになります。
SolrのWikiなどを見てもmain関数で呼び出す例だけですので詳しく書かれていません。
おそらく、この利用方法を見て、かつ、SolrJのJavaDocにも目立つところに注意点は書かれていないので、
リクエストごとに(Solrへの接続が必要になるタイミングで)毎回CommonsHttpSolrServerをnewしているのでしょう。
CommonsHttpSolrServerはApacheのHttpClientというHttp接続用のライブラリを内部で利用しており、HttpClientにあるMultiThreadConnectionManager
というConnectionを管理するクラスを利用しています。
このクラスですが、内部でConnectionをプールする機能を持っています。(http://hc.apache.org/httpclient-3.x/threading.html)
MultiThreadConnectionManagerをnewするたびに、特定の本数のConnectionが生成され、プールされていきます。ConnectionはSocketを開きますので、ファイルディスクリプタが消費されます。
このため、負荷をかけ続けたり、リクエストを処理していくと「Too many open files」が発生し始めるのです。
SolrJでは、特に指定をしない場合、Solrのホスト名に対して最大32本のコネクションが生成されます。
Linuxなどでは通常、ファイルディスクリプタを利用できる上限が1028となっています。ですので、あっという間にエラーが出始めることになるのです。
解決策は次のような方法になります。
- CommonsHttpSolrServerのインスタンスを再利用する(singletonで利用するなど)
- MultiThreadConnectionManagerを再利用する形とし、HttpClientを引数とするCommonsHttpSolrServerのコンストラクタを利用する
ということで、自戒の意味も込めますが、「「Too many open files」エラーが出たときはSolrJの利用方法に問題がないかチェックするのも必要である」というのを忘れないようにしましょう。
※拙著である、Solr入門ではSolrJのサンプルプログラムでCommonsHttpSolrServerを利用していますが、Servletのインスタンス変数として定義し、
initにて1度だけCommonsHttpSolrServerをnewしています。(もう少し詳しく注意点を書いておくべきでした。)