.net6 使用gRPC示例

创建一个gRPC服务项目(grpc服务端)和一个 webapi项目(客户端),测试项目结构如下:

公共模型

测试接口相关类,放在公共类库中,方便服务端和客户端引用相同模型

public class RoleInfo
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

public class UserInfo
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }

        public List<RoleInfo> Roles { get; set; }

        public UserInfo(int id, string name, int age)
        {
            Id = id;
            Name = name;
            Age = age;
        }
    }

gRPC服务端

1.解决方案文件中新增节点

  <ItemGroup>
    <!--Server指定为grpc服务端,Client指定为grpc客户端。在文件保存时候就会生成对应的c#代码,可以调用-->
    <Protobuf Include="Protos\user.proto" GrpcServices="Server" />  
  </ItemGroup>

2. grpc接口协议文件 user.proto

syntax = "proto3";

option csharp_namespace = "KmGrpcService";

package guser;

service GUserService {
  rpc GetUser (GetUserRequest) returns (GetUserReply);//获取用户信息接口
  rpc AddUser(AddUserRequest) returns (AddUserReply);//新增用户接口
}

//获取用户信息-入参
message GetUserRequest {
  int32 id = 1;
}

//获取用户信息-出参
message GetUserReply {
  string user = 1;
}

//新增用户-入参
message AddUserRequest{
	int32 id= 1 ;
	string name = 2 ;
	int32 age = 3;
	repeated RoleRequest roles=4;//repeated 指定为多条,类似数组。用户角色信息集合
}

//角色信息入参
message RoleRequest{
	int32 id=1;
	string name=2;
}

//新增用户-出参
message AddUserReply{
	bool success=1;
	string msg=2;
}

3. grpc服务实现

using Grpc.Core;
using GrpcCommon.Models;
using Newtonsoft.Json;

namespace KmGrpcService.Services
{
   //每次修改.proto接口协议文件,保存一下 都会自动更新。
   //GUserService和GUserServiceBase 都是根据.proto文件自动生成的C#代码
    public class UserService : GUserService.GUserServiceBase
    {
        private readonly ILogger<UserService> _logger;
        public UserService(ILogger<UserService> logger)
        {
            _logger = logger;
        }

        public static List<UserInfo> Users = new List<UserInfo>()
        {
            new UserInfo(1,"qwer",20),
            new UserInfo(2,"asdf",22),
            new UserInfo(3,"zxcv",23),
        };

        public override Task<GetUserReply> GetUser(GetUserRequest request, ServerCallContext context)
        {
            UserInfo? user = Users.FirstOrDefault(x => x.Id == request.Id);
            string? result = user == null ? null : JsonConvert.SerializeObject(user);
            GetUserReply reply = new GetUserReply() { User = result };
            return Task.FromResult(reply);
        }

        public override Task<AddUserReply> AddUser(AddUserRequest request, ServerCallContext context)
        {
            AddUserReply reply = new AddUserReply();
            try
            {
                var user = new UserInfo(request.Id, request.Name, request.Age);
                user.Roles = request.Roles.Select(x => new RoleInfo { Id = x.Id, Name = x.Name }).ToList();
                Users.Add(user);
                reply.Success = true;
            }
            catch (Exception ex)
            {
                reply.Success = false;
                reply.Msg = ex.Message;
                throw;
            }
            return Task.FromResult(reply);
        }
    }
}

4.Program中

using KmGrpcService.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddGrpc();

/*
builder.Services.AddGrpc(opt =>//设置全局grpc参数
{

}).AddServiceOptions<UserService>(opt =>//设置某个grpc服务的参数
{

});
*/

var app = builder.Build();

app.MapGrpcService<UserService>();

app.Run();

客户端

1. 客户端需要引入3个nuget包

  • Google.Protobuf
  • Grpc.Net.Client
  • Grpc.Tools
    或直接引入 Grpc.AspNetCore 。包含以上三个,如果要使用依赖注入的方式将服务注入,就引入这个包

2. 客户端的解决方案文件中新增节点

<ItemGroup>
    <!--Server指定为grpc服务端,Client指定为grpc客户端。在文件保存时候就会生成对应的c#代码,可以调用-->
    <Protobuf Include="Protos\user.proto" GrpcServices="Client" />  
  </ItemGroup>

