diff --git a/botgo/dto/websocket.go b/botgo/dto/websocket.go index dce68501..b31adc91 100644 --- a/botgo/dto/websocket.go +++ b/botgo/dto/websocket.go @@ -13,6 +13,14 @@ type WebsocketAP struct { SessionStartLimit SessionStartLimit `json:"session_start_limit"` } +// WebsocketAP wss 单个接入点信息 +type WebsocketAPSingle struct { + URL string `json:"url"` + ShardCount uint32 `json:"shards"` //最大值 比如是4个分片就是4 + ShardID uint32 `json:"shard_id"` //从0开始的 0 1 2 3 对应上面的 + SessionStartLimit SessionStartLimit `json:"session_start_limit"` +} + // SessionStartLimit 链接频控信息 type SessionStartLimit struct { Total uint32 `json:"total"` diff --git a/botgo/session_manager.go b/botgo/session_manager.go index c73e0fc3..880eac3b 100644 --- a/botgo/session_manager.go +++ b/botgo/session_manager.go @@ -14,4 +14,6 @@ var defaultSessionManager SessionManager = local.New() type SessionManager interface { // Start 启动连接,默认使用 apInfo 中的 shards 作为 shard 数量,如果有需要自己指定 shard 数,请修 apInfo 中的信息 Start(apInfo *dto.WebsocketAP, token *token.Token, intents *dto.Intent) error + // 自己指定具体的shard + StartSingle(apInfo *dto.WebsocketAPSingle, token *token.Token, intents *dto.Intent) error } diff --git a/botgo/sessions/local/local.go b/botgo/sessions/local/local.go index a17308dd..804e72d3 100644 --- a/botgo/sessions/local/local.go +++ b/botgo/sessions/local/local.go @@ -57,6 +57,36 @@ func (l *ChanManager) Start(apInfo *dto.WebsocketAP, token *token.Token, intents return nil } +// Start 启动指定的分片 session manager 适合想要手动指定目前分片的开发者(分片较少) +func (l *ChanManager) StartSingle(apInfo *dto.WebsocketAPSingle, token *token.Token, intents *dto.Intent) error { + defer log.Sync() + if err := manager.CheckSessionLimitSingle(apInfo); err != nil { + log.Errorf("[ws/session/local] session limited apInfo: %+v", apInfo) + return err + } + startInterval := manager.CalcInterval(apInfo.SessionStartLimit.MaxConcurrency) + log.Infof("[ws/session/local] will start %d sessions and per session start interval is %s", + apInfo.ShardCount, startInterval) + + // 只启动一个分片 + + session := dto.Session{ + URL: apInfo.URL, + Token: *token, + Intent: *intents, + LastSeq: 0, + Shards: dto.ShardConfig{ + ShardID: apInfo.ShardID, + ShardCount: apInfo.ShardCount, + }, + } + + time.Sleep(startInterval) + go l.newConnect(session) + + return nil +} + // newConnect 启动一个新的连接,如果连接在监听过程中报错了,或者被远端关闭了链接,需要识别关闭的原因,能否继续 resume // 如果能够 resume,则往 sessionChan 中放入带有 sessionID 的 session // 如果不能,则清理掉 sessionID,将 session 放入 sessionChan 中 diff --git a/botgo/sessions/manager/manager.go b/botgo/sessions/manager/manager.go index 30521862..8689d1cb 100644 --- a/botgo/sessions/manager/manager.go +++ b/botgo/sessions/manager/manager.go @@ -59,3 +59,11 @@ func CheckSessionLimit(apInfo *dto.WebsocketAP) error { } return nil } + +// CheckSessionLimit 检查链接数是否达到限制,如果达到限制需要等待重置 +func CheckSessionLimitSingle(apInfo *dto.WebsocketAPSingle) error { + if apInfo.ShardCount > apInfo.SessionStartLimit.Remaining { + return errs.ErrSessionLimit + } + return nil +} diff --git a/config/config.go b/config/config.go index c57764b0..58a16945 100644 --- a/config/config.go +++ b/config/config.go @@ -142,6 +142,8 @@ type Settings struct { LinkPic string `yaml:"link_pic"` MusicPrefix string `yaml:"music_prefix"` DisableWebui bool `yaml:"disable_webui"` + ShardCount int `yaml:"shard_count"` + ShardID int `yaml:"shard_id"` } // LoadConfig 从文件中加载配置并初始化单例配置 diff --git a/main.go b/main.go index 26776507..caa7b2e9 100644 --- a/main.go +++ b/main.go @@ -184,6 +184,12 @@ func main() { if err != nil { log.Fatalln(err) } + fmt.Printf("分片建议\n") + fmt.Printf("建议的分片数量:%d\n", wsInfo.Shards) + fmt.Printf("每 24 小时可创建 Session 数:%d\n", wsInfo.SessionStartLimit.Total) + fmt.Printf("目前还可以创建的 Session 数:%d\n", wsInfo.SessionStartLimit.Remaining) + fmt.Printf("重置计数的剩余时间(ms):%d\n", wsInfo.SessionStartLimit.ResetAfter) + fmt.Printf("每 5s 可以创建的 Session 数:%d\n", wsInfo.SessionStartLimit.MaxConcurrency) // 定义和初始化intent变量 var intent dto.Intent = 0 @@ -204,12 +210,28 @@ func main() { // 启动session manager以管理websocket连接 // 指定需要启动的分片数为 2 的话可以手动修改 wsInfo - go func() { - wsInfo.Shards = 1 - if err = botgo.NewSessionManager().Start(wsInfo, token, &intent); err != nil { - log.Fatalln(err) - } - }() + if conf.Settings.ShardCount == 1 { + go func() { + wsInfo.Shards = 1 + if err = botgo.NewSessionManager().Start(wsInfo, token, &intent); err != nil { + log.Fatalln(err) + } + }() + log.Printf("不使用分片,所有信息都由当前gensokyo处理...\n") + } else { + go func() { + wsInfoSingle := &dto.WebsocketAPSingle{ + URL: wsInfo.URL, + ShardCount: uint32(conf.Settings.ShardCount), + ShardID: uint32(conf.Settings.ShardID), + SessionStartLimit: wsInfo.SessionStartLimit, + } + if err = botgo.NewSessionManager().StartSingle(wsInfoSingle, token, &intent); err != nil { + log.Fatalln(err) + } + }() + log.Printf("使用%d个分片,当前是第%d个分片,比如:[0,4],代表分为四个片,当前链接是第 0 个片,业务稍后应该继续多开gensokyo,可在不同的服务器和ip地址 shard 为[1,4],[2,4],[3,4]的链接,才能完整接收和处理事件。\n", conf.Settings.ShardCount, conf.Settings.ShardID) + } // 启动多个WebSocket客户端的逻辑 if !allEmpty(conf.Settings.WsAddress) { diff --git a/template/config_template.go b/template/config_template.go index fb921d46..6c5c1224 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -127,6 +127,8 @@ settings: idmap_pro : false #需开启hash_id配合,高级id转换增强,可以多个真实值bind到同一个虚拟值,对于每个用户,每个群\私聊\判断私聊\频道,都会产生新的虚拟值,但可以多次bind,bind到同一个数字.数据库负担会变大. send_delay : 300 #单位 毫秒 默认300ms 可以视情况减少到100或者50 disable_webui: false #禁用webui + shard_count: 1 #分片数量 默认1 + shard_id: 0 #当前分片id 默认从0开始,详细请看 https://bot.q.qq.com/wiki/develop/api/gateway/reference.html title : "Gensokyo © 2023 - Hoshinonyaruko" #程序的标题 如果多个机器人 可根据标题区分 custom_bot_name : "Gensokyo全域机器人" #自定义机器人名字,会在api调用中返回,默认Gensokyo全域机器人