-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add get real-time memory usage sse endpoint
- Loading branch information
Showing
10 changed files
with
219 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
app/src/main/kotlin/com/akagiyui/drive/controller/persist/MemorySseController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.akagiyui.drive.controller.persist | ||
|
||
import com.akagiyui.drive.component.permission.RequirePermission | ||
import com.akagiyui.drive.model.Permission | ||
import com.akagiyui.drive.service.SystemService | ||
import org.springframework.beans.factory.DisposableBean | ||
import org.springframework.beans.factory.InitializingBean | ||
import org.springframework.stereotype.Controller | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.context.request.async.AsyncRequestNotUsableException | ||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter | ||
import java.util.concurrent.CopyOnWriteArrayList | ||
|
||
|
||
/** | ||
* 内存信息 Server-Sent Events 处理器 | ||
* @author AkagiYui | ||
*/ | ||
@Controller | ||
class MemorySseController(private val systemService: SystemService) : InitializingBean, DisposableBean { | ||
val sseEmitters = CopyOnWriteArrayList<SseEmitter>() | ||
|
||
@GetMapping("/system/memory/sse") | ||
@RequirePermission(Permission.SYSTEM_INFO_GET) | ||
fun sse(): SseEmitter { | ||
val sseEmitter = SseEmitter() | ||
sseEmitters.add(sseEmitter) | ||
sseEmitter.onCompletion { sseEmitters.remove(sseEmitter) } | ||
sseEmitter.send(systemService.getMemoryInfoHistory()) // 发送历史数据 | ||
return sseEmitter | ||
} | ||
|
||
private fun broadcast(obj: Any) { | ||
sseEmitters.forEach { | ||
try { | ||
it.send(obj) | ||
} catch (e: AsyncRequestNotUsableException) { | ||
// 客户端已断开连接 | ||
} | ||
} | ||
} | ||
|
||
override fun afterPropertiesSet() { | ||
systemService.addRealTimeInfoCallback(::broadcast) | ||
} | ||
|
||
override fun destroy() { | ||
systemService.removeRealTimeInfoCallback(::broadcast) | ||
val sseEmitters = this.sseEmitters.toList() | ||
for (sseEmitter in sseEmitters) { | ||
sseEmitter.complete() | ||
} | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
app/src/main/kotlin/com/akagiyui/drive/controller/persist/MemoryWebSocketHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.akagiyui.drive.controller.persist | ||
|
||
import com.akagiyui.common.BroadcastWebSocketHandler | ||
import com.akagiyui.common.model.WebSocketHandlerWithPermissions | ||
import com.akagiyui.drive.model.Permission | ||
import com.akagiyui.drive.service.SystemService | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import org.springframework.beans.factory.DisposableBean | ||
import org.springframework.beans.factory.InitializingBean | ||
import org.springframework.stereotype.Component | ||
import org.springframework.web.socket.TextMessage | ||
import org.springframework.web.socket.WebSocketSession | ||
|
||
|
||
/** | ||
* 内存信息 WebSocket 处理器 | ||
* @author AkagiYui | ||
*/ | ||
@Component | ||
class MemoryWebSocketHandler(private val systemService: SystemService) : BroadcastWebSocketHandler(), | ||
WebSocketHandlerWithPermissions, InitializingBean, DisposableBean { | ||
override val permissions = setOf(Permission.SYSTEM_INFO_GET) | ||
private val objectMapper = ObjectMapper() | ||
|
||
override fun afterConnectionEstablished(session: WebSocketSession) { | ||
// 发送历史数据 | ||
session.sendMessage(objectEncode(systemService.getMemoryInfoHistory())) | ||
super.afterConnectionEstablished(session) | ||
} | ||
|
||
private fun objectEncode(obj: Any): TextMessage { | ||
return TextMessage(objectMapper.writeValueAsString(obj)) | ||
} | ||
|
||
private fun callback(memoryInfo: Map<String, Any>) { | ||
broadcast(objectEncode(memoryInfo)) | ||
} | ||
|
||
override fun afterPropertiesSet() { | ||
systemService.addRealTimeInfoCallback(::callback) | ||
} | ||
|
||
override fun destroy() { | ||
systemService.removeRealTimeInfoCallback(::callback) | ||
closeAll() | ||
} | ||
} |
50 changes: 0 additions & 50 deletions
50
app/src/main/kotlin/com/akagiyui/drive/controller/websocket/MemoryWebSocketHandler.kt
This file was deleted.
Oops, something went wrong.
25 changes: 25 additions & 0 deletions
25
app/src/main/kotlin/com/akagiyui/drive/service/SystemService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.akagiyui.drive.service | ||
|
||
/** | ||
* 系统服务接口 | ||
* @author AkagiYui | ||
*/ | ||
|
||
interface SystemService { | ||
|
||
/** | ||
* 添加实时信息回调 | ||
*/ | ||
fun addRealTimeInfoCallback(callback: (Map<String, Any>) -> Unit) | ||
|
||
/** | ||
* 删除实时信息回调 | ||
*/ | ||
fun removeRealTimeInfoCallback(callback: (Map<String, Any>) -> Unit) | ||
|
||
/** | ||
* 获取历史内存信息 | ||
*/ | ||
fun getMemoryInfoHistory(): List<Map<String, Number>> | ||
|
||
} |
51 changes: 51 additions & 0 deletions
51
app/src/main/kotlin/com/akagiyui/drive/service/impl/SystemServiceImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.akagiyui.drive.service.impl | ||
|
||
import cn.hutool.core.collection.ConcurrentHashSet | ||
import com.akagiyui.common.collection.CircularBuffer | ||
import com.akagiyui.drive.service.SystemService | ||
import org.springframework.scheduling.annotation.Scheduled | ||
import org.springframework.stereotype.Service | ||
|
||
/** | ||
* 系统服务实现 | ||
* @author AkagiYui | ||
*/ | ||
@Service | ||
class SystemServiceImpl : SystemService { | ||
|
||
private val realTimeInfoCallbacks = ConcurrentHashSet<(Map<String, Any>) -> Unit>() | ||
private val memoryInfoBuffer: CircularBuffer<Map<String, Number>> = CircularBuffer(60) | ||
|
||
/** | ||
* 定时收集内存信息 | ||
*/ | ||
@Scheduled(fixedDelay = 1000) | ||
private fun collectInfo() { | ||
val runtime = Runtime.getRuntime() | ||
val totalMemory = runtime.totalMemory() | ||
val freeMemory = runtime.freeMemory() | ||
|
||
val memoryInfo = mapOf( | ||
"time" to System.currentTimeMillis(), // 时间戳 | ||
"totalMemory" to runtime.totalMemory(), | ||
"freeMemory" to runtime.freeMemory(), | ||
"usedMemory" to totalMemory - freeMemory, | ||
"maxMemory" to runtime.maxMemory() | ||
) | ||
memoryInfoBuffer.append(memoryInfo) | ||
realTimeInfoCallbacks.forEach { it(memoryInfo) } | ||
} | ||
|
||
override fun addRealTimeInfoCallback(callback: (Map<String, Any>) -> Unit) { | ||
realTimeInfoCallbacks.add(callback) | ||
} | ||
|
||
override fun removeRealTimeInfoCallback(callback: (Map<String, Any>) -> Unit) { | ||
realTimeInfoCallbacks.remove(callback) | ||
} | ||
|
||
override fun getMemoryInfoHistory(): List<Map<String, Number>> { | ||
return memoryInfoBuffer.getAll() | ||
} | ||
|
||
} |