gin使用websocket实现数据实时推送(写法简单好理解,一文说明白)

339 阅读3分钟

业务场景

今天给自己的项目加了几个echarts图表,主要用以显示我的几个系统的近一周访问量,大概是这个样子

charts.jpg 三个图标分别是:我的简历访问量,我的音乐访问量,简历和音乐访问量对比

1.数据采集

这个访问信息的采集是在我的简历和我的音乐页面提交信息,也就是个很简单的接口,提交用户访问该页面的时间和页面类型(简历页面或者是音乐页面)

2,数据整理

把近一周时间内每天的访问量整理出来返回给前端系统

// 获取我的音乐本周访问量
func (this Personal) GetMusicData(ctx *gin.Context) {
	now := time.Now()
	year, month, day := now.Date()
	// 今天的时间
	today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
	//今天的时间-1
	t1 := today.AddDate(0, 0, -1)
	//今天的时间-2
	t2 := today.AddDate(0, 0, -2)
	//今天的时间-3
	t3 := today.AddDate(0, 0, -3)
	//今天的时间-4
	t4 := today.AddDate(0, 0, -4)
	//今天的时间-5
	t5 := today.AddDate(0, 0, -5)
	//今天的时间-6
	t6 := today.AddDate(0, 0, -6)
	tourist1 := []modules.Tourist{}
	tourist2 := []modules.Tourist{}
	tourist3 := []modules.Tourist{}
	tourist4 := []modules.Tourist{}
	tourist5 := []modules.Tourist{}
	tourist6 := []modules.Tourist{}
	tourist7 := []modules.Tourist{}

	err := modules.DB.Where("scene LIKE ? AND add_time > ?", "%music%", today).Find(&tourist1).Error
	err2 := modules.DB.Where("scene LIKE ? AND add_time > ? AND add_time < ?", "%music%", t1, today).Find(&tourist2).Error
	err3 := modules.DB.Where("scene LIKE ? AND add_time > ? AND add_time < ?", "%music%", t2, t1).Find(&tourist3).Error
	err4 := modules.DB.Where("scene LIKE ? AND add_time > ? AND add_time < ?", "%music%", t3, t2).Find(&tourist4).Error
	err5 := modules.DB.Where("scene LIKE ? AND add_time > ? AND add_time < ?", "%music%", t4, t3).Find(&tourist4).Error
	err6 := modules.DB.Where("scene LIKE ? AND add_time > ? AND add_time < ?", "%music%", t5, t4).Find(&tourist4).Error
	err7 := modules.DB.Where("scene LIKE ? AND add_time > ? AND add_time < ?", "%music%", t6, t5).Find(&tourist4).Error
	if err != nil || err2 != nil || err3 != nil || err4 != nil || err5 != nil || err6 != nil || err7 != nil {
		ctx.JSON(400, gin.H{
			"message": "获取失败",
		})
	} else {
		arr := []int{len(tourist7), len(tourist6), len(tourist5), len(tourist4), len(tourist3), len(tourist2), len(tourist1)}
		ctx.JSON(200, gin.H{
			"message": "获取成功",
			"data":    arr,
		})
	}
}

这是整理音乐页面访问量的函数,简历页面同理

3,前端获取到数据后把数据绑定给echarts图标

... ... 此处省略前端代码若干行

4,重点部分:使用websocket和服务端生成长链接,访问量变化了实时通知前端获取最新数据

后端部分
"github.com/gorilla/websocket"

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}
//这里声明一个链接池
var conns []*websocket.Conn

func (this UserController) WS(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		println("upgrade错误:", err)
		return
	}
	defer c.Close()
        //把所有链接存在链接池里
	conns = append(conns, c)
	for {
		_, _, err := c.ReadMessage()
		if err != nil {
			println("read:", err)
			break
		}
	}
}

在数据采集到函数里,当收到有人访问那两个页面,把链接池里的链接都通知一遍

func (this UserController) AddTourist(ctx *gin.Context) {
	scene := ctx.Request.FormValue("scene")
	message := ctx.Request.FormValue("message")
	time := time.Now().UTC()
	tourist := &modules.Tourist{
		Scene:   scene,
		Message: message,
		AddTime: time,
	}
	err := modules.DB.Model(&modules.Tourist{}).Create(&tourist).Error
	if err != nil {
		ctx.JSON(400, gin.H{
			"message": "新增浏览用户信息失败",
		})
	} else {
        //这里是重点,循环链接池,广播消息,消息类型随便定,只要你前端知道就行,我这里通知一个数字1
		for i := range conns {
                        if index := strings.Index(scene, "jianli"); index != -1 {
				conns[i].WriteMessage(websocket.TextMessage, []byte("jianli"))
			} else if index := strings.Index(scene, "music"); index != -1 {
				conns[i].WriteMessage(websocket.TextMessage, []byte("music"))
			}
		}
		ctx.JSON(200, gin.H{
			"message": "新增浏览用户信息成功!",
		})
	}
}

然后是管理系统收到推送的消息,重新刷新数据,这样就实现了数据随访问量实时变化的功能

const websocket=new WebSocket("ws://xxx.xxx.xxx.xxx:8088/ws")
websocket.onopen=(evt)=>{
  console.log("链接成功")
}
websocket.onmessage=(evt)=>{
//收到后端推送的消息,刷新对应数据
  if(evt.data=="jianli"){
    refreshChartJL()
  }else if(evt.data=="music"){
    refreshChartMusic()
  }
}
websocket.onclose=()=>{
  console.log("链接关闭")
}
const getAllData=()=>{
    ...
    ...
    ...
}

总结:这个功能实现下来,也并不难,但一定要动手实现一下,如果你有啥疑问,可以留言,我每天都能看到