[JAVA] Création d'un ExecutionContext personnalisé Scala

Nous allons créer un ExecuitonContext personnalisé.

Contexte

Quand j'utilise habituellement Future, je DI en quelque sorte le ExecutionContext et le passe implicitement à Future.

Jusqu'à présent, un ExecutionContext était passé à Future pour un traitement asynchrone pour Elasticsearch. Cependant, pour diverses raisons, il est devenu nécessaire de modifier non seulement Elastisearch mais également DocumentDB (le service géré AWS de MongoDB) afin qu'une application puisse faire référence et enregistrer des documents.

Jusqu'à présent, le ExecutionContext par défaut utilisé pour effectuer le traitement asynchrone pour Elasticsearch, mais maintenant il est également utilisé pour se connecter à DocumentDB.

Je voulais l'utiliser si je pouvais utiliser un ExecutionContext commun, mais en ce qui concerne DocumentDB cette fois, si je mets une connexion inutilement, il y a trop de threads en attente et je ne peux pas le traiter. (Par défaut, vous pouvez attendre jusqu'à 500 threads)

J'obtiens en fait l'erreur suivante.

contenu de l'erreur

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

Par conséquent, il est nécessaire de régler le nombre maximal de connexions qui peuvent être publiées du côté DocumentDB (le nombre de connexions qui peuvent être regroupées) et le nombre de threads qui se connectent du côté de l'application.

Si le nombre de threads s'exécutant en parallèle du côté application ne dépasse pas le nombre de pools de connexions défini du côté DocumentDB, il doit pouvoir être traité normalement.

Référence: paramètre du nombre de connexions DocumentDB

Par conséquent, il est devenu nécessaire de personnaliser l'ExecutionContext que j'utilise habituellement avec désinvolture.

Qu'est-ce que ExecutionContext?

Une bibliothèque Scala standard qui exécute des programmes de manière asynchrone sans avoir besoin d'un pool de threads.

La vue d'ensemble est longue et commentée dans la bibliothèque ExecutionContext.

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
 * ............Continuer

Créez votre propre ExecutionContext

La bibliothèque ExecutionContext a un peu un bon moyen de la personnaliser.

* 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`.

Il semble qu'il existe une interface pour créer votre propre ExecutionContext, comme ExecutionContext.fromExecutor.

Je vais vraiment le faire

Je souhaite le personnaliser de deux manières.

1. Premier

ExecutionContextTest1.scala



class MongoRepository1 {

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

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

Nous transmettons une instance Executors comme argument à fromExecutorServicen. Avec newFixedThreadPool (), vous pouvez spécifier un nombre fixe de threads pouvant être regroupés.

En le transmettant implicitement à Future, vous pouvez exécuter un traitement asynchrone avec un ExecutionContext personnalisé.

2. Deuxième

Comment personnaliser ExecutionContext sur le framework de jeu. C'est la manière de l'implémenter proprement dans DI.

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}

//Créez votre propre ExecutionContext
class Execution @Inject()(mongoRepository: MongoRepository) {
  mongoRepository.find
}

//Je crée un module pour DI
//Ici, vous pouvez créer votre propre ExecutionContext, afin de pouvoir définir divers paramètres.
class MongoExecutionContext(threadCount: Int) extends ExecutionContext {

  //Vous pouvez créer un pool de threads à l'aide du service d'exécution de Java
  //Le nombre de threads à regrouper est défini par une valeur fixe dans newFixedThreadPool.
  private val executorService: ExecutorService =
    Executors.newFixedThreadPool(threadCount)

  //Future enveloppe en interne le bloc d'arguments dans Rannable et exécute la méthode execute d'ExecutionContext.
  override def execute(runnable: Runnable): Unit =
    executorService.execute(runnable)

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

//Créez votre propre module
//spécifications google guice
// application.En le définissant dans le module de conf, vous pouvez DI avec google guice
class MongoExecutionModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[ExecutionContext])
      .annotatedWith(Names.named("MongoExecutionContext"))
      .toInstance(new MongoExecutionContext(50))
  }
}

//Une seule instance doit être créée.
//Réglage des bogues lors de la création de plusieurs ExecutionContexts
@Singleton
class MongoRepository @Inject()(
  implicit @Named("MongoExecutionContext") ec: ExecutionContext
) {

  //Étant donné que l'exécution impliciteContext est utilisée dans cette recherche, etc.
  //Au lieu d'utiliser le ExecutionContext par défaut ici, j'utiliserai un ExecutionContext adapté à la connexion à mongo.
  //De cette manière, l'ExecutionContext utilisé pour chaque middleware peut être modifié.
  //Dans cet exemple, le traitement passé à Future's apply est traité de manière asynchrone.
  //À ce moment-là, puisque l'ExecutionContext injecté ci-dessus est utilisé, le traitement parallèle est effectué jusqu'à un maximum de 50 threads.
  def find = {
    Future {
      for (i <- 1 to 50) {
        println(i * 2)
      }
    }
  }
}

Les détails sont écrits dans le commentaire, mais le flux général est le suivant.

Finalement, je veux DI un ExecutionContext personnalisé.

―― 1. Créez un MongoExecutionContext qui hérite de l'ExecutionContext. --1-1. En cela, créez un ExecutionService qui spécifie le pool de threads. ―― 2. Créez un MongoExecutionModule qui hérite du AbstractModule --2-1. Implémentez pour que vous puissiez DI avec @Name

Résumé

Comme je l'ai mentionné plus tôt, j'utilisais ExecutionContext comme un sort, mais j'ai approfondi ma compréhension en le créant moi-même.

référence

Cela a été très utile pour sa mise en œuvre.

Recommended Posts

Création d'un ExecutionContext personnalisé Scala
Créer un référentiel local
Créer un cas de test
Créer un lot Liferay
Créer un calendrier avec Ruby
[Rails] Création d'un champ de recherche
Créer une application de minuterie avec de la boue
[Kotlin / Android] Créer une vue personnalisée