lxzan / gws

simple, fast, reliable websocket server & client, supports running over tcp/kcp/unix domain socket. keywords: ws, proxy, chat, go, golang...
https://pkg.go.dev/github.com/lxzan/gws
Apache License 2.0
1.36k stars 87 forks source link

task单元疑问 #41

Closed joestarzxh closed 1 year ago

joestarzxh commented 1 year ago

task单元的任务队列为什么没有进行扩缩容?每次都append,会不会对内存有一定影响呢?

lxzan commented 1 year ago

我以前也怀疑过这个问题, 实测切片容量不会无限增长.

joestarzxh commented 1 year ago

刚刚测试一下跑10000个任务,任务延迟100ms大概2000多,跑50000个任务4000多队列

lxzan commented 1 year ago

你看看这个WriteAsync测试

image

lxzan commented 1 year ago

每次跑10000个任务, 跑了十几轮, 可以看到队列容量没有单调递增

lxzan commented 1 year ago

加了1ms延迟后, 跑一轮挺费时间的, 容量比较大, 但是没有单调递增

image

lxzan commented 1 year ago

2000个函数指针, 大概会占用15KB内存, 问题不大, 下个版本优化下, 看看改成LinkedList还是PriorityQueue.

joestarzxh commented 1 year ago

嗯,好的,是问题不大。

joestarzxh commented 1 year ago

task很有特色,期待下一个版本

lxzan commented 1 year ago

task很有特色,期待下一个版本

在golang里面很少见的非channel任务队列实现.

下版本在获取任务那里加个缩容, 改动很小, 对性能也没多大影响:

image
shaovie commented 1 year ago

可以考虑用ringbuffer,一直复用一个slice

lxzan commented 1 year ago

可以考虑用ringbuffer,一直复用一个slice

和我设想的差不多。记录下消费偏移量offset, 用offset来访问队头节点,pop的时候如果队列为空就去重置。

lxzan commented 1 year ago

dev分支已经修复这个问题了, 我再测一测, 没问题就打新tag了.

joestarzxh commented 1 year ago

dev分支的task看起来很好的解决了这个问题,很棒

shaovie commented 1 year ago

咋还把我踢出来了

shaovie commented 1 year ago

这整的,到底谁情绪化了哦

shaovie commented 1 year ago

你看我都手敲代码了,你们还说我不严谨,我又不能强迫你们。

  1. 我写的示例代码, @lesismal getJob 里边 跟有没有 Concurrency 有啥关系?我只push了3次啊,难道你的并发数量还不能超过3?而且我的输出结果,第一个就错了,
  2. @lxzan 咋就面目全非了呢,展示出核心问题就行啊
lxzan commented 1 year ago

咋还把我踢出来了

我没踢

shaovie commented 1 year ago

格局啊

shaovie commented 1 year ago

我可以打包票,你那段代码有并发问题

lxzan commented 1 year ago

我可以打包票,你那段代码有并发问题

保持代码原样,再用实际证明它有问题而不是讲逻辑。

shaovie commented 1 year ago

^_^ 好吧,我也只能做到这儿了,以后遇到数据异常,你能想起来这儿可能是个雷就好了, 看好你哟

lxzan commented 1 year ago

^_^ 好吧,我也只能做到这儿了,以后遇到数据异常,你能想起来这儿可能是个雷就好了, 看好你哟

你先搞明白问题是什么吧

  1. 并发乱序是正常现象.
  2. 写队列不会乱序, 它是串行的.
lxzan commented 1 year ago

@joestarzxh 错误的测试方式得到了错误的结论. 实际上, 基于slice的ArrayList队列是自带扩缩容机制的, 先批量发10000条消息, 然后批量发100条消息, 结果如下:

image
lxzan commented 1 year ago

正常业务单个连接不会堆积成千上万条消息

joestarzxh commented 1 year ago

不知道前面那个版本有什么问题,目前版本是依靠gc来减少的,task可以作为通用基础单元,不一定要使用在ws上。

lxzan commented 1 year ago

不知道前面那个版本有什么问题,目前版本是依靠gc来减少的.

上个版本在低负载时有很好的复用性, 但是高负载场景表现不佳. 如果源源不断有任务, 就无法重置, 现在的版本(也是最初的版本)表现更加均衡.

更新了一点点优化, 写队列减少了一次加锁解锁, 消息处理队列改用channel, 增加了阻塞机制. 最简单最基础的并发限制机制:

type channel chan struct{}

func (c channel) add() { c <- struct{}{} }

func (c channel) done() { <-c }

func (c channel) Go(f func()) {
    c.add()
    go func() {
        f()
        c.done()
    }()
}

task可以作为通用基础单元,不一定要使用在ws上。

写队列一定要在ws上, 广播和异步写都依赖它. Broadcaster有一个很重要的优化, 发送1000条压缩消息, 只有一次压缩操作, 可以节省大量CPU时间.

消息处理队列保留下来也无妨, 它是默认关闭的.

joestarzxh commented 1 year ago

好的,我想用在流媒体之类的上面,感觉设计很不错

lxzan commented 1 year ago

好的,我想用在流媒体之类的上面,感觉设计很不错

感谢支持

lxzan commented 10 months ago

task很有特色,期待下一个版本

gws v1.6.14 任务队列底层采用优先队列了, 以避免切片扩容

lxzan commented 10 months ago

改进之后切片容量完全符合预期

image