GRPC代替webapi Demo。

gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架。

gRPC 的主要优点是:

  • 现代高性能轻量级 RPC 框架。
  • 协定优先 API 开发,默认使用协议缓冲区,允许与语言无关的实现。
  • 可用于多种语言的工具,以生成强类型服务器和客户端。
  • 支持客户端、服务器和双向流式处理调用。
  • 使用 Protobuf 二进制序列化减少对网络的使用。

这些优点使 gRPC 适用于:

  • 效率至关重要的轻量级微服务。
  • 需要多种语言用于开发的 Polyglot 系统。
  • 需要处理流式处理请求或响应的点对点实时服务。

以上来自微软的文档:https://docs.microsoft.com/zh-cn/aspnet/core/grpc/index?view=aspnetcore-3.0

 个人理解:

  • gRPC采用HTTP/2协议,二进制传输,相比json,xml速度更快,更节省流量。
  • 支持流,只需要建立一次连接,适合服务间通讯。
  • 规范的接口标准。  
  • 跨语言。

补充:

  Restful是一种架构风格,关注的是资源。

    通过每次http请求把资源拿过来,但资源怎么用是客户端的事情。

       gRpc是rpc的一个实现框架,因此关注其中的rpc远程过程调用。

    意思是在客户端调用服务器方法就像调用本地方法一样。如果这样做,那么服务器就必须要有相应的处理的方法(参数和返回值)。

    grpc在proto文件中定义了方法名和返回值,在各种语言中,我们只需要在服务器和客户端实现相应的方法即可。

以下Demo主要体现了服务端与客户端以流式RPC的方式,并对比webapi的方式。

*必须使用http/2,因此需要在服务器上监听端口设置。

 

                    //支持无tls的http/2。
                    webBuilder.ConfigureKestrel(options =>
                    {
                        options.ListenLocalhost(5000, o => o.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2);
                    });

 

*客户端需要设置

          AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

 

 

C#自动生成代码,客户端需要从nuget安装:

  Google.Protobuf

  Grpc.Core

  Grpc.Net.ClientFactory

  Grpc.Tools

项目文件:

  <ItemGroup>
    <Protobuf Include="Protos\Duplicate.proto" GrpcServices="Client">
    </Protobuf>
  </ItemGroup>

 

服务的需要

  Grpc.Tools

  Grpc.AspNetCore.Server

  Grpc.AspNetCore

项目文件:

  <ItemGroup>
    <Protobuf Include="Protos/Duplicate.proto" GrpcServices="Server" />
  </ItemGroup>

 

该Demo模拟了一个判重的服务器和客户端。

    public interface IDuplicate
    {
        /// <summary>
        /// 将标签进入判重。
        /// </summary>
        /// <param name="tag">标签。</param>
        /// <returns>保存成功后将返回一个值。</returns>
        bool EntryDuplicate(string tag);

        /// <summary>
        /// 判断标签是否已经存在。
        /// </summary>
        /// <param name="tag">标签。</param>
        /// <returns>如果标签存在则返回true。</returns>
        bool DuplicateCheck(string tag);

        /// <summary>
        /// 删除一条标签。
        /// </summary>
        /// <param name="tag">标签。</param>
        /// <returns>返回结果。</returns>
        bool RemoveItem(string tag);
    }

 

Proto配置:

syntax = "proto3";

// 命名空间。
option csharp_namespace = "GrpcServer.Protos";

package Duplicate;

service Duplicater{

    // 进队列接口。
    rpc EntryDuplicate(stream EntryRequset) returns (stream EntryResponse);

    // 判重接口。
    rpc DuplicateCheck(stream DuplicateCheckRequset) returns (stream DuplicateCheckResponse);
}

// 进队列请求。
message EntryRequset{
    // tag=1,表示在传输过程中,此数据的名字就是1。
    string tag=1;
}

// 进队后响应。
message EntryResponse{
    bool result=1;
    string msg=2;
}

// 判重请求。
message DuplicateCheckRequset{
    string tag=1;
}

// 判重后响应。
message DuplicateCheckResponse{
    bool result=1;
}

Demo中主要实现了入判重的方法。

        /// <summary>
        /// 入判重。
        /// </summary>
        /// <param name="requestStream">请求流。</param>
        /// <param name="responseStream">响应流。</param>
        /// <param name="context">上下文。</param>
        /// <returns></returns>
        public override async Task EntryDuplicate(IAsyncStreamReader<EntryRequset> requestStream, IServerStreamWriter<EntryResponse> responseStream, ServerCallContext context)
        {
            while (await requestStream.MoveNext())
            {
                var result = _memoryDuplicate.EntryDuplicate(requestStream.Current.Tag);
                var msg = string.Empty;
                if (result)
                    msg = $"{requestStream.Current.Tag} 入判重成功。";
                else
                    msg = $"{requestStream.Current.Tag} 入判重失败,已有重复的数据";
                _logger.LogInformation(msg);

                await responseStream.WriteAsync(new EntryResponse { Result = result, Msg = msg });
            }

            _logger.LogInformation("本次请求已完成");
        }

由客户端告知流传输结束,然后释放连接:

            var token = new CancellationToken();
            var response = Task.Run(async () =>
            {
                while (await entry.ResponseStream.MoveNext(token))
                {
                    if (entry.ResponseStream.Current.Result)
                        Console.WriteLine($"{entry.ResponseStream.Current.Msg}");
                    else
                        Console.WriteLine($"{entry.ResponseStream.Current.Msg}入判重失败。");
                }
            });
            for (int i = 0; i < length; i++)
            {
                SpinWait.SpinUntil(() => false, 200);
                var msg = random.Next(0, 2000).ToString();
                await entry.RequestStream.WriteAsync(new EntryRequset { Tag = msg });
            }

            Console.WriteLine("等待释放链接。");
            await entry.RequestStream.CompleteAsync();
            entry.Dispose();
            Console.WriteLine("完成");

 Grpc:

 

 WebApi:

 

 github地址:https://github.com/yeqifeng2288/GrpcDemo

 

posted @ 2019-11-03 13:13  指左转右  阅读(1697)  评论(0编辑  收藏  举报