3. UserClient 调用grpc服务

using Grpc.Core;
using Grpc.Net.Client;
using GrpcCommon.Models;
using KmGrpcService;
using Newtonsoft.Json;

namespace KmWebApi.GrpcClients
{
    public static class UserClient
    {
        const string ServerAddress = "http://localhost:5079";//grpc服务端地址
        static GrpcChannel? UserGrpcChannel = null;//grpc通道,创建成本高,尽量复用。
        public static async Task<UserInfo?> GetUser(int id)
        {
            if (UserGrpcChannel == null)
            {
                UserGrpcChannel = GrpcChannel.ForAddress(ServerAddress);
            }
            var client = new GUserService.GUserServiceClient(UserGrpcChannel);
            GetUserRequest request = new GetUserRequest() { Id = id };
            try
            {
                GetUserReply result = await client.GetUserAsync(request, deadline: DateTime.UtcNow.AddSeconds(5));
                UserInfo? user = JsonConvert.DeserializeObject<UserInfo>(result.User);
                return user;
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
            {
                throw new Exception("grpc调用超时");
            }

        }

        public static async Task AddUser(int id, string name, int age, List<RoleInfo> roles)
        {
            if (UserGrpcChannel == null)
            {
                UserGrpcChannel = GrpcChannel.ForAddress(ServerAddress);
            }
            var client = new GUserService.GUserServiceClient(UserGrpcChannel);
            AddUserRequest request = new AddUserRequest()
            {
                Id = id,
                Name = name,
                Age = age
            };
            foreach (RoleInfo role in roles ?? new List<RoleInfo>())
            {
                request.Roles.Add(new RoleRequest { Id = role.Id, Name = role.Name });
            }
            try
            {
                AddUserReply result = await client.AddUserAsync(request);
            }
            catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
            {
                throw new Exception("grpc调用超时");
            }
            catch (Exception ex)
            {

            }
        }
    }
}

4. UserController

using GrpcCommon.Models;
using KmWebApi.GrpcClients;
using KmWebApi.Models;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;

namespace KmWebApi.Controllers
{
    [Route("[controller]/[action]")]
    [ApiController]
    public class UserController : ControllerBase
    {

        public UserController()
        {

        }

        [HttpGet]
        public Task<UserInfo?> GetUser(int id)
        {
            return UserClient.GetUser(id);
        }

        [HttpPost]
        public Task AddUser([FromBody][Required] AddUserInput input)
        {
            return UserClient.AddUser(input.Id, input.Name, input.Age, input.roles);
        }
    }
}


public class AddUserInput
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public List<RoleInfo> roles { get; set; }
    }

客户端通过依赖注入的方式调用grpc服务

builder.Services.AddGrpcClient<GrpcUser.GrpcUserClient>((serviceProvider, options) =>
{
    options.Address = new Uri("http://localhost:5079");
    var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
    options.Interceptors.Add(new TestInterceptor(loggerFactory));//过滤器
    options.Interceptors.Add(new ErrorHandlerInterceptor());
}).ConfigureChannel(opt =>
{
    opt.UnsafeUseInsecureChannelCallCredentials = true;
}).AddCallCredentials(async(context, metadata, serviceProvider) =>
{
    IHttpContextAccessor httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
    string? token = httpContextAccessor.HttpContext?.Request.Headers["Authorization"].FirstOrDefault();
    metadata.Add("Authorization", token);
});


//过滤器
public class TestInterceptor : Interceptor
    {
        private readonly ILogger<TestInterceptor> _logger;
        public TestInterceptor(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<TestInterceptor>();
        }

        public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
        {
            Console.WriteLine("TestInterceptor 开始...");
            _logger.LogInformation($"AsyncUnaryCall.Type/Method :{context.Method.Type}/{context.Method.Name}");
            return continuation(request, context);
        }
    }

官方说明 https://learn.microsoft.com/zh-cn/aspnet/core/grpc/performance?view=aspnetcore-7.0

调试

右键解决方案-设置启动项目-多个 将服务端和客户端都勾选

请求客户端的接口->客户端通过grpc调用服务端服务

posted @ 2023-08-01 18:23  Net开发-孔明  阅读(187)  评论(0编辑  收藏  举报