Allenxuxu / gev

🚀Gev is a lightweight, fast non-blocking TCP network library / websocket server based on Reactor mode. Support custom protocols to quickly and easily build high-performance servers.
MIT License
1.72k stars 193 forks source link

调用Connection.Send时,必须创建新的buffer,开销略大 #95

Closed wangyjyj closed 3 years ago

wangyjyj commented 3 years ago

`func (c *Connection) Send(buffer []byte) error { if !c.connected.Get() { return ErrConnectionClosed }

c.loop.QueueInLoop(func() {
    if c.connected.Get() {
        c.sendInLoop(c.protocol.Packet(c, buffer))
    }
})
return nil

}` buffer参数需要等到sendInLoop执行完成后,才能被重用或释放 但目前接口设计上来说,Send调用方是无法知道buffer是否已经被使用完毕,导致无法使用Connection的UserBuffer或者buffer池等方法重复使用buffer,而是每次发送必须创建一个新的buffer,使用完毕后等待gc自动回收,效率略差

暂时想到两个方案:

  1. Connection.Callback增加OnSendInLoopFinish回调,将buffer传参出去,调用方在回调中将buffer重新放回内存池
  2. 修改Protocal的Packet方法的data参数,类型改为interface,允许外部传入protobuf等未打包的对象,在自定义Packet实现时,使用Connection的UserBuffer作为打包buffer

不知道是否可以支持 ?

GareyGit commented 3 years ago
Packet(c *Connection, data interface{}) []byte

func (d *DefaultProtocol) Packet(c *Connection, data interface{}) []byte {
       return data.([]byte)
}

func (c *Connection) Send(data interface{}) error {
}

建议这样调整一下就可以了,上层改动不大,原来直接发送[]byte的方法都不用修改,只需要修改一下Pack方法就可以了。

Allenxuxu commented 3 years ago
Packet(c *Connection, data interface{}) []byte

func (d *DefaultProtocol) Packet(c *Connection, data interface{}) []byte {
       return data.([]byte)
}

func (c *Connection) Send(data interface{}) error {
}

建议这样调整一下就可以了,上层改动不大,原来直接发送[]byte的方法都不用修改,只需要修改一下Pack方法就可以了。

这样的优势是什么,我似乎没有get 到。

按照一楼老哥的方法2,感觉在 packet 里他还需要再copy一次内存到 UserBuffer 。

Allenxuxu commented 3 years ago

`func (c *Connection) Send(buffer []byte) error { if !c.connected.Get() { return ErrConnectionClosed }

c.loop.QueueInLoop(func() {
  if c.connected.Get() {
      c.sendInLoop(c.protocol.Packet(c, buffer))
  }
})
return nil

}` buffer参数需要等到sendInLoop执行完成后,才能被重用或释放 但目前接口设计上来说,Send调用方是无法知道buffer是否已经被使用完毕,导致无法使用Connection的UserBuffer或者buffer池等方法重复使用buffer,而是每次发送必须创建一个新的buffer,使用完毕后等待gc自动回收,效率略差

暂时想到两个方案:

  1. Connection.Callback增加OnSendInLoopFinish回调,将buffer传参出去,调用方在回调中将buffer重新放回内存池
  2. 修改Protocal的Packet方法的data参数,类型改为interface,允许外部传入protobuf等未打包的对象,在自定义Packet实现时,使用Connection的UserBuffer作为打包buffer

不知道是否可以支持 ?

感觉这里确实可以优化。

方案1 提供一个回调确实来通知你可以回收 data 感觉确实比较简洁 方案2 感觉你想复用UserBuffer ,但是感觉大多数场景你还需要一个把你的数据copy到 UserBuffer 的操作。

目前,我更倾向于 方案一 。

Allenxuxu commented 3 years ago

image

按照 方案1 只需要 sendInLoop 这个函数执行完,回调通知你,data 用户就可以回收了。

GareyGit commented 3 years ago

我是这样理解的,Packet的目的是打包,从自定义结构打包成bytes的逻辑就应该放在Packet这个方法内,所以才有这样的建议,当然方案也能用,但明显属于补丁式修复。

我目前用的protobuf协议,UnPacket方法中解包,然后在Send之前打包,总觉得怪怪的,我本地修改了你的代码,现在protocol文件看起来顺眼多了。

Allenxuxu commented 3 years ago

我是这样理解的,Packet的目的是打包,从自定义结构打包成bytes的逻辑就应该放在Packet这个方法内,所以才有这样的建议,当然方案也能用,但明显属于补丁式修复。

我目前用的protobuf协议,UnPacket方法中解包,然后在Send之前打包,总觉得怪怪的,我本地修改了你的代码,现在protocol文件看起来顺眼多了。

我明白你的意思了,对于方案1 ,只是想满足一下,他想回收data 的需求。

对于你这个需求,如果按照你说的方式的话 OnMessage 回调的返回值应该也得改。你本地现在怎么改的?

GareyGit commented 3 years ago

本地修改就只有上面的代码,只是OnMessage复用了ctx这个interface,返回的是UnPacket解包后的自定义结构

wangyjyj commented 3 years ago

感谢大佬~~

Allenxuxu commented 3 years ago

@wangyjyj https://github.com/Allenxuxu/gev/pull/97

@GareyGit https://github.com/Allenxuxu/gev/pull/96

你们可以看下这两个PR,分别对应你们的需求,看看使用上有么有什么问题,有没有完全满足需求

GareyGit commented 3 years ago
// Send 用来在非 loop 协程发送
func (c *Connection) Send(buffer interface{}) error {
    if !c.connected.Get() {
        return ErrConnectionClosed
    }

    c.loop.QueueInLoop(func() {
        if c.connected.Get() {
            c.sendInLoop(c.protocol.Packet(c, buffer))
        }
    })
    return nil
}
Allenxuxu commented 3 years ago
// Send 用来在非 loop 协程发送
func (c *Connection) Send(buffer interface{}) error {
  if !c.connected.Get() {
      return ErrConnectionClosed
  }

  c.loop.QueueInLoop(func() {
      if c.connected.Get() {
          c.sendInLoop(c.protocol.Packet(c, buffer))
      }
  })
  return nil
}

🤔️啥问题?

GareyGit commented 3 years ago

Send的参数没改...

-func (c *Connection) Send(buffer []byte) error {
+func (c *Connection) Send(buffer interface{}) error {
Allenxuxu commented 3 years ago

Send的参数没改...

-func (c *Connection) Send(buffer []byte) error {
+func (c *Connection) Send(buffer interface{}) error {

😅,我傻了。 已经 push 了。

Allenxuxu commented 3 years ago

@GareyGit @wangyjyj 感谢两位的建议🙏 https://github.com/Allenxuxu/gev/releases/tag/v0.3.0 已经发布