gRPC is a way to build distributed systems quickly that focus on interoperability and performance. While third-parties can access these services, gRPC systems are built frequently for internal distributed systems.
- 新建asp.netcore API 项目
- 安装依赖包
<PackageReference Include="Google.Protobuf" Version="3.21.1" />
<PackageReference Include="Grpc.AspNetCore" Version="2.46.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.46.0" />
<PackageReference Include="Grpc.Tools" Version="2.46.3">
- 编写proto文件
新建文件夹Protos,并添加greet.proto
syntax = "proto3";
option csharp_namespace = "GrpcDemo";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
- build整个工程,并修改工程文件
在 GrpcDemo.csproj 里面添加以下信息
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Server,Client" />
</ItemGroup>
- 编写service
Let’s start with implementing our gRPC service. Looking at our previous definition, we can see a single SayHello method. In our project, we can add a new C# class. We’ll be hosting this service in ASP.NET Core, which means we’ll have access to all the features that come with ASP.NET Core, such as dependency injection and services.
In a new C# file, we create a Service class that derives from Greeter.GreeterBase. If this class is not available, be sure to build your solution first, it will be available after our first compilation.
Typing override within the class, we’ll see the method definitions found in our greet.proto file.
添加service类‘’
using Grpc.Core;
using GrpcDemo;
namespace GprcDemo
{
public class Service:Greeter.GreeterBase
{
private readonly ILogger<Service> _logger;
public Service(ILogger<Service> logger)
{
_logger = logger;
}
public override async Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
// 获取客户端传递的头信息
Console.WriteLine(context.RequestHeaders.Get("myCustomHeader"));
//设置返回头
await context.WriteResponseHeadersAsync(new (){ { "serverheader", "hahaha" } });
return new HelloReply
{
Message = "Hello " + request.Name
};
}
}
}
- 编写client,这里为了方便将client也和service放在一起,并以服务的形式进行注册
We’ll leverage ASP.NET Core’s background service to host a client local to our gRPC service. While we are hosting both client/server within the same process, they’ll still communicate over the HTTP/2 protocol.
We’ll start by creating a new C# class called Client, and it will also derive from the BackgroundService class, allowing us to host it in ASP.NET Core’s hosted service infrastructure.
using Grpc.Core;
using Grpc.Net.Client;
using GrpcDemo;
using Newtonsoft.Json;
namespace GprcDemo
{
public class Client : BackgroundService
{
private readonly ILogger<Client> _logger;
private readonly string _url;
public Client(ILogger<Client> logger, IConfiguration configuration)
{
_logger = logger;
_url = configuration["Kestrel:Endpoints:gRPC:Url"];
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var channel = GrpcChannel.ForAddress(_url);
var client = new Greeter.GreeterClient(channel);
Dictionary<string,string> keyValuePairs = new Dictionary<string,string>();
keyValuePairs.Add("name", "ellis");
keyValuePairs.Add("address", "dalian");
//和grpc服务交互的时候,添加头信息
Metadata headers = new()
{
{ "myCustomHeader", JsonConvert.SerializeObject(keyValuePairs) }
};
while (!stoppingToken.IsCancellationRequested)
{
//发送带有header的请求
var call = client.SayHelloAsync(new HelloRequest
{
Name = "Worker"
},headers=headers);
//获取服务端返回的header
Metadata header = await call.ResponseHeadersAsync;
var reply = await call;
var hea = header.Get("serverheader").Value.ToString();
_logger.LogInformation($"Greeting: {reply.Message} -- {DateTime.Now}");
await Task.Delay(1000, stoppingToken);
}
channel.Dispose();
}
}
}
We can see the essential client elements happen within the ExecuteAsync method of our worker service.
We create a new gRPC channel using our URL.
We use the generated client definition and passing in the channel.
We call our SayHelloAsync method with our HelloRequest contract.
Now that we have our server and client implemented let’s wire them up in ASP.NET Core.
- 注册service
在programe.cs 添加如下
builder.Services.AddGrpc(o =>
{
//设置压缩返回
o.ResponseCompressionLevel = CompressionLevel.Optimal;
o.ResponseCompressionAlgorithm = "gzip";
});
builder.Services.AddHostedService<Client>();
.......
.......
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); });
endpoints.MapGrpcService<Service>();
});
- 配置文件
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "https://localhost:5001",
"Protocols": "Http1AndHttp2"
},
"gRPC": {
"Url": "http://localhost:5000",
"Protocols": "Http2"
}
}
}
}
https://docs.microsoft.com/en-us/aspnet/core/grpc/aspnetcore?view=aspnetcore-6.0&tabs=visual-studio
https://cloud.google.com/dotnet/docs/reference/Grpc.Core/latest/Grpc.Core.ServerCallContext
https://www.stevejgordon.co.uk/grpc-response-compression-with-asp-net-core
https://zditect.com/code/csharp/compressing-responses-in-grpc-for-aspnet-core-30.html