REST і gRPC у мікросервісах .NET Core: Всеосяжний посібник

pic

1. Чому мікросервіси в .NET Core?

Перш ніж зануритися в REST або gRPC, давайте встановимо контекст: .NET Core (тепер просто .NET) — це кросплатформений, високопродуктивний та модульний фреймворк, який спрощує створення мікросервісів. Підхід мікросервісів полягає в розробці малих, незалежно розгортаних сервісів, які взаємодіють через легкі протоколи.

Основні переваги мікросервісів

  • Незалежне розгортання: Оновлюйте сервіси окремо, не перезавантажуючи всю програму.
  • Масштабованість: Масштабуйте лише «гарячі» сервіси горизонтально.
  • Ізоляція: Кожен сервіс може використовувати власний стек технологій чи базу даних.

Зауваження з реального світу: Мікросервіси часто вносять складності, такі як затримки в мережі, розподілені транзакції та виявлення сервісів. Переконайтесь, що ваша команда та інфраструктура готові до цих викликів, перш ніж приступити.

2. Що таке REST і gRPC?

REST (Representational State Transfer)

  • Протокол: Зазвичай використовує HTTP/1.1 або HTTP/2.
  • Формат даних: JSON або XML.
  • Стиль: Точки доступу на основі ресурсів (GET /api/products).
  • Переваги: Широко підтримується, легко тестується, зрозумілий для людини.
  • Недоліки: Можуть бути великі корисні навантаження (JSON), немає вбудованого жорсткого контролю схеми.

gRPC (Google Remote Procedure Call)

  • Протокол: Використовує HTTP/2 (або HTTP/3 з новішими інструментами), підтримує стрімінг.
  • Формат даних: Бінарний (Protocol Buffers).
  • Стиль: На основі RPC; визначення сервісів і повідомлень у файлах .proto.
  • Переваги: Висока продуктивність, строгі контракти, підтримка стрімінгу.
  • Недоліки: Бінарний формат менш зрозумілий для людини; не так широко застосовується для публічних/зовнішніх API (хоча це змінюється).

3. Як вибрати між REST і gRPC

Коли використовувати REST:

  • Публічні API з різноманітними клієнтами.
  • Потрібен «людинозрозумілий» формат (JSON).
  • Потрібні потужні інструменти спільноти (Swagger, Postman).

Коли використовувати gRPC:

  • Висока пропускна здатність, низька затримка для внутрішньої комунікації.
  • Строго типізовані контракти і стрімінг.
  • Хочете використовувати можливості HTTP/2 (мультиплексування, контроль потоку).

Порада від професіонала: Багато архітектур використовують обидва підходи. Для зовнішніх або публічних точок доступу REST все ще є королем. Для внутрішньої комунікації між сервісами gRPC може бути великою вигодою.
REST в .NET Core

Налаштування

dotnet new webapi -n MyAwesomeRestService  
cd MyAwesomeRestService

Контролери та Точки Доступу

У файлі Controllers/ProductsController.cs:

using Microsoft.AspNetCore.Mvc;  

namespace MyAwesomeRestService.Controllers  
{  
 [ApiController]  
 [Route("api/[controller]")]  
 public class ProductsController : ControllerBase  
 {  
 // GET: api/Products  
 [HttpGet]  
 public IActionResult GetAllProducts()  
 {  
 return Ok(new[]  
 {  
 new { Id = 1, Name = "Laptop", Price = 999.99 },  
 new { Id = 2, Name = "Headphones", Price = 199.99 }  
 });  
 }  

 // GET: api/Products/{id}  
 [HttpGet("{id}")]  
 public IActionResult GetProductById(int id)  
 {  
 if (id == 1)  
 return Ok(new { Id = 1, Name = "Laptop", Price = 999.99 });  
 else  
 return NotFound();  
 }  

 // POST: api/Products  
 [HttpPost]  
 public IActionResult AddProduct([FromBody] Product product)  
 {  
 // У реальному додатку збереження в базі даних  
 return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, product);  
 }  
 }  

 public class Product   
 {  
 public int Id { get; set; }  
 public string? Name { get; set; }  
 public double Price { get; set; }  
 }  
}

Обробка Помилок у REST

У .NET 6+ можна використовувати Middleware або підхід UseExceptionHandler для обробки глобальних помилок:

