C#怎么创建gRPC服务?ASP.NET Core gRPC通信完整教程

来源:建站作者:长沙GEO公司头衔:草根站长
导读:本期聚焦于小伙伴创作的《C#怎么创建gRPC服务?ASP.NET Core gRPC通信完整教程》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#怎么创建gRPC服务?ASP.NET Core gRPC通信完整教程》有用,将其分享出去将是对创作者最好的鼓励。

在C#生态中,gRPC是基于HTTP/2的高性能远程过程调用框架,适合微服务之间、前后端之间的高效通信场景。本文将以ASP.NET Core为宿主环境,讲解从零搭建gRPC服务到实现复杂通信的完整流程。

环境准备

首先需要安装.NET 6及以上版本的SDK,同时Visual Studio需要安装ASP.NET和Web开发工作负载。gRPC服务依赖Grpc.AspNetCore NuGet包,后续会自动通过项目模板引入。

创建gRPC服务项目

打开Visual Studio,选择创建新项目,搜索并选择gRPC服务模板,项目名称设置为GrpcDemo.Server,点击创建即可生成基础项目结构。项目默认包含以下核心文件:

  • Protos目录:存放proto接口定义文件
  • Services目录:存放gRPC服务实现类
  • Program.cs:服务启动配置入口

定义proto接口文件

proto文件是gRPC的核心,用于定义服务接口和消息结构,默认使用protobuf语法。我们在Protos目录下新建user.proto文件,内容如下:

syntax = "proto3";

option csharp_namespace = "GrpcDemo.Server.Protos";

package user;

// 定义用户服务
service User {
  // 获取用户信息接口
  rpc GetUser (GetUserRequest) returns (GetUserResponse);
  // 批量获取用户接口(服务端流)
  rpc GetUserList (GetUserListRequest) returns (stream GetUserResponse);
  // 创建用户接口(客户端流)
  rpc CreateUser (stream CreateUserRequest) returns (CreateUserResponse);
}

// 获取用户请求参数
message GetUserRequest {
  int32 user_id = 1;
}

// 获取用户响应结果
message GetUserResponse {
  int32 id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;
}

// 批量获取用户请求参数
message GetUserListRequest {
  int32 page = 1;
  int32 page_size = 2;
}

// 创建用户请求参数
message CreateUserRequest {
  string name = 1;
  string email = 2;
  int32 age = 3;
}

// 创建用户响应结果
message CreateUserResponse {
  bool success = 1;
  string message = 2;
  int32 user_id = 3;
}

之后需要在项目文件GrpcDemo.Server.csproj中添加proto文件的引用配置,确保编译时生成对应的C#代码:

<ItemGroup>
  <Protobuf Include="Protosuser.proto" GrpcServices="Server" />
</ItemGroup>

实现gRPC服务端逻辑

在Services目录下新建UserService类,继承proto文件生成的User.UserBase基类,实现定义的接口方法:

using Grpc.Core;
using GrpcDemo.Server.Protos;

namespace GrpcDemo.Server.Services;

// 用户服务实现类
public class UserService : User.UserBase
{
    // 模拟用户数据存储
    private static readonly List<UserModel> _users = new()
    {
        new UserModel { Id = 1, Name = "张三", Email = "zhangsan@ipipp.com", Age = 25 },
        new UserModel = 2, Name = "李四", Email = "lisi@ipipp.com", Age = 28 },
        new UserModel { Id = 3, Name = "王五", Email = "wangwu@ipipp.com", Age = 22 }
    };

    // 实现获取单个用户接口
    public override Task<GetUserResponse> GetUser(GetUserRequest request, ServerCallContext context)
    {
        var user = _users.FirstOrDefault(u => u.Id == request.UserId);
        if (user == null)
        {
            // 抛出gRPC状态异常
            throw new RpcException(new Status(StatusCode.NotFound, $"用户ID {request.UserId} 不存在"));
        }

        return Task.FromResult(new GetUserResponse
        {
            Id = user.Id,
            Name = user.Name,
            Email = user.Email,
            Age = user.Age
        });
    }

    // 实现服务端流接口:批量获取用户
    public override async Task GetUserList(GetUserListRequest request, IServerStreamWriter<GetUserResponse> responseStream, ServerCallContext context)
    {
        var users = _users
            .Skip((request.Page - 1) * request.PageSize)
            .Take(request.PageSize)
            .ToList();

        foreach (var user in users)
        {
            // 检查客户端是否取消请求
            if (context.CancellationToken.IsCancellationRequested)
            {
                break;
            }

            await responseStream.WriteAsync(new GetUserResponse
            {
                Id = user.Id,
                Name = user.Name,
                Email = user.Email,
                Age = user.Age
            });
            // 模拟流传输间隔
            await Task.Delay(100);
        }
    }

    // 实现客户端流接口:创建用户
    public override async Task<CreateUserResponse> CreateUser(IAsyncStreamReader<CreateUserRequest> requestStream, ServerCallContext context)
    {
        int createCount = 0;
        // 循环读取客户端发送的流数据
        while (await requestStream.MoveNextAsync())
        {
            var req = requestStream.Current;
            // 简单校验邮箱格式
            if (!req.Email.Contains("@"))
            {
                return new CreateUserResponse
                {
                    Success = false,
                    Message = $"用户 {req.Name} 的邮箱格式不正确",
                    UserId = 0
                };
            }

            var newUser = new UserModel
            {
                Id = _users.Max(u => u.Id) + 1,
                Name = req.Name,
                Email = req.Email,
                Age = req.Age
            };
            _users.Add(newUser);
            createCount++;
        }

        return new CreateUserResponse
        {
            Success = true,
            Message = $"成功创建 {createCount} 个用户",
            UserId = _users.Max(u => u.Id)
        };
    }

    // 内部用户模型类
    private class UserModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public int Age { get; set; }
    }
}

