当前位置:网站首页>Go+mysql+redis+vue3 simple chat room, the fourth bullet: Gin's websocket communication and multi go task processing
Go+mysql+redis+vue3 simple chat room, the fourth bullet: Gin's websocket communication and multi go task processing
2022-07-18 05:44:00 【Game programming】
establish websocket Connect
We used gin The routing of implements the conventional http request , But chat is based on websocket Of , We need to build a new route , Used to receive websocket request .
websocket The establishment of is based on http Of , At the beginning, we need to pass http Make connections , So we can go through http Route listening websocket Requested , Then determine the request type , yes websokcet Words , Build a real websocket service
gin Handle websocket It is carried out in the above way . modify routes/route.go, as follows
import( ... "github.com/gorilla/websocket")// Filter url, You can do some processing to the request source func checkOrigin(r *http.Request) bool { return true}// Initialize a websockt update var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: checkOrigin,}func NewRouter() *gin.Engine{ ... // Establish a new route , For monitoring websocket Connect r.GET("/ws",func(c *gin.Context) { // Judge whether it is websokcet request if websocket.IsWebSocketUpgrade(c.Request){ // yes websocket If you ask , Need to put http Connection upgrade to websocket Connect conn,err := upgrader.Upgrade(c.Writer,c.Request,c.Writer.Header()) if err != nil { c.JSON(200,gin.H{"msg":" upgrade websocket Failure "}) }else{ // Receive and distribute messages fmt.Println("do work") } // Register a listening client to close socket Methods conn.SetCloseHandler(func(code int, text string) error { fmt.Println(code, text+"websocket closed") return nil }) fmt.Println("websocket Successful connection ") }else{ c.JSON(402,gin.H{"status":0,"msg":" Not websocket Connect "}) } })}stay go in , One http Request is a process , After the code execution, the collaboration ends . But when http Upgrade to websocke After connection , A new monitor will be created socket Connected go cheng , We can use conn towards socket Sending and reading data , until conn Listen to the closing signal of the client ,socket Connected go Cheng will quit
Be careful : We want to be in the new go In process execution conn Receive and send data codes , otherwise conn After the meeting is executed once , With http go It's over
The server code is written , And then we have views/index.html in , Add the following code
...<script> var text = document.getElementById("send-text"); var send = document.getElementById("send"); var username = document.getElementById("username").value; var userid = document.getElementById("userid").value; var data = {username:username,userid:userid,msg:""} // Make a statement socket Variable var websocket; // link socket websocket = new WebSocket("ws://127.0.0.1:8080/ws"); // Link successful websocket.onopen=function(){ console.log("connected"); data.msg = username+": launched "; // Send a message , Only string and binary are accepted , Generally use string // hold json Object is converted to a string and sent websocket.send(JSON.stringify(data)); addContent(data.msg) }; //socket Listen to receive messages websocket.onmessage = function(e){ console.log(e.data); addContent(e.data) }; //socket Monitor the shutdown signal websocket.onclose=function(e){ console.log("closed",e); }; // Send a message send.onclick = function(){ let msg = text.value; data.msg = msg websocket.send(JSON.stringify(data)); addContent(msg) }; function addContent(msg) { let contentDiv = document.getElementById("content"); let child = document.createElement('p'); child.innerHTML = msg; contentDiv.appendChild(child) }</script>many go Process task processing
In the regular chat system , Receiving and sending messages require the cooperation of multiple processes , To avoid code blocking .
With swoole For example ,work The process is used to listen and receive client messages , The task of sending messages is delivered to task Process processing , This can ensure that when a large number of messages need to be sent in groups ,work It won't block
go Then it can pass through the coordination process and channel , Non blocking message distribution can be achieved without multiple processes
Here are the basic principles
go In language , When a user goes online , Will insert a connection map in , Then assign 3 A dedicated customer service ( Three go cheng )
One is responsible for listening to messages sent by the client , And send the message to a global channel. One is responsible for sending the message one in the global channel One insert map One of the channels connected by the client in is responsible for reading messages from the currently linked channel , And send it to the client > First and second go The processes can be merged into one as needed
Create a new directory in the root directory service file , It is used to store some services that will be used later
stay service New China ws-chat.go file , as follows
package wsimport ( "encoding/json" "fmt" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" "net/http" "project1/service" "strconv")//socket The connection type of type clientUser struct{ id int64 username string chanMsg chan []byte chanClose chan int}// Global message channel var globalMsgChan = make(chan []byte,1024)// Save online socket Of the link mapvar clientUserMap = make(map[int64]clientUser)// Main method func Run(conn *Conn){ // Initialize a current socket Of the link id, The user can then id Assign to it var clientID int64 = 0 // Initialize a socket Link to oneClient := clientUser{} // Read data successfully , Parsing data //conn.ReadMessage Is the data read from the channel , When the client does not send information , Or when the amount of information in the channel is insufficient , This method will block subsequent programs // For example, the client sends a message , If you read it twice , It will block , Until the client sends the second message t, m, _ := conn.ReadMessage() // be equal to -1 The link does not exist or has been closed if t != -1 { // The first one to receive client messages map Variable // Types of keys and values of variables , It must be consistent with the data sent by the client msgInfo := make(map[string]string) // The client sends it json character string , Be converted to []byte, Need to switch back if err := json.Unmarshal(m,&msgInfo);err != nil{ // Failure words , Keep a log . The log code will be attached at the end of the article service.Chatlog(err.Error(),"error") c.JSON(420,gin.H{"status":0,"msg":" Failed to parse connection data "}) }else{ // To current socket Connection object assignment clientID,_ = strconv.ParseInt(msgInfo["userid"], 10, 64) oneClient = clientUser{ id: clientID, username: msgInfo["username"], chanMsg: make(chan []byte), chanClose:make(chan int), } // Put the present socket Join to the global map in addClientMap(oneClient) // Send message to global channel globalMsgChan <- m } } // To start a go cheng , Cycle to read and process the data sent by the user go func(){ for { // When the client does not send a message , The code will block here , Until there is a readable message t, c, _ := conn.ReadMessage() if t == -1 { fmt.Println(t, string(c)+":client closed") // Send a close message to the client channel oneClient.chanClose <- -1 // Put the present socket Linked objects from map Delete in delClientMap(clientID) // When all users are offline , Send a shutdown notification to the global channel if len(clientUserMap) == 0{ closeGlobalChan := []byte("all-offline") globalMsgChan <- closeGlobalChan } fmt.Println(clientUserMap) return }else{ // Write the received message , To the global message channel globalMsgChan <- c } } }() // To start a go cheng , Put the message in the message channel of the client object , Send to client go func(){ for { select { case v1 := <-oneClient.chanMsg: err := conn.WriteMessage(websocket.TextMessage,v1) if err != nil{ service.Chatlog(err.Error(),"error") } case <- oneClient.chanClose: return } } }() // The top two go cheng , Is to deal with the current socket Exclusive connection message go cheng , At present socket If it's closed , There is no meaning of being , So it will follow socket Close and exit // Ben go The process is to process global channel messages go cheng , No need to follow the current socket close , Will always run // But whenever a new user goes online , The... Will be created repeatedly go cheng , So we need to make a limit // That is, when the online user is the first user , To establish the go cheng // When all users are offline , Close the go cheng if len(clientUserMap) < 2{ go func(){ for{ msg := <-globalMsgChan // When all users are offline , Close the go cheng if (string(msg) == "all-offline"){ return } // Insert a message into the client object channel for _,cln := range clientUserMap{ cln.chanMsg <- msg } } }() } // Registration monitoring is off socket Methods conn.SetCloseHandler(func(code int, text string) error { fmt.Println(code, text+"fffkkk") return nil }) // towards socket Of the link map Insert a new linked object func addClientMap(client clientUser){ if _,ok := clientUserMap[client.id]; ok{ // There are old clients , Delete delete(clientUserMap,client.id) } // Insert a new link clientUserMap[userinfo.id] = client } // Delete the client object func delClientMap(id int64){ if _,ok := clientUserMap[id]; ok{ delete(clientUserMap,id) } }}Enclosed service/loger.go Code
package serviceimport ( "log" "os" "path/filepath")var objPath stringfunc init(){ objPath,_ = filepath.Abs("")}// Message queue message log func Mqlog(msg string,mType string){ common( objPath+"/logs/mq1.log",msg,mType)}// Chat message log func Chatlog(msg string,mType string){ common( objPath+"/logs/main.log",msg,mType)}func common(filePath string,msg string,mType string){ // Open or create a file logFile,err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil{ panic(" Exception in creating log file :"+filePath) } log.SetOutput(logFile) log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate) log.SetPrefix("["+mType+"]") log.Println(msg)} Save the online user client in a map in , because map Is of reference type , So when other go Program modification map Information in , Ben go Cheng can get the modified information . So be a go Cheng xiangall map When sending data in the channel of the client in , Receiving client channel information go Cheng can read the data immediately , And send the data to the front end , Achieve linkage
author :whynogome
Game programming , A game development favorite ~
If the picture is not displayed for a long time , Please use Chrome Kernel browser .
边栏推荐
- xstream解析xml的时候报错AbstractReflectionConverter$UnknownFieldException
- 【动态规划】—— 状态压缩DP
- 宝藏功能上新!日历视图+卡片视图强强联合,工作效率快到飞起
- 【7.8-7.15】写作社区精彩技术博文回顾
- mysql 创建学生表并查询成绩
- 堪比猎头简历整理技巧 如何快速整理简历
- 【 7.8-7.15 】 examen de l'excellent blog technique de la Communauté d'écriture
- 浏览器缓存机制详解
- If you don't want to step on those holes in SaaS, you must first understand the "SaaS architecture"
- 【NDI】关于NDI的注意事项
猜你喜欢

一文搞懂│什么是跨域?如何解决跨域?

助力开发者,全方位解读 APISIX 测试案例

论文精读: 深度强化学习与智慧交通(一)

阿里云E-MapReduce 极客大赛开放报名 数十万奖金等你挑战

代码合规性:开发人员使用Helix QAC的5大原因

北航&信工所&美团提出LBDT,基于语言桥接的时空交互来进行准确指向性视频对象分割,性能SOTA!代码开源(CVPR 2022)...

DBeaver连接mysql错误:The server time zone value ‘Öйú±ê׼ʱ¼ä‘ is unrecognized or represents more than

【面试:并发篇14:多线程: Monitor 概念】

i7-12700H 和 R7-6800H,这两个 CPU 差距有多大?

重装系统Win11后玩lol没有声音怎么办?
随机推荐
Use excel2016's functions to generate random 16, 32, and 36 bit ID string contents
深度学习基础:9.复现经典网络:LeNet5与AlexNet
理财平台选择哪个安全可靠
Single view reconstruction -- deduction of shadow vanishing points, shadow vanishing lines, camera internal parameters and plane normal vectors
Les risques pour la sécurité de la chaîne d'approvisionnement logicielle liés à la numérisation des entreprises et à la gestion des TI
Pycharm使用教程:5个非常有用的技巧
代码合规性:开发人员使用Helix QAC的5大原因
曲线上点的曲率半径计算
【动态规划】—— 状态压缩DP
授人以渔-在 SAP MM 物料显示界面上看到一个字段,如何查找哪张数据库表的哪个字段进行的存储的试读版
重装系统Win11后玩lol没有声音怎么办?
Foundation of deep learning: 8 Convolution and pooling
游戏有什么用?| 游戏应用价值研究案例征集
ComboBoxEdit设置选项值(单选 多选)
@Difference between controller and @restcontroller
IM即时通讯软件开发之扫码登录功能
微信内H5页面唤起小程序&App
使用1个盘三个5G分区创建12G逻辑卷
MySQL 添加用户并授予只能查询权限
数据库每日一题---第23天:游戏玩法分析 l