[JAVA] Implémentation du traitement asynchrone compatible multi-tenant dans Tomcat

Implémentation du traitement asynchrone compatible multi-tenant dans Tomcat

Cet article fait partie de "Implémentation du traitement asynchrone dans Tomcat".

introduction

Dans "Implémentation du traitement asynchrone dans Tomcat", [@Async](https://docs.spring.io/spring-framework/docs/current/ J'ai introduit que vous pouvez contrôler le flux des tâches asynchrones en combinant javadoc-api / org / springframework / scheduling / annotation / Async.html) et le pool de threads. Cependant, il existe une correspondance univoque entre @Async et le pool de threads. Par conséquent, il n'est pas possible de prendre en charge le multi-tenant.

Multi-tenant est un mécanisme permettant de fournir des services à plusieurs clients avec une seule instance d'application. Par exemple, lorsque l'ID de locataire est spécifié comme suit, les locataires «t1» et «t2» sont affectés à différents pools de threads et différentes ressources / actifs sont utilisés pour chaque locataire, comme l'accès à différents schémas du magasin de données. C'est une image que vous faites.

- 「GET http://localhost/async?tenantId=t1」
- 「GET http://localhost/async?tenantId=t2」

** Figure 4.0 Requête HTTP pour multi-tenant **

Alors, réfléchissons à la façon d'implémenter le traitement asynchrone pour le multi-tenant. Nous vous présenterons également que certaines parties ne relèvent pas de la gestion de Spring Framework et que vous devez implémenter vous-même la gestion du cycle de vie du pool de threads.

Ajout d'une classe de gestion de pool de threads

Créez une classe ThreadPoolTaskExecutors qui gère un pool de threads multi-locataires.

Dans cette classe, le type Map avec l'ID de locataire et le pool de threads comme valeur-clé est conservé dans le champ et getExecutor () renvoie le pool de threads correspondant à l'ID de locataire. Cependant, enregistrer cette classe en tant que bean ne signifie pas que Spring Framework reconnaîtra qu'elle contient un pool de threads.

Par conséquent, pour arrêter le pool de threads par vous-même, détruisez avec @PreDestroy ajouté. () Est ajouté. Puisque la méthode de @PreDestroy est rappelée lorsque Tomcat se termine normalement, le thread est threadé à ce moment. Il ferme la piscine. Cependant, [Create @Configuration class](https://qiita.com/sipadan2003/items/3277024b7da63c844663#configuration%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE% Notez que cet arrêt détruit également les tâches qui sont dans la file d'attente, comme mentionné dans E4% BD% 9C% E6% 88% 90).

public class ThreadPoolTaskExecutors {
	private Map<String, ThreadPoolTaskExecutor> map = new HashMap<>();

	public ThreadPoolTaskExecutors(ThreadPoolSettings settings) {
		//Créer un pool de threads pour chaque client
		map.put("Tenant1", newExecutor("Tenant1", settings));
		map.put("Tenant2", newExecutor("Tenant2", settings));
		map.put("Tenant3", newExecutor("Tenant3", settings));
	}

	//Créer un pool de threads
	private static ThreadPoolTaskExecutor newExecutor(String tenantId, ThreadPoolSettings settings) {
		final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(settings.getCorePoolSize());
		executor.setQueueCapacity(settings.getQueueCapacity());
		executor.setMaxPoolSize(settings.getMaxPoolSize());
		executor.setKeepAliveSeconds(settings.getKeepAliveSeconds());
		executor.setThreadNamePrefix("Executor2-"+tenantId+"-");
		executor.setDaemon(false);
		executor.initialize();
		return executor;
	}

	//Renvoie le pool de threads correspondant à l'ID de locataire
	public ThreadPoolTaskExecutor getExecutor(String tenantId) {
		return map.get(tenantId);
	}

	//Arrêtez le pool de threads
	@PreDestroy
	private void destroy() {
		this.map.values().forEach(ThreadPoolTaskExecutor::shutdown);
	}
}

** Figure 4.1 Contenu de la classe ThreadPoolTaskExecutors **

@Configuration Modification de classe

[SampleConfig] mentionné ci-dessus (https://qiita.com/sipadan2003/items/3277024b7da63c844663#configuration%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BD% 9C% E6% 88% 90) Ajoutez taskExecutors2 () à la classe comme indiqué ci-dessous. Cette méthode utilise @Bean pour [multi-tenant Enregistrez l'objet de gestion du pool de threads pour (#Add Thread Pool Management Class) en tant que bean.

public class SampleConfig {
		:
	@Bean
	public ThreadPoolTaskExecutors taskExecutors2() {
		return new ThreadPoolTaskExecutors(threadPoolSettings);
	}

** Figure 4.2 Contenu de SampleConfig # taskExecutors2 () **

@Service Modification de classe

[Sample Service] mentionné ci-dessus (https://qiita.com/sipadan2003/items/3277024b7da63c844663#service%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BD% 9C% E6% 88% 90) Ajoutez cmd2 () à la classe comme indiqué ci-dessous. Contrairement à cmd (), @Async n'est pas ajouté et retourne. La valeur est un objet String, pas un Future.

public class SampleService {
		:
	public String cmd2(final String[] command) {
		final int exitValue = run(command);
		return "exitValue="+exitValue;
	}
		:
}

** Figure 4.3 Contenu de SampleService # cmd2 () **

@Controller Modification de classe

[Sample Controller] mentionné ci-dessus (https://qiita.com/sipadan2003/items/3277024b7da63c844663#controller%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BD% 9C% E6% 88% 90) Remettez à neuf la classe. Tout d'abord, ajoutez le champ taskExecutors2. Beans enregistrés dans la classe SampleConfig car il existe @Autowired Est automatiquement injecté.

public class SampleController {
		:
	@Autowired
	private ThreadPoolTaskExecutors taskExecutors2;
		:
}

** Figure 4.4.1 Contenu de SampleService # taskExecutors2 **

Ensuite, ajoutez une méthode qui appelle cmd2 () de SampleService. Le flux de traitement de cette méthode est le même que celui de la méthode cmd (), sauf qu'elle obtient le pool de threads et effectue explicitement des appels asynchrones.

public class SampleController {
		:
	@RequestMapping(value="/cmd2", method=RequestMethod.GET)
	@ResponseBody
	public String cmd2(@RequestParam(value="id", required=true)final String id) {
		final String tenantId = String.format("%s#%03d", id, counter.getAndIncrement());

		//Obtenir ThreadPool
		final ThreadPoolTaskExecutor executor = taskExecutors2.getExecutor(id);
		if(executor == null) {
			return "Invalid id: id="+id+"\n";
		}

		try{
			final Future<String> future = executor.submit(()->{
				return sampleService.cmd2(COMMAND); //Service téléphonique
			});

			synchronized(this.futures) {
				this.futures.put(tenantId, future);
			}
			return "Accepted: id="+tenantId+"\n";
		}catch(RejectedExecutionException e) {
			final CompletableFuture<String> future = new CompletableFuture<>();
			future.complete("Rejected");
			synchronized(this.futures) {
				this.futures.put(tenantId, future);
			}
			return "Rejected: id="+tenantId+"\n";
		}
	}

** Figure 4.4.2 Contenu de SampleService # cmd2 () **

Contrôle de fonctionnement

Vous pouvez démarrer le conteneur Tomcat localement et exécuter la commande comme suit pour vérifier l'opération.

$ curl -X GET http://localhost:8080/cmd2?id=Tenant1
Accepted: id=Tenant1#001

$ curl -X GET http://localhost:8080/cmd2?id=Tenant1
Accepted: id=Tenant1#001

$ curl -X GET http://localhost:8080/cmd2?id=Tenant1
Accepted: id=Tenant1#001
$ curl -X GET http://localhost:8080/cmd2?id=Tenant1

$ curl -X GET http://localhost:8080/cmd2?id=Tenant1
Rejected: id=Tenant1#001

$ curl -X GET http://localhost:8080/cmd2?id=Tenant1
Rejected: id=Tenant1#001

#Le pool de threads est "maxPoolSize"=2」、「queueCapacity=Lorsque "2"
#Après avoir attendu un peu plus de 10 secondes/Lorsque vous obtenez le statut
#2 tâches dont l'exécution est terminée, 2 tâches en cours d'exécution, 2 tâches qui ont été rejetées
#Est sortie
$ curl -X GET http://localhost:8080/status
Tenant1#001: exitValue=0
Tenant1#002: Running
Tenant1#003: Running
Tenant1#004: exitValue=0
Tenant1#005: Rejected
Tenant1#006: Rejected

#Arrêt normal du conteneur Tomcat
$ curl -X POST http://localhost:8080/shutdown

** Figure 4.5 Confirmation du fonctionnement de la tâche asynchrone pour le multi-tenant **

Recommended Posts

Implémentation du traitement asynchrone compatible multi-tenant dans Tomcat
Implémentation du traitement asynchrone dans Tomcat
Implémentation du traitement asynchrone pour un seul locataire dans Tomcat
Implémentation Java de tri-tree
Implémentation de HashMap avec kotlin
[Rails] Implémentation asynchrone de la fonction similaire
Essayez d'implémenter le traitement asynchrone dans Azure
Ordre de traitement dans le programme
Implémentation d'une fonction similaire en Java
Implémentation de DBlayer en Java (RDB, MySQL)
Traitement des threads de l'interface utilisateur asynchrone Android
Comment implémenter le traitement asynchrone dans Outsystems
Traitement asynchrone avec exécution régulière dans Spring Boot
Implémentation du regroupement de chiffres dans l'application Furima
Mémo d'implémentation SKStoreReviewController dans l'interface utilisateur Swift d'iOS14
[Rails] Mise en œuvre de "notifier la notification d'une manière ou d'une autre"
Format de la sortie du journal par Tomcat lui-même dans Tomcat 8
Préparer l'environnement d'exécution de Tomcat dans IntelliJ Community
Accédez au fichier war dans le répertoire racine de Tomcat
À propos du problème de blocage dans le traitement parallèle dans la version 4.0 de gem'sprockets
Exemple d'implémentation d'un système de traitement LISP simple (version Java)
Traitement asynchrone et intégration d'API Web dans Android Studio
À propos de l'ajout de variables dans le traitement itératif dans l'instruction while
Exemple d'implémentation d'un système de traitement LISP simple (version Ruby)
Implémentation de l'interpréteur par Java
Implémentation de la fonction de recherche
[Java] Je souhaite écrire un traitement asynchrone à l'aide de Promise dans Java-Trial of Promise-like syntax of JavaScript-
Implémentation Boyer-Moore en Java
Implémentation appliquée de l'espace de chat
Implémentation du tri de tas (en java)
Utiliser MouseListener avec le traitement
Mise en œuvre de la fonction de pagénation
Sortie en multiples de 3
À propos de l'implémentation du traitement de fusion, y compris la fonction de tri de l'API Stream
Mise en place de la fonction de tri des rails (affichés par ordre de nombre de like)