[JAVA] Implementierung einer mandantenfähigen kompatiblen asynchronen Verarbeitung in Tomcat

Implementierung einer mandantenfähigen kompatiblen asynchronen Verarbeitung in Tomcat

Dieser Artikel ist Teil von "Implementieren der asynchronen Verarbeitung in Tomcat".

Einführung

In "Implementierung der asynchronen Verarbeitung in Tomcat", [@Async](https://docs.spring.io/spring-framework/docs/current/ Ich habe eingeführt, dass Sie den Fluss asynchroner Aufgaben steuern können, indem Sie javadoc-api / org / springframework / scheduling / annotation / Async.html) und den Thread-Pool kombinieren. Es besteht jedoch eine Eins-zu-Eins-Korrespondenz zwischen @Async und dem Thread-Pool. Daher ist es nicht möglich, mehrere Mandanten zu unterstützen.

Multi-Tenant ist ein Mechanismus zum Bereitstellen von Diensten für mehrere Kunden mit einer Anwendungsinstanz. Wenn die Mandanten-ID beispielsweise wie folgt angegeben wird, werden die Mandanten "t1" und "t2" unterschiedlichen Thread-Pools zugewiesen, und für jeden Mandanten werden unterschiedliche Ressourcen / Assets verwendet, z. B. der Zugriff auf verschiedene Schemas des Datenspeichers. Es ist ein Bild, das Sie tun.

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

** Abbildung 4.0 HTTP-Anforderung für mehrere Mandanten **

Überlegen wir uns also, wie die asynchrone Verarbeitung für mehrere Mandanten implementiert werden kann. Wir werden auch vorstellen, dass es einige Teile gibt, die nicht in der Verwaltung des Spring Framework enthalten sind, und dass Sie die Verwaltung des Thread-Pool-Lebenszyklus selbst implementieren müssen.

Thread-Pool-Verwaltungsklasse hinzugefügt

Erstellen Sie eine ThreadPoolTaskExecutors-Klasse, die einen Thread-Pool mit mehreren Mandanten verwaltet.

In dieser Klasse wird der Map-Typ mit der Mandanten-ID und dem Thread-Pool als Schlüsselwert im Feld gespeichert, und getExecutor () gibt den Thread-Pool zurück, der der Mandanten-ID entspricht. Das Registrieren dieser Klasse als Bean bedeutet jedoch nicht, dass das Spring Framework erkennt, dass es einen Thread-Pool enthält.

Um den Thread-Pool selbst herunterzufahren, zerstören Sie ihn mit @PreDestroy. () Hinzugefügt. Da die Methode von @PreDestroy zurückgerufen wird, wenn Tomcat normalerweise endet, wird der Thread zu diesem Zeitpunkt eingefädelt. Es schließt den Pool. [Create @Configuration class](https://qiita.com/sipadan2003/items/3277024b7da63c844663#configuration%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE% Beachten Sie, dass dieses Herunterfahren auch die Aufgaben zerstört, die in der Warteschlange stecken, wie in E4% BD% 9C% E6% 88% 90) erwähnt.

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

	public ThreadPoolTaskExecutors(ThreadPoolSettings settings) {
		//Erstellen Sie für jeden Mandanten einen Thread-Pool
		map.put("Tenant1", newExecutor("Tenant1", settings));
		map.put("Tenant2", newExecutor("Tenant2", settings));
		map.put("Tenant3", newExecutor("Tenant3", settings));
	}

	//Thread-Pool erstellen
	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;
	}

	//Gibt den Thread-Pool zurück, der der Mandanten-ID entspricht
	public ThreadPoolTaskExecutor getExecutor(String tenantId) {
		return map.get(tenantId);
	}

	//Fahren Sie den Thread-Pool herunter
	@PreDestroy
	private void destroy() {
		this.map.values().forEach(ThreadPoolTaskExecutor::shutdown);
	}
}

** Abbildung 4.1 Inhalt der ThreadPoolTaskExecutors-Klasse **

@Configuration Klassenänderung

