Dieser Artikel ist Teil von "Implementieren der asynchronen Verarbeitung in Tomcat".
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.
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 **
[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 () **
[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 () **
[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 () **
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