gRPC為什麼使用截止時間而不是超時時間?

張哥說技術發表於2023-03-02

在 HTTP 請求中,我們傳送請求的時候,可以設定一個請求超時時間-connectTimeout,即在指定的時間內,如果請求沒有到達服務端,為了避免客戶端一直進行不必要的等待,就會丟擲一個請求超時異常。

但是在微服務系統中,我們卻很少設定請求超時時間,一般都是用另外一個概念代替,那就是請求截止時間。

這是什麼原因呢?今天我們就來簡單聊一聊這個話題。

在微服務中我們客戶端的請求在服務端往往會有比較複雜的鏈條,我想起來 Spring Cloud Sleuth 官方給的一個請求鏈路追蹤的圖,我們直接拿來看下:

gRPC為什麼使用截止時間而不是超時時間?

這張圖中,請求從客戶端發起之後,在服務端一共經歷了四個 SERVICE,對於這樣的請求,如果我們還是按照之前傳送普通 HTTP 請求的方式,設定一個 connectTimeout 顯然是不夠的。

我舉個例子:

假設我們傳送一個請求,為該請求設定 connectTimeout 為 5s,那麼這個時間只對第一個服務 SERVICE1 有效,也就是請求在 5s 之內沒有到達 SERVICE1,那麼就會丟擲連線超時異常;請求如果在 5s 之內到達 SERVICE1,那麼就不會丟擲異常,但是!!!,請求到達 SERVICE1 並不意味著請求結束,後面從 SERVICE1 到 SERVICE2,從 SERVICE2 到 SERVICE3,從 SERVICE3 到 SERVICE4,還有四個 HTTP 請求待處理,這些請求超時了怎麼辦?很明顯,connectTimeout 屬性對於後面幾個請求就鞭長莫及了。

所以,對於這種場景,我們一般使用截止時間來處理。

截止時間相當於設定整個請求生命週期的時間,也就是這個請求,我要多久拿到結果。很明顯,這個時間應該在客戶端發起請求的時候設定。

gRPC 中提供了對應的方法,我們可以非常方便的設定請求的截止時間 DeadLineTime,如下:

public class LoginClient {
    public static void main(String[] args) throws InterruptedException {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost"50051)
                .usePlaintext()
                .build();
        LoginServiceGrpc.LoginServiceStub stub = LoginServiceGrpc.newStub(channel).withDeadline(Deadline.after(3, TimeUnit.SECONDS));
        login(stub);
    }

    private static void login(LoginServiceGrpc.LoginServiceStub stub) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        stub.login(LoginBody.newBuilder().setUsername("javaboy").setPassword("123").build(), new StreamObserver<LoginResponse>() {
            @Override
            public void onNext(LoginResponse loginResponse) {
                System.out.println("loginResponse.getToken() = " + loginResponse.getToken());
            }

            @Override
            public void onError(Throwable throwable) {
                System.out.println("throwable = " + throwable);
            }

            @Override
            public void onCompleted() {
                countDownLatch.countDown();
            }
        });
        countDownLatch.await();
    }
}

服務端透過 Thread.sleep 做個簡單的休眠就行了,超時之後,客戶端的 onError 方法會被觸發,丟擲如下異常:

throwable = io.grpc.StatusRuntimeException: DEADLINE_EXCEEDED: deadline exceeded after 2.939621462s. [closed=[], open=[[buffered_nanos=285550823, remote_addr=localhost/127.0.0.1:50051]]]

好啦,一個簡單的小細節,感興趣的小夥伴不妨去試試啦~

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2937810/,如需轉載,請註明出處,否則將追究法律責任。

相關文章