Java EE EJB Multithreading

java
ejb
cdi
webservice
multithreading

#1

Hallo,

ich habe einen WebService auf einem WildFly laufen, welcher jetzt um Multithreading erweitert werden soll. Bei meinen Recherchen, wie man das am besten angehen sollte, bin ich darauf gestoßen, das im EJB Umfald darauf verzichtet werden sollte eigene Threads zu erzeugen.

Why is thread creation and management disallowed?
The EJB specification assigns to the EJB container the responsibility for managing threads. Allowing enterprise bean instances to create and manage threads would interfere with the container’s ability to control its components’ lifecycle. Thread management is not a business function, it is an implementation detail, and is typically complicated and platform-specific. Letting the container manage threads relieves the enterprise bean developer of dealing with threading issues. Multithreaded applications are still possible, but control of multithreading is located in the container, not in the enterprise bean.

EJB Restrictions

Um mein Testprojekt vor meiner eigentlichen Frage noch kurz zu skizzieren. Ich habe einen Web-Service mittels JAX-WS erstellt. In diesem Web-Service befindet sich keine Business-Logik. Diese wird mittels CDI und @Inject dem WebService zur Verfügung gestellt (@ApplicationScoped). In der Business Logik existiert eine Warteschlange, welche Aufgaben vom WebService entgegen nehmen soll um diese dann “in Ruhe” abzuarbeiten. Die Abarbeitung kann teilweise ein paar Minuten in anspruch nehmen. Aus diesem Grund würde ich gerne diese Abarbeitung von Arbeitsthreads übernehmen lassen. Mir schwebt vor, das es je nach Konfiguration ca. 5 Arbeitsthreads geben soll, welche sukzessiv die Warteschlange abarbeiten. Wenn nichts in der Warteschlange ist, dann sollen diese Threads warten bis wieder was rein kommt.

Wie kann ich die oben beschriebenen Arbeitsthreads erstellen, ohne die EJB-Restriktion zu verletzen?

Danke für eure Hilfe
Grüße Hans


#2

Eine @Stateless Bean mit @Asynchronous Methode sollte das eigentlich ermöglichen.


#3

Wenn ich eine Arbeitsklasse erstelle, welche mit @Stateless und @Asynchronous versehen ist, würden die einzelnen Aufgabe zwar jeweils in einem eigene Thread abgearbeitet, aber ich habe keine Kontrolle über die maximale Anzahl der Arbeits-Instanzen. Ich möchte vermeiden, das bei vielen “zeitgleichen” Aufgaben die CPU an den Anschlag geht und/oder der Speicher voll läuft.

Gibt es eine Möglichkeit die Anzahl an Arbeitsthreads unter Verwendung von @Stateless und @Asynchronous einzugrenzen/ zu kontrollieren?


#4

Es gibt in der Wildfly-Configuration eine Möglichkeit die maximalen Threads zu konfigurieren. Das müsste sich auch auf asynchrone Methoden auswirken.


#5

Ich kann mich mit dieser Lösung irgendwie noch nicht so richtig anfreunden. Wenn ich die Threads zentral im Wildfly begrenze, müsste sich das ja zum einen auf alle anderen Deployments auswirken. Zum Andren stellt sich mir die Frage, was passiert, wenn der WebService einen Auftrag abgeben will und die max Thread belegt sind. Kann der WebService für die Anfrage überhaupt instanziiert werden, es ist ja kein Thread mehr frei?

Ich stebe wenn möglich eine Lösung an, bei der die WebService-Anfrage lediglich die Anfrage in eine Warteschlange zur Abarbeitung ablegt und als Result lediglich ein “true” oder eine ID-Nummer zurück bekommt. Die Bearbeitungszeit würde eh in den meisten Fällen den TimeOut überschreiten.

Ich hatte vorhin als ich nach @Stateless und @Asynchronous gesucht habe eine Art Manager Klasse für EJB Thread gefunden, aber leider finde ich den Beitrag bei Google nicht mehr. Kann sowas (was auch immer ich da kurz gelesen habe) die Lösung für mein Problem sein?


#6

Wäre so etwas nicht der Anwendungsfall einer Message Driven Bean? Diese könntest du von den Events aus der Warteschlange triggern lassen, und sie im Application Server poolen.


#7

Da EJBs in einem Pool vorgehalten werden können, kann man darüber etwas Konfigurieren

https://docs.jboss.org/ejb3/docs/reference/1.0.7/html/SessionBean_and_MDB_configuration.html

https://docs.oracle.com/cd/E13222_01/wls/docs81/ejb/DDreference-ejb-jar.html#1115211

Ist aber alles sehr serverspezifisch.


#8

Also, die Threads in einem AppServer sind meines Wissens nach immer begrenzt. Es wäre nicht gut, wenn dies nicht so wäre.

