golang --gin+websocket实现指定的数据点推送

这篇具有很好参考价值的文章主要介绍了golang --gin+websocket实现指定的数据点推送。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这里提到的endpointId是一个负载了数据的逻辑点,就像一根水管的出口,有新数据来就会根据后端记录的endpointId推送到用户正在查看的endpointId。用户没有正在查看的endpoint就不会有新数据推送。这里如果如果对endpoint加上权限就相当于实现对实时数据的准确推送。

main包

mian.go

package main

import (
	"mcs/backend/core"
	"mcs/backend/global"
)
func main() {
	global.DS_LOG.Info("Server Starting.......")
	core.RunWindowsServer()
}

core包

server.go

package core

import (
	"context"
	"fmt"

	"mcs/backend/global"
)

func RunWindowsServer() {
//初始化global.DS_ROUTER变量
	InitRouter()
	address := fmt.Sprintf(":%d", global.DS_CONFIG.System.Addr)
	global.DS_LOG.Infof("服务端口:%s", address)
	go global.DS_WS_MANAGER.Start()
	global.DS_ROUTER.Run(address)
}

router.go

package core

import (
	"mcs/backend/controller/register"
	"mcs/backend/global"
	"mcs/backend/middleware"

	"github.com/gin-gonic/gin"
)

func InitRouter() {
	global.DS_ROUTER = gin.Default()
	rootGroup := global.DS_ROUTER.Group("api")

	publicGroup := rootGroup.Group("v1")
	{
		// 健康监测
		register.HealthRouter.InitHealthRouter(publicGroup)
	}
	privateGroup := rootGroup.Group("v1")

	privateGroup.Use(middleware.JWTAuth())

	{
		register.WebSocketRouter.InitWebsocketRouter(privateGroup)
	}
	global.DS_LOG.Info("路由注册完成")
}

api包

webSocket.go

package api

import (
	"net/http"

	"mcs/backend/global"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

type webSocketApi struct{}

func (wsa *webSocketApi) PingV2(c *gin.Context) {
	// 升级get请求为websocket协议
	ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		return
	}
	client := global.NewWSClient(ws)
	go client.Write()
	go client.Read()
}

global包

ws.go

package global

import (
	"encoding/json"

	"github.com/gorilla/websocket"
	uuid "github.com/satori/go.uuid"
	"github.com/sirupsen/logrus"
)

// ws客户端管理
type ClientManager struct {
	clients    map[string]*Client
	broadcast  chan []byte
	register   chan *Client
	unregister chan *Client
}

// ws客户端
type Client struct {
	Alive bool   // 是否还存活
	id    string // 客户端自身的id,多个客户端管理
	// UserId        uint                   // 唯一标识客户端属于哪个请求的用户
	EndpointIdMap map[string]interface{} // 放当前该用户正在查看的endpoint,到时就只推送这个几个endpoint的新数据
	socket        *websocket.Conn
	send          chan []byte
}

func NewWSClient(socket *websocket.Conn) *Client {
	client := &Client{socket: socket, id: uuid.NewV4().String(), send: make(chan []byte)}
	DS_LOG.Infof("New user with uuid %s", client.id)
	// 客户端注册
	DS_WS_MANAGER.register <- client

	return client
}

// 发送到前端的消息结构体,前端可以根据endpointId选择把数据推送到指定的位置
type WSMessage struct {
	EndpointId *string `json:"endpointId"`
	Code       uint    `json:"code"`    // 消息代码
	Content    []byte  `json:"content"` // 消息内容
}

func (manager *ClientManager) Start() {
	logrus.Info("Websocket manager start")
	for {
		select {
		case client := <-manager.register:
			client.Alive = true
			manager.clients[client.id] = client

			// go func() {
			// 	time.Sleep(1 * time.Second)
			// 	msg := &Message{Code: UserCount, Content: manager.ClientsTotal()}
			// 	manager.Broadcast(msg)
			// }()

		case client := <-manager.unregister:
			if _, ok := manager.clients[client.id]; ok {
				DS_LOG.Infof("管道【%s】关闭", client.id)
				close(client.send)
				delete(manager.clients, client.id)
				// msg := &Message{Code: UserCount, Content: manager.ClientsTotal()}
				// manager.send(msg.JSON(), client)
				DS_LOG.Infof("管道【%s】已经关闭", client.id)
			}
		case message := <-manager.broadcast:
			msg := jsonUnmarshall(message)
			if msg.EndpointId != nil {
				for clientId := range manager.clients {
					if _, ok := manager.clients[clientId].EndpointIdMap[*msg.EndpointId]; ok {
						select {
						case manager.clients[clientId].send <- msg.Content:
							DS_LOG.Info("数据开始推送")
						default:
							/*logrus.Error("broadcast closed")
							close(conn.send)
							delete(manager.clients, conn)*/
						}
					}
				}
			}
		}
	}
}

func (msg *WSMessage) JSONMarshal() []byte {
	c, _ := json.Marshal(&msg)
	return c
}

func jsonUnmarshall(b []byte) WSMessage {
	msg := WSMessage{}
	json.Unmarshal(b, &msg)
	return msg
}

func (manager *ClientManager) send(message []byte, ignore *Client) {
	for userId := range manager.clients {
		if manager.clients[userId] != ignore {
			manager.clients[userId].send <- message
		}
	}
}

func (manager *ClientManager) Broadcast(msg *WSMessage) {
	select {
	// TODO: 需要为每个websocket管道配置单独的channel
	case manager.broadcast <- msg.JSONMarshal():
	default:
		DS_LOG.Error("无法立即写入通道,协程结束")
	}
}

func (manager *ClientManager) ClientsTotal() int {
	return len(manager.clients)
}

const (
	UserOnline  = 101 // 用户上线
	UserOffline = 102 // 用户下线
	UserCount   = 103 // 用户总数
	NewMsg      = 104 // 新消息
)

