Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bf337c5

Browse files
committed
Improve SSEClient and SSEPushManager.
1 parent 35a94bd commit bf337c5

File tree

2 files changed

+91
-29
lines changed

2 files changed

+91
-29
lines changed

src/main/java/org/tinystruct/http/SSEClient.java

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,67 @@
11
package org.tinystruct.http;
22

3-
import org.tinystruct.ApplicationException;
3+
import org.tinystruct.data.component.Builder;
44

55
import java.nio.charset.StandardCharsets;
6+
import java.util.concurrent.BlockingQueue;
67
import java.util.concurrent.LinkedBlockingQueue;
8+
import java.util.logging.Level;
9+
import java.util.logging.Logger;
710

811
public class SSEClient implements Runnable {
912
private final Response out;
10-
private final LinkedBlockingQueue<String> messageQueue = new LinkedBlockingQueue<>();
13+
private final BlockingQueue<Builder> messageQueue;
1114
private volatile boolean active = true;
15+
private static final Logger logger = Logger.getLogger(SSEClient.class.getName());
1216

1317
public SSEClient(Response out) {
18+
this(out, new LinkedBlockingQueue<>());
19+
}
20+
21+
public SSEClient(Response out, BlockingQueue<Builder> messageQueue) {
1422
this.out = out;
23+
this.messageQueue = messageQueue;
24+
// Set SSE headers
25+
if (out instanceof ResponseBuilder) {
26+
ResponseBuilder builder = (ResponseBuilder) out;
27+
builder.setContentType("text/event-stream");
28+
builder.addHeader("Cache-Control", "no-cache");
29+
builder.addHeader("Connection", "keep-alive");
30+
}
1531
}
1632

17-
public void send(String message) {
33+
public void send(Builder message) {
1834
messageQueue.offer(message);
1935
}
2036

2137
public void close() {
2238
this.active = false;
23-
// 清空消息队列,防止阻塞
2439
messageQueue.clear();
2540
}
2641

2742
@Override
2843
public void run() {
2944
try {
3045
while (active) {
31-
String message = messageQueue.poll();
32-
try {
33-
if (message != null) {
34-
writeEvent("message", message);
35-
} else {
36-
writeEvent("heartbeat", "{\"time\": " + System.currentTimeMillis() + "}");
37-
}
38-
} catch (ApplicationException e) {
39-
// 写入失败,通常是客户端断开或流已关闭
40-
this.active = false;
41-
break;
46+
Builder message = messageQueue.take();
47+
if (message != null) {
48+
String event = formatSSEMessage(message);
49+
out.writeAndFlush(event.getBytes(StandardCharsets.UTF_8));
4250
}
43-
Thread.sleep(2000); // 每2秒一次
4451
}
4552
} catch (InterruptedException e) {
4653
Thread.currentThread().interrupt();
47-
this.active = false;
4854
} catch (Exception e) {
49-
this.active = false;
55+
logger.log(Level.SEVERE, "Error in SSE client: " + e.getMessage(), e);
56+
} finally {
57+
close();
5058
}
5159
}
5260

53-
private void writeEvent(String event, String data) throws ApplicationException {
54-
String payload = "event: " + event + "\n" +
55-
"data: " + data.replace("\n", "\ndata: ") + "\n\n";
56-
out.writeAndFlush(payload.getBytes(StandardCharsets.UTF_8));
61+
private String formatSSEMessage(Builder message) {
62+
StringBuilder sb = new StringBuilder();
63+
sb.append("data: ").append(message.toString()).append("\n\n");
64+
return sb.toString();
5765
}
5866

5967
public boolean isActive() {

src/main/java/org/tinystruct/http/SSEPushManager.java

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
package org.tinystruct.http;
22

3+
import org.tinystruct.data.component.Builder;
4+
35
import java.util.Set;
46
import java.util.concurrent.ConcurrentHashMap;
57
import java.util.concurrent.ExecutorService;
68
import java.util.concurrent.Executors;
9+
import java.util.concurrent.atomic.AtomicBoolean;
10+
import java.util.concurrent.BlockingQueue;
11+
import java.util.logging.Level;
12+
import java.util.logging.Logger;
713

814
public class SSEPushManager {
15+
private static final Logger logger = Logger.getLogger(SSEPushManager.class.getName());
916
private final ConcurrentHashMap<String, SSEClient> clients = new ConcurrentHashMap<>();
1017
private final ExecutorService executor = Executors.newCachedThreadPool();
18+
private final AtomicBoolean isShutdown = new AtomicBoolean(false);
1119

1220
private static final SSEPushManager instance = new SSEPushManager();
1321

@@ -18,24 +26,59 @@ public static SSEPushManager getInstance() {
1826
}
1927

2028
public void register(String sessionId, Response out) {
21-
SSEClient client = new SSEClient(out);
29+
register(sessionId, out, null);
30+
}
31+
32+
public void register(String sessionId, Response out, BlockingQueue<Builder> messageQueue) {
33+
if (isShutdown.get()) {
34+
logger.log(Level.WARNING, "SSEPushManager is shutting down, cannot register new client");
35+
return;
36+
}
37+
38+
SSEClient oldClient = clients.get(sessionId);
39+
if (oldClient != null) {
40+
oldClient.close();
41+
}
42+
43+
SSEClient client = messageQueue != null ?
44+
new SSEClient(out, messageQueue) :
45+
new SSEClient(out);
46+
2247
clients.put(sessionId, client);
2348
executor.submit(client);
2449
}
2550

26-
public void push(String sessionId, String message) {
51+
public void push(String sessionId, Builder message) {
52+
if (isShutdown.get()) {
53+
logger.log(Level.WARNING, "SSEPushManager is shutting down, cannot push message");
54+
return;
55+
}
56+
2757
SSEClient client = clients.get(sessionId);
28-
if (client != null && client.isActive()) {
29-
client.send(message);
58+
if (client != null) {
59+
if (client.isActive()) {
60+
client.send(message);
61+
} else {
62+
// Clean up inactive client
63+
clients.remove(sessionId, client);
64+
}
3065
}
3166
}
3267

33-
public void broadcast(String message) {
34-
for (SSEClient client : clients.values()) {
68+
public void broadcast(Builder message) {
69+
if (isShutdown.get()) {
70+
logger.log(Level.WARNING, "SSEPushManager is shutting down, cannot broadcast message");
71+
return;
72+
}
73+
74+
clients.forEach((sessionId, client) -> {
3575
if (client.isActive()) {
3676
client.send(message);
77+
} else {
78+
// Clean up inactive client
79+
clients.remove(sessionId, client);
3780
}
38-
}
81+
});
3982
}
4083

4184
public void remove(String sessionId) {
@@ -48,4 +91,15 @@ public void remove(String sessionId) {
4891
public Set<String> getClientIds() {
4992
return clients.keySet();
5093
}
94+
95+
public void shutdown() {
96+
if (isShutdown.compareAndSet(false, true)) {
97+
// Close all clients
98+
clients.forEach((sessionId, client) -> client.close());
99+
clients.clear();
100+
101+
// Shutdown executor
102+
executor.shutdown();
103+
}
104+
}
51105
}

0 commit comments

Comments
 (0)