1. 程式人生 > 其它 >React+Go+WEBSocket

React+Go+WEBSocket

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
		}
	}
}