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());
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

小伙伴们看到,在登录失败时我们通过 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();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

小伙伴们看到,直接在 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());
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

同步阻塞请求就通过异常捕获去获取服务端返回的异常信息即可。

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());
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

好啦,一个关于 gRPC 的小小知识点~

责任编辑:武晓燕 来源: 江南一点雨
相关推荐

2023-02-02 08:56:25

线程池线程submit

2024-08-29 08:54:35

2017-05-26 11:32:44

程序应用测试

2010-02-23 17:12:01

WCF字符串

2022-04-12 07:32:40

引擎模式Spring策略模式

2012-07-03 11:18:20

运维disable tab

2018-11-21 12:27:21

JavaScript 货币值区域

2019-09-20 08:38:51

Python编程语言Java

2012-11-14 14:07:23

Mysql

2018-01-24 19:59:03

数据库Oracle坏块

2011-04-11 17:10:16

Oracle

2019-11-08 08:00:00

ASP .NETASP .NET Cocookie

2010-08-26 12:12:19

LMHOSTS文件

2021-08-18 08:20:14

SQL除数统计

2012-07-30 09:35:10

项目管理

2019-05-09 15:31:23

攻击服务器安全

2019-08-15 10:20:19

云计算技术安全

2011-04-29 09:56:13

一体机传真件

2025-02-05 10:02:03

Locust测试异常处理

2021-03-24 10:40:26

Python垃圾语言
点赞
收藏

51CTO技术栈公众号