Ansonsten kannst Du auch selbst etwas bauen. Speichere die Requestinformationen in die DB und nutzen einen Scheduler, der diese Dinge der Reihe nach abarbeitet. Da kannst Du dann ganz genau angeben, wieviele Requests der Scheduler parallel bearbeiten können soll. (Das ist eigentlich gängige Praxis).


#9

Diese Idee klingt auch nicht schlecht. Damit würde ich mich jetzt mal außeinander setzen :wink:

Ist der Scheduler mit seinen Threads Java EE konform?


#10

Ok streicht die Frage ob der Scheduler Java EE konform ist … Google hilft dann doch :wink:


#11

Ich habe mich jetzt ein wenig mit den Schedulern beschäftigt und habe aber mein Ziel noch nicht erreicht.

Die Verwendung der Annotation @Scheduler scheine ich nicht verwenden zu können, da ich gerne die Parameter des Schedulers über die Software einstellen können möchte.

Dann bin ich auf den ManagedScheduledExecutorService gestoßen. Mit diesem Service konnte ich einen Arbeitsthread aufbauen, der genau so arbeitet wie ich mir das vorgestellt habe. Der Thread wird mit WildFly Shutdown sauber beendet, super. Jetzt kommt aber leider das ABER, der ManagedScheduledExecutorService kann nur einen Thread erzeugen. Ich habe keinen Weg gefunden, zB. 5 separat laufende Scheulder parallel laufen zu lassen. Falls hier jemand eine Idee hat, wäre ich über diese nicht abgeneigt :wink: Aber auch hier hätte ich gerne die Einstellmöglichkeiten in der Software selber und nicht im irgendwelchen WildFly Configs.

Als Queue habe ich die LinkedBlockingQueue<> verwendet, welche laut Doku thread safe sein sollte.

Mein falscher Ansatz um mehrere Scheduler zu erzeugen ist folgender.

@Singleton
@Startup
@ApplicationScoped
@Named("business")
public class Business {
    private ManagedScheduledExecutorService executor;
	
	final private LinkedBlockingQueue<String> linkedBlockingQueue;
	
	private final ArrayList<ScheduledFuture<?>> schedulers;
	
	private boolean isSchedulerStart = false;
	
	public Business() throws NamingException{
		this.schedulers = new ArrayList<ScheduledFuture<?>>();

		this.executor = InitialContext.doLookup("java:comp/DefaultManagedScheduledExecutorService");
		
		this.linkedBlockingQueue = new LinkedBlockingQueue<String>();

		for(int i= 0; i<10;i++){
			Thread t = new Thread(new TestTask(this.linkedBlockingQueue));
			ScheduledFuture<?> future = this.executor.scheduleWithFixedDelay(t, 0, 250, TimeUnit.MILLISECONDS);
			this.schedulers.add(future);
		}
	}
	
	public void add(String value){
		this.linkedBlockingQueue.offer(value);
	}
	
	public void startScheduler() throws Exception{
		if(this.isSchedulerStart){
			throw new Exception("Scheduler bereits gestartet");
		}
		
		this.isSchedulerStart = true;
	}
	
	@PreDestroy
	public void shutdown(){
            //TODO
	}

}

public class TestTask implements Runnable {
	
	private final LinkedBlockingQueue<String> linkedBlockingQueue;
	
	public TestTask(LinkedBlockingQueue<String> linkedBlockingQueue){
		this.linkedBlockingQueue = linkedBlockingQueue;
	}

	@Override
	public void run() {
		if(this.linkedBlockingQueue.size() == 0){
			return;
		}
		
            String data = null;
		try{
			data = this.linkedBlockingQueue.poll(10, TimeUnit.MILLISECONDS);
		} catch(InterruptedException e){
			System.out.println("InterruptedException");
			data = null;
			Thread.currentThread().interrupt();
		}
		
		if(data == null){
			return;
		}

		System.out.println("Queue-Value: " + data):
	}
}

Kann man diesen Ansatz irgendwie soweit Ausbauen, das eine parallele Verarbeitung über mehrere Thread möglich ist?
Wenn es möglich ist, würde ich JNDI gerne vermeiden, da ich mich zum einen damit nicht auskenne und mir somit jegliches Verständnis über die Datensicherheit im JNDI fehlt.


#12

Brauchst du denn die Managementfunktion des ExecutorService überhaupt (also Zugriff über JMX)? Wenn nicht, dann wäre der ScheduledThreadPoolExecutor ein geeigneter Kandidat.

Im übrigen fände ich es aus Sicht der Zuständigkeit nicht falsch, wenn die Anzahl der Threads durch den Applicationserver begrenzt würden. Denn die sinnvolle Anzahl der Threads ist eine Größe, die durch die zur Verfügung stehenden Ressourcen vorgegeben wird. Dies ist eine Konsequenz des Deployment und in der Regel keine Anforderung an die Software.


#13