func (c *Client) Write() {
	defer func() {
		DS_LOG.Infof("User:%s closed conn", c.id)
		c.socket.Close()
	}()

	for {
		select {
		case message, ok := <-c.send:
			if !ok {
				c.socket.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			c.socket.WriteMessage(websocket.TextMessage, message)
			DS_LOG.Info("数据已经推送")
		}
	}
}

func (c *Client) Read() {
	defer func() {
		DS_WS_MANAGER.unregister <- c
		c.socket.Close()
	}()

	for {
		t, b, err := c.socket.ReadMessage()
		if err != nil {
			DS_LOG.Error(err)
			DS_WS_MANAGER.unregister <- c
			c.socket.Close()
			break
		}
		DS_LOG.Info(t)
		DS_LOG.Info(string(b))
		endpointIdArr := EndpointIdList{}
		err = json.Unmarshal(b, &endpointIdArr)
		if err != nil {
			DS_LOG.Error("WS--READ ERR:", err)
			continue
		}
		// DS_LOG.Info("endpointArr=", endpointIdArr)
		endpointIdMap := make(map[string]interface{})
		for i := 0; i < len(endpointIdArr.EndpointIds); i++ {
			endpointIdMap[endpointIdArr.EndpointIds[i]] = nil
		}
		c.EndpointIdMap = endpointIdMap

	}
}

type EndpointIdList struct {
	EndpointIds []string `json:"endpointIds"`
}

在其他包里使用

byteMsgData:=[]byte("ahdaasdsada")
	msg := global.WSMessage{
			EndpointId: &endpointId,
			Content:    byteMsgData,
		}
		global.DS_WS_MANAGER.Broadcast(&msg)

这里的代码并不能复制之后直接使用,但是websocket部分已经全部在这文章来源地址https://www.toymoban.com/news/detail-791259.html

到了这里,关于golang --gin+websocket实现指定的数据点推送的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • golang利用redis和gin实现保存登录状态,校验登录

    保存用户登录状态,一般常用的方式有两种 一、生成token,然后token保存到数据库用户表里面,每次登录的时候,自动更新,容纳后每次用的时候,去取出来校验,这种方式,数据库压力大,而且不是很灵活 二、每次登录生成token,然后token保存到Redis缓存中,每次都去校验,

    2024年01月20日
    浏览(41)
  • [golang gin框架] 26.Gin 商城项目-前台自定义商品列表模板, 商品详情数据渲染,Markdown语法使用

    当在首页分类点击进入分类商品列表页面时,可以根据后台分类中的分类模板跳转到对应的模板商品列表页面 (1).商品控制器方法Category()完善 修改controllers/frontend/productController.go中的方法Category(), 判断分类模板,如果后台没有设置,则使用默认模板 (2).模板页面案例 先来回顾一

    2024年02月01日
    浏览(55)
  • Golang实现简单WebSocket服务

    我们每天接触到各类应用,如社交、在线文档、直播等,后端都需要使用WebSocket技术提供实时通信能力。本文介绍如何使用Golang实现实时后端WebSocket服务,首先使用Gin框架搭建http服务,然后使用 gorilla/websocket 库实现简单后端WebSocket服务,示例实现从0到1的过程,适合初学者快

    2024年02月16日
    浏览(44)
  • golang微框架Gin

    Gin是一个golang的微框架,基于httprouter,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点 Gin特征 速度快:基于基数树的路由,内存占用小,没有反射,可预测的APi性能 中间件支持 传入的http请求可以有中间件链和最终操作处理,例如:Logger,Aut

    2024年02月09日
    浏览(43)
  • Golang中Gin 自定义验证方法

    1.怎样写一个自定义验证方法 2. 怎样管理自定义验证方法 1. 怎样写一个自定义验证方法 在Gin框架中,可以使用 binding 标签实现参数的校验。但有些特殊的需求,可能需要自己定义一些校验方法。下面是一些例子: 》自定义验证正则表达式: 可以通过 binding 标签中的自定义函

    2024年02月09日
    浏览(35)
  • Golang Gin 接口返回 Excel 文件

    Web 页面导出表数据到 Excel(或其他格式)可以由前端或后台来实现,具体的实现方式取决于你的应用需求和架构。以下是一些考虑因素: (1)前端实现。 如果你的数据导出不涉及复杂的数据处理、数据权限控制或数据来源的保护,你可以考虑在前端实现数据导出。 前端实

    2024年02月06日
    浏览(44)
  • Golang Gin框架HTTP上传文件

    HTTP上传的文件的原理 HTTP协议的文件上传是通过HTTP POST请求实现的,使用multipart/form-data格式将待上传的文件放入请求体中。 服务器根据请求头中的boundary参数来解析请求体,并根据Content-Disposition字段获取文件名等信息,根据Content-Type字段判断文件类型并保存到相应位置。

    2024年02月05日
    浏览(41)
  • Golang Gin HTTP 请求和参数解析

    我们介绍了Gin框架,并做了Gin框架的安装,完成了第一个Gin工程的创建。 创建Engine 在gin框架中,Engine被定义成为一个结构体,Engine代表gin框架的一个结构体定义, 其中包含了路由组、中间件、页面渲染接口、框架配置设置等相关内容。 默认的Engine可以通过gin.Default进行创建

    2024年02月01日
    浏览(79)
  • [golang gin框架] 39.Gin商城项目-微服务实战之微服务架构

    单体架构在 中小企业内部 用的是非常多的,当 业务不复杂 , 团队规模不大 的时候,单体架构比微服务架构具有 更高的生产率 单体架构 当 业务比较复杂 , 并发量 比较大, 团队规模扩大的时候, 就需要引入微服务架构了,它比单体架构 具有 更高的生产率, 可以 节省成本 , 解

    2024年02月12日
    浏览(43)
  • 【golang】Windows环境下Gin框架安装和配置

    我终于搞定了Gin框架的安装,花了两三个小时,只能说道阻且长,所以写下这篇记录文章 先需要修改一些变量,这就需要打开终端,为了一次奏效,我们直接设置全局的: 首先创建一个项目 进去之后先创建go.mod文件,创建完之后通常会为你自动配置参数 然后我们打开Files

    2024年02月07日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包