2016-11-23 1 views
0

Ich habe eine REST-API, die Spring ThreadPoolTaskExecutor verwendet. Die API sendet abrufbare Jobs intern an ThreadPoolTaskExecutor, der einen Aufruf an einen Dritten tätigt. Sobald der Anruf beendet ist, wird die Geschäftslogik ausgeführt und die Ergebnisse werden an den Anrufer zurückgegeben.Spring: Erstellen eines wirklich skalierbaren Threadpools mit ThreadPoolTaskExecutor

Der Code funktioniert gut, aber wenn die Last steigt, wird die Leistung wirklich schlecht. Ich vermute, dass dies das Ergebnis der Threadpoolgröße für ThreadPoolTaskExecutor ist. Sagen wir also, wenn die gleichzeitigen Benutzer n sind, aber wir nur x Anzahl der Threads haben (x ist kleiner als n), dann muss der x-Thread unnötig auf eine begrenzte Anzahl von Threads warten, um ihre Anfrage zu verarbeiten.

Ich möchte die Third-Party-Aufrufe parallel verarbeiten, aber möchte nicht einen Thread-Pool mit einer großen Anzahl von Threads erstellen.

Optionen für mich ist Executors.newFixedThreadPool (y) zu verwenden. Verwenden Sie es innerhalb der Methode und sobald der Prozess abgeschlossen ist, schließen Sie das Objekt. Dies ist möglich, aber nicht sicher über Nebenwirkungen wie Ist es eine gute Praxis, einen festen Thread-Pool aus der Methode zu erstellen.

Eine andere Option könnte darin bestehen, eine Art Objektpool wie GenericObjectPoolConfig zu verwenden und damit die Threads abzurufen.

Eine andere Option könnte sein, die maximale Poolgröße auf Integer.max zu setzen und die Warteschlangenkapazität auf 1 zu reduzieren. So wird jedes Mal, wenn eine neue Anfrage kommt und das Objekt in der Warteschlange gespeichert wird, ein neuer Thread erstellt.

Es wird hilfreich sein, wenn jemand seine Gedanken teilen kann.

@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

Vielleicht ist der erste Schritt für Ihre ist, einige Instrumente zu tun und zu bestätigen, ob Ihr Verdacht richtig ist - _Ich vermute, dass dies das Ergebnis der Thread-Pool-Größe_ ist.Sie können sich Lösungen ansehen, nachdem Sie bestätigt haben, was der Engpass ist. – jay

+0

Thread-Pool-Größe ist der Grund. Erhöhung der Größe, verbessert die Leistung. – Suchit

Antwort

0

Ich denke wirklich, dass das, was Sie haben, gut aussieht. Alles, was Sie tun müssen, sind gute Leistungstests, um verschiedene Werte der Warteschlangenkapazität und der Poolgröße auszuprobieren. Und für jeden anderen Bereich.

Mit diesem gesagt, gibt es einige allgemeine Richtlinien, denen Sie folgen können. Wenn beispielsweise die Threads CPU-intensive Berechnungen ausführen, möchten Sie möglicherweise die Poolgröße auf 2 * Anzahl der Kerne oder irgendwo in dieser Umgebung beschränken. Wenn die Threads mit IO-intensiven Aufgaben arbeiten (z. B. mit externen APIs kommunizieren), können Sie höher gehen. In meinen eigenen Tests in der Vergangenheit habe ich festgestellt, dass Return-on-Investment bei etwa 50 Threads geht, wenn IO-intensive Aufgaben auf einem Duo-Core Intel durchgeführt werden. Nach 50 würde ich keine Leistungssteigerung mehr sehen. Aber bitte testet das selbst und stellt sicher, dass die CPUs sich ständig verbessern. Nach einiger Zeit kostet das Hinzufügen von mehr gleichzeitigen Threads aufgrund von Kontextwechsel zu viel.

Erwägen, zu duplizieren, was diese Threads tun werden, möglicherweise Remote-Stubs verwenden, die die Latenz der tatsächlichen APIs nachahmen, die Sie aufrufen.

Bedenken Sie auch, dass Sie nachgelagerte Systeme bis zu ihrem Bruchpunkt treiben könnten. Bei mindestens einer Gelegenheit haben wir einen sehr parallelen Code geschrieben, nur um herauszufinden, dass die nachgelagerte externe API, die wir trafen, nicht so parallel war und einen Ausfall für ihre anderen Kunden während unserer Hauptnutzungen verursachte. Wir haben die API versehentlich DOS-DOS.

Bitte profilieren Sie Ihr System und prüfen Sie, ob die CPU oder der Speicher oder die Festplatte belastet sind. Wenn die Speicher-, CPU- und Festplatten-IO in Ordnung sind, treffen Sie möglicherweise Downstream-Einschränkungen. Bei einer Gelegenheit haben wir während eines hochparallelen Codes zu viel protokolliert, und der Datenträger IO war unser Flaschenhals.

Verwandte Themen