[JAVA] Erstellen eines benutzerdefinierten Scala ExecutionContext

Wir werden einen benutzerdefinierten ExecuitonContext erstellen.

Hintergrund

Wenn ich normalerweise Future verwende, DI ich den ExecutionContext irgendwie DI und übergebe ihn implizit an Future.

Bisher wurde ein ExecutionContext zur asynchronen Verarbeitung für Elasticsearch an Future übergeben. Aus verschiedenen Gründen war es jedoch erforderlich, nicht nur Elastisearch, sondern auch DocumentDB (MongoDBs AWS Managed Service) so zu ändern, dass eine Anwendung auf Dokumente verweisen und diese speichern kann.

Bisher wurde ein Standard-ExecutionContext verwendet, um die asynchrone Verarbeitung für Elasticsearch durchzuführen. Jetzt wird er jedoch auch für die Verbindung mit DocumentDB verwendet.

Ich wollte es verwenden, wenn ich einen gemeinsamen ExecutionContext verwenden könnte, aber wenn ich diesmal unnötig eine Verbindung zu DocumentDB herstelle, gibt es zu viele wartende Threads und ich kann sie nicht verarbeiten. (Standardmäßig können Sie bis zu 500 Threads warten.)

Ich bekomme tatsächlich den folgenden Fehler.

Fehlerinhalt

com.mongodb.MongoWaitQueueFullException: Too many threads are already waiting for a connection. Max number of threads (maxWaitQueueSize) of 500 has been exceeded.

Daher ist es erforderlich, die maximale Anzahl von Verbindungen, die auf der DocumentDB-Seite veröffentlicht werden können (die Anzahl der Verbindungen, die gepoolt werden können) und die Anzahl der Threads, die auf der Anwendungsseite verbunden werden, zu optimieren.

Wenn die Anzahl der auf der Anwendungsseite parallel ausgeführten Threads die Anzahl der auf der DocumentDB-Seite festgelegten Verbindungspools nicht überschreitet, sollte sie normal verarbeitet werden können.

Referenz: Einstellung der DocumentDB-Verbindungsanzahl

Daher wurde es notwendig, den ExecutionContext anzupassen, den ich normalerweise gelegentlich verwende.

Was ist ExecutionContext?

Eine Standard-Scala-Bibliothek, die Programme asynchron ausführt, ohne dass ein Thread-Pool erforderlich ist.

Die Übersicht ist lang und in der ExecutionContext-Bibliothek auskommentiert.

ExecutionContext.scala


