searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Go语言基于Gin框架实现websocket通信

2023-09-13 06:05:27
85
0

在项目实际运行过程中,随着集群服务器数量的指数级别增长,需要自研一套维护集群内所有服务器server-client架构的系统,主要功能是采集客户端服务器各项指标,并对异常指标进行告警,达到监控服务器及时处理异常的作用,保证集群内各项服务的稳定正常运行。

   由于系统有很强的实时通信的需求,因此决定采用websocket方式实现客户端和服务端的通信。websocket通信比http的好处主要是支持长连接,全双工通信,相比http传输数据量小

Gin 是 Go语言写的一个 web 框架,它具有运行速度快,分组的路由器,良好的崩溃捕获和错误处理能力,非常好的支持中间件和 json。Gin 的功能不只是简单输出 Json 数据。它同时是一个轻量级的 WEB 框架,支持 RestFull 风格 API,支持 GET,POST,PUT,PATCH,DELETE,OPTIONS 等 http 方法,支持文件上传,分组路由,Multipart/Urlencoded FORM,以及支持 JsonP,参数处理等等功能,这些都和 web 紧密相关,通过提供这些功能,使开发人员更方便地处理 web 业务。作为Go语言下优秀的 Web 框架,通过Gin框架可以轻松实现websocket通信功能

Demo代码如下:

服务端:

package main

import (
	"log"

	"g i t h u b .c o m /gin-gonic/gin"
	"g i t h u b .c o m/gorilla/websocket"
)

var upgrader = websocket.Upgrader{}

func main() {
	// 使用gin框架,和普通的http协议的服务器基本一致
	s := gin.Default()
	s.GET("/echo", echo)
	_ = s.Run("127.0.0.1:8090")
}

func echo(c *gin.Context) {
	//服务升级,对于来到的http连接进行服务升级,升级到ws
	cn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	defer cn.Close()
	if err != nil {
		panic(err)
	}
	for {
		mt, message, err := cn.ReadMessage()
		if err != nil {
			log.Println("server read:", err)
			break
		}
		log.Printf("server recv msg: %s", message)
		msg := string(message)
		if msg == "woshi client1" {
			message = []byte("client1 去服务端了一趟")

		} else if msg == "woshi client2" {
			message = []byte("client2 去服务端了一趟")
		}

		err = cn.WriteMessage(mt, message)
		if err != nil {
			log.Println(" server write err:", err)
			break
		}
	}
}

客户端:

package main

import (
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"g i t h u b . c o m /gorilla/websocket"
)

func main() {

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: "127.0.0.1:8090", Path: "/echo"}
	log.Printf("client1 connecting to %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("dial server:", err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer close(done)
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				log.Println("client read err:", err)
				return
			}
			log.Printf("client recv msg: %s", message)
		}
	}()

	//ticker := time.NewTicker(time.Second)
	//defer ticker.Stop()
	for {
		select {
		// if the goroutine is done , all are out
		case <-done:
			return
		case <-time.Tick(time.Second * 5):
			err := c.WriteMessage(websocket.TextMessage, []byte("woshi client2"))
			if err != nil {
				log.Println("client write:", err)
				return
			}
		case <-interrupt:
			log.Println("client interrupt")

			// Cleanly close the connection by sending a close message and then
			// waiting (with timeout) for the server to close the connection.
			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("client1 write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			return
		}
	}
}

在实战中,每台服务器需要安装客户端和服务端进行通信。对于上百上千台服务器通信基本是不会有拥塞现象的。但如果达到上万级别,服务器端这么集中接收这么多客户端的消息肯定还是会有性能瓶颈,所以技术层面还需要通道channel去接收消息;架构层面需要分流去接收消息。而对于直播等百万级别服务,还是需要研究新的架构。

0条评论
作者已关闭评论
Mr. 油
89文章数
0粉丝数
Mr. 油
89 文章 | 0 粉丝
原创

Go语言基于Gin框架实现websocket通信

