Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用interface自定义好友列表、群列表、群成员列表存储 #249

Open
lz1998 opened this issue Feb 3, 2022 · 27 comments
Open
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@lz1998
Copy link
Contributor

lz1998 commented Feb 3, 2022

经过测试,1600好友+400群,占用内存85MB,可以考虑把 好友列表、群列表、群成员列表 放在磁盘

提供 interface 参数,允许自定义存储方式,使用磁盘、Redis等方式,保存 好友列表、群列表、群成员列表

type Storage interface {
	Get(key []byte) ([]byte, error)
	Put(key, value []byte) error
}
@qianjunakasumi
Copy link
Contributor

是否考虑可用性?
个人认为采用非内存存储,可能增加不确定性,加大外部风险,甚至带来安全问题
5L5%9O92BV(W@~B5TN$L`}N

@tsurumi-yizhou
Copy link

tsurumi-yizhou commented Feb 3, 2022 via email

@Mrs4s
Copy link
Owner

Mrs4s commented Feb 3, 2022

这其实是可以考虑的方案, 因为目前内存绝大部分都是 群 好友 缓存使用的, 如果能持久化这部分数据确实可以大幅度降低内存使用

@qianjunakasumi
Copy link
Contributor

我认为是超前优化。

好友、群列表属于高频热点数据

例,每次接收群消息时,会调用 func (c *QQClient) FindGroup(code int64) *GroupInfo

倘若您的群活跃度高、数量多,使用磁盘存储上述数据可能造成不小的压力。

内存存储应该是当下的最优解

个人认为,提供其他方式存储热点数据只是转移了压力,并无多大的收益

@lz1998
Copy link
Contributor Author

lz1998 commented Feb 3, 2022

我认为是超前优化。

好友、群列表属于高频热点数据

例,每次接收群消息时,会调用 func (c *QQClient) FindGroup(code int64) *GroupInfo

倘若您的群活跃度高、数量多,使用磁盘存储上述数据可能造成不小的压力。

内存存储应该是当下的最优解

个人认为,提供其他方式存储热点数据只是转移了压力,并无多大的收益

不一定用磁盘,也可以考虑 Redis 这些缓存,interface 只是一种灵活的方式,允许自定义如何存储数据

FindGroup 现在是遍历 slice,如果改成 K-V 形式(比如sync.map),查询、删除都会更方便

@qianjunakasumi
Copy link
Contributor

不一定用磁盘,也可以考虑 Redis 这些缓存,interface 只是一种灵活的方式,允许自定义如何存储数据

FindGroup 现在是遍历 slice,如果改成 K-V 形式(比如sync.map),查询、删除都会更方便

不是很明白,Redis 与 MiralGo 里直接存储有何不同,提供了何种能力,请指教

@qianjunakasumi
Copy link
Contributor

从进步角度来说,提供这样的接口确实能为 MiraiGo 提供更高级、更全面自定义的实现,无可厚非

但从普罗大众角度来说,我认为绝大多数人应该会选择直接用内存存储数据。同时这也是成本最低、最便捷的方式。

运行这样体量的帐号,好马应当配好鞍,不必要为这热点数据过度优化

为了少部分人的特殊需要,而支持可能极少数人才会用到的功能,我认为是不值得的

部分需要更高级特性的人,想必功底不会差,为何不自己 Fork 一份本地修改实现呢,反正都是开源作品

@qianjunakasumi
Copy link
Contributor

这其实是可以考虑的方案, 因为目前内存绝大部分都是 群 好友 缓存使用的, 如果能持久化这部分数据确实可以大幅度降低内存使用

倘若目的是为了减小内存占用,我觉得可以对部分数据,即非热点数据进行本地磁盘持久化选项或提供自定义的高级方式

@qianjunakasumi
Copy link
Contributor

热点访问数据应当予以在内存中保留,以确保高可用、低风险、快响应

@tsurumi-yizhou
Copy link

tsurumi-yizhou commented Feb 3, 2022 via email

@Ink-33
Copy link
Contributor

Ink-33 commented Feb 3, 2022

我并不觉得群成员列表属于热点数据。2022年2月3日 23:31,千橘 雫霞 @.***>写道:

热点访问数据应当予以在内存中保留,以确保高可用、低风险、快响应

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: @.***>

事实上群成员列表在内部有相当多的隐性访问

@lz1998
Copy link
Contributor Author

lz1998 commented Feb 3, 2022

这是我想到的一些可以优化的点:

  1. 现在每一次解析消息时,都会进行一次好友列表遍历,这一部分可以做成 lazy 的(给 Member 实现 IsFriend() 方法)
  2. 目前的保存好友、群、群成员的实现方式是 slice,可以改为 map 这样的 K-V 形式,新增、删除、查询都会更方便
  3. 内存占用比较大,可以考虑提供 interface 允许开发者自定义存储方式(go-cqhttp 可以继续使用原先的方式进行存储)

如果要做这些优化,MiraiGo 肯定会有 break change(对 go-cqhttp 用户无感),可以考虑一次性更新上

并且 go-cqhttp 本身也可以使用 leveldb,对于群、好友数量较多的情况,内存占用会更小。对于群少的情况来说,可能几MB没什么区别,但是群多的情况,可能会存在几十MB的差距。

另外持久化可以让 go-cqhttp 启动速度更快,现在 400群 大概需要几十秒加载群列表。

@lz1998
Copy link
Contributor Author

lz1998 commented Feb 3, 2022

我并不觉得群成员列表属于热点数据。2022年2月3日 23:31,千橘 雫霞 @.>写道:
热点访问数据应当予以在内存中保留,以确保高可用、低风险、快响应
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: _
@
_.**>

事实上群成员列表在内部有相当多的隐性访问

隐性访问我觉得可以做成尽量 lazy 的,比如说 GroupMessage 可以去掉 Sender 结构体,只保留 from_uin,并且提供 Member() 方法,在需要的时候再去读取 nickname 等信息

@qianjunakasumi
Copy link
Contributor

事实上群成员列表在内部有相当多的隐性访问

隐性访问我觉得可以做成尽量 lazy 的,比如说 GroupMessage 可以去掉 Sender 结构体,只保留 from_uin,并且提供 Member() 方法,在需要的时候再去读取 nickname 等信息

很遗憾,lazy 并不 lazy

例,https://docs.go-cqhttp.org/event/#%E7%BE%A4%E6%B6%88%E6%81%AF 中,
每一次消息都有 sender 内容

@lz1998
Copy link
Contributor Author

lz1998 commented Feb 3, 2022

事实上群成员列表在内部有相当多的隐性访问

隐性访问我觉得可以做成尽量 lazy 的,比如说 GroupMessage 可以去掉 Sender 结构体,只保留 from_uin,并且提供 Member() 方法,在需要的时候再去读取 nickname 等信息

很遗憾,lazy 并不 lazy

例,https://docs.go-cqhttp.org/event/#%E7%BE%A4%E6%B6%88%E6%81%AF 中, 每一次消息都有 sender 内容

依赖 MiraiGo 的其他 golang 项目可以使用 lazy 的特性

https://github.com/Mrs4s/MiraiGo/network/dependents?package_id=UGFja2FnZS0yMjc0MTE4OTYx

@qianjunakasumi
Copy link
Contributor

依赖 MiraiGo 的其他 golang 项目可以使用 lazy 的特性

https://github.com/Mrs4s/MiraiGo/network/dependents?package_id=UGFja2FnZS0yMjc0MTE4OTYx

本项目为协议实现,api非常原始,并不推荐使用。

UX4B UWZKB9MC3905KZOR

@buhuang28
Copy link
Contributor

我支持持久化和提供Get、Set接口

@qianjunakasumi
Copy link
Contributor

另外持久化可以让 go-cqhttp 启动速度更快,现在 400群 大概需要几十秒加载群列表。

有个问题,倘若你需要启动速度快,那么在加入了新群或群加入新成员而 gocqhttp 未同步完成前,收到了该群或该成员的消息,作何处理?丢弃?

gocqhttp 不是 QQ 客户端,说白是转发消息,对时效性要求高,且一旦发出难以修改

我认为,未完全和服务器同步前,不具备接收消息的能力。等待所有必要数据加载完成后,有能力接收消息时再完成启动流程开始接收消息是符合逻辑的
@lz1998

@lz1998
Copy link
Contributor Author

lz1998 commented Feb 3, 2022

另外持久化可以让 go-cqhttp 启动速度更快,现在 400群 大概需要几十秒加载群列表。

有个问题,倘若你需要启动速度快,那么在加入了新群或群加入新成员而 gocqhttp 未同步完成前,收到了该群或该成员的消息,作何处理?丢弃?

gocqhttp 不是 QQ 客户端,说白是转发消息,对时效性要求高,且一旦发出难以修改

我认为,未完全和服务器同步前,不具备接收消息的能力。等待所有必要数据加载完成后,有能力接收消息时再完成启动流程开始接收消息是符合逻辑的 @lz1998

如果是 lazy 获取 member,且开发者正好不需要使用 nick 字段,只用 group_code、from_uin 这些字段,实际上是可以在未同步前接受消息的。

@qianjunakasumi
Copy link
Contributor

如果是 lazy 获取 member,且开发者正好不需要使用 nick 字段,只用 group_code、from_uin 这些字段,实际上是可以在未同步前接受消息的。

gocqhttp:那我呢?

很遗憾,你很难假设开发者正好不需要使用。在大多数的框架里,往往包含有权限系统,其中需要有发送者的群权限信息,而这正好是需要你的 lazy 来获取
通过优化普通用户没那么多群的时间,来换取不确定、不稳定性,我觉得不值得

这样只会加大终端用户的理解负担,给调试带来不便,收益浅薄

@lz1998
Copy link
Contributor Author

lz1998 commented Feb 3, 2022

如果是 lazy 获取 member,且开发者正好不需要使用 nick 字段,只用 group_code、from_uin 这些字段,实际上是可以在未同步前接受消息的。

gocqhttp:那我呢?

很遗憾,你很难假设开发者正好不需要使用。在大多数的框架里,往往包含有权限系统,其中需要有发送者的群权限信息,而这正好是需要你的 lazy 来获取 通过优化普通用户没那么多群的时间,来换取不确定、不稳定性,我觉得不值得

这样只会加大终端用户的理解负担,给调试带来不便,收益浅薄

可以在获取不到群,但是又急着使用的情况下,优先去获取需要使用的这个群的信息。(其他群可以慢点)

另外如果改成线程安全的 K-V 形式,可以避免因为多线程造成 group_list 中存在多个相同群的问题 https://github.com/Mrs4s/MiraiGo/blob/master/client/group_msg.go#L431

因为 GroupInfo 里面包含 members,如果同时保存了多份大群的 members,会占用非常多的内存(尤其是发口令红包的时候)

@qianjunakasumi
Copy link
Contributor

可以在获取不到群,但是又急着使用的情况下,优先去获取需要使用的这个群的信息。(其他群可以慢点)

请考虑在群较多,多个群并发量较大的情况下,集中请求获取群信息接口,是否易造成腾讯风控

另外如果改成线程安全的 K-V 形式,可以避免因为多线程造成 group_list 中存在多个相同群的问题 https://github.com/Mrs4s/MiraiGo/blob/master/client/group_msg.go#L431

因为 GroupInfo 里面包含 members,如果同时保存了多份大群的 members,会占用非常多的内存(尤其是发口令红包的时候)

依您所言,您可能更希望是实现自定义的或接管miraigo的逻辑,或是优化,而不是一开始所讨论的存储方式

@lz1998
Copy link
Contributor Author

lz1998 commented Feb 4, 2022

可以在获取不到群,但是又急着使用的情况下,优先去获取需要使用的这个群的信息。(其他群可以慢点)

请考虑在群较多,多个群并发量较大的情况下,集中请求获取群信息接口,是否易造成腾讯风控

另外如果改成线程安全的 K-V 形式,可以避免因为多线程造成 group_list 中存在多个相同群的问题 https://github.com/Mrs4s/MiraiGo/blob/master/client/group_msg.go#L431
因为 GroupInfo 里面包含 members,如果同时保存了多份大群的 members,会占用非常多的内存(尤其是发口令红包的时候)

依您所言,您可能更希望是实现自定义的或接管miraigo的逻辑,或是优化,而不是一开始所讨论的存储方式

现在的 MiraiGo 在启动之后就可以接受消息(刷新群列表之前),并发情况下会造成集中请求获取群信息接口。如果是 lazy 的,可以一定程度上避免你说的问题,不一定需要每次都获取群信息

https://github.com/Mrs4s/MiraiGo/blob/master/client/group_msg.go#L28
https://github.com/Mrs4s/MiraiGo/blob/master/client/group_msg.go#L300
https://github.com/Mrs4s/MiraiGo/blob/master/client/group_msg.go#L421

@qianjunakasumi
Copy link
Contributor

现在的 MiraiGo 在启动之后就可以接受消息(刷新群列表之前),并发情况下会造成集中请求获取群信息接口。如果是 lazy 的,可以一定程度上避免你说的问题,不一定需要每次都获取群信息

您说的不无道理,但我仍想提醒您,MiraiGo 是为 gocqhttp 设计的,这个功能对于 gocqhttp 而言,名存实亡。

转折:
gocqhttp 上报数据时每每会携带必要的群信息,从协议规范来看,该字段并不是 Onebot 标准中定义的。
https://12.onebot.dev/interface/event/message/group/

倘若您能够说服 gocqhttp 用户,删去该字段,那么您提出的功能就有实际意义

上述内容假设于您将群成员列表数据存放至本地磁盘

@qianjunakasumi
Copy link
Contributor

对于您提出的 现在的 MiraiGo 在启动之后就可以接受消息(刷新群列表之前)

本身 MiraiGo 只提供低级 API,https://github.com/Mrs4s/go-cqhttp/blob/f8fa906a952dff1cb111c40411d1ae8632e83326/cmd/gocq/main.go#L341 中可见,gocqhttp 认为是等待群列表同步完成后再行注册事件接收回调

@lz1998
Copy link
Contributor Author

lz1998 commented Feb 4, 2022

对于您提出的 现在的 MiraiGo 在启动之后就可以接受消息(刷新群列表之前)

本身 MiraiGo 只提供低级 API,https://github.com/Mrs4s/go-cqhttp/blob/f8fa906a952dff1cb111c40411d1ae8632e83326/cmd/gocq/main.go#L341 中可见,gocqhttp 认为是等待群列表同步完成后再行注册事件接收回调

在刷新之前只是gocq不上报,但是MiraiGo收到message如果没有group_info,是会获取group_info的,并发情况下会造成多次获取相同群的信息,存到GroupList,可能会产生相同群的情况(这应该可以开另一个issue,和存储无关)

@qianjunakasumi
Copy link
Contributor

通过接口,提供更优解,
以牺牲部分性能、安全性换取部分极端环境设备可用性,权由用户抉择
应当尊重并给予用户自主选择权

个人建议实现后可以完善这部分文档

@fumiama fumiama added enhancement New feature or request help wanted Extra attention is needed labels May 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

7 participants