通信
毫无疑问,涉及到后端大规模向前端推送消息,需要借助WebSocket实现,WebSocket是一种在单个TCP连接上进行全双工通信的协议,能够快速便捷的实现前后端消息的实时交互
后端
后端基于SpringBoot框架实现,需要额外引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
获取实时日志的方式有多种,这里提供一种比较简单的方法,建立一个logback日志监听器,抓取需要的日志
public class LoggerListener extends AppenderBase<ILoggingEvent> {
@Override
protected void append(ILoggingEvent event) {
System.out.printf("log name:[%s] level:[%s] timestamp:[%s] message:[%s]",event.getLoggerName(),event.getLevel(),event.getTimeStamp(),event.getMessage());
...过滤和操作日志
}
}
同时需要在配置文件中声明
<appender name="my-log-listener" class="priv.sivi.test.LoggerListener"/>
<root level="INFO">
<appender-ref ref="stdout"/>
<appender-ref ref="my-log-listener"/>
</root>
我们需要注册websocket的请求
@Configuration
@EnableWebSocket
public class WebSocketRegistrar implements WebSocketConfigurer {
@Autowired
private WsHandler wsHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//注册路径
registry.addHandler(wsHandler, "/ws/*")
.setAllowedOrigins("*");
}
}
管理session
@Service
public class WsHandler extends AbstractWebSocketHandler {
public final static Map<String, WebSocketSession> SESSIONS = new HashMap<>();
@Override
public void afterConnectionEstablished(@NonNull WebSocketSession session) {
Optional.ofNullable(session.getUri())
.ifPresent(uri -> SESSIONS.put(uri.getPath(),session));
}
@Override
public void afterConnectionClosed(WebSocketSession session, @NonNull CloseStatus status) {
Optional.ofNullable(session.getUri())
.ifPresent(uri -> {
try {
SESSIONS.remove(uri.getPath()).close();
} catch (IOException exception) {
exception.printStackTrace();
}
});
}
}
从而可以在程序任意位置调用session向前端发送消息
SESSIONS.get(uri).sendMessage(new TextMessage(message));
前端
前端基于Vue框架实现,UI采用quasar,相当不错的UI框架,有很丰富的组件库,全平台支持(但是亲测移动端不咋地)
这里演示了一个简单的websocket单例,一般来说需要做一个工厂模式,同时由于涉及网络通信,需要考虑断连,重连,超时等问题
class WsSingle {
webSocket;
load(url, callback) {
this.webSocket = new WebSocket(url)
this.webSocket.onmessage = function (e) {
callback(e);
};
this.webSocket.onError = function (e){
console.log(e)
}
}
get() {
return this.webSocket;
}
close() {
if (this.webSocket != null) {
this.webSocket.close();
}
}
}
const ws = new WsSingle();
export default ws;
显示界面
<template>
<div>
<div className="q-pa-md row justify-center">
<div style="width: 100%; max-width: 400px">
<div v-for="(row,index) in display" :key="index">
<q-chat-message
:text=[row.message]
sent
/>
</div>
</div>
</div>
</div>
</template>
一般在初始化创建session,销毁时关闭session
destroyed() {
ws.close()
},
mounted() {
ws.load(`ws://${window.location.host}/ws/chat`, this.print.bind(this))
},
methods: {
print(message) {
this.display.push({message: message.data})
}
从而可以实现一个简单的实时日志显示窗口