func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error)
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
func (c *Conn) Close() error
defer ws.Close()
func (c *Conn) ReadMessage() (messageType int, p []byte, err error)
_, message, err := ws.ReadMessage()
package router
import (
"crow-logger/service"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"log"
"net/http"
)
func GetLog(c *gin.Context) {
//升级接口
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
fmt.Print("升级websocket连接错误:", err)
return
}
defer ws.Close()
//传参(这个不是本例重点,结构体也就不放出来了)
var sshInfo service.SshInfo
sshInfo.UserName = c.DefaultQuery("user_name","root")
sshInfo.SshPort = c.DefaultQuery("ssh_port","22")
sshInfo.IpAddr = c.Query("ip_addr")
sshInfo.ProjectName = c.Query("project_name")
sshInfo.ServiceName = c.Query("service_name")
// 接受消息
_, message, err := ws.ReadMessage()
if err != nil {
fmt.Println("接收错误:", err)
} else {
fmt.Println("接收到前端消息:", string(message))
err := service.SendCmd(ws,sshInfo)
if err !=nil {
c.JSON(400,gin.H{"status":"END"} )
return
}
log.Println("停止发送")
}
}
func (c *Conn) WriteMessage(messageType int, data []byte) error
err = conn.WriteMessage(websocket.TextMessage,[]byte(txt))
接前边的示例,ssh到服务器,将docker-compose的实时日志传入 websocket
package service
import (
"context"
"crow-logger/config"
"github.com/gorilla/websocket"
"github.com/yahoo/vssh"
"log"
"time"
)
func SendCmd(conn *websocket.Conn,sshInfo SshInfo)(err error) {
//前边这些都是ssh获取信息【不是本例重点】
vs := vssh.New().Start()
log.Println(sshInfo)
config,err := vssh.GetConfigPEM(sshInfo.UserName,config.IdRSAFilePath)
if err !=nil {
return err
}
for _, addr := range []string{sshInfo.IpAddr+":"+sshInfo.SshPort} {
err := vs.AddClient(addr, config, vssh.SetMaxSessions(10))
if err != nil {
log.Println(err)
}
}
vs.Wait()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
//cmd:= "ping 10.10.xxx.101"
cmd:= "cd /data/"+sshInfo.ProjectName+"/"+sshInfo.ServiceName +" && docker-compose logs -f"
log.Println(cmd)
timeout, _ := time.ParseDuration("10m")
respChan := vs.Run(ctx,cmd,timeout)
resp := <- respChan
if err = resp.Err(); err != nil {
log.Fatal(err)
}
stream := resp.GetStream()
defer stream.Close()
for stream.ScanStdout() {
txt := stream.TextStdout()
log.Println(txt)
//将ssh得到的信息通过websocket接口循环传出
err := conn.WriteMessage(websocket.TextMessage,[]byte(txt))
if err != nil{
log.Println("写入错误",err)
return err
}
}
return nil
}
gin的router设置没有特别之处,和之前一样。
package router
import (
"github.com/gin-gonic/gin"
)
func ServerWebsocket() {
r := gin.Default()
……
deployment := r.Group("/api/v1/logger")
{
deployment.GET("/",GetLog)
……
}
r.Run(":1911")
}
一个简约的前端测试代码,和后端建立websocket链接。根据需要修改地址和传参:
DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>TestWebsockettitle>
<script type="text/javascript">
function LinkServer() {
// 声明连接
var Server_Com;
if ("WebSocket" in window) {
Server_Com = new WebSocket("ws://127.0.0.1:1911/api/v1/logger/?user_name=root&ssh_port=22&ip_addr=10.10.239.97&project_name=crust&service_name=gis-server");
console.log("新建连接到->127.0.0.1:1911");
}
// 建立连接后发送
Server_Com.onopen = function() {
Server_Com.send("Hello Server!"); // Web Socket 已连接上,使用 send() 方法发送数据
console.log("已连接上服务器");
}
// 接收服务器消息
Server_Com.onmessage = function(event) {
var recv_msg = event.data;
if (recv_msg == "Hello Client!") {
console.log("接收到服务器的问候: " + recv_msg); // 用于提示收到信息
} else {
document.getElementById("Time").textContent = recv_msg; // 实时更新显示服务器发回的时间
console.log("接收到服务器数据: " + recv_msg);
}
}
}
script>
head>
<body>
<p>接收到的信息:p>
<p id="Time">crow-logger测试p>
<button onclick="LinkServer()">连接button>
body>
html>