[JAVA] Untersuchte asynchrone Ausführung von Abfragen in Spring Boot 1.5.9

Überblick

Ich habe gelernt, dass es möglich ist, Repository-Abfragen mithilfe der asynchronen Verarbeitung von Spring asynchron auszuführen, und habe daher untersucht, wie sie implementiert werden können. Dieser Artikel fasst eine einfache Implementierung asynchroner Abfragen und die Ergebnisse ihrer Operation zusammen.

Umgebung

Referenz

Vorbereitung auf der Datenbankseite

Erstellen Sie eine Ansicht, deren Suche lange dauert, um die Überprüfung des Vorgangs zu vereinfachen.

CREATE OR REPLACE VIEW async_test_view (
      id
    , sleep
    , create_at ) AS
SELECT MD5(UUID()) AS id
    , SLEEP(10) AS sleep
    , NOW() AS create_at
;

Das Durchsuchen dieser Ansicht dauert ca. 10 Sekunden, um die Ergebnisse zurückzugeben.

> select * from pseudo_delay_view;
+----------------------------------+-------+---------------------+
| id                               | sleep | create_at           |
+----------------------------------+-------+---------------------+
| da863db6ff1b064ebff03f00efdd224b |     0 | 2017-12-23 17:27:08 |
+----------------------------------+-------+---------------------+
1 row in set (10.00 sec)

Implementierung auf der Spring Boot-Seite

Aktivieren Sie die asynchrone Verarbeitung und konfigurieren Sie den Thread-Pool

  1. Fügen Sie die EnableAsync-Annotation hinzu, um die asynchrone Verarbeitung zu aktivieren.
  2. Obwohl nicht erforderlich, um die asynchrone Verarbeitung zu aktivieren, implementiert dieses Beispiel die Verwendung eines Thread-Pools.
  3. Geben Sie die Größe der Aufgabenwarteschlange an. Ein inaktiver Thread verarbeitet die in der Warteschlange gespeicherten Aufgaben. Der Standardwert für ThreadPoolTaskExecutor ist Integer.MAX_VALUE.
  4. Geben Sie die Poolgröße mit CorePoolSize an. MaxPoolSize ist die maximale Anzahl.
  5. Geben Sie die Lebensdauer der inaktiven Threads über CorePoolSize an. Der Standardwert für ThreadPoolTaskExecutor beträgt 60 Sekunden.
@SpringBootApplication
// 1
@EnableAsync
public class DemoGradleApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoGradleApplication.class, args);
  }

  // 2
  @Bean
  public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

    // 3
    executor.setQueueCapacity(2);
    // 4
    executor.setCorePoolSize(2);
    executor.setMaxPoolSize(3);
    // 5
    executor.setKeepAliveSeconds(10);

    executor.afterPropertiesSet();

    return executor;
  }

}

Wenn die Poolgrößenbeschränkung erreicht ist

Wenn die Anzahl der Anforderungen MaxPoolSize + QueueCapacity überschreitet, wird standardmäßig eine Ausnahme namens RejectedExecutionException ausgelöst.

Wenn Sie eine beliebige Verarbeitung für die Ablehnungsverarbeitung ausführen möchten, wenn die Anforderung die Obergrenze erreicht, übergeben Sie die Klasse, die die RejectedExecutionHandler-Schnittstelle implementiert, an ThreadPoolTaskExecutor, wie unten gezeigt. Wenn nicht angegeben, ist der Standardwert ThreadPoolExecutor # AbortPolicy. (Löst eine RejectedExecutionException-Ausnahme aus)

executor.setRejectedExecutionHandler((r,e) -> {
  //Implementieren Sie jeden Prozess, den Sie ausführen möchten
  throw new RejectedExecutionException("Task:" + r.toString() + " rejected from " + e.toString());
});

Entität

Eine Implementierung der Entität, die der Ansicht entspricht. Es gibt keine besonderen Hinweise.

@Entity
@Table(name="pseudo_delay_view")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PseudoDelay implements Serializable {

    private static final long serialVersionUID = -9169553858944816379L;

    @Id
    private String id;
    @Column(name="sleep", nullable=false)
    private Integer sleep;
    @Column(name="create_at", nullable=false)
    private LocalDateTime createAt;

}

Repository

Eine Implementierung des Repositorys, das die Abfrage ausgibt. Implementiert die asynchrone Ausführung von Abfragen. Sie müssen lediglich die Async-Annotation zur Methode hinzufügen.

  1. Fügen Sie die Async-Annotation zu der Methode hinzu, die die Abfrage asynchron ausführt. Verwenden Sie außerdem CompletableFuture als Rückgabetyp.
  2. Dies ist eine synchrone Vergleichsmethode.
public interface PseudoDelayRepository extends JpaRepository<PseudoDelay, String> {

    // 1
    @Async
    @Query("SELECT p FROM PseudoDelay AS p")
    CompletableFuture<PseudoDelay> findAsync();

    // 2
    @Query("SELECT p FROM PseudoDelay AS p")
    PseudoDelay findSync();

}