Verstehe ehrlich gesagt nicht warum du ueberhaupt einen JEE Server nutzen willst wenn du alles selber kontrollieren willst, das widerspricht dem Grundgedanken von JEE Server wie Wildfly/JBoss/etc. pp.

Nimm 'nen embedded Tomcat/Jetty, vielleicht sogar mit Spring Boot, dann bist du flexibler wenn du das moechtest.


#14

Ich habe viele Jahre in einem Unternehmen unterschiedliche Softwaresysteme verwaltet und administriert. Dabei stand auf der einen Seite die schöne heile ideale Welt und auf der anderen Seite die Realität. Bei den Softwarelösungen welche ich betreut habe, waren auch Lösungen, welche auf einem JEE Server gelaufen sind. Dabei haben sich auch mehrere Applikationen einen JEE Server geteilt. Diese Entscheidung wurde zentral getroffen und von den Softwareanbietern auch immer abgenickt. Dabei ist immer wieder aufgefallen, das es zwar schön ist, wenn der JEE Server alles verwalten kann, aber die eine Applikation das so und die Andere Applikation das anders haben wollte. Dann kommen die Consulter der einen Firma und stellen was ein, was zu Problemen bei der Anwendung einer anderen Firma führt. Auch nett ist immer wenn eine Firma kommt und änderungen an der WildFly-Config durchführen will, welche ein neustart vom WildFly bedeuten. Wenn man dann in allen betroffenen Abteilungen rumfragt, ist dafür nie Zeit. Ein Deployment neu zu starten oder die Applikationsconfig neu einlesen zu lassen, ist dagegen nicht das Problem.
Sicher hat das alles was damit zu tun, das die “Regeln” um JEE und JEE-Servern nicht durchweg eingehalten werden, aber es ist nun leider einmal so. Aus diesem Grund bevorzuge ich ganz persönlich pro Applikation zentrale Konfigurationen welche auch nur Auswirkungen auf die jeweilig Applikation haben. Der JEE-Server solle das Gerüst und die Ressourcengrenzwerte vorgeben und die Anwendungen sollen sich dann je nach ihrer ganz speziellen Konfig daran bedienen können.

So soviel zum Thema warum ich meine Anforderungen so definiere wie ich es getan habe.

Ein anderer Aspekt, warum ich gerne bestimmte Sachen über die Software konfigurieren möchte, ist das ich dem User auch die Möglichkeit geben möchte selber zu entscheiden viewiele Ressourcen die Anwendung für was verwenden soll.

Ein Beispiel:
Es gibt eine Anwendung welche in mehrere Teilbereiche aufgeteilt ist.

Teilbereiche

  1. Auftragsannahme ( <1s pro Auftrag)
  2. Auftragsplausibilisierung (ca. 1-2min pro Auftrag)
  3. Auftragsabarbeitung ( bis zu 10min pro Auftrag)
  4. Datenweitergabe ( < 10s pro Auftrag)

Alle Teilbereiche laufen im Normalfall so, da es pro Teilauftrag 1-3 Parallelprozesse gibt, was für das normale Datenaufkommen ausreicht. Kommt jetzt aber z.B. zum Monatsanfang eine übermäßig große Anfragewelle auf das System zu, wäre es doch sinnvoll, wenn der Anwender zb. beim Teilbereich 3 aus 3 Parallelprozesen kurzzeitig 20 machen kann. Das muss sicherlich mit allen Beteiligten abgestimmt werden, ob die Rechenleistung etc. dafür verfügbar sind, aber es würde helfen, die Aufträge schneller abzuarbeiten. Das stellt wiederrum sicher, dass Fristen eingehalten werden können etc.

Wenn mir jemand erklärt oder Hinweise gibt, wie man im JEE Umfeld ohne harten Eingriff in die JEE-Server Config diese Dynamik aufbauen kann, bin ich sehr gerne bereit diese Hinweise bzw. das Wissen aufzunehmen und es so umzusetzen. Ich will ja mit diesem Testprojekt was lernen und mein Wissen weiter ausbauen. Ich versuche halt über Übungsprojekte / Testprojekte wo mir die Aufgabenstellungen im Alltag schon mal untergekommen sind, JEE praktisch am Beispiel zu lernen … Trocken mit nem Buch geht bei mir nicht wirklich :wink:


#15

Danke fuer die Erklaerung :slight_smile:

Bin selber lange nicht mehr mit JBoss (und nie mit WildFly) in Beruehrung gekommen, kann also nicht mit Erfahrung aushelfen.

Im Netz findet einiges, WildFly scheint etwas zu bieten, die “EE Concurrency Utilities”:
https://docs.jboss.org/author/display/WFLY10/Subsystem+configuration#Subsystemconfiguration-EEConcurrencyUtilities

Da scheint es einen passenden JSR zu geben:
https://jcp.org/aboutJava/communityprocess/final/jsr236/index.html