From 13c37269de7dfded85ef4e6437b56e75654880a4 Mon Sep 17 00:00:00 2001 From: jack ning Date: Fri, 7 Mar 2025 18:19:36 +0800 Subject: [PATCH] update starter/src: add 2 mod 5 files --- .../dashscope/SpringAIDashscopeService.java | 71 ++++-- .../bytedesk/core/config/BytedeskMetrics.java | 220 ++++++++++++++++++ .../bytedesk/core/config/MetricsConfig.java | 32 +++ modules/pom.xml | 6 - 4 files changed, 301 insertions(+), 28 deletions(-) create mode 100644 modules/core/src/main/java/com/bytedesk/core/config/BytedeskMetrics.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/config/MetricsConfig.java diff --git a/modules/ai/src/main/java/com/bytedesk/ai/springai/dashscope/SpringAIDashscopeService.java b/modules/ai/src/main/java/com/bytedesk/ai/springai/dashscope/SpringAIDashscopeService.java index 1aa197d206..62ef94a046 100644 --- a/modules/ai/src/main/java/com/bytedesk/ai/springai/dashscope/SpringAIDashscopeService.java +++ b/modules/ai/src/main/java/com/bytedesk/ai/springai/dashscope/SpringAIDashscopeService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2025-02-28 17:56:26 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-03-07 17:00:40 + * @LastEditTime: 2025-03-07 18:08:39 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -13,12 +13,9 @@ */ package com.bytedesk.ai.springai.dashscope; -import java.util.List; import java.util.Optional; import org.springframework.ai.chat.client.ChatClient; -import org.springframework.ai.chat.messages.AssistantMessage; -import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -32,6 +29,9 @@ import com.bytedesk.core.message.MessageProtobuf; import com.bytedesk.core.message.MessageTypeEnum; import lombok.extern.slf4j.Slf4j; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; @Slf4j @Service @@ -43,34 +43,61 @@ public class SpringAIDashscopeService extends BaseSpringAIService { @Qualifier("bytedeskDashScopeChatClient") private final ChatClient bytedeskDashScopeChatClient; + private final Counter aiRequestCounter; + private final Timer aiResponseTimer; + public SpringAIDashscopeService( @Qualifier("bytedeskDashScopeChatClient") ChatClient bytedeskDashScopeChatClient, Optional springAIVectorService, - IMessageSendService messageSendService) { + IMessageSendService messageSendService, + MeterRegistry registry) { super(springAIVectorService, messageSendService); // this.bytedeskDashScopeChatModel = bytedeskDashScopeChatModel; this.bytedeskDashScopeChatClient = bytedeskDashScopeChatClient; + + // 初始化监控指标 + this.aiRequestCounter = Counter.builder("bytedesk.ai.dashscope.requests") + .description("Number of DashScope AI requests") + .register(registry); + + this.aiResponseTimer = Timer.builder("bytedesk.ai.dashscope.response.time") + .description("DashScope AI response time") + .register(registry); } @Override protected void processPrompt(Prompt prompt, MessageProtobuf messageProtobuf) { - bytedeskDashScopeChatClient.prompt(prompt.toString()) - .stream() - .content() - .subscribe( - content -> { - messageProtobuf.setType(MessageTypeEnum.STREAM); - messageProtobuf.setContent(content); - messageSendService.sendProtobufMessage(messageProtobuf); - }, - error -> { - log.error("DashScope API error: ", error); - messageProtobuf.setType(MessageTypeEnum.ERROR); - messageProtobuf.setContent("服务暂时不可用,请稍后重试"); - messageSendService.sendProtobufMessage(messageProtobuf); - }, - () -> log.info("Chat stream completed") - ); + aiRequestCounter.increment(); + + Timer.Sample sample = Timer.start(); + try { + bytedeskDashScopeChatClient.prompt(prompt.toString()) + .stream() + .content() + .subscribe( + content -> { + messageProtobuf.setType(MessageTypeEnum.STREAM); + messageProtobuf.setContent(content); + messageSendService.sendProtobufMessage(messageProtobuf); + }, + error -> { + log.error("DashScope API error: ", error); + messageProtobuf.setType(MessageTypeEnum.ERROR); + messageProtobuf.setContent("服务暂时不可用,请稍后重试"); + messageSendService.sendProtobufMessage(messageProtobuf); + }, + () -> { + sample.stop(aiResponseTimer); + log.info("Chat stream completed"); + } + ); + } catch (Exception e) { + sample.stop(aiResponseTimer); + log.error("Error in processPrompt", e); + messageProtobuf.setType(MessageTypeEnum.ERROR); + messageProtobuf.setContent("服务暂时不可用,请稍后重试"); + messageSendService.sendProtobufMessage(messageProtobuf); + } } diff --git a/modules/core/src/main/java/com/bytedesk/core/config/BytedeskMetrics.java b/modules/core/src/main/java/com/bytedesk/core/config/BytedeskMetrics.java new file mode 100644 index 0000000000..9528e45428 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/config/BytedeskMetrics.java @@ -0,0 +1,220 @@ +package com.bytedesk.metrics; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import lombok.extern.slf4j.Slf4j; + +/** + * 统一管理监控指标 + */ +@Slf4j +@Component +public class BytedeskMetrics { + + private final MeterRegistry registry; + + // 系统指标 + private final Counter totalRequestCounter; + private final Timer httpRequestTimer; + private final Gauge jvmMemoryGauge; + + // 用户相关指标 + private final Counter onlineUsersCounter; + private final Counter totalUsersCounter; + private final Counter activeUsersCounter; + + // 消息相关指标 + private final Counter messageCounter; + private final Counter messageErrorCounter; + private final Timer messageProcessTimer; + + // AI相关指标 + private final Counter aiRequestCounter; + private final Counter aiErrorCounter; + private final Timer aiResponseTimer; + + // WebSocket相关指标 + private final Counter wsConnectionCounter; + private final Counter wsMessageCounter; + private final Gauge wsActiveConnectionGauge; + + @Autowired + public BytedeskMetrics(MeterRegistry registry) { + this.registry = registry; + + // 初始化系统指标 + this.totalRequestCounter = Counter.builder("bytedesk.requests.total") + .description("Total number of requests") + .register(registry); + + this.httpRequestTimer = Timer.builder("bytedesk.http.request.time") + .description("HTTP request processing time") + .register(registry); + + this.jvmMemoryGauge = Gauge.builder("bytedesk.jvm.memory", + Runtime.getRuntime(), + runtime -> runtime.totalMemory() - runtime.freeMemory()) + .description("JVM memory usage") + .register(registry); + + // 初始化用户相关指标 + this.onlineUsersCounter = Counter.builder("bytedesk.users.online") + .description("Number of online users") + .register(registry); + + this.totalUsersCounter = Counter.builder("bytedesk.users.total") + .description("Total number of registered users") + .register(registry); + + this.activeUsersCounter = Counter.builder("bytedesk.users.active") + .description("Number of active users") + .register(registry); + + // 初始化消息相关指标 + this.messageCounter = Counter.builder("bytedesk.messages.total") + .description("Total number of messages") + .register(registry); + + this.messageErrorCounter = Counter.builder("bytedesk.messages.errors") + .description("Number of message errors") + .register(registry); + + this.messageProcessTimer = Timer.builder("bytedesk.message.process.time") + .description("Message processing time") + .register(registry); + + // 初始化AI相关指标 + this.aiRequestCounter = Counter.builder("bytedesk.ai.requests") + .description("Number of AI requests") + .register(registry); + + this.aiErrorCounter = Counter.builder("bytedesk.ai.errors") + .description("Number of AI errors") + .register(registry); + + this.aiResponseTimer = Timer.builder("bytedesk.ai.response.time") + .description("AI response time") + .register(registry); + + // 初始化WebSocket相关指标 + this.wsConnectionCounter = Counter.builder("bytedesk.ws.connections") + .description("Number of WebSocket connections") + .register(registry); + + this.wsMessageCounter = Counter.builder("bytedesk.ws.messages") + .description("Number of WebSocket messages") + .register(registry); + + this.wsActiveConnectionGauge = Gauge.builder("bytedesk.ws.active.connections", + () -> 0D) // 初始值为0 + .description("Number of active WebSocket connections") + .register(registry); + } + + // 系统指标操作方法 + public void incrementRequestCount() { + totalRequestCounter.increment(); + } + + public Timer.Sample startHttpRequestTimer() { + return Timer.start(registry); + } + + public void stopHttpRequestTimer(Timer.Sample sample) { + sample.stop(httpRequestTimer); + } + + // 用户相关指标操作方法 + public void userConnected() { + onlineUsersCounter.increment(); + } + + public void userDisconnected() { + onlineUsersCounter.increment(-1); + } + + public void newUserRegistered() { + totalUsersCounter.increment(); + } + + public void userBecameActive() { + activeUsersCounter.increment(); + } + + public void userBecameInactive() { + activeUsersCounter.increment(-1); + } + + // 消息相关指标操作方法 + public void messageReceived() { + messageCounter.increment(); + } + + public void messageError() { + messageErrorCounter.increment(); + } + + public Timer.Sample startMessageProcessTimer() { + return Timer.start(registry); + } + + public void stopMessageProcessTimer(Timer.Sample sample) { + sample.stop(messageProcessTimer); + } + + // AI相关指标操作方法 + public void aiRequestMade() { + aiRequestCounter.increment(); + } + + public void aiError() { + aiErrorCounter.increment(); + } + + public Timer.Sample startAiResponseTimer() { + return Timer.start(registry); + } + + public void stopAiResponseTimer(Timer.Sample sample) { + sample.stop(aiResponseTimer); + } + + // WebSocket相关指标操作方法 + public void wsConnectionOpened() { + wsConnectionCounter.increment(); + } + + public void wsConnectionClosed() { + wsConnectionCounter.increment(-1); + } + + public void wsMessageSent() { + wsMessageCounter.increment(); + } + + public void updateWsActiveConnections(int count) { + registry.gauge("bytedesk.ws.active.connections", count); + } + + // 获取指标值的方法 + public double getOnlineUsersCount() { + return onlineUsersCounter.count(); + } + + public double getTotalMessagesCount() { + return messageCounter.count(); + } + + public double getAiRequestsCount() { + return aiRequestCounter.count(); + } + + public double getWsConnectionsCount() { + return wsConnectionCounter.count(); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/config/MetricsConfig.java b/modules/core/src/main/java/com/bytedesk/core/config/MetricsConfig.java new file mode 100644 index 0000000000..720c781922 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/config/MetricsConfig.java @@ -0,0 +1,32 @@ +package com.bytedesk.starter.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; + +@Configuration +public class MetricsConfig { + + @Bean + public Counter onlineUsersCounter(MeterRegistry registry) { + return Counter.builder("bytedesk.online.users") + .description("Number of online users") + .register(registry); + } + + @Bean + public Counter messageCounter(MeterRegistry registry) { + return Counter.builder("bytedesk.messages") + .description("Number of messages processed") + .register(registry); + } + + @Bean + public Counter aiRequestCounter(MeterRegistry registry) { + return Counter.builder("bytedesk.ai.requests") + .description("Number of AI requests processed") + .register(registry); + } +} \ No newline at end of file diff --git a/modules/pom.xml b/modules/pom.xml index 030ec61dce..7e3862ffd5 100644 --- a/modules/pom.xml +++ b/modules/pom.xml @@ -154,12 +154,6 @@ ${querydsl.version} --> - - io.micrometer - micrometer-observation - provided - -