然后在Program.cs中注册gRPC服务,配置代码如下:

var builder = WebApplication.CreateBuilder(args);

// 添加gRPC服务
builder.Services.AddGrpc(options =>
{
    // 配置最大接收消息大小
    options.MaxReceiveMessageSize = 2 * 1024 * 1024; // 2MB
    // 配置最大发送消息大小
    options.MaxSendMessageSize = 5 * 1024 * 1024; // 5MB
});

var app = builder.Build();

// 映射gRPC服务
app.MapGrpcService<UserService>();
// 配置HTTP端口
app.MapGet("/", () => "gRPC服务运行中,请使用gRPC客户端访问");

app.Run();

创建gRPC客户端项目

新建一个控制台应用项目,命名为GrpcDemo.Client,通过NuGet安装以下包:

  • Grpc.Net.Client
  • Google.Protobuf
  • Grpc.Tools

将服务端的user.proto文件复制到客户端的Protos目录下,修改项目文件添加proto引用:

<ItemGroup>
  <Protobuf Include="Protosuser.proto" GrpcServices="Client" />
</ItemGroup>

客户端调用代码示例:

using Grpc.Core;
using Grpc.Net.Client;
using GrpcDemo.Client.Protos;

// 创建gRPC客户端通道,地址为服务端启动地址
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new User.UserClient(channel);

try
{
    // 1. 调用获取单个用户接口
    Console.WriteLine("=== 获取单个用户 ===");
    var getUserResp = await client.GetUserAsync(new GetUserRequest { UserId = 1 });
    Console.WriteLine($"用户ID:{getUserResp.Id},姓名:{getUserResp.Name},邮箱:{getUserResp.Email},年龄:{getUserResp.Age}");

    // 2. 调用服务端流接口
    Console.WriteLine("n=== 批量获取用户(服务端流)===");
    using var call = client.GetUserList(new GetUserListRequest { Page = 1, PageSize = 2 });
    await foreach (var user in call.ResponseStream.ReadAllAsync())
    {
        Console.WriteLine($"用户ID:{user.Id},姓名:{user.Name}");
    }

    // 3. 调用客户端流接口
    Console.WriteLine("n=== 创建用户(客户端流)===");
    using var createCall = client.CreateUser();
    // 发送多个用户数据
    await createCall.RequestStream.WriteAsync(new CreateUserRequest
    {
        Name = "赵六",
        Email = "zhaoliu@ipipp.com",
        Age = 30
    });
    await createCall.RequestStream.WriteAsync(new CreateUserRequest
    {
        Name = "孙七",
        Email = "sunqi@ipipp.com",
        Age = 26
    });
    // 标记客户端流发送完成
    await createCall.RequestStream.CompleteAsync();
    // 获取服务端响应
    var createResp = await createCall;
    Console.WriteLine($"创建结果:{createResp.Message},最新用户ID:{createResp.UserId}");
}
catch (RpcException ex)
{
    Console.WriteLine($"gRPC调用异常:{ex.Status.Detail}");
}

高级配置技巧

添加gRPC拦截器

拦截器可以实现日志、权限校验、异常处理等通用逻辑,首先定义服务端拦截器:

using Grpc.Core;
using Grpc.Core.Interceptors;

namespace GrpcDemo.Server.Interceptors;

public class LoggingInterceptor : Interceptor
{
    private readonly ILogger<LoggingInterceptor> _logger;

    public LoggingInterceptor(ILogger<LoggingInterceptor> logger)
    {
        _logger = logger;
    }

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        _logger.LogInformation($"收到请求:{context.Method},请求参数:{request}");
        try
        {
            var response = await continuation(request, context);
            _logger.LogInformation($"请求处理完成:{context.Method}");
            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"请求处理异常:{context.Method}");
            throw;
        }
    }
}

在Program.cs中注册拦截器:

builder.Services.AddGrpc(options =>
{
    options.Interceptors.Add<LoggingInterceptor>();
});

配置TLS加密

生产环境建议启用TLS加密,首先生成自签名证书,然后在Program.cs中配置Kestrel使用HTTPS:

builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(5001, listenOptions =>
    {
        listenOptions.UseHttps("server.pfx", "证书密码");
    });
});

全局异常处理

可以自定义异常处理器,统一返回gRPC标准错误响应:

builder.Services.AddGrpc(options =>
{
    options.EnableDetailedErrors = true; // 开发环境开启详细错误
    options.Interceptors.Add<ExceptionInterceptor>();
});

// 异常拦截器实现
public class ExceptionInterceptor : Interceptor
{
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        try
        {
            return await continuation(request, context);
        }
        catch (ArgumentException ex)
        {
            throw new RpcException(new Status(StatusCode.InvalidArgument, ex.Message));
        }
        catch (Exception ex)
        {
            throw new RpcException(new Status(StatusCode.Internal, "服务内部错误"));
        }
    }
}

常见问题排查

问题现象可能原因解决方式
客户端调用提示连接失败服务端未启动、端口配置错误、防火墙拦截检查服务端运行状态,确认客户端地址与服务端监听地址一致
提示proto文件未生成对应代码项目文件中未正确配置Protobuf引用检查csproj文件中Protobuf节点的GrpcServices属性是否正确
消息大小超过限制报错发送或接收的消息超过默认大小限制在AddGrpc中调整MaxReceiveMessageSize和MaxSendMessageSize配置
流接口调用无响应未正确标记流结束、客户端或服务端提前取消检查流写入后是否调用CompleteAsync,确认未触发取消令牌

C#gRPCASP.NET_Coreprotobuf修改时间:2026-06-08 18:45:53

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。