天天看點

Go gRPC進階-逾時設定(六)

前言

gRPC預設的請求的逾時時間是很長的,當你沒有設定請求逾時時間時,所有在運作的請求都占用大量資源且可能運作很長的時間,導緻服務資源損耗過高,使得後來的請求響應過慢,甚至會引起整個程序崩潰。

為了避免這種情況,我們的服務應該設定逾時時間。前面的入門教程提到,當用戶端發起請求時候,需要傳入上下文

context.Context

,用于結束

逾時

取消

的請求。

本篇以簡單RPC為例,介紹如何設定gRPC請求的逾時時間。

用戶端請求設定逾時時間

修改調用服務端方法

1.把逾時時間設定為目前時間+3秒

clientDeadline := time.Now().Add(time.Duration(3 * time.Second))
	ctx, cancel := context.WithDeadline(ctx, clientDeadline)
	defer cancel()
           

2.響應錯誤檢測中添加逾時檢測

// 傳入逾時時間為3秒的ctx
	res, err := grpcClient.Route(ctx, &req)
	if err != nil {
		//擷取錯誤狀态
		statu, ok := status.FromError(err)
		if ok {
			//判斷是否為調用逾時
			if statu.Code() == codes.DeadlineExceeded {
				log.Fatalln("Route timeout!")
			}
		}
		log.Fatalf("Call Route err: %v", err)
	}
	// 列印傳回值
	log.Println(res.Value)
           

完整的client.go代碼

服務端判斷請求是否逾時

當請求逾時後,服務端應該停止正在進行的操作,避免資源浪費。

// Route 實作Route方法
func (s *SimpleService) Route(ctx context.Context, req *pb.SimpleRequest) (*pb.SimpleResponse, error) {
	data := make(chan *pb.SimpleResponse, 1)
	go handle(ctx, req, data)
	select {
	case res := <-data:
		return res, nil
	case <-ctx.Done():
		return nil, status.Errorf(codes.Canceled, "Client cancelled, abandoning.")
	}
}

func handle(ctx context.Context, req *pb.SimpleRequest, data chan<- *pb.SimpleResponse) {
	select {
	case <-ctx.Done():
		log.Println(ctx.Err())
		runtime.Goexit() //逾時後退出該Go協程
	case <-time.After(4 * time.Second): // 模拟耗時操作
		res := pb.SimpleResponse{
			Code:  200,
			Value: "hello " + req.Data,
		}
		// //修改資料庫前進行逾時判斷
		// if ctx.Err() == context.Canceled{
		// 	...
		// 	//如果已經逾時,則退出
		// }
		data <- &res
	}
}
           

一般地,在寫庫前進行逾時檢測,發現逾時就停止工作。

完整server.go代碼

運作結果

服務端:

:8000 net.Listing...
goroutine still running
           

用戶端:

Route timeout!
           

總結

逾時時間的長短需要根據自身服務而定,例如傳回一個

hello grpc

,可能隻需要幾十毫秒,然而處理大量資料的同步操作則可能要很長時間。需要考慮多方面因素來決定這個逾時時間,例如系統間端到端的延時,哪些RPC是串行的,哪些是可以并行的等等。

教程源碼位址:https://github.com/Bingjian-Zhu/go-grpc-example

參考:https://grpc.io/blog/deadlines/

看完之後若覺得對自己有幫助,懇請點贊或評論。這是對我最大的鼓勵!