In der Vergangenheit war die Anwendungsverarbeitung häufig ausreichend, um Ergebnisse synchron auszuführen und zu empfangen. In den letzten Jahren wurden jedoch mit dem Fortschritt mobiler Geräte wie Smartphones und Multi-Core-Prozessoren Eine asynchrone Verarbeitung, die mehrere Verarbeitungen gleichzeitig ausführen kann, ist ebenfalls erforderlich.
In Spring können Sie mithilfe der Annotation "@ Async" problemlos eine asynchrone Verarbeitung für einen anderen Thread durchführen. Lassen Sie uns überprüfen, wie eine asynchrone Verarbeitung mit "@ Async" erstellt wird.
Wenn Sie eine Anweisung in Java ausführen, erfolgt die Verarbeitung im Thread. Bei einem einzelnen Thread werden die Anweisungen nacheinander auf dem Thread verarbeitet. Eine gleichzeitige Verarbeitung ist daher nicht möglich.
Bereiten Sie daher mehrere Threads vor und führen Sie Anweisungen für jeden Thread aus. Führen Sie die Anweisung in einem anderen Thread aus, ohne darauf zu warten, dass das Ende die parallele Verarbeitung asynchron realisiert.
Spring Dependencies
Nehmen Sie Einstellungen vor, um die asynchrone Verarbeitung in der Spring Boot-Anwendung zu aktivieren. Sie müssen lediglich die Annotation "@ EnableAsync" zur Konfigurationsklasse hinzufügen (Annotation "@ Configuration" oder die Klasse mit der Annotation "@ SpringBootApplication").
@SpringBootApplication
@EnableAsync
class SimpleApplication
Fügen Sie die Annotation "@ Async" zu der Funktion hinzu, die Sie in einem anderen Thread asynchron verarbeiten möchten. Die Funktion mit dieser Annotation "@ Async" wird als asynchron zu verarbeitende Funktion behandelt, und die verarbeitete Anweisung wird in einem anderen Thread ausgeführt.
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service
import java.util.concurrent.TimeUnit
@Service("Async Task Service")
class AsyncTaskService {
val logger = LoggerFactory.getLogger(this::class.java.name)
@Async
fun standardTask() {
logger.info("Task Start")
TimeUnit.SECONDS.sleep(5)
logger.info("Task End")
}
}
Platzieren Sie einen REST-Controller, der die für die asynchrone Verarbeitung verantwortliche Klasse mit der Annotation @ Async
aufruft.
Stellen Sie den Zugriffsendpunkt auf "/ async" ein.
Dadurch kann dieser asynchrone Prozess unter "http: // localhost: 8080 / async" aufgerufen werden.
@RestController
@RequestMapping("/async")
class AsyncTaskController {
@GetMapping
fun callStandardTask() = service.standardTask()
}
Rufen wir die asynchrone Verarbeitung fünfmal hintereinander auf.
$ curl http://localhost:8080/async
$ curl http://localhost:8080/async
$ curl http://localhost:8080/async
$ curl http://localhost:8080/async
$ curl http://localhost:8080/async
Das Laufzeitprotokoll von Start und Ende jedes asynchronen Prozesses wird in der Standardausgabe wie unten gezeigt angezeigt.
2018-12-19 20:50:22.662 INFO 22121 --- [cTaskExecutor-1] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 20:51:00.545 INFO 22121 --- [cTaskExecutor-2] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 20:51:01.886 INFO 22121 --- [cTaskExecutor-3] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 20:51:04.397 INFO 22121 --- [cTaskExecutor-4] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 20:51:05.550 INFO 22121 --- [cTaskExecutor-5] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 20:51:06.887 INFO 22121 --- [cTaskExecutor-1] i.p.s.simple.service.AsyncTaskService : Normal Task End
2018-12-19 20:51:09.400 INFO 22121 --- [cTaskExecutor-2] i.p.s.simple.service.AsyncTaskService : Normal Task End
2018-12-19 20:52:03.338 INFO 22121 --- [cTaskExecutor-3] i.p.s.simple.service.AsyncTaskService : Normal Task End
2018-12-19 20:52:05.123 INFO 22121 --- [cTaskExecutor-4] i.p.s.simple.service.AsyncTaskService : Normal Task End
2018-12-19 20:52:06.290 INFO 22121 --- [cTaskExecutor-5] i.p.s.simple.service.AsyncTaskService : Normal Task End
Wenn man sich dieses Ergebnis ansieht, kann bestätigt werden, dass 5 Threads von "cTaskExecutor-1" bis "cTaskExecutor-5" erstellt und verarbeitet werden und jeder asynchron verarbeitet wird.
In dem zuvor ausgeführten asynchronen Prozess wurden Threads für die Anzahl der Aufrufe erstellt. Was ist, wenn dies eine Anwendung ist, die sehr oft aufgerufen wird? Es ist möglich, dass eine große Anzahl von Threads erstellt wird, wodurch die Systemressourcen erschöpft, die Leistung verringert und im schlimmsten Fall das System heruntergefahren wird. Es gibt viele Java EE-Anwendungsserver, mit denen die Anzahl der Threads als Plattform gesteuert werden kann. Aber was ist mit einer Spring Boot-Anwendung, die keinen Java EE-Anwendungsserver wie diesen verwendet?
Wenn Sie im Frühjahr eine asynchrone Verarbeitung durchführen, wird durch Hinzufügen der Annotation "@ Async" standardmäßig "SimpleAsyncTaskExecutor" verwendet, um einen weiteren Thread zu erstellen und eine asynchrone Verarbeitung durchzuführen.
Wenn Sie andererseits den Thread-Pool konfigurieren möchten, um die Anzahl der Threads zu steuern, konfigurieren und verwenden Sie "ThreadPoolTaskExecutor". Definieren Sie in der Konfigurationsklasse mit "@ EnableAsync".
Im Folgenden erstellen wir zwei Arten von Pools.
@SpringBootApplication
@EnableAsync
class SimpleApplication {
@Bean
fun normalTaskExecutor(): TaskExecutor = ThreadPoolTaskExecutor().apply {
corePoolSize = 1
setQueueCapacity(5)
maxPoolSize = 1
setThreadNamePrefix("NormalThread-")
setWaitForTasksToCompleteOnShutdown(true)
}
@Bean
fun prioritizedTaskExecutor(): TaskExecutor = ThreadPoolTaskExecutor().apply {
corePoolSize = 5
setQueueCapacity(5)
maxPoolSize = 5
setThreadNamePrefix("PrioritizedThread-")
setWaitForTasksToCompleteOnShutdown(true)
}
}
Legen Sie die folgenden Attribute fest, um den Thread-Pool zu konfigurieren.
Name | Inhalt |
---|---|
corePoolSize | Erstellen Sie die Anzahl der Threads bis zu diesem Einstellungswert |
setQueueCapacity | Wenn die Anzahl von corePoolSize überschritten wird, wird bis zu diesem Einstellungswert in die Warteschlange gestellt |
maxPoolSize | Bei einer Warteschlange von maximal setQueueCapacity wird die Anzahl der Threads bis zu diesem Einstellungswert erstellt |
Geben Sie den Bean-Namen von "ThreadPoolTaskExecutor" explizit mit der Annotation "@ Async" an, wie unten gezeigt. Legt fest, welcher ThreadPoolTaskExecutor asynchron ist.
@Async("prioritizedTaskExecutor")
fun prioritizedTask() {
logger.info("Prioritized Task Start")
TimeUnit.SECONDS.sleep(5)
logger.info("Prioritized Task End")
}
@Async("normalTaskExecutor")
fun normalTask() {
logger.info("Normal Task Start")
TimeUnit.SECONDS.sleep(5)
logger.info("Normal Task End")
}
Schauen wir uns das Ergebnis der asynchronen Verarbeitung an, wobei die Anzahl der Thread-Pools auf "1" gesetzt ist.
$ curl http://localhost:8080/async/normal
$ curl http://localhost:8080/async/normal
$ curl http://localhost:8080/async/normal
$ curl http://localhost:8080/async/normal
$ curl http://localhost:8080/async/normal
Wie unten gezeigt, können Sie sehen, dass die Verarbeitung nur vom Thread "NormalThread-1" ausgeführt wird. Wie angegeben, wird es mit der Anzahl der Threads "1" verarbeitet.
2018-12-19 22:30:17.654 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 22:30:22.658 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task End
2018-12-19 22:30:24.437 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 22:30:29.441 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task End
2018-12-19 22:30:29.441 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 22:30:34.442 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task End
2018-12-19 22:30:34.443 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 22:30:39.445 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task End
2018-12-19 22:30:39.446 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task Start
2018-12-19 22:30:44.455 INFO 22746 --- [ NormalThread-1] i.p.s.simple.service.AsyncTaskService : Normal Task End
Als nächstes betrachten wir die asynchrone Verarbeitung mit der Anzahl der Thread-Pools auf "5".
$ curl http://localhost:8080/async/high
$ curl http://localhost:8080/async/high
$ curl http://localhost:8080/async/high
$ curl http://localhost:8080/async/high
$ curl http://localhost:8080/async/high
Hier können Sie sehen, dass 5 Threads von "HighThread-1" bis "HighThread-5" erstellt und verwendet wurden. Wie Sie sehen, können Sie unter Berücksichtigung der Steuerung des Thread-Pools problemlos eine asynchrone Verarbeitung durchführen.
2018-12-19 22:37:55.784 INFO 22746 --- [HighThread-1] i.p.s.simple.service.AsyncTaskService : Prioritized Task Start
2018-12-19 22:37:57.469 INFO 22746 --- [HighThread-2] i.p.s.simple.service.AsyncTaskService : Prioritized Task Start
2018-12-19 22:37:57.898 INFO 22746 --- [HighThread-3] i.p.s.simple.service.AsyncTaskService : Prioritized Task Start
2018-12-19 22:37:58.956 INFO 22746 --- [HighThread-4] i.p.s.simple.service.AsyncTaskService : Prioritized Task Start
2018-12-19 22:37:59.582 INFO 22746 --- [HighThread-5] i.p.s.simple.service.AsyncTaskService : Prioritized Task Start
2018-12-19 22:38:00.787 INFO 22746 --- [HighThread-1] i.p.s.simple.service.AsyncTaskService : Prioritized Task End
2018-12-19 22:38:02.473 INFO 22746 --- [HighThread-2] i.p.s.simple.service.AsyncTaskService : Prioritized Task End
2018-12-19 22:38:02.900 INFO 22746 --- [HighThread-3] i.p.s.simple.service.AsyncTaskService : Prioritized Task End
2018-12-19 22:38:03.957 INFO 22746 --- [HighThread-4] i.p.s.simple.service.AsyncTaskService : Prioritized Task End
2018-12-19 22:38:04.586 INFO 22746 --- [HighThread-5] i.p.s.simple.service.AsyncTaskService : Prioritized Task End
Asynchrone Verarbeitung ist eine Verarbeitung, die nicht nur in Webanwendungen, sondern auch in Stapelanwendungen effektiv verwendet werden kann. Auf diese Weise kann eine asynchrone Verarbeitung durchgeführt werden, während auf einfache Weise Threads erstellt und der Thread-Pool gesteuert werden kann. Es scheint also, dass es viele mögliche Situationen gibt, in denen sie verwendet werden kann.
Recommended Posts