In diesem Beispiel wird CompletableFuture verwendet, aber zusätzlich können Future und Spring ListenableFuture verwendet werden. Weitere Informationen finden Sie unter Spring Data JPA - Referenzdokumentation --3.4.7. Asynchrone Abfrageergebnisse. In kann bestätigt werden.

Überprüfen Sie, ob die Abfrage asynchron ausgeführt wird

Die Implementierung der Serviceklasse zur Bestätigung ist wie folgt.

  1. Dies ist eine Methode zum Überprüfen des Betriebs von asynchronen Abfragen.
  2. Eine Methode zum Überprüfen synchroner Abfragen zum Vergleich.
@Service
@Slf4j
public class AsyncTestServiceImpl implements AsyncTestService {

  private PseudoDelayRepository repository;

  public AsyncTestServiceImpl(PseudoDelayRepository repository) {
    this.repository = repository;
  }

  // 1
  @Transactional(readOnly = true)
  @Override
  public PseudoDelay async() {
    log.debug("start async");

    CompletableFuture<PseudoDelay> future = repository.findAsync();

    //Führen Sie etwas aus, während Sie die Abfrage asynchron ausführen
    log.debug("execute somethings");

    PseudoDelay result = null;
    try {
      //Empfangen Sie das Ergebnis der Abfrageausführung
      result = future.thenApply(res -> {
        log.debug("async result : {}", res);
        return res;
      })
      .get();

      } catch (InterruptedException | ExecutionException e) {
        throw new RuntimeException(e);
      }
    }

    log.debug("end async");
    return result;
  }

  // 2
  @Transactional(readOnly = true)
  @Override
  public PseudoDelay sync() {
    log.debug("start sync");

    PseudoDelay result = repository.findSync();
    log.debug("sync result : {}", result);

    log.debug("end sync");
    return result;
  }

}

Bestätigung des asynchronen Abfragevorgangs

Sie können sehen, dass eine andere Verarbeitung ("*** etwas ausführen ***") unmittelbar nach dem Ausgeben der Abfrage ausgeführt wird.

2017-12-23 19:55:36.194 DEBUG 5304 --- [nio-9000-exec-4] : start async
2017-12-23 19:55:36.195 DEBUG 5304 --- [nio-9000-exec-4] : *** execute somethings ***
2017-12-23 19:55:46.198 DEBUG 5304 --- [ taskExecutor-2] : async result : PseudoDelay(id=9904388341a9d8dbdfb230fb5b675224, sleep=0, createAt=2017-12-23T19:55:36)
2017-12-23 19:55:46.199 DEBUG 5304 --- [nio-9000-exec-4] : end async

Überprüfen der Funktion synchroner Abfragen

Sie können sehen, dass es blockiert ist, bis das Ergebnis der Abfrage zurückgegeben wird.

2017-12-23 19:57:49.465 DEBUG 5304 --- [nio-9000-exec-8] : start sync
2017-12-23 19:57:59.467 DEBUG 5304 --- [nio-9000-exec-8] : sync result : PseudoDelay(id=3a19a242c0207cd9ddad551ec2ccae66, sleep=0, createAt=2017-12-23T19:57:49)
2017-12-23 19:57:59.467 DEBUG 5304 --- [nio-9000-exec-8] : end sync

Geben Sie eine Zeitüberschreitung an

Sie können das Zeitlimit angeben. Wenn es nicht innerhalb der angegebenen Zeit abgeschlossen wird, wird eine TimeoutException-Ausnahme ausgelöst.

result = future.get(5, TimeUnit.SECONDS);

Informationen zum Transaktionszeitlimit

Wir haben ein Zeitlimit für die Transaktion festgelegt und untersucht, was passieren würde, wenn eine Abfrage, die länger als dieses Zeitlimit dauerte, asynchron ausgeführt würde. Dieses Mal habe ich das Transaktionszeitlimit auf 5 Sekunden festgelegt und eine Abfrage ausgeführt, die sowohl asynchron als auch synchron 10 Sekunden dauerte, wie im obigen Beispiel.

@Transactional(readOnly = true, timeout = 5)

** Für asynchrone Abfragen **

Wenn das Zeitlimit überschritten wurde, wurde keine Ausnahme ausgelöst und das erwartete Ergebnis wurde nicht erreicht. Ich kenne die Ursache nicht, aber es scheint, dass wenn Sie es in einem anderen Thread innerhalb der Transaktion ausführen, die Transaktionsverwaltung nicht mehr möglich ist. Ich habe überprüft, ob das offizielle Dokument eine Beschreibung enthält, aber ich habe sie nicht vollständig überprüft. Vorerst gab es in einem solchen Artikel Spring @ Async und Transaktionsmanagement.

** Für synchrone Abfragen **

Ich denke, es hängt von der Implementierung des JDBC-Treibers ab, aber im Fall von MySQL tritt der folgende Fehler auf.

2017-12-23 20:17:18.297 ERROR 2260 --- [nio-9000-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : Statement cancelled due to timeout or client request
2017-12-23 20:17:18.319 ERROR 2260 --- [nio-9000-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/app] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: could not extract ResultSet; nested exception is org.hibernate.exception.GenericJDBCException: could not extract ResultSet] with root cause

