Ошибка транспорта при использовании сервера Grpc на основе Java с клиентом на основе C #

Я разрабатываю сервер Grpc на основе Java, используя Spring boot (2.2.2) и java-библиотеку grpc-server-spring-boot-starter. Мое клиентское приложение - это приложение C # (netcoreapp3.1). Оба приложения работают на моем ноутбуке для этого теста.

Это мой прото-файл

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.honeywell.EOM.SystemAPI.webapi.web.grpc.service";
//option java_outer_classname = "TestProto";

service Simple {
    rpc GetData (Empty) returns (Data) {
    }
}

message Empty {

}

message Data {
    string name = 1;
}

Это сервисный код

@GrpcService
public class TestService extends SimpleGrpc.SimpleImplBase {
    @Override
    public void getData(Empty request, StreamObserver<Data> responseObserver) {
        Data data = Data.newBuilder().setName("Somename").build();
        responseObserver.onNext(data);
        responseObserver.onCompleted();
    }
}

Сначала я протестировал сервер с помощью этого инструмента tool

Сервис отлично работает с этим клиентским инструментом.

Однако, когда я тестирую с помощью клиента C #, я получаю эту ошибку

io.grpc.netty.shaded.io.grpc.netty.NettyServerTransport notifyTerminated
INFO: Transport failed
io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 16030100b6010000b203035e95b0402c6320969d3d5fba04
    at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:103)
    at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:306)
    at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:239)
    at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:438)
    at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:505)
    at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:444)
    at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:283)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
    at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
    at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931)
    at io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
    at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:700)
    at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635)
    at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552)
    at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514)
    at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044)
    at io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:834)

Клиентский код C # находится здесь

using var channel = GrpcChannel.ForAddress("https://localhost:9090");
var client = new Simple.SimpleClient(channel);
var reply = client.GetData(new Empty { });

Что я делаю неправильно? Это открытый дефект?


person Aaron Dsouza    schedule 14.04.2020    source источник
comment
что это за TFM? это выглядит так, как будто должно работать нормально; у вас, возможно, есть прокси или http-инспектор (скрипач и т. д.) здесь? если это так: они могут сломать транспорт, неправильно продолжая как http / 2   -  person Marc Gravell    schedule 14.04.2020
comment
netcoreapp3.1. Я добавил это к вопросу   -  person Aaron Dsouza    schedule 14.04.2020
comment
и любые инструменты сетевого инспектора, такие как скрипач или аналогичные?   -  person Marc Gravell    schedule 14.04.2020
comment
Предложение: чтобы сузить проблему, возможно, используйте транспорт Google (а не транспорт Microsoft), добавив ссылку на пакет на Grpc.Core из nuget и Channel channel = new Channel("localhost:9090", ChannelCredentials.Insecure); (остальная часть вашего клиентского кода такая же); если проблема такая же, значит проблема либо в сети, либо на сервере; если проблема исчезнет, вам следует связаться с JamesNK, зарегистрировав ее (с максимально возможным контекстом, желательно репродукцией) здесь: github.com/grpc/grpc-dotnet/issues   -  person Marc Gravell    schedule 14.04.2020
comment
Это сработало! Можете ли вы добавить это в качестве ответа, чтобы я мог отметить вопрос как ответ.   -  person Aaron Dsouza    schedule 15.04.2020
comment
это говорит о том, что HttpClient по какой-то причине не использует http2; вам следует вероятно зарегистрировать это с помощью grpc-dotnet, чтобы очистить информацию о вашей ОС и т. д.   -  person Marc Gravell    schedule 15.04.2020


Ответы (2)


Как указано в ответе Эрика Андерсона, клиент действительно использует TLS.

Клиент .NET Core для gRPC требует дополнительной настройки при взаимодействии с небезопасными (не TLS) серверами (начиная с .NET Core 3.1).

// This switch must be set before creating the GrpcChannel/HttpClient.
AppContext.SetSwitch(
    "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

using var channel = GrpcChannel.ForAddress("http://localhost:9090");
var client = new Simple.SimpleClient(channel);
var reply = client.GetData(new Empty { });
person Sourabh Shirhatti    schedule 15.04.2020
comment
Спасибо. Это работает. Однако https следует изменить на http. - person Aaron Dsouza; 16.04.2020
comment
Приносим извинения за опечатку. Фиксированный - person Sourabh Shirhatti; 17.04.2020

Сообщение об ошибке сообщает о полученных байтах (в шестнадцатеричном формате):

16030100b6010000b203035e95b0402c6320969d3d5fba04

Три начальных байта 160301 выглядят как рукопожатие TLS. Сервер, вероятно, находится в текстовом режиме, а клиент использует TLS.

Чтобы использовать обычный текст на клиенте, используйте «http»:

using var channel = GrpcChannel.ForAddress("http://localhost:9090");

Изменить: образец кода этого ответа не работает; он неправильно включает простой текст

person Eric Anderson    schedule 14.04.2020
comment
Это дает следующую ошибку в консоли загрузки Spring INFO: Ошибка транспорта io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: Неожиданный запрос HTTP / 1.x: POST / Simple / GetData - person Aaron Dsouza; 15.04.2020