mental.dIseASe
Lieutenant
- Registriert
- Dez. 2008
- Beiträge
- 674
Guten Abend,
ich bin gerade dabei, so etwas wie einen Application-Server zu entwickeln. Dieser ist zweigeteilt: auf der einen Seite habe ich so etwas wie einen Admin-Query-Thread, der auf einem Port auf Admin-Anfragen lauscht(zb. den Call, den Server als ganzes zu beenden). Auf der anderen Seite habe ich einen Thread, der auf einem anderen Port lauscht, um die eigentlichen Anfragen an die Application zu beantworten. Ich lausche in einer Schleife und delegiere die TCP-Connections dann in Worker-Threads eines Pools.
Der start-Code meines Servers sieht dann in etwa so aus:
Wenn ich also mit dem Admin-Thread fertig bin, dann möchte ich dem ServiceQueryThread sagen, dass er aufhören soll zu lauschen, weil er dann auch demnächst abgewickelt wird. Aber hier liegt genau mein Problem: wenn der Interrupt kommt, bin ich in 99% aller Fälle grad im accept() und blockiere folglich. Leider zählt accept() nicht als blockierende Operation in dem Sinne, dass es beim interrupt() wie sleep() eine InterruptedException schmeisst.
Ich habe bisher 3 Lösungen sondiert, die mir aber irgendwie alle zu dirty sind:
1. statt service.interrupt() aufzurufen, gebe ich dem ServiceQueryThread noch eine Methode kill() oder überschreibe interrupt(), sodass dann der _serverSocket gewaltsam close()d wird. Dadurch wird accept() unterbrochen und ich bekomme eine IOException und kann den Schleifenkopf auswerten. Das gefällt mir aber nicht, weil ich damit die normalen Mechanismen zum Thread beenden unsauber ausheble.
2. ich gebe dem serverSocket mit setSoTimeout einen Timeout und bekomme dann potenziell bei jedem Schleifendurchlauf, in dem keine Verbindungsanfrage eingeht, eine SocketTimeoutException und der Schleifenkopf wird regelmäßig, je nach Timeout, geprüft. Bewusst mit Exceptions zu rechnen und sie böswillig zu erzeugen, ist aber auch blöd.
3. Ich erstelle innerhalb des Servers kurz eine Dummy-Client-Verbindung zum Server, die so das accept() nochmal "klicken" laesst, damit ich zum Schleifenkopf komme. Auch irgendwie billig.
Gibt es bessere Varianten, die mir nicht einfallen? Ich hoffe, ich konnte mich trotz "Wall of Text" halbwegs verständlich ausdrücken und hoffe auf eure Expertise.
Gruß,
mental
ich bin gerade dabei, so etwas wie einen Application-Server zu entwickeln. Dieser ist zweigeteilt: auf der einen Seite habe ich so etwas wie einen Admin-Query-Thread, der auf einem Port auf Admin-Anfragen lauscht(zb. den Call, den Server als ganzes zu beenden). Auf der anderen Seite habe ich einen Thread, der auf einem anderen Port lauscht, um die eigentlichen Anfragen an die Application zu beantworten. Ich lausche in einer Schleife und delegiere die TCP-Connections dann in Worker-Threads eines Pools.
Code:
private class ServiceQueryThread extends Thread {
private final ServerSocket _serverSocket;
private final ExecutorService _pool;
public ServiceQueryThread(int listenPort) {
_serverSocket = new ServerSocket(listenPort);
_pool = Executors.newFixedThreadPool(20);
}
@Override
public void run() {
Socket connection = null;
while(!isInterrupted()){
connection = _serverSocket.accept();
_pool.execute(new Handler(connection));
}
//finishStuff
}
}
Code:
public void start() {
adminThread admin = new adminThread(aPort);
serviceQueryThread service = new ServiceQueryThread(anotherPort);
admin.start();
service.start();
admin.join(); // Thread ist dann zuende, wenn der Server beendet werden soll
service.interrupt(); //semantisch: Hoer auf zu lauschen und beende saemtliche Kommunikation
}
Ich habe bisher 3 Lösungen sondiert, die mir aber irgendwie alle zu dirty sind:
1. statt service.interrupt() aufzurufen, gebe ich dem ServiceQueryThread noch eine Methode kill() oder überschreibe interrupt(), sodass dann der _serverSocket gewaltsam close()d wird. Dadurch wird accept() unterbrochen und ich bekomme eine IOException und kann den Schleifenkopf auswerten. Das gefällt mir aber nicht, weil ich damit die normalen Mechanismen zum Thread beenden unsauber ausheble.
2. ich gebe dem serverSocket mit setSoTimeout einen Timeout und bekomme dann potenziell bei jedem Schleifendurchlauf, in dem keine Verbindungsanfrage eingeht, eine SocketTimeoutException und der Schleifenkopf wird regelmäßig, je nach Timeout, geprüft. Bewusst mit Exceptions zu rechnen und sie böswillig zu erzeugen, ist aber auch blöd.
3. Ich erstelle innerhalb des Servers kurz eine Dummy-Client-Verbindung zum Server, die so das accept() nochmal "klicken" laesst, damit ich zum Schleifenkopf komme. Auch irgendwie billig.
Gibt es bessere Varianten, die mir nicht einfallen? Ich hoffe, ich konnte mich trotz "Wall of Text" halbwegs verständlich ausdrücken und hoffe auf eure Expertise.
Gruß,
mental