英文:
Should I care about providing asynchronous calls in my go library?
问题
我正在开发一个简单的Go库,用于通过HTTP进行JSON-RPC。
有以下方法:
rpcClient.Call("myMethod", myParam1, myParam2)
该方法内部执行http.Get()
并返回结果或错误(元组)。
当调用者调用该方法时,它是同步的,并在Get()
调用返回时返回结果。
这是提供Go库的方式吗?我应该让我的库的用户自行决定是否将其设置为异步吗?
还是应该提供一个名为rpcClient.CallAsync()
的第二个函数,并在这里返回一个通道?因为通道无法提供元组,所以我必须将(响应,错误)元组打包到一个结构体中,并返回该结构体。
这样做有意义吗?
否则,用户将不得不将每个调用包装在一个丑陋的方法中,例如:
result := make(chan AsyncResponse)
go func() {
res, err := rpcClient.Call("myMethod", myParam1, myParam2)
result <- AsyncResponse{res, err}
}()
在Go库和异步性方面是否有最佳实践?
英文:
I am developing a simple go library for jsonrpc over http.
There is the following method:
rpcClient.Call("myMethod", myParam1, myParam2)
This method internally does a http.Get() and returns the result or an error (tuple).
This is of course synchron for the caller and returns when the Get() call returns.
Is this the way to provide libraries in go? Should I leave it to the user of my library to make it asynchron if she wants to?
Or should I provide a second function called:
rpcClient.CallAsync()
and return a channel here? Because channels cannot provide tuples I have to pack the (response, error) tuple in a struct and return that struct instead.
Does this make sense?
Otherwise the user would have to wrap every call in an ugly method like:
result := make(chan AsyncResponse)
go func() {
res, err := rpcClient.Call("myMethod", myParam1, myParam2)
result <- AsyncResponse{res, err}
}()
Is there a best practice for go libraries and asynchrony?
答案1
得分: 4
go的执行模型的整个重点是隐藏开发者的异步操作,并像具有阻塞操作的线程模型一样运行。在幕后,有绿色线程、异步IO和一个非常复杂的调度器。
所以,不,你不应该为你的库提供异步API。在go中,网络操作以伪阻塞的方式完成,从代码的角度来看,你可以打开尽可能多的goroutine,因为它们非常廉价。
所以你的最后一个例子是正确的方式,我不认为它丑陋。因为它允许开发者选择并发模型。在一个http服务器的上下文中,每个命令都在单独的goroutine中处理,我只需调用rpcClient.Call("myMethod", myParam1, myParam2)
。
或者,如果我想要一个扇出(fanout)操作,我会创建扇出逻辑。
你还可以创建一个方便的函数来执行调用并在一个通道上返回:
func CallAsync(method, p1, p2) chan AsyncResponse {
result := make(chan AsyncResponse)
go func() {
res, err := rpcClient.Call(method, p1, p2)
result <- AsyncResponse{res, err}
}()
return result
}
英文:
The whole point of go's execution model is to hide the asynchronous operations from the developer, and behave like a threaded model with blocking operations. Behind the scenes there are green-threads and asynchronous IO and a very sophisticated scheduler.
So no, you shouldn't provide an async API to your library. Networking in go is done in a pseudo-blocking way from the code's perspective, and you open as many goroutines as needed, as they are very cheap.
So your last example is the way to go, and I don't consider it ugly. Because it allows the developer to choose the concurrency model. In the context of an http server, where each command is handled in separate goroutine, I'd just call rpcClient.Call("myMethod", myParam1, myParam2)
.
Or if I want a fanout - I'll create fanout logic.
You can also create a convenience function for executing the call and returning on a channel:
func CallAsync(method, p1, p2) chan AsyncResponse {
result := make(chan AsyncResponse)
go func() {
res, err := rpcClient.Call(method, p1, p2)
result <- AsyncResponse{res, err}
}()
return result
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论