2 回答

TA貢獻1851條經驗 獲得超4個贊
是的,每個服務有一個 GRPC 客戶端連接很好。此外,我在這里看不到任何其他選項。GRPC 在幕后完成所有繁重的工作:例如,您不需要編寫自己的客戶端連接池(就像您為典型的 RDBMS 所做的那樣),因為它不會提供比單個 GRPC 連接更好的結果。
但是我建議您避免使用全局變量和初始化函數,尤其是對于網絡設置。此外,您不需要c := user.NewUserClient(userConn)在每次向 GRPC 服務發送請求時都創建 GRPC 客戶端 ( ):這只是垃圾收集器的額外工作,您可以在應用程序啟動時創建客戶端的唯一實例。
更新
假設您正在編寫服務器應用程序(因為它可以從您調用遠程 GRPC 服務的方法中看出),您可以簡單地定義一個類型,該類型將包含與整個應用程序本身具有相同生命周期的所有對象。按照傳統,這些類型通常被稱為“服務器上下文”,盡管這有點令人困惑,因為 Gocontext在其標準庫中有一個非常重要的概念。
// 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...
}
細節可能會根據您的實際工作而改變,但我只是想表明,將應用程序的狀態封裝在不同類型的實例中比感染全局命名空間要好得多。

TA貢獻1776條經驗 獲得超12個贊
一些事情使這個實現工作。
gRPC 通道(即
c
from?c := user.NewUserClient(userConn)
)由 http/2 連接支持。當連接關閉或失效時,它會自動重新連接或重試連接。http/2 支持在單個連接中同時發送消息。考慮到這種場景,service Order 對service Product 一次獲取一個產品庫存,更新產品庫存,更改產品優惠券。三個 grpc 請求可以重用單個 http/2 連接,grpc 將并發處理數據交換。所以只使用一個連接是可以的,而不是創建三個連接(比如 http/1)來實現這一點。
為避免過早優化,一個連接應該可以為一項服務啟動。為了將來的池性能,考慮為熱點 grpc 請求創建一個單獨的 tcp 連接(然后單獨的 http/2 連接)。
保持連接活動可能是件好事,以防某些代理可能會終止空閑連接。
- 2 回答
- 0 關注
- 272 瀏覽
添加回答
舉報