2023-09-13 06:05:27
85
0

在项目实际运行过程中,随着集群服务器数量的指数级别增长,需要自研一套维护集群内所有服务器server-client架构的系统,主要功能是采集客户端服务器各项指标,并对异常指标进行告警,达到监控服务器及时处理异常的作用,保证集群内各项服务的稳定正常运行。

   由于系统有很强的实时通信的需求,因此决定采用websocket方式实现客户端和服务端的通信。websocket通信比http的好处主要是支持长连接,全双工通信,相比http传输数据量小

Gin 是 Go语言写的一个 web 框架,它具有运行速度快,分组的路由器,良好的崩溃捕获和错误处理能力,非常好的支持中间件和 json。Gin 的功能不只是简单输出 Json 数据。它同时是一个轻量级的 WEB 框架,支持 RestFull 风格 API,支持 GET,POST,PUT,PATCH,DELETE,OPTIONS 等 http 方法,支持文件上传,分组路由,Multipart/Urlencoded FORM,以及支持 JsonP,参数处理等等功能,这些都和 web 紧密相关,通过提供这些功能,使开发人员更方便地处理 web 业务。作为Go语言下优秀的 Web 框架,通过Gin框架可以轻松实现websocket通信功能

Demo代码如下:

服务端:

package main

import (
	"log"

	"g i t h u b .c o m /gin-gonic/gin"
	"g i t h u b .c o m/gorilla/websocket"
)

var upgrader = websocket.Upgrader{}

func main() {
	// 使用gin框架,和普通的http协议的服务器基本一致
	s := gin.Default()
	s.GET("/echo", echo)
	_ = s.Run("127.0.0.1:8090")
}

func echo(c *gin.Context) {
	//服务升级,对于来到的http连接进行服务升级,升级到ws
	cn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	defer cn.Close()
	if err != nil {
		panic(err)
	}
	for {
		mt, message, err := cn.ReadMessage()
		if err != nil {
			log.Println("server read:", err)
			break
		}
		log.Printf("server recv msg: %s", message)
		msg := string(message)
		if msg == "woshi client1" {
			message = []byte("client1 去服务端了一趟")

		} else if msg == "woshi client2" {
			message = []byte("client2 去服务端了一趟")
		}

		err = cn.WriteMessage(mt, message)
		if err != nil {
			log.Println(" server write err:", err)
			break
		}
	}
}

客户端:

package main

import (
	"log"
	"net/url"
	"os"
	"os/signal"
	"time"

	"g i t h u b . c o m /gorilla/websocket"
)

func main() {

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: "127.0.0.1:8090", Path: "/echo"}
	log.Printf("client1 connecting to %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("dial server:", err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer close(done)
		for {
			_, message, err := c.ReadMessage()
			if err != nil {
				log.Println("client read err:", err)
				return
			}
			log.Printf("client recv msg: %s", message)
		}
	}()

	//ticker := time.NewTicker(time.Second)
	//defer ticker.Stop()
	for {
		select {
		// if the goroutine is done , all are out
		case <-done:
			return
		case <-time.Tick(time.Second * 5):
			err := c.WriteMessage(websocket.TextMessage, []byte("woshi client2"))
			if err != nil {
				log.Println("client write:", err)
				return
			}
		case <-interrupt:
			log.Println("client interrupt")

			// Cleanly close the connection by sending a close message and then
			// waiting (with timeout) for the server to close the connection.
			err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				log.Println("client1 write close:", err)
				return
			}
			select {
			case <-done:
			case <-time.After(time.Second):
			}
			return
		}
	}
}

在实战中,每台服务器需要安装客户端和服务端进行通信。对于上百上千台服务器通信基本是不会有拥塞现象的。但如果达到上万级别,服务器端这么集中接收这么多客户端的消息肯定还是会有性能瓶颈,所以技术层面还需要通道channel去接收消息;架构层面需要分流去接收消息。而对于直播等百万级别服务,还是需要研究新的架构。

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0