com.mysql.jdbc.exceptions.MySQLTimeoutException: Statement cancelled due to timeout or client request
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2827) ~[mysql-connector-java-5.1.44.jar:5.1.44]

/...Kürzung

Recommended Posts

Untersuchte asynchrone Ausführung von Abfragen in Spring Boot 1.5.9
Asynchrone Verarbeitung mit regelmäßiger Ausführung in Spring Boot
Zusammenfassung dessen, was ich über Spring Boot gelernt habe
Zusammenfassung dessen, was ich in Spring Batch gelernt habe
05. Ich habe versucht, die Quelle von Spring Boot zu löschen
Ich habe versucht, die Kapazität von Spring Boot zu reduzieren
Verwendung von CommandLineRunner im Spring Batch von Spring Boot
Geben Sie spring.profiles.active über context-param in web.xml in Spring Boot an
Legen Sie den Kontextparameter in Spring Boot fest
Spring Boot 2 Multiprojekt mit Gradle
Wichtige Änderungen in Spring Boot 1.5
NoHttpResponseException in Spring Boot + WireMock
Beschleunigen Sie das Testen von Validatoren, für die DI im Spring Boot erforderlich ist
Ausführen der Erstverarbeitung mit Spring Boot Command Line Runner
Frühlingsstiefel Hallo Welt in Eclipse
Spring Boot-Anwendungsentwicklung in Eclipse
Memorandum beim Spring Boot 1.5.10 → Spring Boot 2.0.0
Rufen Sie in Spring Boot eine Proxy-Instanz der Komponente selbst ab
Schreiben Sie den Testcode mit Spring Boot
Ich habe am JJUG CCC 2019 Spring teilgenommen
Nachricht erlöschen (Spring Boot)
Implementierung der asynchronen Verarbeitung in Tomcat
Was ich bei der Migration von der Spring Boot 1.4-Serie zur 2.0-Serie getan habe
Implementieren Sie die REST-API mit Spring Boot
Was ist @Autowired im Spring Boot?
Implementieren Sie die Spring Boot-Anwendung in Gradle
Was ich bei der Migration von der Spring Boot 1.5-Serie zur 2.0-Serie getan habe
Ich habe GraphQL mit Spring Boot ausprobiert
Ich möchte die Standardfehlermeldung von Spring Boot steuern
Ich habe Flyway mit Spring Boot ausprobiert
Ich möchte die Methode des Controllers kennen, bei der die Ausnahme im ExceptionHandler von Spring Boot ausgelöst wurde
Verwendung von Thymeleaf mit Spring Boot
Unbekannter Fehler in Zeile 1 von pom.xml bei Verwendung von Spring Boot in Eclipse
[Spring Boot] Ich habe untersucht, wie die Nachbearbeitung der empfangenen Anforderung implementiert werden kann.
Schritte zum Ausführen von Spring Boot beziehen sich auf die Werte in der Eigenschaftendatei
Ich habe die Verwendung der Schlangenhülle für den Variablennamen in Spring Boot nicht mehr verstanden
Mein Memorandum, dass ich ValidationMessages.properties UTF8 in Spring Boot erstellen möchte
Starten Sie mit IntelliJ ein (altes) Spring Boot-Projekt
Erstellen Sie mit Gradle ein Spring Boot + Docker-Image
Statische Dateizugriffspriorität beim Spring Boot
Implementierung einer mandantenfähigen kompatiblen asynchronen Verarbeitung in Tomcat
Spring Boot-Protokoll im JSON-Format ausgeben
Memorandum zum Herunterladen lokaler Dateien mit Spring Boot
Erstellen Sie mit IntelliJ ein Java Spring Boot-Projekt
Lösen Sie die Thymeleaf-Syntaxprüfung in Spring Boot
[Trainieren! ] Zeigen Sie Hello World mit Spring Boot an
WebMvcConfigurer-Memorandum von Spring Boot 2.0 (Spring 5)
Verwenden Sie die DynamoDB-Abfragemethode mit Spring Boot
Ich habe Lazy Initialization mit Spring Boot 2.2.0 ausprobiert
Ich war süchtig nach Spring's @Transactional
Asynchrone Verarbeitung mit Spring Boot unter Verwendung von @Async
DI SessionScope Bean im Spring Boot 2-Filter
[* Java *] Ich habe am JJUG CCC 2019 Spring teilgenommen
Ändern Sie das Sitzungszeitlimit in Spring Boot
Organisieren Sie die Unterschiede im Verhalten von @NotBlank, @NotEmpty und @NotNull mit Spring Boot + Thymeleaf
Rufen Sie den in der Controller-Klasse von Spring Boot definierten Pfad als Liste ab
Ich habe versucht, eine Webanwendung voller Fehler mit Spring Boot zu klonen
So legen Sie Umgebungsvariablen in der Eigenschaftendatei der Spring-Boot-Anwendung fest
Herbst 2017 Sicherheitsspezialist Ich habe die Häufigkeit der Wörter überprüft, die am Morgen 2 erschienen sind