github.com/gin-gonic/gin
nhooyr.io/websocket
github.com/docker/docker/client
├── docker
│ ├── conn.go
│ └── conn_test.go
├── go.mod
├── go.sum
├── html
│ └── index.html
└── main.go
代码展示
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
style>
head>
<body>
<div id="app">
<div>
<input type="button" value="查看" v-on:click="connWs">
div>
<div v-for="msgn in msgList">
<span>${ msgn }span>
div>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script type="text/javascript">
let gWs
let app = new Vue({
el: '#app',
data: {
msgList: [],
msg: "",
},
methods: {
connWs: function () {
let that = this
let host = location.host;
gWs = new WebSocket("ws://" + host + "/ws");
gWs.onopen = function () {
}
gWs.onmessage = function (evt) {
let receive = JSON.parse(evt.data)
that.msgList.push(receive)
}
gWs.onerror = function (evt) {
console.log("websocket 发生错误")
console.log(evt)
}
gWs.onclose = function () {
console.log("conn 已经关闭")
}
},
sendMessage: function () {
let msg = JSON.stringify({"content": this.msg})
gWs.send(msg)
this.msg = ""
}
},
delimiters: ['${', '}']
})
script>
html>
前端 WebSocket 的核心是构造函数和几个回调函数。
在用户点击进入聊天室时,根据 Vue 绑定的事件,会执行上面的代码,发起 WebSocket 连接,服务端会将相关信息通过 WebSocket 长连接返回给客户端,客户端通过 WebSocket.onmessage
回调进行处理。
得益于 Vue 的双向绑定,在数据显示、事件绑定等方面,处理起来很方便。
关于前端的实现,这里有几点提醒下读者:
{{}}
,和 Go 的一样,避免冲突进行了修改;代码展示
获取容器日志(conn.go)
package docker
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"io"
"log"
)
func Conn() io.Reader {
//创建连接
client, err := client.NewClientWithOpts(client.WithHost("tcp://10.0.0.20:2375"))
if err != nil {
log.Println(err)
}
//使用连接获取容器日志,返回一个io.Reader
logs, err := client.ContainerLogs(context.TODO(), "xenodochial_blackburn", types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Follow: true,
})
return logs
}
方法 client.ContainerLogs(ctx context.Context, container string, options types.ContainerLogsOptions)返回一个io.Reader和错误,第一个参数为上下文,第二个参数为容器id或者容器名称,第三个参数是容器日志选项结构体,
type ContainerLogsOptions struct {
ShowStdout bool // 是否返回日志从stdout 默认false
ShowStderr bool // 是否返回日志从stderr 默认false
Since string // 从这个时间返回日志,时间类型为unix timestamp
Until string // 返回这个时间前的日志,时间类型为unix timestamp
Timestamps bool // 是否在日志行前显示时间戳,默认false
Follow bool // 是否返回日志流,默认false
Tail string // 返回日志行数,默认为all
Details bool
}
main.go
package main
import (
"bufio"
"github.com/gin-gonic/gin"
"kuludi-websocket/docker"
"log"
"net/http"
"nhooyr.io/websocket"
"nhooyr.io/websocket/wsjson"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("html/*")
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
r.GET("/ws", func(c *gin.Context) {
//创建websocker连接
conn, err := websocket.Accept(c.Writer, c.Request, nil)
if err != nil {
log.Println("websocket accept error: ", err)
return
}
//获取容器日志的io.Reader
reader := docker.Conn()
//bufio新建Reader
r := bufio.NewReader(reader)
for {
//循环从reader中根据换行符读取并转换为string
s, err := r.ReadString('\n')
if err != nil {
log.Println("read err: ", err)
}
//发送数据
err = wsjson.Write(c.Request.Context(), conn, &s)
if err != nil {
log.Println("wsjson.writer err: ", err)
}
}
})
r.Run()
}
访问http://localhost:8080