Jetty 7.2.2を実行していますが、Jetty 7.2.2が処理する接続の数を制限したいので、制限(例:5000)に達すると、接続が拒否されます。
残念ながら、すべてのConnectors
は、先に進み、着信接続をできる限り速く受け入れて、構成されたスレッドプールにディスパッチします。
私の問題は、制限された環境で実行していて、8Kのファイル記述子にしかアクセスできないことです。大量の接続が着信すると、ファイル記述子がすぐに不足し、不整合な状態になる可能性があります。
HTTP 503 Service Unavailable
を返すという選択肢がありますが、それでも接続を受け入れて応答する必要があります。また、おそらくサーブレットフィルターを作成することによって、着信接続の数を追跡する必要があります。
これに対するより良い解決策はありますか?
最終的に、リクエストの数を追跡し、負荷が高すぎる場合に503を送信するソリューションを採用しました。これは理想的ではありません。ご覧のとおり、継続リクエストを常に通過させる方法を追加して、リクエストが不足しないようにする必要がありました。私のニーズにうまく機能します:
public class MaxRequestsFilter implements Filter {
private static Logger cat = Logger.getLogger(MaxRequestsFilter.class.getName());
private static final int DEFAULT_MAX_REQUESTS = 7000;
private Semaphore requestPasses;
@Override
public void destroy() {
cat.info("Destroying MaxRequestsFilter");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
long start = System.currentTimeMillis();
cat.debug("Filtering with MaxRequestsFilter, current passes are: " + requestPasses.availablePermits());
boolean gotPass = requestPasses.tryAcquire();
boolean resumed = ContinuationSupport.getContinuation(request).isResumed();
try {
if (gotPass || resumed ) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
} finally {
if (gotPass) {
requestPasses.release();
}
}
cat.debug("Filter duration: " + (System.currentTimeMillis() - start) + " resumed is: " + resumed);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
cat.info("Creating MaxRequestsFilter");
int maxRequests = DEFAULT_MAX_REQUESTS;
requestPasses = new Semaphore(maxRequests, true);
}
}
スレッドプールにはキューが関連付けられています。デフォルトでは、無制限です。ただし、スレッドプールを作成するときは、それをベースとする境界キューを提供できます。例えば:
Server server = new Server();
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(maxQueueSize);
ExecutorThreadPool pool = new ExecutorThreadPool(minThreads, maxThreads, maxIdleTime, TimeUnit.MILLISECONDS, queue);
server.setThreadPool(pool);
これで問題は解決したようです。それ以外の場合、無制限のキューを使用すると、サーバーは高負荷で起動したときにファイルハンドルを使い果たしました。
acceptQueueSize
私が正しく理解している場合、これはより低いレベルですTCP設定、サーバーアプリケーションがaccept()を受け入れるときに追跡される着信接続の数を、着信の場合よりも遅いレートで制御します接続。 http://download.Oracle.com/javase/6/docs/api/Java/net/ServerSocket.html#ServerSocket(int、%20int) の2番目の引数を参照
これは、Jetty QueuedThreadPoolでキューに入れられた要求の数とはまったく異なるものです。キューに入れられた要求はすでに完全に接続されており、スレッドがプールで使用可能になるのを待っています。その後、処理を開始できます。
同様の問題があります。私はCPUにバインドされたサーブレットを持っています(ほとんどI/Oも待機もしていないので、非同期は役に立ちません)。 Jettyプール内のスレッドの最大数を簡単に制限できるため、スレッド切り替えのオーバーヘッドを抑えることができます。ただし、キューに入れられたリクエストの長さを制限することはできません。これは、負荷が増加するにつれて、応答時間がそれぞれ増加することを意味しますが、これは私が望んでいることではありません。
すべてのスレッドがビジーで、キューに入れられた要求の数がNに達した場合、キューを永久に大きくするのではなく、それ以降のすべての要求に対して503またはその他のエラーコードを返します。
ロードバランサー(haproxyなど)を使用して、突堤サーバーへの同時リクエストの数を制限できることは承知していますが、突堤だけでそれを実行できますか?
追伸これを書いた後、Jetty DoSフィルターを発見しました。事前構成された同時実行レベルを超えた場合、503で着信要求を拒否するように構成できるようです:-)
アプリケーションにJettyをデプロイしていません。ただし、配備のために他のいくつかのオープンソースプロジェクトでJettyを使用しました。その経験に基づいて:以下のようにコネクタの設定があります:
acceptors:着信接続の受け入れ専用のスレッドの数。
acceptQueueSize:オペレーティングシステムが拒否の送信を開始する前にキューに入れることができる接続要求の数。
http://wiki.Eclipse.org/Jetty/Howto/Configure_Connectors
あなたはあなたの設定の下のブロックにそれらを追加する必要があります
<Call name="addConnector">
<Arg>
<New class="org.mortbay.jetty.nio.SelectChannelConnector">
<Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
<Set name="maxIdleTime">30000</Set>
<Set name="Acceptors">20</Set>
<Set name="confidentialPort">8443</Set>
</New>
</Arg>
</Call>
<Configure id="Server" class="org.Eclipse.jetty.server.Server">
<Set name="ThreadPool">
<New class="org.Eclipse.jetty.util.thread.QueuedThreadPool">
<!-- specify a bounded queue -->
<Arg>
<New class="Java.util.concurrent.ArrayBlockingQueue">
<Arg type="int">6000</Arg>
</New>
</Arg>
<Set name="minThreads">10</Set>
<Set name="maxThreads">200</Set>
<Set name="detailedDump">false</Set>
</New>
</Set>
</Configure>