作者:link1st的开源项目
- Go 1.18 新特性,多模块工作区教程
- 介绍如何实现一个基于websocket分布式聊天(IM)系统
- Go 实现的压测工具
- Go实现双向链表
golang基于websocket单台机器支持百万连接分布式聊天(IM)系统
License: Other
作者:link1st的开源项目
为什么一个连接进入,首先会有 Register,再然后才是 Login,是因为没有登录功能而做的妥协吗?或者是基于其他什么考虑呢?望回复,另外这个项目给我帮助很大,请问有交流群可以分享一下吗?
完整报错如下:
panic: Fatal error config file: Config File "config/app" Not Found in "[D:\Workspace\Go\src\github.com\link1st\gowebsocket]"
goroutine 1 [running]:
main.initConfig()
D:/Workspace/Go/src/github.com/link1st/gowebsocket/main.go:72 +0x29f
main.main()
D:/Workspace/Go/src/github.com/link1st/gowebsocket/main.go:27 +0x2d
为了跑这个项目,我go get了 14个小时,终于get完了,结果找不到配置文件。
你们都是怎么跑起来的呀?
非常好的项目 根据这个做了进一步的开发 但是有个问题 想请教一下
将您目前的代码修改为 用户登录后再登录之后 关闭旧的登录 但是由于EventUnregister 执行的不及时导致 AddUser 之后被删除;
也就是说 用户在A设备上登录 然后又去B设备登录 登录B设备的时候想将A设备踢下线
原本想的流程为:将设备A踢下线manager.Socket.Close() (这里会触发 client.write 的defer 方法 在这里往管道clientManager.Unregister
里面写入了数据; 之后执行 manager.AddUser() 新的用户
但是实际执行的流程却是 先增加后删除 新登录的 和旧的 都被删除了 不知道我这样表达能不能让您明白
原本在A设备登录的时候 manager.User 大概是 map[1:0xc000035b00]
但是B登录后 就变空了
最后 希望您留个赞助码什么的 对目前的项目帮助挺大的!
采用 HTTP 发送消息,为了收发数据分离,进行数据解耦,提升发送数据的成功率。
基于这个项目的websocket只用于推送消息到客户端,而客户端发送消息则调用http接口,那么SSE似乎是一个更好的选择。
这个项目和 gochat 不同,客户端使用http接口来发消息,而不是使用grpc proto来通信,项目中的grpc仅用于服务间的通讯,那么websocket相对于SSE支持二进制和客户端主动发消息的双向通讯优势也就没有了,剩下的相比SSE全是劣势。
gin本身对SSE提供了支持,前端用起来更简单,创建一个EventSource对象就可以实现对数据流的监听,不需要前端去考虑断线重连,心跳等问题,同时还支持服务优雅降级,支持单独特定事件监听,也可指定事件ID等。
每个websocket服务的单一责任就是推消息到客户端,客户端则通过接口来发消息,那么选择更简单更轻量的SSE似乎更好。
ProcessData(c, message) 可以用 go ProcessData(c, message) 吗
protoc --go_out=./ *.proto
protoc-gen-go: unable to determine Go import path for "im_protobuf.proto"
Please specify either:
• a "go_package" option in the .proto source file, or
• a "M" argument on the command line.
使用这个可以生成,与之前的比较又不一样。请问是用的那个版本,如何生成im_protobuf.pb.go 文件?
我用的方式是brew install protobuf
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go
go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
这个命令可以执行,但是与原来的不一样,代码未改动,我想生成一个。使用的是mac m1
protoc -I=. --go_out=. --go-grpc_out=. im_protobuf.proto
请教前辈一个问题:
前提说明:
本地和云端都没有使用Nginx,仅仅是单纯的运行。
问题如下:
本地Windows环境IDEA跑这个项目,多次重启都没有问题,聊天页面可以进行聊天;
把项目放到阿里云CentOS 7 服务器上,第一次启动也没问题,聊天页面可以进行聊天。
但是,一旦重新启动,即Ctrl+C停止服务后,再次go run main.go,聊天页面就无法访问。
前端Http状态码返回503
后端报错如下:
2019/12/25 14:29:23 http: panic serving 114.64.253.3:3257: runtime error: invalid memory address or nil pointer dereference
goroutine 171 [running]:
net/http.(*conn).serve.func1(0xc00018e8c0)
/usr/lib/golang/src/net/http/server.go:1767 +0x139
panic(0xc26460, 0x13bd940)
/usr/lib/golang/src/runtime/panic.go:679 +0x1b2
github.com/gin-gonic/gin.(*Context).Next(0xc00009cbb0)
/home/go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:124 +0x36
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000141400, 0xc00009cbb0)
/home/go/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:389 +0x5b2
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000141400, 0xe562e0, 0xc00017e380, 0xc0000d7600)
/home/go/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:351 +0x134
net/http.serverHandler.ServeHTTP(0xc00017e1c0, 0xe562e0, 0xc00017e380, 0xc0000d7600)
/usr/lib/golang/src/net/http/server.go:2802 +0xa4
net/http.(*conn).serve(0xc00018e8c0, 0xe58920, 0xc000288700)
/usr/lib/golang/src/net/http/server.go:1890 +0x875
created by net/http.(*Server).Serve
/usr/lib/golang/src/net/http/server.go:2927 +0x38e
如果把项目文件夹从阿里云服务器删除,然后重新上传,再次启动,依然是第一次启动可以访问通,再次重启无法访问,一样的报错。
云端启停都是root用户。
我根据 runtime error: invalid memory address or nil pointer dereference
去网上搜索:
网上有些人说是代码的问题,但是,第一次运行的时候,确实是能跑通的。
还有人说是centOS7 防火墙问题,我检测了一下,防火墙是关闭状态。
前辈是否也遇到过同样的问题呢?
我正在写一片论文,使用golang服务的+uni-app客户端,需要一些文档,看见您的项目,故想拿来参考一下,会拜读您的项目,理解后,进行重写,您的项目功能太多了,所以只会使用您的一部分功能。不会原封不动抄,望回复。
老师,您好。看你这个项目是使用nginx做的负载均衡,我有一个疑问:
感谢作者的开源项目,看了下源码,有两处疑问希望作者帮忙解答下,不确定是不是我有遗漏
我理解的如果是分布式部署,所有机器共用一个redis的,用户在线信息是可以直接查redis拿到的,这里设计一个grpc的接口作用是啥呢,我想到一种,就是不能直接访问redis的服务,通过rpc调用去获得用户在线信息
在这里 https://github.com/link1st/gowebsocket/blob/master/servers/websocket/user_srv.go#L103
只是把用户在线信息从redis中取出来,并没有检查其中的用户是否在线,其中有个心跳时间,还有个是否退出的标识,我的理解既然用心跳来维持用户在线状态了,是不是得检查下,这里是不是真的遗漏了,还是有其他考虑呢
比如我集成在自己的系统里,前端要直接连接到websocket服务上,后端还要用http发信息到websocket上,那这个安全的问题应该怎么做呢
不使用httpp请求,可以构建分布式ws吗?
(The purpose of this report is to alert link1st/gowebsocket
to the possible problems when link1st/gowebsocket
try to upgrade the following dependencies)
-Latest Version: V1.6.3 (Latest commit 6a8b8b6 on 3 May)
-Where did you use it:
https://github.com/link1st/gowebsocket/search?q=gin-gonic%2Fgin&unscoped_q=gin-gonic%2Fgin
-Detail:
module github.com/gin-gonic/gin
go 1.13
require (
github.com/gin-contrib/sse v0.1.0
github.com/go-playground/validator/v10 v10.2.0
…
)
package binding
import (
"reflect"
"sync"
"github.com/go-playground/validator/v10"
)
This problem was introduced since gin-gonic/gin v1.6.0 . Now you used the version v1.4.0 . If you try to upgrade gin-gonic/gin to version v1.6.0 and above, you will get an error--- no package exists at "github.com/go-playground/validator/v10"
These dependencies all added Go modules in the recent versions.
They all comply with the specification of "Releasing Modules for v2 or higher" available in the Modules documentation. Quoting the specification:
A package that has migrated to Go Modules must include the major version in the import path to reference any v2+ modules. For example, Repo github.com/my/module migrated to Modules on version v3.x.y. Then this repo should declare its module path with MAJOR version suffix "/v3" (e.g., module
github.com/my/module/v3
), and its downstream project should use"github.com/my/module/v3/mypkg"
to import this repo’s package.
physical path
. So earlier versions of Go (including those that don't have minimal module awareness) plus all tooling (like dep, glide, govendor, etc) don't have minimal module awareness
as of now and therefore don't handle import paths correctly See golang/dep#1962, golang/dep#2139.Note: creating a new branch is not required. If instead you have been previously releasing on master and would prefer to tag v3.0.0 on master, that is a viable option. (However, be aware that introducing an incompatible API change in master can cause issues for non-modules users who issue a go get -u given the go tool is not aware of semver prior to Go 1.11 or when module mode is not enabled in Go 1.11+).
Pre-existing dependency management solutions such as dep currently can have problems consuming a v2+ module created in this way. See for example dep#1962.
https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher
Go Modules is the general trend of ecosystem, if you want a better upgrade package experience, migrating to Go Modules is a good choice.
Migrate to modules will be accompanied by the introduction of virtual paths(It was discussed above).
This "github.com/my/module/v3/mypkg" is not the
physical path
. So Go versions older than 1.9.7 and 1.10.3 plus all third-party dependency management tools (like dep, glide, govendor, etc) don't haveminimal module awareness
as of now and therefore don't handle import paths correctly.
Then the downstream projects might be negatively affected in their building if they are module-unaware (Go versions older than 1.9.7 and 1.10.3; Or use third-party dependency management tools, such as: Dep, glide, govendor…).
If link1st/gowebsocket
want to keep using the dependency manage tools (like dep, glide, govendor, etc), and still want to upgrade the dependencies, can choose this fix strategy.
Manually download the dependencies into the vendor directory and do compatibility dispose(materialize the virtual path or delete the virtual part of the path). Avoid fetching the dependencies by virtual import paths. This may add some maintenance overhead compared to using modules.
As the import paths have different meanings between the projects adopting module repos and the non-module repos, materialize the virtual path is a better way to solve the issue, while ensuring compatibility with downstream module users. A textbook example provided by repo github.com/moby/moby
is here:
https://github.com/moby/moby/blob/master/VENDORING.md
https://github.com/moby/moby/blob/master/vendor.conf
In the vendor directory, github.com/moby/moby
adds the /vN subdirectory in the corresponding dependencies.
This will help more downstream module users to work well with your package.
The gin-gonic/gin
have 2104 module-unaware users in github, such as: yogaagungk/assets-management, aduermael/dockerblog, gdevillele/calvados…
https://github.com/search?q=gin-gonic%2Fgin+filename%3Avendor.conf+filename%3Avendor.json+filename%3Aglide.toml+filename%3AGodep.toml+filename%3AGodep.json
You can make a choice when you meet this DM issues by balancing your own development schedules/mode against the affects on the downstream projects.
For this issue, Solution 1 can maximize your benefits and with minimal impacts to your downstream projects the ecosystem.
Do you plan to upgrade the libraries in near future?
Hope this issue report can help you ^_^
Thank you very much for your attention.
Best regards,
Kate
测试的时候,短时间登录上200时候(测试机器8G内存) 突然就会有好几十账号突然掉线。而且用NGINX 配置wss,跑不上40个链接,到了40左右其他账户马上全部掉线。请问这个可能是什么问题呢
请问为什么不直接通过 WebSocket 向服务器发送消息,而是由客户端调用 HTTP 接口将消息数据传送至服务器后,服务器再发送 WebSocket 到目标客户端?这样设计有什么用意吗?
当用 userId 为同一个id 时,再次登录时,而先前登录的 则收不到信息。
不是知道 底层是否如下描述?
限制已经登录的 不能再次登录,必须执行在异地或本地另开网页,输入密码执行退出登录前面的, 然后再次登陆,先前登录的则收到自己被迫下线。
我看gorilla/websocket包使用的是SetReadDeadline
、SetPongHandler
实现的ping-pong,你这个项目是通过定时轮询整个clientIds+自定义heatbeat请求刷新心跳时间。
我想知道SetReadDeadline
、SetPongHandler
是不是更好的解决办法
看到 go-redis 版本还是 2019 年的,目前 go-redis 已经到 V9
啦
是否可以升级一下,起码能到 V8
,V9
是最好的
我看您的架构图是可以部署多个gowebcoket的。 如果user1 分配到gowebsocket1登录,user2分配到gowebsocket2登录。 他们可以可以在同一个房间appid=102聊天吗。 我看的您的源码好像做不到,gowebsocket1的client不能发送消息到gowebsocket2的client中吧。
真是太棒了.希望能持续更新
分别用不同的机器打开同一个聊天室的时候,各聊各的,不能一起聊天。
离线后,再次上线,已发送数据是否支持未接受数据同步
这个项目文档真的是太详细了,对我这种刚学习的人来说,收益很大。强烈建议录制一些收费的教学视频。
rpcServer会定时注册,每60秒获取当前机器信息,并存入 redis hSet 中(acc:hash:servers)
number, err := redisClient.Do("hSet", key, server.String(), value).Int()
if err != nil {
fmt.Println("SetServerInfo", key, number, err)
return
}
// 如果字段是哈希表中的一个新建字段,并且值设置成功,返回 1 。 如果哈希表中域字段已经存在且旧值已被新值覆盖,返回 0
if number != 1 {
return
}
redisClient.Do("Expire", key, serversHashCacheTime)
问题在 number,如果机器固定,那么后续定时任务并不会执行 EXPIRE刷新redis键(acc:hash:servers)生命周期
导致在2小时后,rpcServer 是为空的状态,持续时间不固定(小于2分钟)
进而导致用户进入房间不会通知其它用户,收发消息也会有问题
建议删除 if number 判断语句
你好,nginx做websocket代理转发要分别跟客户端和websocket服务维持长连接,对于websocket服务来说nginx也是客户端,一个客户端一个ip的话只能创建65535个长连接,那这是不是成了性能瓶颈了?
代码看了下,很不错,就是不知道是不是缺了个中间件模块
Vuejs+Vuetify+nuxtjs
https://cn.vuejs.org/
https://vuetifyjs.com/zh-Hans/
https://www.nuxtjs.cn/
gowebsocket/servers/websocket/client_manager.go
Lines 181 to 197 in cf34330
为什么使用 全局变量 clientManager 而不是 方法接收者变量 manager ,是手误还是有啥讲究?
gowebsocket/servers/websocket/client_manager.go
Lines 134 to 138 in cf34330
这里的 len 需要加锁吗?
会加RabbitMQ和单聊么,因为现在只有公共聊天的,但是对于私有群聊以及单聊的逻辑不是很清楚,请问下是否会加呢?想请教一下!
项目还在维护吗
您好,请问gowebsocket项目中是否有调用其他第三方收费产品?谢谢
有点意思的项目,还会继续完善吗
client_manager.go中ClearTimeoutConnections
1.client manager遍历时没加锁;
2.client关闭的时候,client中的SendChan没关闭,clients的map中没有删除这个client。
不知道说的对不对。从你的项目吸收了不少东西,借此感谢!
还有好友系统, 能不能给个思路
我们在浏览器前端要监听2类消息,
建立1个websocket连接能不能做同时监听这两类消息?
作者你好,在这个函数中是不是要增加 如果心跳超时,就要删除对于的 map 如 delete (client,Client)防止key太多占内存, 同时设置对应的UserOnline 为nil ,已清空变量及其redis 中UserOnline 的相应的键名
nginx的端口限制,导致了同时间最多只有65535的长连接,无法实现百万单台的需求
at main.go, command "open" out is "failed to call Output(): exec: "open": executable file not found in $PATH".
why run command "open httpurl" here?
func open() {
time.Sleep(1000 * time.Millisecond)
httpUrl := viper.GetString("app.httpUrl")
httpUrl = "http://" + httpUrl + "/home/index"
fmt.Println("访问页面体验:", httpUrl)
cmd := exec.Command("open", httpUrl)
cmd.Output()
}
我想使用mysql 持久化 是否可行 ?只 持久化Redis 可以吗?
WebSocket 与客户端建立持久的双向通信连接,这样客户端可以直接通过 WebSocket 连接发送和接收实时的消息,而不需要每次都发起一个新的 HTTP 请求。这种实时性对于即时聊天应用非常重要,因为它允许消息的快速传递和实时更新。
使用 HTTP 发送消息需要建立一个新的请求-响应循环,每次发送消息都需要发起一个新的 HTTP 请求,这会引入较大的延迟,并且增加服务器的负载。使用 WebSocket 可以在客户端和服务器之间建立实时的双向通信,服务器可以直接将消息推送到客户端,而不需要等待客户端发起请求。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.