Java版gRPCのStatusのメモ。
gRPCでは、gRPCサーバーで発生したエラーをgRPCクライアントに伝えるのに、Statusを使う。
Statusのエラーコードは規定されている。
JavaではStatusExceptionあるいはStatusRuntimeExceptionでStatusを扱う。
これらの例外にはMetadata(trailers)を指定することが出来る。
Metadataではユーザー独自のメッセージを扱うことが出来る。
独自メッセージを扱う例として、protobufで独自のmessageを定義する。
syntax = "proto3";
package example.grpc;
option java_multiple_files = true;
option java_package = "com.example.grpc.proto";
message MyErrorInfo {
int64 error_code = 1;
string error_message = 2;
}
@Override
public void sendInt64Pair(Int64PairRequest request, StreamObserver<Int64PairResponse> responseObserver) {
〜
{
StatusException e = createStatusException();
responseObserver.onError(e);
return;
}
〜
}
gRPCサーバーでresponseObserverのonError()を呼ぶと、gRPCクライアントにエラーが通知される。
import com.example.grpc.proto.MyErrorInfo; import com.google.protobuf.Any; import io.grpc.Status; import io.grpc.StatusException; import io.grpc.protobuf.StatusProto;
public static StatusException createStatusException() {
var builder = com.google.rpc.Status.newBuilder();
var status = Status.INTERNAL;
builder.setCode(status.getCode().value());
builder.setMessage("my status message");
var errorInfo = MyErrorInfo.newBuilder()
.setErrorCode(789)
.setErrorMessage("example error")
.build();
builder.addDetails(Any.pack(errorInfo));
var statusProto = builder.build();
return StatusProto.toStatusException(statusProto);
}
通常使うio.grpc.Statusの他に、com.google.rpc.Statusという(パッケージ違いの)クラスがあるので注意。
Metadataを扱う際は、com.google.rpcの方を使う。
同様に、StatusProtoクラスもio.grpc.protobuf.StatusProtoとcom.google.rpc.StatusProtoがあるので注意。
ここで使うのはio.grpc.protobufの方。
try {
return blockingStub.sendInt64Pair(request);
} catch (StatusRuntimeException e) {
handleStatusException(e);
return null;
}
import com.example.grpc.proto.MyErrorInfo; import com.google.protobuf.Any; import com.google.protobuf.InvalidProtocolBufferException; import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.protobuf.StatusProto;
public static void handleStatusException(StatusRuntimeException e) {
{
Status status = e.getStatus();
String message = e.getMessage();
System.out.printf("StatusRuntimeException: %s, %s%n", status, message);
}
com.google.rpc.Status protoStatus = StatusProto.fromThrowable(e);
Status status = Status.fromCodeValue(protoStatus.getCode());
String message = protoStatus.getMessage();
System.out.printf("protoStatus: %s, %s%n", status, message);
for (Any detail : protoStatus.getDetailsList()) {
if (detail.is(MyErrorInfo.class)) {
try {
var errorInfo = detail.unpack(MyErrorInfo.class);
System.out.printf("MyErrorInfo: %d, %s%n", errorInfo.getErrorCode(), errorInfo.getErrorMessage());
} catch (InvalidProtocolBufferException e1) {
e1.printStackTrace();
}
}
}
}
StatusRuntimeException: Status{code=INTERNAL, description=my status message, cause=null}, INTERNAL: my status message
protoStatus: Status{code=INTERNAL, description=null, cause=null}, my status message
MyErrorInfo: 789, example error
StatusRuntimeExceptionから直接getMessage()で取得したメッセージと、StatusProto経由で取得したメッセージは、Statusコードの部分が異なる。