You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
4.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package xyz.wbsite.mcp.basic.controller;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import xyz.wbsite.mcp.basic.model.McpResponse;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* SSE广播器类
* 用于管理和广播SSE事件给所有已连接的客户端
*
* @author wangbing
*/
@Service
public class SseBroadcaster {
private static final Logger log = LoggerFactory.getLogger(SseBroadcaster.class);
private final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();
/**
* 注册新的SSE发射器
*
* @param emitter sse发射器
*/
public void registerEmitter(SseEmitter emitter) {
// Add the emitter to the list
emitters.add(emitter);
log.info("New SSE emitter registered, total emitters: {}", emitters.size());
// Set up completion handlers to remove the emitter when the connection is closed
emitter.onCompletion(() -> {
emitters.remove(emitter);
log.info("SSE emitter completed, remaining emitters: {}", emitters.size());
});
emitter.onTimeout(() -> {
emitters.remove(emitter);
log.info("SSE emitter timed out, remaining emitters: {}", emitters.size());
});
emitter.onError((e) -> {
emitters.remove(emitter);
log.error("Error on SSE emitter: {}", e.getMessage(), e);
});
}
/**
* 将初始端点事件发送到特定发射器
*
* @param emitter sse发射器
*/
public void sendEndpointEvent(SseEmitter emitter) {
try {
emitter.send(SseEmitter.event()
.name("endpoint")
.data("/mcp/post"));
log.info("Sent endpoint event to SSE emitter");
} catch (IOException e) {
log.error("Failed to send endpoint event: {}", e.getMessage(), e);
emitter.completeWithError(e);
}
}
/**
* 由POST端点处理程序调用通过SSE发回响应
*
* @param response 响应.
*/
public void broadcastResponse(McpResponse response) {
if (emitters.isEmpty()) {
log.warn("No active SSE emitters to broadcast response (ID: {})\n", response.getId());
return;
}
// Responses are sent as events of type "message"
String jsonResponse = JSONUtil.toJsonStr(response);
for (SseEmitter emitter : emitters) {
try {
emitter.send(SseEmitter.event()
.name("message")
.data(jsonResponse));
log.debug("Successfully sent SSE message for Response ID: {}", response.getId());
} catch (IOException e) {
log.error("Failed to send SSE message (Response ID: {}): {}", response.getId(), e.getMessage(), e);
// 考虑移除有故障的发射器
emitter.completeWithError(e);
}
}
}
/**
* Sends a simple notification (like log messages, tool updates) over SSE.
* This isn't used in the simple weather example but shows how non-response
* messages would be sent.
*
* @param notification An object representing the notification.
* @param eventName The SSE event name (e.g., "notifications/message").
*/
public void broadcastNotification(Object notification, String eventName) {
if (emitters.isEmpty()) {
log.warn("No active SSE emitters to broadcast notification ({})\n", eventName);
return;
}
String jsonNotification = JSONUtil.toJsonStr(notification);
for (SseEmitter emitter : emitters) {
try {
emitter.send(SseEmitter.event()
.name(eventName)
.data(jsonNotification));
log.debug("Successfully sent SSE notification ({})\n", eventName);
} catch (IOException e) {
log.error("Failed to send SSE notification ({}): {}", eventName, e.getMessage(), e);
emitter.completeWithError(e);
}
}
}
}

Powered by TurnKey Linux.