AspNetCore中 使用 Grpc 簡單Demo

為什麼要用Grpc

跨語言進行,調用服務,獲取跨服務器調用等
目前我的需要使用 我的抓取端是go 寫的 查詢端用 Net6 寫的 導致很多時候 我需要把一些臨時數據寫入到 Redis 在兩個服務器進行處理

參考地址:

嗶哩嗶哩楊旭大佬://www.bilibili.com/video/BV1eE411T7GC/?spm_id_from=333.999.0.0
強烈建議去看一遍楊旭大佬講的這篇Grpc 沒有那麼繁瑣 清晰明了

1、依賴安裝:

Grpc.AspNetCore Server 端包

2、編寫proto 文件

點擊查看代碼
syntax = "proto3";


option csharp_namespace= "GrpcServer.Web.Protos";

message Employee{
	int32 id = 1;
	int32 no = 2;
	string firstName = 3;
	string lastName =4;
	float salary = 5;

}

message GetByNoRequest{
	int32 no =1;
}

message EmployeeResponse{
	Employee employee =1;

}

message GetAllRequest{

}

message AddPhotoRequest{
	bytes data = 1;

}

message AddPhotoResponse{
	bool isOK = 1;
}

message EmployeeRequest{
	Employee employee = 1;
}



service EmployeeService {
	rpc GetByNo(GetByNoRequest) returns (EmployeeResponse);
	rpc GetAll(GetAllRequest) returns (stream EmployeeResponse);
	rpc AddPhoto(stream AddPhotoRequest) returns (AddPhotoResponse);
	rpc Save (EmployeeRequest) returns(EmployeeResponse);
	rpc SaveAll(stream EmployeeRequest) returns(stream EmployeeResponse);
}

3、編寫Server端代碼

在編寫服務端代碼之前,我們需要對proto 文件進行生成代碼
image
如上圖:1)我們要先進行勾選Build Action 選擇,如圖所示,
因為我們這裡需要的只是 Server 端,選擇生成Server Only
2) 選擇這兩個之後 我們需要重新生成一下項目

EmployeeService.EmployeeServiceBase 是我們Proto 生成的代碼,我們要去重寫父類的這些方法
點擊查看代碼
   public class MyEmployeeService : EmployeeService.EmployeeServiceBase
    {
        private readonly ILogger<MyEmployeeService> _logger;

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

        public override async Task<EmployeeResponse>
            GetByNo(GetByNoRequest request, ServerCallContext context)
        {
            var employee = InMemoryData.Employees.SingleOrDefault(x => x.No == request.No);
            if (employee != null)
            {
                var response = new EmployeeResponse
                {
                    Employee = employee
                };
                return await Task.FromResult(response);
            }
            throw new Exception($"employee is not found :{request.No}");

        }
        public override async Task GetAll(GetAllRequest request, IServerStreamWriter<EmployeeResponse> responseStream, ServerCallContext context)
        {
            foreach (var item in InMemoryData.Employees)
            {
                await responseStream.WriteAsync(new EmployeeResponse
                {
                    Employee = item
                });
            }
            //return base.GetAll(request, responseStream, context);
        }

        public override async Task<AddPhotoResponse> AddPhoto(
            IAsyncStreamReader<AddPhotoRequest> requestStream
            , ServerCallContext context)
        {
            Metadata md = context.RequestHeaders;
            foreach (var item in md)
            {
                Console.WriteLine($"{item.Key}:{item.Value}");
            }

            var data = new List<byte>();
            while (await requestStream.MoveNext())
            {
                Console.WriteLine($"Received: {requestStream.Current.Data.Length}");
                data.AddRange(requestStream.Current.Data);
            }
            Console.WriteLine($"data file with {data.Count} bytes");
            return new AddPhotoResponse
            {
                IsOK = true
            };

        }


        public override async Task SaveAll(
            IAsyncStreamReader<EmployeeRequest> requestStream
            , IServerStreamWriter<EmployeeResponse> responseStream
            , ServerCallContext context)
        {
            while (await requestStream.MoveNext())
            {
                var employee = requestStream.Current.Employee;
                lock (this)
                {
                    InMemoryData.Employees.Add(employee);
                }
                await responseStream.WriteAsync(new EmployeeResponse
                {
                    Employee = employee
                });
            }
            Console.WriteLine("Employees");
            foreach (var item in InMemoryData.Employees)
            {
                Console.WriteLine(item);
            }
            //return base.SaveAll(requestStream, responseStream, context);
        }
    }

4、編寫Client客戶端代碼

  1. 我們把服務端的Proto 文件複製一份到Client 端代碼 生成一份Client 代碼如下圖
    image
  2. 需要注意

在編寫客戶端代碼的時候,我遇到了一個錯誤,提示證書驗證失敗!,(沒有配置證書)
所以我們要加上不對證書進行驗證
使用 GrpcChannelOptions() 對證書驗證

 using var channel = GrpcChannel.ForAddress("//localhost:5001", new GrpcChannelOptions()
            {
                HttpClient = null,
                HttpHandler = new HttpClientHandler
                {
                    //方法一
                    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
                    //方法二
                    //ServerCertificateCustomValidationCallback = (a, b, c, d) => true
                }
            });

Demo 地址://gitee.com/Lovely_Rabbit/basic-principle-exercise

Tags: