我最近将我的 gRPC 客户端迁移到 Stream 方法,之前使用 Camouflage 进行的模拟实现不再起作用。 我的客户(Java):
private final TestAdapterStub testAdapterStub;
private CompletableFuture<GetTestResponse> responseFuture;
public GetTestResponse getTest(String id) {
GetTestRequest request = GetTestRequest.newBuilder()
.setId(id)
.build();
responseFuture = new CompletableFuture<>();
StreamObserver<GetTestResponse> responseObserver = getResponseObserver();
var requestObserver = testAdapterStub.getTest(responseObserver);
requestObserver.onNext(request);
requestObserver.onCompleted();
try {
return responseFuture.join(); // here I am not getting response, failing by timeout
} catch (CompletionException exception) {
...
}
}
private StreamObserver<GetTestAdapter.GetTestResponse> getTestObserver() {
return new StreamObserver<>() {
@Override
public void onNext(GetTestResponse getTestResponse) {
responseFuture.complete(getTestResponse);
}
@Override
public void onError(Throwable exception) {
responseFuture.completeExceptionally(exception);
}
@Override
public void onCompleted() {
}
};
}
}
所以,基本上,我不想调用其他服务,而是想使用伪装来模拟响应。 根据文档https://testinggospels.github.io/camouflage/mocking-gRPC/,我已经在正确的目录下创建了.proto文件和.mock文件,proto文件通过Postman进行了测试,我得到了响应从服务器,我知道目录是正确的,因为当我在 Docker 中启动伪装容器时,我在日志中看到那些 .mock 文件和 .proto 文件已注册。
但无论我如何尝试根据文档中的选项编写 .mock 文件,我要么在伪装日志中收到错误,表明文件有问题,要么没有得到任何响应并因超时而失败。
我尝试过:
Unary Or Client Side Streaming
模拟文件:{
"testResponse": {
}
}
出现伪装错误:
Cannot read properties of undefined (reading 'delay')
我尝试在文件中添加 "delay": {{num_between lower=500 upper=600}}
选项,但显然这不是问题,而且我无法确定此日志的含义。
Server Side Streaming
模拟文件:{
"testResponse": {
}
}
====
{
"testResponse": {
}
}
出现伪装错误:
Unexpected non-whitespace character after JSON at position 28
。在这种情况下,无论文档如何说明,伪装都不会将“====”符号视为分隔符。
Bidi Streaming
模拟文件:{
"data": {
"testResponse": {
}
}
}
这是不会产生伪装错误的方法,我在日志中看到它应该被返回,但在客户端本身我正在等待调试并且没有得到响应,因超时而失败。
显然,我也尝试了所有这些选项以及来自服务器的真实响应,为每个 .mock 文件添加了延迟,但仍然没有得到积极的结果。
如果您对我的实施有什么问题以及如何解决它有任何建议,我将非常感激。
无论如何,我要关闭这个问题,因为问题不在于伪装,而在于我的客户。我修改了客户端,添加了正确的
requestObserver
:
private StreamObserver<GetTestRequest> getRequestObserver(StreamObserver<GetTestResponse> responseObserver) {
return TestAdapterGrpc.newStub(grpcChannel()).getTest(
responseObserver);
}
private ManagedChannel grpcChannel() {
return ManagedChannelBuilder.forTarget("host:port").intercept(grpcInterceptor)
.build();
}
以及注入到我的客户端中的 GRPCInterceptor grpcInterceptor 类:
public class GRPCInterceptor implements ClientInterceptor {
public static final Metadata.Key<String> TRACE_ID_KEY = Metadata.Key.of("traceId",
ASCII_STRING_MARSHALLER);
@Override
public <M, R> ClientCall<M, R> interceptCall(
final MethodDescriptor<M, R> method, CallOptions callOptions, Channel next) {
return new BackendForwardingClientCall<>(method,
next.newCall(method, callOptions.withDeadlineAfter(10000, TimeUnit.MILLISECONDS))) {
@Override
public void sendMessage(M message) {
log.info("Method: {}, Message: {}", methodName, message);
super.sendMessage(message);
}
@Override
public void start(Listener<R> responseListener, Metadata headers) {
headers.put(TRACE_ID_KEY, UUID.randomUUID().toString());
BackendListener<R> backendListener = new BackendListener<>(methodName, responseListener);
super.start(backendListener, headers);
}
};
}
private static class BackendListener<R> extends ClientCall.Listener<R> {
String methodName;
ClientCall.Listener<R> responseListener;
protected BackendListener(String methodName, ClientCall.Listener<R> responseListener) {
super();
this.methodName = methodName;
this.responseListener = responseListener;
}
@Override
public void onMessage(R message) {
log.info("Method: {}, Response: {}", methodName, message);
responseListener.onMessage(message);
}
@Override
public void onHeaders(Metadata headers) {
responseListener.onHeaders(headers);
}
@Override
public void onClose(Status status, Metadata trailers) {
responseListener.onClose(status, trailers);
}
@Override
public void onReady() {
responseListener.onReady();
}
}
private static class BackendForwardingClientCall<M, R> extends
io.grpc.ForwardingClientCall.SimpleForwardingClientCall<M, R> {
String methodName;
protected BackendForwardingClientCall(MethodDescriptor<M, R> method, ClientCall delegate) {
super(delegate);
methodName = method.getBareMethodName();
}
}
}