.Net Core微服务rpc框架GRPC通信基础

下面是详细的“.Net Core微服务rpc框架GRPC通信基础”的完整攻略,包括框架介绍、使用方法以及两条示例说明。

一、什么是GRPC?

GRPC是Google开源的跨语言高性能的RPC(Remote Procedure Call,远程过程调用)框架。它使用Protocol Buffers作为数据序列化方式,支持多种语言的实现。

GRPC可以让你像调用本地方法一样调用远程方法。它通过定义一份服务协议来描述服务,然后自动生成客户端和服务端的代码。客户端和服务端之间的通信是通过HTTP2实现的。相比于传统的HTTP1.1协议,HTTP2支持多路复用,头部压缩和双向流等特性,提高了网络传输的效率,这也是GRPC高性能的关键之一。

二、如何使用GRPC?

  1. 安装GRPC

在使用GRPC之前,我们需要在.NET Core项目中添加GRPC依赖。在Visual Studio中,打开NuGet包管理器并搜索“Grpc.AspNetCore”,然后选择安装该依赖。

  1. 创建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参数指定使用的编译插件。

执行完成后,将生成的代码添加到项目中。

  1. 实现服务接口

将服务代码添加到项目后,我们需要实现定义的服务接口。在这个示例中,我们需要实现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服务添加自定义选项。

  1. 创建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通信方式,客户端向服务端发送一条消息,服务端收到消息后返回同样的消息,客户端再次收到这条消息后就结束通信。

  1. 创建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实现聊天室的核心功能。

  1. 创建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>();
  1. 创建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”作为事件名来发送和接收消息。

  1. 创建客户端

在客户端,我们首先需要通过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技术站

(0)
上一篇 2023年6月3日
下一篇 2023年6月3日

相关文章

  • C# Linq读取XML文件的实例

    下面是关于” C# Linq 读取 XML 文件的实例”的攻略,包括示例说明。 1. 准备工作 安装 .NET Framework 创建一个 C# 控制台程序 2. 创建 XML 文件 我们首先需要创建一个 XML 文件,这里我们以一个字符串列表为例,创建一个名为 “test.xml” 的 XML 文件,代码如下: <?xml version=&quo…

    C# 2023年6月1日
    00
  • C#操作进程的方法介绍

    C# 操作进程的方法介绍 C# 中可以通过 Process 类来实现对进程的操作,包括启动进程、杀死进程、查找进程等。 以下是常用的操作进程的方法: 启动进程 启动新进程可以使用 Process.Start 方法,该方法返回一个 Process 对象,通过该对象可以得到该进程的一些详细信息,比如进程 ID,句柄等。 以下示例代码演示了如何启动计算器程序: u…

    C# 2023年6月7日
    00
  • C#实现下载网页HTML源码的方法

    下面是“C#实现下载网页HTML源码的方法”的完整攻略,具体流程如下: 1. 发送HTTP请求 使用C#自带的WebRequest类向目标网址发送HTTP请求,获取服务器响应。HTTP请求的方式分为GET和POST,这里以GET为例,构造请求如下: string url = "http://www.example.com"; WebReq…

    C# 2023年6月3日
    00
  • 在SQL Server中使用CLR调用.NET方法实现思路

    在SQL Server中使用CLR调用.NET方法可以扩展数据库的功能,下面是实现思路的完整攻略: 1.启用CLR 要在SQL Server中使用CLR,首先需要在服务器级别启用CLR。可以通过以下步骤启用CLR: 在SQL Server Management Studio中打开一个新的查询窗口。 运行以下T-SQL代码: sp_configure ‘clr…

    C# 2023年6月3日
    00
  • AOP从静态代理到动态代理(Emit实现)详解

    AOP从静态代理到动态代理(Emit实现)详解 概述 AOP(面向切面编程)是一种程序设计思想,可以在不改变原有代码逻辑的情况下,通过在程序中动态地新增一些逻辑代码,来实现例如日志记录、权限控制、性能监测等功能。而在 AOP 中,一个被增强的方法称为“切入点”,对该切入点进行增强的代码称为“切面”。 在实现 AOP 功能时,静态代理和动态代理是两种比较常见的…

    C# 2023年6月6日
    00
  • C#:foreach与yield语句的介绍

    C#: foreach与yield语句的介绍 什么是foreach foreach 是 C# 中常用的遍历集合的循环结构,它可以方便地遍历数组、集合、字典等集合数据类型。其基本语法结构如下: foreach (var item in collection) { // 循环体 } 其中,item 为当前循环的元素,collection 为要遍历的集合,可以是数…

    C# 2023年6月7日
    00
  • ASP.NET MVC获取多级类别组合下的产品

    以下是ASP.NET MVC获取多级类别组合下的产品的完整攻略: 简介 在ASP.NET MVC应用程序中,我们可能需要获取多级类别组合下的产品,例如,我们可能需要获取所有属于“电子产品”类别及其子类别的产品。在这种情况下,我们可以使用递归查询或LINQ查询获取多级类别组合下的产品。 步骤 ASP.NET MVC获取多级类别组合下的产品的步骤如下: 创建类别…

    C# 2023年5月12日
    00
  • .NET Core系列之MemoryCache 初识

    .NET Core系列之MemoryCache 初识 在本攻略中,我们将详细讲解.NET Core中的MemoryCache,包括其基本概念、使用方法和示例说明。 MemoryCache简介 MemoryCache是.NET Core中的一个内存缓存库,可以用于缓存应用程序中的数据。它提供了一种快速、可靠和高效的方式来缓存数据,以提高应用程序的性能和响应速度…

    C# 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部