panjf2000 / gnet

🚀 gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go.

Home Page:https://gnet.host

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Feature]: 为 Client.Enroll/Dial 提供同步 callback

IrineSistiana opened this issue · comments

Description of new feature

场景:

context 是要传给 SetContext() 的请求上下文。

用于服务端时,OnOpen() 可用初始化 context, 然后 SetContext() 可以在 OnOpen() 里安全的调用。然后其余方法后续也可以很方便的取出 context 。

用于客户端时,先有请求需求,然后构建 context,然后建立连接,然后 Enroll。然后并不能直接 SetContext(context)。

client, err := gnet.NewClient()
reqCtx := &requestContext{}
c, err := net.Dial("tcp", "")
gnetConn, err := client.Enroll(c)
gnetConn.SetContext(reqCtx) // X

并且客户端构建好的请求 context 很难传进 OnOpen()。

目前采用的方法是借用 Wake() 的 AsyncCallback 来调用 SetContext()。 但因为 Wake() 的时间不确定所以 eventloop 里的其他需要用到 context 的方法时还要做格外的同步措施,增加心智负担。

希望:

客户端 Enroll() 时 提供同步的 callback。简化使用难度。

比如新增方法 EnrollWithCallback(call func(c Conn) (action Action))。 call 会在成功 Enroll 后在 eventloop 里调用。call 的功能相当于服务端的 OnOpen(),但可以更自由的传入参数。这样客户端就能在 Enroll 后很方便的发送请求,SetContext() 等等,而不需要考虑和 Wake 同步等问题。

gnetConn, err := client.EnrollWithCallback(c, func(gnetConn gnet.Conn) gnet.Action {
    gnetConn.SetContext(reqCtx) // √
    gnetConn.Write(req)         // √
    return gnet.None
})

Scenarios for new feature

Breaking changes or not?

No

Code snippets (optional)

No response

Alternatives for new feature

None.

Additional context (optional)

None.

并且客户端构建好的请求 context 很难传进 OnOpen()。

这里我不太理解,为什么不行?你想在 EnrollWithCallback 里做的事情在我看来在 OnOpen 里也一样能做?

目前没想到能把 context 传进 OnOpen 的方法。

场景:先有请求需求,然后构建 context。然后打开一个连接处理这个请求。所以这个 context 是提前准备好的,必须要 SetContext 传给 Conn。

因为 OnOpen 接收的参数只有 Conn 和 EventHandler。目前没有方法能把这个 context 传给 OnOpen。

还有如果是 udp 连接 OnOpen 不会执行。


自己的例子不方便复制过来。搜了一下 GitHub。这哥们的需求和我很像。必须要传一个 context。

https://github.com/djshow832/gnet-proxy/blob/f670115f86dc40b32830148d59fbd6840d8cb89a/dcli/double_cli.go#L49

他 SetContext() Context 都用锁了。

https://github.com/djshow832/gnet-proxy/blob/f670115f86dc40b32830148d59fbd6840d8cb89a/dcli/double_cli.go#L88

如果可以确保 OnOpen 里,或者 Enroll 后,首先就能 SetContext,就不要锁了。

自己尝试改了改。 IrineSistiana@92ed9e2

因为我只要 SetContext 所以就只加了 EnrollWithContext

但觉得 EnrollWithCallback 可能更通用些。

commented

是的, 我也遇到同样的问题, 需要再OnOpen用一个for循环等待context绑定上了, 希望能够Dial的时候传入参数就能够让gnet.Conn SetContext绑定上一个Context对象

commented

QQ图片20240313104135
如果能加入DialWithContext, 内部构件gnet.Conn的时候就SetContext, 我就不需要在OnOpen的去用一个for循环进行等待绑定上我的网络处理类, 后面OnTraffic数据来的时候直接就从Context中取, 执行自己的业务逻辑
QQ图片20240313104316

commented

Server端是不会存在这样的不便, 这种不方便的地方会存在Client端, 因为调用Dial跟OnOpen回调是不在同一个协程上处理, 所以这个时候没办法在Dial之后安全的绑定上我需要的一个Context, 如果在Dial的时候和gnet Conn绑定上就不需要处理这个问题, OnTraffic直接从Conn里面拿出Context处理逻辑就行了, 现在Dial没办法绑定上Context, 我就需要在OnOpen写个for循环等到syncMap有值, 太麻烦了也很不方便

自己尝试改了改。 IrineSistiana@92ed9e2

因为我只要 SetContext 所以就只加了 EnrollWithContext

但觉得 EnrollWithCallback 可能更通用些。

EnrollWithCallback 在我看来如果一旦实现了 OnOpen 对于 client 来说好像就没有存在的价值了,因为从功能上来说后者就变成了前者的子集,那么 gnet 的代码库里就多了一些重复的代码实现。所以我想和你确认下 EnrollWithCallback 除了传递额外信息之外还能有什么潜在的实际用处?(必须是 OnOpen 做不到的)

如果没有的话,感觉实现 DialContext/EnrollContext 性价比更高一些。
@IrineSistiana

@panjf2000

如果没有的话,感觉实现 DialContext/EnrollContext 性价比更高一些。

是的。我觉得 DialContext/EnrollContext 也更简单。

当时提 EnrollWithCallback 就是觉得和其他 api 更相似,因为很多 api 都是提供 callback。可能有其他人需要。

必须是 OnOpen 做不到的

UDP 连接不会调用 OnOpen 。(update: 直觉上好像应该要调用,因为用于 client 连接是 connected udp。

commented

有空可以看下这个PR, 我测试用例也写了 a0050b4