Merhaba arkadaşlar, servisler arasında stream tabanlı yüksek performanslı bir iletişim sağlayan gRPC den bahsedeceğiz.

- gRPC, HTTP/2 protokolünde çalışan bir RPC (Remote Procedure Call) frameworküdür.
- CNCF (Cloud Native Computing Foundation) tarafından yürütülen popüler bir açık kaynaklı kütüphanedir.
- Verileri json yada xml yerine Google tarafından geliştirilen protobuf(.proto) ile binary formatında yüksek performanslı bir şekilde serileştirir.
Proto hakkında daha detaylı bilgi için;
-
.Net 6 ile beraber gRPC tarafında performans, yük dengeleme, retry patternler ve en önemlisi HTTP/3 protokolünün(MSQUIC) desteklenmesi gibi konularda geliştirmeler yapılmıştır.
serialization_speed_large-emreyalvac restvsgrpc-emreyalvac İsterseniz örneğimize başlayalım. Yapmak istediğimiz işlem şu olsun verilen öğrenci listesini kaydedelim. Her kayıt işleminde de consol ekranına bilgi verelim. Bunu gRPC client streaming yöntemiyle yapacağız.Öncelikle gRPC servisimiz ile yani server tarafı ile başlayalım.1dotnet new grpc -n StudentServiceGRPCŞimdi “protos” klasörü altına “student.proto” dosyamızı ekleyelim ve csproj içerisinde bunu belirtelim.123456789101112131415161718192021syntax = "proto3";option csharp_namespace = "StudentServiceGRPC";package student;// The service definition.service StudentGRPCService {rpc AddStudentStream(stream StudentRequest) returns (StudentResponse);}// The request messagemessage StudentRequest {int32 studentId = 1;string name = 2;}// The response messagemessage StudentResponse {string message = 1;}
123<ItemGroup><Protobuf Include="Protos\student.proto" GrpcServices="Server" /></ItemGroup>Artık servisimizi oluşturma zamanı geldi. Servisimizi oluşturmadan önce projemizi build edelim ve belirttiğimiz proto dosyasınıa göre gerekli code generationların yapılmasını sağlayalım.1dotnet buildServisimizi tanımlarken kalıtım aldığımız sınıfa dikkat edelim ve proto da belirttiğimiz RPC methodu override ederek içini dolduralım.12345678910111213141516171819202122232425262728using Grpc.Core;namespace StudentServiceGRPC.Services;public class StudentService : StudentGRPCService.StudentGRPCServiceBase{private readonly ILogger<StudentService> _logger;public StudentService(ILogger<StudentService> logger){_logger = logger;}public override async Task<StudentResponse> AddStudentStream(IAsyncStreamReader<StudentRequest> requestStream, ServerCallContext context){var result = new StudentResponse();var count = 0;await foreach (var student in requestStream.ReadAllAsync()){var message = $"{student.StudentId} - {student.Name} added.";result.Message += message;Console.WriteLine(message);count++;}Console.WriteLine($"Add students stream has been ended. Count : {count}");return result;}}Şimdi program.cs içerisine servisimizi register edelim.1app.MapGrpcService<StudentService>();Artık öğrenci listesini alacağımız api projemizi oluşturabiliriz.1dotnet new webapi -n StudentClientApiÖncelikle kullanacağımız nuget paketlerini ekleyelim.1234dotnet add package Grpc.Net.Clientdotnet add package Google.Protobufdotnet add package Grpc.Toolsdotnet add package Grpc.Net.ClientFactoryOluşturduğumuz proto dosyasını bu projeye kopyalayarak, namespacemizi güncelleyelim.1option csharp_namespace = "StudentClientApi";Daha sonrasında csproj altında tanımlamamızı yapalım. Bu sefer tanımlama yaparken “GrpcServices” attribute değerinin “Client” olmasına dikkat edelim ve projemizi yine build edelim.123<ItemGroup><Protobuf Include="Protos\student.proto" GrpcServices="Client" /></ItemGroup>
1dotnet buildGelen isteği karşılayacak endpointimizi, modelimizi ve endpointten gelen istekleri işleyeceğimiz servisimizi yazalım.123456789101112namespace StudentClientApi.Models;public class StudentReq{public IAsyncEnumerable<Student> Students { get; set; }}public class Student{public int StudentId { get; set; }public string Name { get; set; }}
1234567891011121314151617181920212223using Microsoft.AspNetCore.Mvc;using StudentClientApi.Models;using StudentClientApi.Services;namespace StudentClientApi.Controllers;[ApiController][Route("students")]public class StudentController : ControllerBase{private readonly IStudentService _studentService;public StudentController(IStudentService studentService){_studentService = studentService;}[HttpPost]public async Task<IActionResult> AddStudents(StudentReq request){return Ok(await _studentService.InsertStudentsAsync(request));}}
12345678910111213141516171819202122232425262728293031323334353637using StudentClientApi.Models;namespace StudentClientApi.Services;public interface IStudentService{Task<string> InsertStudentsAsync(StudentReq request);}public class StudentService : IStudentService{private readonly StudentGRPCService.StudentGRPCServiceClient _studentGRPCServiceClient;public StudentService(StudentGRPCService.StudentGRPCServiceClient studentGRPCServiceClient){_studentGRPCServiceClient = studentGRPCServiceClient;}public async Task<string> InsertStudentsAsync(StudentReq request){using var addStudentStream = _studentGRPCServiceClient.AddStudentStream();await foreach (Student student in request.Students){StudentRequest studentRequest = new(){StudentId = student.StudentId,Name = student.Name};await addStudentStream.RequestStream.WriteAsync(studentRequest);}await addStudentStream.RequestStream.CompleteAsync();StudentResponse response = await addStudentStream;return response.Message;}}Servislerimizi program.cs içerisine register etmeyi unutmayalım.12345builder.Services.AddScoped<IStudentService, StudentService>();builder.Services.AddGrpcClient<StudentGRPCService.StudentGRPCServiceClient>(o =>{o.Address = new Uri("https://localhost:7026"); //StudentServiceGRPC port});gRPC kendi içerisinde Resiliency için farklı konfigurasyonlar ve retry patternler barındırır. Örnek bir config;1234567891011121314151617var retryMethodConfig = new MethodConfig{Names = { MethodName.Default },RetryPolicy = new RetryPolicy{MaxAttempts = 3,InitialBackoff = TimeSpan.FromSeconds(1),MaxBackoff = TimeSpan.FromSeconds(4),BackoffMultiplier = 1.5,RetryableStatusCodes = { StatusCode.Unavailable }}};builder.Services.AddGrpcClient<StudentGRPCService.StudentGRPCServiceClient>(o =>{o.Address = new Uri("https://localhost:7026");o.ChannelOptionsActions.Add(opt => opt.ServiceConfig = new ServiceConfig { MethodConfigs = { retryMethodConfig } });});Daha detaylı retry optionları için;
“dotnet run” komutu ile iki projemizi de ayağa kaldıralım ve swagger/postman aracılığı ile uygulamamızı test edelim.


Kaynak Kod: https://github.com/EnesAys/Net6gRPC
Umarım faydalı olmuştur arkadaşlar, kalın sağlıcakla… 😀
Kaynaklar
https://grpc.io/
https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-6.0&tabs=visual-studio
https://docs.microsoft.com/en-us/aspnet/core/grpc/?view=aspnetcore-6.0
https://docs.microsoft.com/en-us/aspnet/core/grpc/clientfactory?view=aspnetcore-6.0
https://emreyalvac.com/grpc/
https://www.aydinwebs.com/blog/grpc-ve-net-6-kullanarak-yuksek-performansli-mikro-hizmetler-olusturma
https://www.gokhan-gokalp.com/high-performance-stream-based-communication-between-services-with-net-5-and-grpc/