gRPC 中的異常該如何處理?
今天來和小夥伴們聊一聊該如何處理 gRPC 中遇到的異常。
在之前的幾篇文章中,其實我們也遇到過異常問題,只是當時沒有和小夥伴們細說,只是囫圇吞棗寫了一個案例而已,今天我們就來把這個話題跟小夥伴們仔細捋一捋。
我們之前寫過一個登入的案例,在之前的案例中,如果使用者在登入時輸入了錯誤的使用者名稱密碼的話,那麼我們是透過一個普通的資料流返回異常資訊,其實,對於異常資訊,我們可以透過專門的異常通道來寫回到客戶端。
1. 服務端處理異常
先來看看服務端如何處理異常。
還是以我們之前的 gRPC 登入案例為例,我們修改服務端的登入邏輯如下(完整程式碼小夥伴們可以參考之前的 手把手教大家在 gRPC 中使用 JWT 完成身份校驗 一文):
public class LoginServiceImpl extends LoginServiceGrpc.LoginServiceImplBase {
@Override
public void login(LoginBody request, StreamObserver<LoginResponse> responseObserver) {
String username = request.getUsername();
String password = request.getPassword();
if ("javaboy".equals(username) && "123".equals(password)) {
System.out.println("login success");
//登入成功
String jwtToken = Jwts.builder().setSubject(username).signWith(AuthConstant.JWT_KEY).compact();
responseObserver.onNext(LoginResponse.newBuilder().setToken(jwtToken).build());
responseObserver.onCompleted();
}else{
System.out.println("login error");
//登入失敗
responseObserver.onError(Status.UNAUTHENTICATED.withDescription("login error").asException());
}
}
}
小夥伴們看到,在登入失敗時我們透過 responseObserver.onError
方法將異常資訊寫回到客戶端。這個方法的引數是一個 Throwable 物件,對於這個物件,在 Status 這個列舉類中定義了一些常見的值,分別如下:
OK(0):請求成功。 CANCELLED(1):操作被取消。 UNKNOWN(2):未知錯誤。 INVALID_ARGUMENT(3):客戶端給了無效的請求引數。 DEADLINE_EXCEEDED(4):請求超過了截止時間。 NOT_FOUND(5):請求資源未找到。 ALREADY_EXISTS(6):新增的內容已經存在。 PERMISSION_DENIED(7):請求許可權不足。 RESOURCE_EXHAUSTED(8):資源耗盡。 FAILED_PRECONDITION(9):服務端上為準備好。 ABORTED(10):請求被中止。 OUT_OF_RANGE(11):請求超出範圍。 UNIMPLEMENTED(12):未實現的操作。 INTERNAL(13):服務內部錯誤。 UNAVAILABLE(14):服務不可用。 DATA_LOSS(15):資料丟失或者損毀。 UNAUTHENTICATED(16):請求未認證。
系統預設給出的請求型別大致上就這些。當然,如果這些並不能滿足你的需求,我們也可以擴充套件這個列舉類。
2. 客戶端處理異常
當服務端給出異常資訊之後,客戶端的處理分為兩種情況。
2.1 非同步請求
如果客戶端是非同步請求,則直接在異常回撥中處理即可,如下:
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("1234").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();
}
}
小夥伴們看到,直接在 onError 回到中處理異常即可。
2.2 同步請求
如果客戶端請求是同步阻塞請求,那麼就要透過異常捕獲的方式獲取服務端返回的異常資訊了,如下:
public class LoginClient2 {
public static void main(String[] args) throws InterruptedException {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
.usePlaintext()
.build();
LoginServiceGrpc.LoginServiceBlockingStub stub = LoginServiceGrpc.newBlockingStub(channel).withDeadline(Deadline.after(3, TimeUnit.SECONDS));
login(stub);
}
private static void login(LoginServiceGrpc.LoginServiceBlockingStub stub) throws InterruptedException {
try {
LoginResponse resp = stub.login(LoginBody.newBuilder().setUsername("javaboy").setPassword("1234").build());
System.out.println("resp.getToken() = " + resp.getToken());
} catch (Exception e) {
System.out.println("e.getMessage() = " + e.getMessage());
}
}
}
同步阻塞請求就透過異常捕獲去獲取服務端返回的異常資訊即可。
3. 題外話
最後,再來和小夥伴們說一個提高 gRPC 資料傳輸效率的小技巧,那就是傳輸的資料可以使用 gzip 進行壓縮。
具體處理方式就是在客戶端呼叫 withCompression
方法指定資料壓縮,如下:
public class LoginClient2 {
public static void main(String[] args) throws InterruptedException {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
.usePlaintext()
.build();
LoginServiceGrpc.LoginServiceBlockingStub stub = LoginServiceGrpc.newBlockingStub(channel).withDeadline(Deadline.after(3, TimeUnit.SECONDS));
login(stub);
}
private static void login(LoginServiceGrpc.LoginServiceBlockingStub stub) throws InterruptedException {
try {
LoginResponse resp = stub.withCompression("gzip").login(LoginBody.newBuilder().setUsername("javaboy").setPassword("123").build());
System.out.println("resp.getToken() = " + resp.getToken());
} catch (Exception e) {
System.out.println("e.getMessage() = " + e.getMessage());
}
}
}
好啦,一個關於 gRPC 的小小知識點~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024924/viewspace-2938987/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- GRpc異常處理FilterRPCFilter
- JSP 異常處理如何處理?JS
- 教你如何優雅處理Golang中的異常Golang
- 翻譯 | Java流中如何處理異常Java
- Java中如何處理空指標異常Java指標
- 如何優雅的處理異常
- Python 中的異常處理Python
- React 16 中的異常處理React
- Ruby中的TypeError異常處理Error
- 異常的處理
- 異常篇——異常處理
- 說說在 Python 中如何處理異常Python
- 異常-throws的方式處理異常
- Java 中的異常處理機制Java
- 異常處理
- 如何優雅處理前端異常?前端
- SpringBoot中異常處理Spring Boot
- [譯]Go如何優雅的處理異常Go
- spring中的統一異常處理Spring
- C#中的異常處理機制C#
- .NET中異常處理的最佳實踐
- pl/sql中錯誤的異常處理SQL
- Java中的異常處理最佳實踐Java
- 異常處理與異常函式函式
- java優雅的處理程式中的異常Java
- 直播軟體原始碼,異常偶有發生我們該如何處理?原始碼
- 如何優雅地處理前端異常?前端
- 如何使用SpringMvc處理Rest異常SpringMVCREST
- 請問EJB容器如何處理異常
- JavaScript 異常處理JavaScript
- ThinkPHP 異常處理PHP
- React 異常處理React
- 08、異常處理
- JAVA 異常處理Java
- JAVA異常處理Java
- Abp 異常處理
- oracle異常處理Oracle
- PowerShell 異常處理