/**
 * An `ExecutionContext` can execute program logic asynchronously,
 * typically but not necessarily on a thread pool.
 *
 * A general purpose `ExecutionContext` must be asynchronous in executing
 * any `Runnable` that is passed into its `execute`-method. A special purpose
 * `ExecutionContext` may be synchronous but must only be passed
 * ............Fortsetzen

Erstellen Sie Ihren eigenen ExecutionContext

Die ExecutionContext-Bibliothek bietet eine gute Möglichkeit, sie anzupassen.

* A custom `ExecutionContext` may be appropriate to execute code
 * which blocks on IO or performs long-running computations.
 * `ExecutionContext.fromExecutorService` and `ExecutionContext.fromExecutor`
 * are good ways to create a custom `ExecutionContext`.

Es scheint, dass es eine Schnittstelle gibt, über die Sie Ihren eigenen ExecutionContext erstellen können, z. B. ExecutionContext.fromExecutor.

Ich werde es tatsächlich schaffen

Ich möchte es auf zwei Arten anpassen.

1. Zuerst

ExecutionContextTest1.scala



class MongoRepository1 {

  implicit val service: ExecutionContextExecutorService = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(1))

  def find = {
    Future {
      for (i <- 1 to 50) {
        println(i * 2)
      }
    }
  }
}

Wir übergeben eine Executors-Instanz als Argument an fromExecutorServicen. Mit newFixedThreadPool () können Sie eine feste Anzahl von Threads angeben, die gepoolt werden können.

Indem Sie es implizit an Future übergeben, können Sie die asynchrone Verarbeitung mit einem angepassten ExecutionContext ausführen.

2 Sekunden

So passen Sie ExecutionContext im Play Framework an. Dies ist der Weg, um es ordentlich in DI zu implementieren.

ExecutionContextTest.scala



package study.executionContextTest

import java.util.concurrent.{ExecutorService, Executors}

import com.google.inject.name.{Named, Names}
import com.google.inject.{AbstractModule, Inject, Singleton}

import scala.concurrent.{ExecutionContext, Future}

//Erstellen Sie Ihren eigenen ExecutionContext
class Execution @Inject()(mongoRepository: MongoRepository) {
  mongoRepository.find
}

//Ich mache ein Modul zu DI
//Hier können Sie Ihren eigenen ExecutionContext erstellen, um verschiedene Einstellungen zu definieren.
class MongoExecutionContext(threadCount: Int) extends ExecutionContext {

  //Sie können einen Thread-Pool mithilfe von Java ExecutorService erstellen
  //Die Anzahl der zu bündelnden Threads wird durch einen festen Wert in newFixedThreadPool definiert.
  private val executorService: ExecutorService =
    Executors.newFixedThreadPool(threadCount)

  //Future umschließt den Argumentblock intern mit Rannable und führt die Methode execute von ExecutionContext aus.
  override def execute(runnable: Runnable): Unit =
    executorService.execute(runnable)

  override def reportFailure(cause: Throwable): Unit = throw cause
}

//Erstellen Sie Ihr eigenes Modul
//Google Guice Spezifikationen
// application.Indem Sie es im Modul von conf definieren, können Sie mit Google Guice DI
class MongoExecutionModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[ExecutionContext])
      .annotatedWith(Names.named("MongoExecutionContext"))
      .toInstance(new MongoExecutionContext(50))
  }
}

//Es sollte nur eine Instanz erstellt werden.
//Optimierungsfehler beim Erstellen mehrerer ExecutionContexts
@Singleton
class MongoRepository @Inject()(
  implicit @Named("MongoExecutionContext") ec: ExecutionContext
) {

  //Da der implizite Ausführungskontext in dieser Suche usw. verwendet wird.
  //Anstatt hier den Standard-ExecutionContext zu verwenden, verwende ich einen ExecutionContext, der für die Verbindung mit Mongo geeignet ist.
  //Auf diese Weise kann der für jede Middleware verwendete ExecutionContext geändert werden.
  //In diesem Beispiel wird die an Future's Apply übergebene Verarbeitung asynchron verarbeitet.
  //Zu diesem Zeitpunkt wird, da der oben injizierte ExecutionContext verwendet wird, eine parallele Verarbeitung bis zu maximal 50 Threads durchgeführt.
  def find = {
    Future {
      for (i <- 1 to 50) {
        println(i * 2)
      }
    }
  }
}

Details sind im Kommentar angegeben, aber der allgemeine Ablauf ist wie folgt.

Schließlich möchte ich einen benutzerdefinierten ExecutionContext erstellen.

―― 1. Erstellen Sie einen MongoExecutionContext, der den ExecutionContext erbt. Erstellen Sie darin einen ExecutionService, der den Thread-Pool angibt. ―― 2. Erstellen Sie ein MongoExecutionModule, das das AbstractModule erbt --2-1. Implementieren Sie, damit Sie mit @Name DI können --3. DI das MongoExecutionModule, das in 2 mit MongoRepository erstellt wurde.

Zusammenfassung

Wie ich bereits erwähnt habe, habe ich ExecutionContext verwendet, als wäre es ein Zauber, aber ich habe mein Verständnis vertieft, indem ich es selbst gemacht habe.

Referenz

Es war sehr hilfreich bei der Implementierung.

Recommended Posts

Erstellen eines benutzerdefinierten Scala ExecutionContext
Erstellen eines lokalen Repositorys
Testfall erstellen
Erstellen eines Liferay-Stapels
Erstellen eines Kalenders mit Ruby
[Rails] Erstellen eines Suchfelds
Erstellen einer Timer-App mit Schlamm
[Kotlin / Android] Erstellen Sie eine benutzerdefinierte Ansicht