app.UseExceptionHandler(errorApp =>  
{  
 errorApp.Run(async context =>  
 {  
 context.Response.StatusCode = 500;  
 context.Response.ContentType = "application/json";  

 // Логування помилки або серіалізація власного об'єкта помилки  
 await context.Response.WriteAsync("{ \"error\": \"Сталася непередбачувана помилка.\" }");  
 });  
});

Версійність RESTful API

Ви можете версіонувати свої точки доступу API, щоб дозволити безперервні оновлення:

dotnet add package Microsoft.AspNetCore.Mvc.Versioning

У Program.cs:

builder.Services.AddApiVersioning(options =>  
{  
 options.DefaultApiVersion = new ApiVersion(1, 0);  
 options.AssumeDefaultVersionWhenUnspecified = true;  
 options.ReportApiVersions = true;  
});

Потім додаємо атрибути до контролерів:

[ApiVersion("1.0")]  
[Route("api/v{version:apiVersion}/[controller]")]  
public class ProductsController : ControllerBase { ... }

Приклад: Сервіс для електронної комерції

  • Зовнішні клієнти (Web, Mobile) звертаються до https://yourdomain.com/api/v1/products для отримання списку товарів, створення, оновлень тощо.
  • Досвід розробника: Легко тестується за допомогою Swagger та Postman.
  • Масштабованість: Масштабування горизонтально під час пикових навантажень — особливо під час святкових розпродажів.

5.

gRPC в .NET Core

Налаштування

dotnet new grpc -n MyAwesomeGrpcService  
cd MyAwesomeGrpcService

Опис файлу .proto

Protos/orders.proto:

syntax = "proto3";  

option csharp_namespace = "MyAwesomeGrpcService";  
package orders;  

service OrderService {  
 rpc GetOrder (GetOrderRequest) returns (OrderResponse);  
 rpc PlaceOrder (PlaceOrderRequest) returns (OrderResponse);  
}  

message GetOrderRequest {  
 int32 order_id = 1;  
}  

message PlaceOrderRequest {  
 string product_name = 1;  
 int32 quantity = 2;  
}  

message OrderResponse {  
 int32 order_id = 1;  
 string status = 2;  
 string message = 3;  
}

Реалізація gRPC Сервісу

Services/OrderService.cs:

using System.Threading.Tasks;  
using Grpc.Core;  
using orders;  

namespace MyAwesomeGrpcService.Services  
{  
 public class OrderServiceImpl : OrderService.OrderServiceBase  
 {  
 public override Task GetOrder(GetOrderRequest request, ServerCallContext context)  
 {  
 if (request.OrderId == 1)  
 {  
 return Task.FromResult(new OrderResponse  
 {  
 OrderId = 1,  
 Status = "Shipped",  
 Message = "Your order is on the way!"  
 });  
 }  
 else  
 {  
 return Task.FromResult(new OrderResponse  
 {  
 OrderId = request.OrderId,  
 Status = "NotFound",  
 Message = "Order not found."  
 });  
 }  
 }  

 public override Task PlaceOrder(PlaceOrderRequest request, ServerCallContext context)  
 {  
 var newOrderId = new System.Random().Next(2, 1000);   
 return Task.FromResult(new OrderResponse  
 {  
 OrderId = newOrderId,  
 Status = "Created",  
 Message = $"Order created for {request.ProductName} (Qty: {request.Quantity})."  
 });  
 }  
 }  
}

Реєстрація Сервісу

Program.cs:

var builder = WebApplication.CreateBuilder(args);  

// Реєстрація gRPC  
builder.Services.AddGrpc();  

var app = builder.Build();  

// Маршрутизація gRPC сервісу  
app.MapGrpcService();  
app.MapGet("/", () => "gRPC service is running...");  

app.Run();

Розширений gRPC: Стрімінг та Код помилок

  • Серверний стрімінг: Сервер може відправляти кілька повідомлень у відповідь на один запит клієнта (корисно для оновлень прогресу в реальному часі).
  • Клієнтський стрімінг: Клієнт може відправляти потік даних на сервер (наприклад, завантаження шматками).
  • Двосторонній стрімінг: І клієнт, і сервер можуть одночасно обмінюватися потоками повідомлень.
  • Коди помилок: Замість використання HTTP статус кодів, gRPC використовує об'єкти Status (StatusCode.NotFound, StatusCode.InvalidArgument тощо). Це корисно для сильно типізованої обробки помилок.

