下面是详细的“.Net Core微服务rpc框架GRPC通信基础”的完整攻略,包括框架介绍、使用方法以及两条示例说明。
一、什么是GRPC?
GRPC是Google开源的跨语言高性能的RPC(Remote Procedure Call,远程过程调用)框架。它使用Protocol Buffers作为数据序列化方式,支持多种语言的实现。
GRPC可以让你像调用本地方法一样调用远程方法。它通过定义一份服务协议来描述服务,然后自动生成客户端和服务端的代码。客户端和服务端之间的通信是通过HTTP2实现的。相比于传统的HTTP1.1协议,HTTP2支持多路复用,头部压缩和双向流等特性,提高了网络传输的效率,这也是GRPC高性能的关键之一。
二、如何使用GRPC?
- 安装GRPC
在使用GRPC之前,我们需要在.NET Core项目中添加GRPC依赖。在Visual Studio中,打开NuGet包管理器并搜索“Grpc.AspNetCore”,然后选择安装该依赖。
- 创建GRPC服务
接下来,我们需要创建GRPC服务端。首先,定义一个服务接口并使用ProtoBuf进行标注:
syntax = "proto3";
option csharp_namespace = "GrpcServiceDemo";
package Math;
service MathService {
rpc Add (AdditionRequest) returns (AdditionResponse);
}
message AdditionRequest {
int32 number1 = 1;
int32 number2 = 2;
}
message AdditionResponse {
int32 result = 1;
}
其中,service MathService是定义的一个服务接口,Add是其中的一个方法,AdditionRequest是Add方法的入参,AdditionResponse是Add方法的返回值。
然后使用protobuf提供的编译器生成对应的服务代码。我们需要在项目根目录下创建一个proto文件夹,将上面的.proto文件存放在这个文件夹中。然后,在Visual Studio中打开“包管理器控制台”,并执行以下命令:
> dotnet tool install -g protobuf-net.Grpc.Tools
> cd src/
> mkdir grpc
> protoc -I ../proto --csharp_out ./grpc ../proto/Math.proto --grpc_out ./grpc --plugin=protoc-gen-grpc=./packages/Grpc.Tools.2.37.1/tools/windows_x64/grpc_csharp_plugin.exe
其中,proto参数指定.proto文件所在的文件夹,csharp_out参数指定生成C#代码的输出目录,grpc参数指定生成服务代码的输出目录,plugin参数指定使用的编译插件。
执行完成后,将生成的代码添加到项目中。
- 实现服务接口
将服务代码添加到项目后,我们需要实现定义的服务接口。在这个示例中,我们需要实现MathService服务接口中的Add方法,这个方法接收两个整数并返回它们的和。在完成实现后,将服务实现类注册到DI容器中,以便服务端能够调用它。
public class MathService : GrpcServiceDemo.Math.MathService.MathServiceBase
{
public override Task<AdditionResponse> Add(AdditionRequest request, ServerCallContext context)
{
return Task.FromResult(new AdditionResponse
{
Result = request.Number1 + request.Number2
});
}
}
services.AddGrpc(options =>
{
options.EnableDetailedErrors = true;
options.MaxReceiveMessageSize = null;
options.MaxSendMessageSize = null;
}).Services.AddServiceOptions<MathService>();
在上面的代码中,我们定义了一个MathService服务实现类,并实现了Add方法。服务实现类需要继承auto-generated类GrpcServiceDemo.Math.MathService.MathServiceBase,同时重写MathServiceBase中定义的Add方法。
最后,我们将服务实现类注册到DI容器中,并用AddGrpc方法指定我们的GRPC服务。在这个方法选项中,我们可以设置一些选项,例如启用详细错误信息,设置消息大小等。使用AddServiceOptions方法可以为GRPC服务添加自定义选项。
- 创建GRPC客户端
GRPC客户端使用的是Channel连接,我们需要创建一个GRPC Channel来连接GRPC服务端。可以使用以下代码创建Channel:
var channel = GrpcChannel.ForAddress("https://localhost:50051");
其中,ForAddress方法指定远程GRPC服务的地址和端口号。接下来,我们可以使用Channel创建GRPC客户端的实例,并调用服务接口提供的方法。
var client = new MathService.MathServiceClient(channel);
var response = client.Add(new AdditionRequest { Number1 = 1, Number2 = 2 });
int result = response.Result;
在上面的代码中,我们创建了一个MathService客户端实例,并调用了Add方法。Add方法接受一个AdditionRequest对象作为参数,并返回一个AdditionResponse对象。
三、GRPC示例说明
示例1:使用GRPC实现PingPong
这个示例演示了如何使用GRPC实现PingPong功能。PingPong是一种简单的RPC通信方式,客户端向服务端发送一条消息,服务端收到消息后返回同样的消息,客户端再次收到这条消息后就结束通信。
- 创建GRPC服务和客户端
首先,在服务端创建PingPong服务,定义Ping和Pong方法:
syntax = "proto3";
option csharp_namespace = "GrpcDemo";
package PingPong;
service PingPongService {
rpc Ping (PingRequest) returns (PongResponse);
rpc Pong (PongRequest) returns (PingResponse);
}
message PingRequest {
string message = 1;
}
message PongRequest {
string message = 1;
}
message PingResponse {
string message = 1;
}
message PongResponse {
string message = 1;
}
为了证明服务器和客户机代码之间的互操作性,下面的C#示例同时为服务端和客户端功能创建单个项目:
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc(options =>
{
options.EnableDetailedErrors = true;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<PingPongService>();
});
}
在这个示例中,我们在服务端设置了PingPongService服务,通过MapGrpcService方法将该服务注册到DI容器中。
接下来,在客户端创建PingPong客户端,调用Ping和Pong方法:
// Program.cs
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new PingPongService.PingPongServiceClient(channel);
// Create a deadline for the call
var callOptions = new CallOptions(deadline: DateTime.UtcNow.AddSeconds(1));
var pingRequest = new PingRequest { Message = "Ping" };
var pongRequest = new PongRequest { Message = "Pong" };
var pingResponse = client.Ping(pingRequest, cancellationToken: CancellationToken.None, callOptions);
Console.WriteLine($"Ping: {pingResponse.Message}");
var pongResponse = client.Pong(pongRequest, cancellationToken: CancellationToken.None, callOptions);
Console.WriteLine($"Pong: {pongResponse.Message}");
在这个示例中,我们创建了一个PingPong客户端实例,并调用了Ping和Pong方法。Ping和Pong方法接受一个PingRequest或PongRequest对象作为参数,并返回一个PingResponse或PongResponse对象。
示例2:使用GRPC实现在线聊天室
这个示例演示了如何使用GRPC实现在线聊天室。聊天室是一种广泛使用的应用程序,它使用户能够在线聊天并交流。在这个示例中,我们使用GRPC和ASP.NET Core SignalR实现聊天室的核心功能。
- 创建GRPC服务和客户端
首先,在服务端创建Chat服务,定义AddUser,RemoveUser和SendMessage方法:
syntax = "proto3";
option csharp_namespace = "GrpcDemo";
package Chat;
service ChatService {
rpc AddUser (AddUserRequest) returns (AddUserResponse);
rpc RemoveUser (RemoveUserRequest) returns (RemoveUserResponse);
rpc SendMessage (SendMessageRequest) returns (SendMessageResponse);
}
message AddUserRequest {
string username = 1;
}
message AddUserResponse {
bool result = 1;
repeated string users = 2;
}
message RemoveUserRequest {
string username = 1;
}
message RemoveUserResponse {
bool result = 1;
repeated string users = 2;
}
message SendMessageRequest {
string message = 1;
}
message SendMessageResponse {
bool result = 1;
string message = 2;
}
接下来,在服务端实现Chat服务的接口:
public class ChatService : GrpcDemo.Chat.ChatService.ChatServiceBase
{
private static IList<string> _users = new List<string>();
private static readonly object _lockObj = new object();
public override Task<AddUserResponse> AddUser(AddUserRequest request, ServerCallContext context)
{
lock(_lockObj){
if (!_users.Contains(request.Username)){
_users.Add(request.Username);
}
}
var response = new AddUserResponse{
Result = true,
Users = { _users }
};
return Task.FromResult(response);
}
public override Task<RemoveUserResponse> RemoveUser(RemoveUserRequest request, ServerCallContext context)
{
lock(_lockObj){
_users.Remove(request.Username);
}
var response = new RemoveUserResponse{
Result = true,
Users = { _users }
};
return Task.FromResult(response);
}
public override async Task<SendMessageResponse> SendMessage(IAsyncStreamReader<SendMessageRequest> requestStream, ServerCallContext context)
{
var response = new SendMessageResponse {
Result = true
};
var message = "";
try
{
await foreach (SendMessageRequest item in requestStream.ReadAllAsync())
{
var username = context.RequestHeaders.FirstOrDefault(h => h.Key == "username")?.Value;
message = $"{username}: {item.Message}";
var hubContext = context.GetHttpContext().RequestServices.GetRequiredService<IHubContext<ChatHub>>();
await hubContext.Clients.All.SendAsync("ReceiveMessage", message);
response.Message = message;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
response.Result = false;
response.Message = "Message sending failed.";
}
return response;
}
}
在服务端实现了AddUser,RemoveUser和SendMessage方法。我们使用一个静态变量_users来保存当前在线的用户,当用户连接聊天室时,我们将其添加到在线列表中。当用户离开聊天室时,从在线列表中删除该用户。当用户发送消息时,我们向所有在线用户广播该消息。
最后,我们将ChatService服务注册到DI容器中:
services.AddGrpc(options =>
{
options.EnableDetailedErrors = true;
}).Services.AddServiceOptions<ChatService>();
- 创建SignalR Hub
接下来,我们需要使用SignalR创建一个Hub来处理客户端聊天请求。在这个示例中,我们创建ChatHub并实现ReceiveMessage方法,该方法接受来自客户端的消息并向所有其他客户端广播它:
public class ChatHub : Hub
{
public async Task ReceiveMessage(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
在SignalR Hub中,我们使用“ReceiveMessage”作为事件名来发送和接收消息。
- 创建客户端
在客户端,我们首先需要通过GRPC连接到Chat服务,然后使用SignalR建立与聊天室的连接,并实现对ReceiveMessage事件的处理。以下是客户端代码:
using Grpc.Core;
using GrpcDemo.Chat;
using Microsoft.AspNetCore.SignalR.Client;
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new ChatService.ChatServiceClient(channel);
var username = "User_" + Guid.NewGuid().ToString("N");
var response = client.AddUser(new AddUserRequest { Username = username });
if (response.Result)
{
var hubConnection = new HubConnectionBuilder()
.WithUrl("https://localhost:5001/chat")
.Build();
hubConnection.On<string>("ReceiveMessage", (message) =>
{
Console.WriteLine(message);
});
await hubConnection.StartAsync();
while (true)
{
var message = Console.ReadLine();
if (string.IsNullOrEmpty(message))
continue;
var sendMessageClient = client.SendMessage();
await sendMessageClient.RequestStream.WriteAsync(new SendMessageRequest
{
Message = message
});
await sendMessageClient.RequestStream.CompleteAsync();
}
}
在上面的代码中,我们使用GRPC的客户端程序集生成GRPC Chat服务客户端,并将要发送的消息作为请求参数。使用SignalR客户端程序集,我们通过信号器在客户端上提供消息的接收功能,并将事件名称设置为"ReceiveMessage"来发送和接收消息。
以上就是使用GRPC实现PingPong和在线聊天室的示例说明。需要注意的是,GRPC的使用方式和.NET Core Web API非常不同,需要通过protobuf定义服务接口,并将服务代码添加到项目中。在实际应用中,我们可以结合使用GRPC和SignalR来实现高效的跨语言、跨平台的实时通信功能。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:.Net Core微服务rpc框架GRPC通信基础 - Python技术站