[SampleConfig] oben erwähnt (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) Fügen Sie der Klasse taskExecutors2 () wie unten gezeigt hinzu. Diese Methode verwendet @Bean für [Multi-Tenant Registrieren Sie das Thread-Pool-Verwaltungsobjekt für (# Thread-Pool-Verwaltungsklasse hinzufügen) als Bean.

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

** Abbildung 4.2 Inhalt von SampleConfig # taskExecutors2 () **

@Service Klassenänderung

[Beispielservice] oben erwähnt (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) Fügen Sie der Klasse cmd2 () wie unten gezeigt hinzu. Im Gegensatz zu cmd () wird @Async nicht hinzugefügt und zurückgegeben. Der Wert ist ein String-Objekt, keine Zukunft.

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

** Abbildung 4.3 Inhalt von SampleService # cmd2 () **

@Controller Klassenänderung

[Beispiel-Controller] oben erwähnt (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) Renovieren Sie die Klasse. Fügen Sie zunächst das Feld taskExecutors2 hinzu. In der SampleConfig-Klasse registrierte Beans, weil [@Autowired] vorhanden ist (https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html) Wird automatisch injiziert.

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

** Abbildung 4.4.1 Inhalt von SampleService # taskExecutors2 **

Fügen Sie als Nächstes eine Methode hinzu, die cmd2 () von SampleService aufruft. Der Verarbeitungsablauf dieser Methode ist der gleiche wie bei der Methode cmd (), außer dass der Thread-Pool abgerufen und explizit asynchrone Aufrufe ausgeführt werden.

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());

		//Holen Sie sich 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); //Rufen Sie den Service an
			});

			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";
		}
	}

** Abbildung 4.4.2 Inhalt von SampleService # cmd2 () **

Funktionsprüfung

Sie können den Tomcat-Container lokal starten und den Befehl wie folgt ausführen, um den Vorgang zu überprüfen.

$ 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

#Der Thread-Pool ist "maxPoolSize"=2」、「queueCapacity=Wenn "2"
#Nach etwas mehr als 10 Sekunden warten/Wenn Sie den Status erhalten
#2 Aufgaben, die ausgeführt wurden, 2 Aufgaben, die ausgeführt werden, 2 Aufgaben, die abgelehnt wurden
#Wird ausgegeben
$ 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

#Normale Beendigung des Tomcat-Containers
$ curl -X POST http://localhost:8080/shutdown

** Abbildung 4.5 Bestätigung des Betriebs der asynchronen Task für mehrere Mandanten **

Recommended Posts

Implementierung einer mandantenfähigen kompatiblen asynchronen Verarbeitung in Tomcat
Implementierung der asynchronen Verarbeitung in Tomcat
Implementierung der asynchronen Verarbeitung für einen einzelnen Mandanten in Tomcat
Java-Implementierung von Tri-Tree
Implementierung von HashMap mit Kotlin
[Rails] Asynchrone Implementierung der Like-Funktion
Versuchen Sie, die asynchrone Verarbeitung in Azure zu implementieren
Reihenfolge der Verarbeitung im Programm
Implementierung einer ähnlichen Funktion in Java
Implementierung von DBlayer in Java (RDB, MySQL)
Android Asynchrone UI-Thread-Verarbeitung
So implementieren Sie die asynchrone Verarbeitung in Outsystems
Asynchrone Verarbeitung mit regelmäßiger Ausführung in Spring Boot
Implementierung der Zifferngruppierung in der Furima App
Implementierungsnotiz für SKStoreReviewController in der Swift-Benutzeroberfläche von iOS14
[Rails] Implementierung von "Benachrichtigung auf irgendeine Weise benachrichtigen"
Format der Protokollausgabe von Tomcat selbst in Tomcat 8
Bereiten Sie die Ausführungsumgebung von Tomcat in der IntelliJ Community vor
Greifen Sie auf die War-Datei im Stammverzeichnis von Tomcat zu
Über das Problem des Deadlocks bei der Parallelverarbeitung in gem'sprockets '4.0
Implementierungsbeispiel eines einfachen LISP-Verarbeitungssystems (Java-Version)
Asynchrone Verarbeitung und Web-API-Integration in Android Studio
Informationen zum Hinzufügen von Variablen in der iterativen Verarbeitung in der while-Anweisung
Implementierungsbeispiel eines einfachen LISP-Verarbeitungssystems (Ruby-Version)
Interpreter-Implementierung durch Java
Implementierung der Suchfunktion
[Java] Ich möchte eine asynchrone Verarbeitung mit Promise in der Java-Testversion der Promise-ähnlichen Syntax von JavaScript schreiben.
Boyer-Moore-Implementierung in Java
Angewandte Implementierung von Chat-Space
Implementierung der Heap-Sortierung (in Java)
Verwenden Sie MouseListener für die Verarbeitung
Implementierung der Pagenationsfunktion
Ausgabe in Vielfachen von 3
Informationen zur Implementierung der Zusammenführungsverarbeitung einschließlich der Sortierfunktion der Stream-API
Implementierung der Rails-Sortierfunktion (angezeigt in der Reihenfolge der Anzahl der Gleichen)