目前实现web聊天室的方式主要有三种:
-
Refresh 刷新
-
Long Polling 长轮询
-
WebSocket 长连接
本文将基于Go语言实现一个web聊天室demo。
后端框架
- gin
- gorilla/websocket
下面是效果图
首页
index.jpg聊天页
image.pngCore 核心实现
我们要实现的是一个订阅,推送的聊天室模型。聊天室需要维护一个用户列表,把每条消息都推送给聊天室内的用户,这里的难点是并发控制。
这里需要对用户加入、用户离开、聊天室广播消息等事件采取并发控制。本项目通过 Go Channel 和 select 语法实现。用户加入聊天室后,服务端为用户开启一个订阅通道,并把用户通道统一存储在聊天室的用户列表里。聊天室里有一个推送通道,每个用户通过这个推送通道向聊天室推送消息。一个聊天室里同一时刻上述事件只允许一个在执行。
用户
- 订阅通道
- 推送通道
- 退出通道
当用户向消息频道发送聊天消息(也可以是登录登出等各种消息)服务器收到后就会向频道用户列表里的所有订阅通道推送这条消息,这就实现了聊天室的功能。
代码
// 处理聊天室中的事件
func (r *Room) Serve() {
for {
select {
// 用户加入房间
case ch := <-r.joinChn:
chE := make(chan Event, chanSize)
r.userCount++
r.idx++
r.users[r.idx] = chE
ch <- Subscription{
id: r.idx,
Pipe: chE,
emit: r.publishChn,
leave: r.leaveChn,
}
case arch := <-r.archiveChan:
events := []Event{}
//历史事件
for e := r.archive.Front(); e != nil; e = e.Next() {
events = append(events, e.Value.(Event))
}
arch <- events
// 有新的消息
case event := <-r.publishChn:
// 推送给所有用户
for _, v := range r.users {
v <- event
}
// 推送消息后,限制本地只保存指定条历史消息
if r.archive.Len() >= archiveSize {
r.archive.Remove(r.archive.Front())
}
r.archive.PushBack(event)
// 用户退出房间
case k := <-r.leaveChn:
if _, ok := r.users[k]; ok {
delete(r.users, k)
r.userCount--
}
}
}
}
前端框架
前端采用目前比较流行的技术
- Vuejs + ElementUI
- js-cookie
项目目录结构
image.png项目代码
欢迎Star
网友评论