2016-11-23 6 views
0

Spring ThreadPoolTask​​Executorを使用するREST APIがあります。 APIは内部的に呼び出し可能なジョブを第三者に呼び出すThreadPoolTask​​Executorに送信します。コールが完了すると、ビジネスロジックが実行され、結果がコール元に返されます。Spring:本当にスケーラブルなスレッドプールをThreadPoolTask​​Executorで作成する

コードは正常に動作しますが、負荷が増加するとパフォーマンスが実際に悪くなります。私はこれがThreadPoolTask​​Executorのスレッドプールサイズの結果であると思われます。したがって、同時ユーザーがn個であるが、x個のスレッドしかない場合(xがnより小さい場合)、xスレッドは、要求を処理するスレッドの数が制限されていることを不必要に待たなければなりません。

サードパーティの呼び出しを並列に処理したいが、膨大な数のスレッドを持つスレッドプールを作成したくない。

私のオプションは、Executors.newFixedThreadPool(y)を使用することです。メソッド内でそれを使用し、プロセスが完了したらオブジェクトを閉じます。これは可能ですが、そのような副作用についてはわかりません。メソッドから固定スレッドプールを作成するのがよい方法ですか。

その他のオプションは、GenericObjectPoolConfigのようなオブジェクトプールを使用し、スレッドをフェッチするために使用される場合があります。

最大プールサイズをInteger.maxに設定し、キューの容量を1に減らすこともできます。キューにオブジェクトを格納する代わりに新しい要求が来るたびに、新しいスレッドが作成されます。

ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); 
    threadPoolTaskExecutor.setCorePoolSize(20); 
    threadPoolTaskExecutor.setMaxPoolSize(Integer.MAX_VALUE); 
    threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); 
    threadPoolTaskExecutor.setQueueCapacity(1); 
    threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 
    threadPoolTaskExecutor.initialize(); 

誰でも自分の考えを分かち合うことができれば助かります。

@Configuration 
public class TestConfiguration{ 

    @Bean 
public ConcurrentTaskExecutor concurrentTaskExecutor() { 
    ConcurrentTaskExecutor concurrentTaskExecutor = new ConcurrentTaskExecutor(); 
    concurrentTaskExecutor.setConcurrentExecutor(getExecutor()); 
    return concurrentTaskExecutor; 
} 

private Executor getExecutor() { 
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); 
    threadPoolTaskExecutor.setCorePoolSize(20); 
    threadPoolTaskExecutor.setMaxPoolSize(30); 
    threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); 
    threadPoolTaskExecutor.setQueueCapacity(75); 
    threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 
    threadPoolTaskExecutor.initialize(); 
    return threadPoolTaskExecutor; 
} 
} 

@Service 
public class TestServiceImpl{ 
    @Autowired 
    private ConcurrentTaskExecutor concurrentTaskExecutor; 

    @Override 
    @Transactional 
    public DTO getDTO() { 
    Callable<TESTDTO> test1Callable = new Test1Callable(); 
    Future<TESTDTO> testDTO1 = concurrentTaskExecutor.submit(test1Callable); 

    Callable<TESTDTO> test2Callable = new Test2Callable(); 
    Future<TESTDTO> testDTO2 =concurrentTaskExecutor.submit(test2Callable); 

    Callable<TESTDTO> test3Callable = new Test3Callable(); 
    Future<TESTDTO> testDTO3 =concurrentTaskExecutor.submit(test3Callable); 

    // Perform logic on DTO's 
    return DTO; 
    } 
+0

あなたの疑問が正しかったかどうかを確認するための最初のステップは多分でしょうか?_これはスレッドプールサイズ_の結果であると思われます。ボトルネックが何であるかを確認した後、ソリューションを見ることができます。 – jay

+0

スレッドプールのサイズが理由です。サイズを大きくすると、パフォーマンスが向上します。 – Suchit

答えて

0

私は本当にあなたが持っているものがよく見えると思います。キュー容量とプールサイズのさまざまな値を試してみるために、パフォーマンステストを行うだけです。そして、そのことについては、他の分野です。

このように、あなたが従うことができる一般的なガイドラインがいくつかあります。たとえば、スレッドがCPU集約的な計算を行っている場合は、プールサイズを2 *コア数またはその近傍のどこかに制限することができます。スレッドがIO集約的なタスク(外部APIとの通信など)を処理している場合は、より高いレベルに進むことができます。過去の私自身のテストでは、デュアルコアのインテルで集中的なIOタスクを実行するときに、約50スレッドの投資収益率を達成したことに気付きました。 50後、私はパフォーマンスの向上を見るのをやめます。しかし、これを自分でテストし、CPUが常に改善していることを確認してください。いくつかのポイントを追加した後、より多くの並列スレッドはコンテキスト切り替えのために余りにも多くのコストを要します

実際に呼び出すAPIのレイテンシを模倣するリモートスタブを使用して、これらのスレッドが何をするかを複製することを検討してください。

さらに、ダウンストリームシステムをブレークポイントにプッシュしている可能性があると考えてください。少なくとも1つの機会に、非常に並列化されたコードを書いて、私たちが打っていた下流の外部APIが並行ではなく、ピーク時の使用中に他の顧客のための停止を引き起こしていたことを知りました。私たちは誤ってAPIをDOSしました。

システムをプロファイリングして、CPU、メモリ、またはディスクIOが緊張しているかどうかを確認してください。メモリ、CPU、ディスクIOが正常であれば、下流の制約を打つかもしれません。私たちはいくつかの非常に並列なコードの間にあまりにも多くのログを記録していましたが、ディスクIOはボトルネックでした。

関連する問題