React+Go+WEBSocket
React
import { Component } from 'react'
import { Terminal } from 'xterm';
import 'xterm/css/xterm.css';
import Socket from './websocket';
import CodeMirror from '@uiw/react-codemirror';
import 'codemirror/keymap/sublime';
import 'codemirror/theme/monokai.css';
export default class JenkinsLog extends Component {
state = {
jenkinsterminal: new Terminal({
// cursorStyle: 'block', // 光标样式
// cursorBlink: true, // 光标闪烁
theme: {
foreground: '#dddddd', // 字体
cursor: 'gray', // 设置光标
},
// windowsMode: true, // 自动换行
// cols: 120,
}),
logData: "",
}
componentDidMount() {
// 获取DOM实例
// const jenkinsTerminalDOM = document.getElementById('jenkinsterminal');
// 绑定DOM和Terminal实例
// this.state.jenkinsterminal.open(jenkinsTerminalDOM);
var socketUrl = "ws://127.0.0.1:8081"
if (process.env.NODE_ENV === "production") {
socketUrl = "wss://cmp.airudder.com"
}
this.socket = new Socket({
socketUrl: `${socketUrl}/api/ws/jenkinslog`,
timeout: 5000,
socketMessage: (receive) => {
console.log(receive); //后端返回的数据,渲染页面
// this.state.jenkinsterminal.write(receive.data)
this.setState({ logData: receive.data })
},
socketClose: (msg) => {
// const { setjenkinsLogVisible } = this.props
// setjenkinsLogVisible(false)
console.log(msg);
},
socketError: () => {
console.log(this.state.taskStage + '连接建立失败');
},
socketOpen: () => {
console.log('连接建立成功');
// 心跳机制 定时向后端发数据
const { jenkinsurl } = this.props
this.socket.sendMessage(jenkinsurl)
}
});
try {
this.socket.connection();
} catch (e) {
console.log("捕获异常: ", e);
// 捕获异常,防止js error
// donothing
}
}
render() {
// const { jenkinsLog } = this.props
// this.state.jenkinsterminal.write(jenkinsLog)
return (
// <div id="jenkinsterminal" />
<CodeMirror
value={this.state.logData}
options={{
theme: 'monokai',
keyMap: 'sublime',
mode: 'jsx',
lineNumbers: false,
}}
/>
)
}
}
WEBSocket
/**
* 参数:[socketOpen|socketClose|socketMessage|socketError] = func,[socket连接成功时触发|连接关闭|发送消息|连接错误]
* timeout:连接超时时间
* @type {module.webSocket}
*/
module.exports = class webSocket {
constructor(param = {}) {
this.param = param;
this.reconnectCount = 0;
this.socket = null;
this.taskRemindInterval = null;
this.isSucces = true;
}
connection = () => {
let { socketUrl, timeout = 0 } = this.param;
// 检测当前浏览器是什么浏览器来决定用什么socket
if ('WebSocket' in window) {
console.log('WebSocket');
this.socket = new WebSocket(socketUrl);
}
else if ('MozWebSocket' in window) {
console.log('MozWebSocket');
this.socket = new MozWebSocket(socketUrl);
}
else {
console.log('SockJS');
this.socket = new SockJS(socketUrl);
}
this.socket.onopen = this.onopen;
this.socket.onmessage = this.onmessage;
this.socket.onclose = this.onclose;
this.socket.onerror = this.onerror;
this.socket.sendMessage = this.sendMessage;
this.socket.closeSocket = this.closeSocket;
// 检测返回的状态码 如果socket.readyState不等于1则连接失败,关闭连接
if (timeout) {
let time = setTimeout(() => {
if (this.socket && this.socket.readyState !== 1) {
this.socket.close();
}
clearInterval(time);
}, timeout);
}
};
// 连接成功触发
onopen = () => {
let { socketOpen } = this.param;
this.isSucces = false //连接成功将标识符改为false
socketOpen && socketOpen();
};
// 后端向前端推得数据
onmessage = (msg) => {
let { socketMessage } = this.param;
socketMessage && socketMessage(msg);
// 打印出后端推得数据
// console.log(msg);
};
// 关闭连接触发
onclose = (e) => {
this.isSucces = true //关闭将标识符改为true
console.log('关闭socket收到的数据');
let { socketClose } = this.param;
socketClose && socketClose(e);
// 根据后端返回的状态码做操作
// 我的项目是当前页面打开两个或者以上,就把当前以打开的socket关闭
// 否则就20秒重连一次,直到重连成功为止
this.socket.close();
// if (e.code == '4500') {
// this.socket.close();
// } else {
// this.taskRemindInterval = setInterval(() => {
// if (this.isSucces) {
// this.connection();
// } else {
// clearInterval(this.taskRemindInterval)
// }
// }, 20000)
// }
};
onerror = (e) => {
// socket连接报错触发
let { socketError } = this.param;
this.socket = null;
socketError && socketError(e);
};
sendMessage = (value) => {
// 向后端发送数据
if (this.socket) {
this.socket.send(JSON.stringify(value));
}
};
};
GO
import (
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 解决跨域问题
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func SocketHandler(w http.ResponseWriter, r *http.Request) {
// Upgrade our raw HTTP connection to a websocket based one
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("Error during connection upgradation:", err)
return
}
defer conn.Close()
_, msgBytes, err := conn.ReadMessage()
if err != nil {
olog.GetLogger().Error("read message failed", zap.Error(err))
return
}
jenkinsUrl := strings.Trim(string(msgBytes), `"`)
if jenkinsUrl == "" {
return
}
fmt.Println(jenkinsUrl)
errCh := make(chan error)
go func(errCh chan error) {
_, _, err := conn.ReadMessage()
errCh <- err
}(errCh)
var i int
// releaseLogMgr := repos.NewReleaseAppLogMgr()
for {
ticker := time.NewTicker(2 * time.Second)
select {
case <-ticker.C:
i++
buildlog, err := tool.GetJenkinsBuildLog(jenkinsUrl)
if err != nil {
olog.GetLogger().Info("GetJenkinsBuildLog failed", zap.Error(err))
return
}
connWriter, err := conn.NextWriter(websocket.TextMessage)
if err != nil {
olog.GetLogger().Error("get writer failed", zap.Error(err))
continue
}
if _, err := connWriter.Write([]byte(buildlog)); err != nil {
olog.GetLogger().Error("write message failed", zap.Error(err))
continue
}
if connWriter != nil {
connWriter.Close()
}
if i <= 10 {
continue
} else {
return
}
case <-errCh:
log.Info("read message failed", zap.Error(err))
return
}
}
}