Версійність gRPC Сервісів

  • У файлах .proto зазвичай додаються нові поля з новими тегами для зворотної сумісності (Protocol Buffers може ігнорувати невідомі поля).
  • Також можна додавати нові RPC методи в той самий сервіс або версіонувати назву сервісу для великих змін.

Приклад: Обробка Замовлень у Реальному Часі

  • Внутрішні виклики високої пропускної здатності: Внутрішні мікросервіси можуть викликати OrderService для оновлення статусів майже в реальному часі.
  • Затримка: Protobuf є бінарним форматом і швидше серіалізується/десеріалізується, ніж JSON.
  • Стрімінг: Якщо потрібно отримувати оновлення в реальному часі по етапах обробки замовлення, серверний стрімінг може миттєво передавати ці оновлення.

6. Безпека та Продуктивність

Безпека

  • REST: Зазвичай захищено за допомогою JWT, OAuth 2.0 або API ключів. ASP.NET Core Identity для інтегрованого управління користувачами.
  • gRPC: Зазвичай працює через TLS/SSL; можна передавати токени в метаданих для автентифікації.

Продуктивність

  • REST: Текстовий JSON може бути більшим і повільніше розбиратися. Кешування з HTTP заголовками (ETags) може допомогти.
  • gRPC: Protobuf є компактним; HTTP/2 мультиплексує кілька викликів через одне з'єднання, часто зменшуючи накладні витрати.

7. Стратегії Тестування

REST:

  • Swagger/OpenAPI: Вбудовано в ASP.NET Core для генерації та тестування кінцевих точок.
  • Postman або curl: Швидке ручне тестування.
  • xUnit/NUnit: Юніт-тести та інтеграційні тести для контролерів.

2. gRPC:

  • BloomRPC, grpcurl або Postman (з підтримкою gRPC): Ручні виклики до ваших gRPC кінцевих точок.
  • xUnit: Використовуйте gRPC клієнтські стуби у тестах для виклику методів сервісу безпосередньо.

8. Розгортання, масштабування та спостережуваність

Docker та Kubernetes

  1. Контейнеризація кожного мікросервісу за допомогою Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base  
WORKDIR /app  
COPY . .  
ENTRYPOINT ["dotnet", "MyAwesomeService.dll"]
  1. Kubernetes:
  • Розгорніть кожен контейнер як Pod.
  • Використовуйте Deployments для оновлень і відкатів.
  • Експонуйте сервіси через ClusterIP або Ingress.

Логування, моніторинг та трасування

  • Логування: Використовуйте Serilog, NLog або вбудоване логування в .NET. Централізуйте логи (наприклад, Elasticsearch, Azure Monitor).
  • Моніторинг: Експортуйте метрики до Prometheus або Azure Monitor.
  • Трасування: Реалізуйте OpenTelemetry для розподіленого трасування (Jaeger, Zipkin).

Виявлення сервісів

  • В середовищах мікросервісів потрібно мати спосіб виявлення кінцевих точок сервісів:
  • Kubernetes: Виявлення сервісів на основі DNS (наприклад, orderservice.default.svc.cluster.local).
  • Service Mesh: Інструменти, такі як Istio або Linkerd, керують маршрутизацією трафіку, спостережуваністю та безпекою.

9. Підсумок

Основні висновки

  • REST: Найкраще підходить для публічних API або там, де важлива читабельність для людини та широка сумісність.
  • gRPC: Надзвичайно ефективний і сильно типізований — чудово підходить для внутрішніх мікросервісів на великій шкалі.
  • Комбінація: Багато реальних систем відкривають REST шар для зовнішнього використання, а всередині використовують gRPC.

Остаточні думки

Використовуючи потужність .NET та сучасні платформи оркестрації контейнерів, ви можете створювати надійні, масштабовані мікросервіси, які безперешкодно інтегрують як REST, так і gRPC. Не забувайте враховувати безпеку, спостережуваність (логування/трасування) та стратегії тестування з першого дня — ці деталі часто визначають успішність або провал реальних розгортань.

Тепер, коли ви бачили, як налаштувати як REST, так і gRPC мікросервіси, ви готові побудувати екосистему кінцевих точок, яка відповідатиме вашим вимогам щодо продуктивності та інтеграції. Щасливого кодування — і нехай ваші сервіси залишаються здоровими, доступними та блискавично швидкими!

Перекладено з: REST and gRPC in .NET Core Microservices: A Comprehensive Guide

Leave a Reply

Your email address will not be published. Required fields are marked *