分类:
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
50010702506256