Golang中的GRPC连接管理 [英] GRPC Connection Management in Golang
问题描述
我是GRPC的新手,并希望确保我正确地使用golang进行了连接管理.我不想为每个呼叫都创建一个新的连接,但是我也不想在扩展时创建瓶颈.
I am relatively new to GRPC and want to be sure that I am doing connection management correctly with golang. I don't want to have to create a new connection for every call but I also don't want to create bottlenecks as I scale.
我所做的是在init函数中创建一个连接:
What I did was to create a single connection in the init function:
var userConn *grpc.ClientConn
var userServiceName string
func init() {
userServiceName := os.Getenv("USER_SERVICE_URL")
if userServiceName == "" {
userServiceName = "localhost"
}
logging.LogDebug("userClient: Connecting to: "+userServiceName, "")
tempConn, err := grpc.Dial(userServiceName, grpc.WithInsecure())
if err != nil {
logging.LogEmergency("account_user_client.Init() Could not get the connection. "+err.Error(), "")
return
}
userConn = tempConn
}
然后针对每个功能,我将使用该连接来创建客户端:
And then for each function I will use that connection to create a Client:
c := user.NewUserClient(userConn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.GetUserFromTokenID(ctx, &user.GetUserFromTokenRequest{TransactionID: transactionID, OathToken: *oathToken})
//Handle Error and Response
这是处理grpc连接的可接受方法吗?有更好的建议吗?
is this an acceptable way to handle grpc connections? Any recommendations on better ways?
非常感谢您.
推荐答案
是的,每个服务只有一个GRPC客户端连接是可以的.而且,我在这里看不到任何其他选择.GRPC在后台进行了所有繁重的工作:例如,您不需要编写自己的客户端连接池(就象典型的RDBMS那样),因为它不会提供比单个GRPC连接更好的结果.
Yes, it's fine to have single GRPC client connection per service. Moreover, I don't see any other options here. GRPC does all the heavy lifting under the hood: for example, you don't need to write your own client connection pool (as you would do for a typical RDBMS), because it won't provide better results than a single GRPC connection.
但是,我建议您避免使用全局变量和init函数,尤其是对于网络设置.另外,您不需要在每次向GRPC服务发布请求时都创建GRPC客户端( c:= user.NewUserClient(userConn)
):这只是垃圾收集器的一项额外工作,在应用程序启动时可以创建客户端的唯一实例.
However I would suggest you to avoid using global variables and init functions, especially for networking setup. Also you don't need to create GRPC client (c := user.NewUserClient(userConn)
) every time you post a request to the GRPC service: this is just an extra work for garbage collector, you can create the only instance of client at the time of application startup.
更新
假设您正在编写服务器应用程序(因为可以从您在远程GRPC服务上调用的方法中看到),则可以简单地定义一个类型,该类型将包含与整个应用程序具有相同生存期的所有对象本身.按照传统,这些类型通常被称为服务器上下文",尽管由于Go在其标准库中具有 context
的非常重要的概念而使它有些混乱,但仍然有些混乱.
Assuming that you're writing server application (because it can be seen from the method you call on the remote GRPC service), you can simply define a type that will contain all the objects that have the same lifetime as the whole application itself. According to the tradition, these types are usually called "server context", though it's a little bit confusing because Go has very important concept of context
in its standard library.
// this type contains state of the server
type serverContext struct {
// client to GRPC service
userClient user.UserClient
// default timeout
timeout time.Duration
// some other useful objects, like config
// or logger (to replace global logging)
// (...)
}
// constructor for server context
func newServerContext(endpoint string) (*serverContext, error) {
userConn, err := grpc.Dial(endpoint, grpc.WithInsecure())
if err != nil {
return nil, err
}
ctx := &serverContext{
userClient: user.NewUserClient(userConn),
timeout: time.Second,
}
return ctx, nil
}
type server struct {
context *serverContext
}
func (s *server) Handler(ctx context.Context, request *Request) (*Response, error) {
clientCtx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
response, err := c.GetUserFromTokenID(
clientCtx,
&user.GetUserFromTokenRequest{
TransactionID: transactionID,
OathToken: *oathToken,
},
)
if err != nil {
return nil, err
}
// ...
}
func main() {
serverCtx, err := newServerContext(os.Getenv("USER_SERVICE_URL"))
if err != nil {
log.Fatal(err)
}
s := &server{serverCtx}
// listen and serve etc...
}
细节可能会根据您的实际工作而发生变化,但我只是想表明,将应用程序的状态封装在不同类型的实例中而不是感染全局名称空间要好得多.
Details may change depending on what you're actually working on, but I just wanted to show that it's much more better to encapsulate state of your application in an instance of distinct type instead of infecting global namespace.
这篇关于Golang中的GRPC连接管理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!