tnblog
首页
视频
资源
登录

.NetCore 3.x 与 grpc

5680人阅读 2020/3/28 17:31 总访问:2536955 评论:0 收藏:0 手机
分类: grpc

  目录


 本篇文章主要讲解如下内容

   一。grpc在 .Net Core 3.x 中的应用

   二。grpc如何分段上传图片

   代码与实现过程比较简单,大家可以无限延伸对 grpc 的使用



环境部署

Server


(一) 创建服务器端 GrpcServer.Web



(二) 项目结构如下



(三) Startup.cs 内容如下


public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddGrpc();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseStaticFiles();

        app.UseHttpsRedirection();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGrpcService<MyEmployeeService>();
        });
    }
}


(四) 安装 Grpc.AspNetCore      



launchSettings.json

{
  "profiles": {
    "GrpcServer.Web": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "https://localhost:5001",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}



Client


(一) 创建 GrpcClient 控制台应用程序  



(二) 安装相关 Grpc 客户端的包        




Grpc 建立普通通信方式

Server


Proto 文件夹下添加 Message.proto 文件




添加内容


syntax = "proto3";

option csharp_namespace = "GrpcServer.Web.Protos";

message Employee {
	int32 id = 1;
	int32 no = 2;
	string firstName = 3;
	string lasterName = 4;
	float salary = 5;
}

message GetByNoRequest{
	int32 no = 1;
}

message EmployeeResponse{
	Employee employee =1;
}

message GetAllRequest {}

message AddPhotoRequest{
	bytes data = 1;
}

message AddPhotoResponse{
	bool isOk = 1;
}

message EmployeeRequest{
	Employee employee = 1;
}

service EmployeeService{
	rpc GetByNo(GetByNoRequest) returns (EmployeeResponse);
	rpc GetAll(GetAllRequest) returns (stream EmployeeResponse);
	rpc AddPhoto(stream AddPhotoRequest) returns (AddPhotoResponse);
	rpc Save(EmployeeRequest) returns (EmployeeResponse);
	rpc SaveAll(stream EmployeeRequest) returns (stream EmployeeResponse);
}


并选中 Message.proto 修改为如下属性



在 Data 文件夹下添加 InMemoryData.cs


public class InMemoryData
{
    public static List<Employee> Employees = new List<Employee>() {
        new Employee{
            Id = 1,
            No = 1994,
            FirstName = "Chandler",
            LasterName = "Bing",
            Salary = 2200
        },
        new Employee{
            Id = 2,
            No = 1999,
            FirstName = "Rachl",
            LasterName = "Green",
            Salary = 2400
        },
        new Employee{
            Id = 3,
            No = 2452,
            FirstName = "MI",
            LasterName = "UIMI",
            Salary = 2600
        }
    };
}


在 Servcies 文件夹下添加 MyEmployeeService.cs


public class MyEmployeeService: EmployeeService.EmployeeServiceBase
{
    private readonly ILogger<MyEmployeeService> _logger;
    private readonly IHostingEnvironment _hostingEnvironment;

    public MyEmployeeService(ILogger<MyEmployeeService> logger, IHostingEnvironment hostingEnvironment) 
    {
        _logger = logger;
        _hostingEnvironment = hostingEnvironment;
    }

    public async override Task<EmployeeResponse> GetByNo(GetByNoRequest request, ServerCallContext context)
    {
        //获取 Metadata = context.RequestHeaders
        var md = context.RequestHeaders;
        foreach (var item in md)
        {
            _logger.LogInformation($"{ item.Key } : { item.Value }");
        }

        var employee = InMemoryData.Employees
            .SingleOrDefault(x => x.No == request.No);
        if (employee!=null)
        {
            var response = new EmployeeResponse { 
                Employee = employee
            };
            return await Task.FromResult(response);
        }
        throw new System.Exception($"Employee not found with no: {request.No}");
    }

    public override async Task GetAll(GetAllRequest request, IServerStreamWriter<EmployeeResponse> responseStream, ServerCallContext context)
    {
        foreach (var employee in InMemoryData.Employees)
        {
            await responseStream.WriteAsync(new EmployeeResponse { 
                Employee = employee
            });
        }
    }
}


Client


将服务器上的 Message.proto 文件复制到 Protos 文件夹下面

并修改其属性如下



修改 Program.cs 


static async Task Main(string[] args)
{
    //
    using var channel = GrpcChannel.ForAddress("https://localhost:5001");
    //var client = new 
    var client = new EmployeeService.EmployeeServiceClient(channel);



    //await GetPhotoAsync(client);

    var option = int.Parse(args[0]);

    switch (option)
    {
        case 1:
            await GetByNoAsync(client);
            break;
        case 2:
            await GetAllAsync(client);
            break;
        default:
            break;
    }

    Console.ReadKey();

}

private static async Task GetAllAsync(EmployeeService.EmployeeServiceClient client)
{
    using var call = client.GetAll(new GetAllRequest());
    var responseStream = call.ResponseStream;
    while (await responseStream.MoveNext())
    {
        Console.WriteLine(responseStream.Current.Employee);
    }

}

private static async Task GetByNoAsync( EmployeeService.EmployeeServiceClient client)
{
    //定义元数据
    var md = new Metadata {
        { "username","administrator" },
        { "role","administrator" }
    };

    var response = await client.GetByNoAsync(new GetByNoRequest
    {
        No = 1994
    }, md);

    Console.WriteLine($"Response messages:{response}");
    Console.WriteLine("Press any key to exit.");
}


 《 Test 》


先启动服务器端,然后再在控制台中启动客户端




通过客户端分段上传图片到服务器端

Server


实现 AddPhoto 方法


        public async override Task<AddPhotoResponse> AddPhoto(IAsyncStreamReader<AddPhotoRequest> requestStream, ServerCallContext context)
        {
            //获取 Metadata
            Metadata md = context.RequestHeaders;
            string FileName = "";
            foreach (var item in md)
            {
                Console.WriteLine($"{item.Key}: {item.Value}");
                if (item.Key== "filename")
                {
                    FileName = item.Value;
                }
            }
            //获取
            var data = new List<byte>();
            //获取流
            while (await requestStream.MoveNext())
            {
                Console.WriteLine($"Received:{requestStream.Current.Data.Length} bytes ");
                data.AddRange(requestStream.Current.Data);
            }
            string fullnamepath = _hostingEnvironment.WebRootPath +"\\"+ DateTime.Now.ToString("yyyyMMddhhmmss") + FileName;
            using var file = new MemoryStream(data.ToArray());
            using (FileStream filestream = new FileStream(fullnamepath, FileMode.Create))
            {
                await file.CopyToAsync(filestream);
                filestream.Flush();
            }

            Console.WriteLine($"Received file with {data.Count} bytes");
            

            //返回流结果
            return new AddPhotoResponse { 
                IsOk = true
            };
        }


Client


修改 Program.cs 


         。。。
                case 3:
                    await GetPhotoAsync(client);
                    break;
         。。。
         
private static async Task GetPhotoAsync(EmployeeService.EmployeeServiceClient client)
{
    FileStream fs = File.OpenRead("logo.png");
    //定义元数据
    var md = new Metadata {
        { "username","administrator" },
        { "role","administrator" },
        { "filename", fs.Name.Substring(fs.Name.LastIndexOf('\\')+1) }
    };

    using var call = client.AddPhoto(md);
    //向 server 端发起请求
    var stream = call.RequestStream;
    //然后进行分段上传图片
    while (true)
    {
        byte[] buffer = new byte[1024];
        int numRead = await fs.ReadAsync(buffer, offset: 0, count: buffer.Length);
        //读取读满了退出
        if (numRead == 0)
        {
            break;
        }
        //读取长度不一致将变为一致
        if (numRead < buffer.Length)
        {
            Array.Resize(ref buffer, numRead);
        }
        //向服务端写数据
        await stream.WriteAsync(new AddPhotoRequest() { 
            Data = ByteString.CopyFrom(buffer) 
        });
    }

    //告诉 server 我的数据都传完了
    await stream.CompleteAsync();
    //获取上传结果
    var res = await call.ResponseAsync;

    Console.WriteLine(res.IsOk);
}


 《 Test 》




扩展资料

如果你想进一步了解 gRPC,请查阅 ASP.NET Core gRPC 文档。请向我们提出你对 gRPC-Web 的意见和经验反馈,因为这将帮助我们选择如何以及是否在未来的 ASP.NET Core 版本中使 gRPC-Web 成 一个标准特性。你可以在这里发布评论,或者在 GitHub 上发布标题中带有“反馈”的 issue。

翻译自原文:https://blog.stevensanderson.com/2020/01/15/2020-01-15-grpc-web-in-blazor-webassembly

相关文章:

近期,观测分析平台 SkyWalking 的 .NET 自动探针 (SkyAPM-dotnet) 也已经支持了 grpc-dotnet 远程调用的链路跟踪采集,欢迎大家使用!如果喜欢,也请大家给点个星星!

项目地址:https://github.com/SkyAPM/SkyAPM-dotnet



欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价
这一世以无限游戏为使命!
排名
6
文章
6
粉丝
16
评论
8
{{item.articleTitle}}
{{item.blogName}} : {{item.content}}
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2024TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
欢迎加群交流技术