第八章 微服务通信实现
第八章 微服务通信实现
微服务之间的通信是分布式系统中最具挑战性的部分。我见过太多团队因为通信设计不当而导致系统性能低下、故障频发。这一章,我想分享一些实战经验,帮助你设计高效可靠的微服务通信方案。
8.1 同步通信:REST vs gRPC
同步通信就像打电话,你必须等待对方接听并回应。虽然直观,但在分布式系统中需要谨慎使用。
8.1.1 HTTP/REST:最通用的选择
REST是微服务通信的"通用语言",几乎所有技术栈都支持。但用好REST并不容易。
RESTful API设计实战
// 服务端实现 - ASP.NET Core Web API
[ApiController]
[Route("api/v1/[controller]")]
[Produces("application/json")]
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger<ProductsController> _logger;
public ProductsController(IMediator mediator, ILogger<ProductsController> logger)
{
_mediator = mediator;
_logger = logger;
}
[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(ProductDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ProductDto>> GetProduct(Guid id)
{
_logger.LogInformation("Getting product {ProductId}", id);
try
{
var result = await _mediator.Send(new GetProductByIdQuery(id));
return result.Match<ActionResult<ProductDto>>(
product => Ok(product),
notFound => NotFound());
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting product {ProductId}", id);
return Problem(
title: "An error occurred while retrieving the product",
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError);
}
}
[HttpPost]
[Authorize(Roles = "Admin,ProductManager")]
[ProducesResponseType(typeof(ProductDto), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<ProductDto>> CreateProduct([FromBody] CreateProductCommand command)
{
var result = await _mediator.Send(command);
return result.Match<ActionResult<ProductDto>>(
product => CreatedAtAction(
nameof(GetProduct),
new { id = product.Id },
product),
validationError => BadRequest(validationError));
}
[HttpPut("{id:guid}")]
[Authorize(Roles = "Admin,ProductManager")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult> UpdateProduct(Guid id, [FromBody] UpdateProductCommand command)
{
if (id != command.ProductId)
{
return BadRequest("Product ID mismatch");
}
var result = await _mediator.Send(command);
return result.Match<ActionResult>(
_ => NoContent(),
error => BadRequest(new { Error = error }));
}
[HttpDelete("{id:guid}")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> DeleteProduct(Guid id)
{
var result = await _mediator.Send(new DeleteProductCommand(id));
return result.Match<ActionResult>(
_ => NoContent(),
error => NotFound(new { Error = error }));
}
// 批量操作 - 避免N+1查询问题
[HttpGet]
[ProducesResponseType(typeof(PagedResult<ProductDto>), StatusCodes.Status200OK)]
public async Task<ActionResult<PagedResult<ProductDto>>> GetProducts(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] string category = null,
[FromQuery] decimal? minPrice = null,
[FromQuery] decimal? maxPrice = null)
{
var query = new GetProductsQuery(page, pageSize, category, minPrice, maxPrice);
var result = await _mediator.Send(query);
return Ok(result);
}
}
HTTP客户端实现
// 使用HttpClientFactory实现健壮的HTTP客户端
public class ProductServiceClient : IProductServiceClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<ProductServiceClient> _logger;
private readonly JsonSerializerOptions _jsonOptions;
public ProductServiceClient(HttpClient httpClient, ILogger<ProductServiceClient> logger)
{
_httpClient = httpClient;
_logger = logger;
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
}
public async Task<ProductDto> GetProductAsync(Guid productId)
{
try
{
var response = await _httpClient.GetAsync($"/api/v1/products/{productId}");
if (response.StatusCode == HttpStatusCode.NotFound)
{
return null;
}
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<ProductDto>(content, _jsonOptions);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "HTTP error getting product {ProductId}", productId);
throw new ServiceCommunicationException($"Failed to get product {productId}", ex);
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error getting product {ProductId}", productId);
throw;
}
}
public async Task<PagedResult<ProductDto>> SearchProductsAsync(
string category = null,
decimal? minPrice = null,
int page = 1,
int pageSize = 20)
{
var queryParameters = new Dictionary<string, string>
{
["page"] = page.ToString(),
["pageSize"] = pageSize.ToString()
};
if (!string.IsNullOrEmpty(category))
queryParameters["category"] = category;
if (minPrice.HasValue)
queryParameters["minPrice"] = minPrice.Value.ToString();
var queryString = QueryHelpers.AddQueryString("/api/v1/products", queryParameters);
var response = await _httpClient.GetAsync(queryString);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<PagedResult<ProductDto>>(content, _jsonOptions);
}
public async Task<ProductDto> CreateProductAsync(CreateProductRequest request)
{
var jsonContent = new StringContent(
JsonSerializer.Serialize(request, _jsonOptions),
Encoding.UTF8,
"application/json");
var response = await _httpClient.PostAsync("/api/v1/products", jsonContent);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync();
_logger.LogError("Failed to create product. Status: {StatusCode}, Error: {Error}",
response.StatusCode, errorContent);
throw new ServiceCommunicationException($"Failed to create product: {response.StatusCode}");
}
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<ProductDto>(content, _jsonOptions);
}
}
// 服务注册
builder.Services.AddHttpClient<IProductServiceClient, ProductServiceClient>(client =>
{
client.BaseAddress = new Uri(builder.Configuration["Services:ProductService"]);
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.Timeout = TimeSpan.FromSeconds(30);
})
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryCount, context) =>
{
var logger = context.Values["logger"] as ILogger;
logger?.LogWarning("Retry {RetryCount} after {Timespan}s", retryCount, timespan.TotalSeconds);
});
}
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (exception, duration) =>
{
// 记录熔断器打开
},
onReset: () =>
{
// 记录熔断器关闭
});
}
8.1.2 gRPC:高性能的内部通信
gRPC特别适合服务间通信,它的高效性和强类型特性让开发更加可靠。
gRPC服务定义
// product.proto
syntax = "proto3";
option csharp_namespace = "ProductService.Grpc";
package product;
service ProductService {
rpc GetProduct (GetProductRequest) returns (GetProductResponse);
rpc GetProducts (GetProductsRequest) returns (GetProductsResponse);
rpc CreateProduct (CreateProductRequest) returns (CreateProductResponse);
rpc UpdateProduct (UpdateProductRequest) returns (UpdateProductResponse);
rpc DeleteProduct (DeleteProductRequest) returns (DeleteProductResponse);
// 服务器流式调用 - 实时库存更新
rpc SubscribeStockUpdates (StockSubscriptionRequest) returns (stream StockUpdate);
// 双向流式调用 - 批量产品操作
rpc BatchProductOperations (stream BatchProductRequest) returns (stream BatchProductResponse);
}
message GetProductRequest {
string product_id = 1;
}
message GetProductResponse {
Product product = 1;
}
message Product {
string id = 1;
string sku = 2;
string name = 3;
string description = 4;
Money price = 5;
string category_id = 6;
repeated ProductImage images = 7;
int32 stock_quantity = 8;
}
message Money {
decimal amount = 1;
string currency = 2;
}
message ProductImage {
string url = 1;
string alt_text = 2;
}
message GetProductsRequest {
int32 page = 1;
int32 page_size = 2;
string category_id = 3;
Money min_price = 4;
Money max_price = 5;
}
message GetProductsResponse {
repeated Product products = 1;
int32 total_count = 2;
int32 page = 3;
int32 page_size = 4;
}
message StockUpdate {
string product_id = 1;
int32 new_quantity = 2;
int32 reserved_quantity = 3;
google.protobuf.Timestamp update_time = 4;
}
message StockSubscriptionRequest {
repeated string product_ids = 1;
}
gRPC服务实现
// gRPC服务实现
public class ProductGrpcService : global::ProductService.Grpc.ProductService.ProductServiceBase
{
private readonly IProductRepository _repository;
private readonly IStockSubscriptionService _subscriptionService;
private readonly ILogger<ProductGrpcService> _logger;
public ProductGrpcService(
IProductRepository repository,
IStockSubscriptionService subscriptionService,
ILogger<ProductGrpcService> logger)
{
_repository = repository;
_subscriptionService = subscriptionService;
_logger = logger;
}
public override async Task<GetProductResponse> GetProduct(
GetProductRequest request,
ServerCallContext context)
{
try
{
var productId = Guid.Parse(request.ProductId);
var product = await _repository.GetByIdAsync(new ProductId(productId));
if (product == null)
{
throw new RpcException(new Status(StatusCode.NotFound, $"Product {request.ProductId} not found"));
}
return new GetProductResponse
{
Product = MapToGrpcProduct(product)
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting product {ProductId}", request.ProductId);
throw new RpcException(new Status(StatusCode.Internal, "Internal server error"));
}
}
public override async Task SubscribeStockUpdates(
StockSubscriptionRequest request,
IServerStreamWriter<StockUpdate> responseStream,
ServerCallContext context)
{
var productIds = request.ProductIds.Select(Guid.Parse).ToList();
var cancellationToken = context.CancellationToken;
await _subscriptionService.SubscribeToStockUpdatesAsync(
productIds,
async update =>
{
if (!cancellationToken.IsCancellationRequested)
{
await responseStream.WriteAsync(new StockUpdate
{
ProductId = update.ProductId.ToString(),
NewQuantity = update.NewQuantity,
ReservedQuantity = update.ReservedQuantity,
UpdateTime = Timestamp.FromDateTime(update.UpdateTime.ToUniversalTime())
});
}
},
cancellationToken);
}
public override async Task BatchProductOperations(
IAsyncStreamReader<BatchProductRequest> requestStream,
IServerStreamWriter<BatchProductResponse> responseStream,
ServerCallContext context)
{
await foreach (var request in requestStream.ReadAllAsync())
{
try
{
var response = await ProcessBatchOperation(request);
await responseStream.WriteAsync(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing batch operation");
await responseStream.WriteAsync(new BatchProductResponse
{
Success = false,
ErrorMessage = ex.Message
});
}
}
}
private Product MapToGrpcProduct(Domain.Product product)
{
return new Product
{
Id = product.Id.Value.ToString(),
Sku = product.SKU,
Name = product.Name,
Description = product.Description,
Price = new Money
{
Amount = decimal.ToDouble(product.Price.Amount),
Currency = product.Price.Currency
},
CategoryId = product.CategoryId.Value.ToString(),
StockQuantity = product.StockQuantity
};
}
}
gRPC客户端使用
// gRPC客户端实现
public class ProductGrpcClient : IProductServiceClient
{
private readonly ProductService.ProductServiceClient _client;
private readonly ILogger<ProductGrpcClient> _logger;
public ProductGrpcClient(ProductService.ProductServiceClient client, ILogger<ProductGrpcClient> logger)
{
_client = client;
_logger = logger;
}
public async Task<ProductDto> GetProductAsync(Guid productId)
{
try
{
var request = new GetProductRequest { ProductId = productId.ToString() };
var response = await _client.GetProductAsync(request);
return MapToDto(response.Product);
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.NotFound)
{
return null;
}
catch (RpcException ex)
{
_logger.LogError(ex, "gRPC error getting product {ProductId}", productId);
throw new ServiceCommunicationException($"Failed to get product {productId}", ex);
}
}
public async Task SubscribeToStockUpdatesAsync(
List<Guid> productIds,
Func<StockUpdateDto, Task> onUpdate,
CancellationToken cancellationToken)
{
var request = new StockSubscriptionRequest();
request.ProductIds.AddRange(productIds.Select(id => id.ToString()));
using var call = _client.SubscribeStockUpdates(request, cancellationToken: cancellationToken);
await foreach (var update in call.ResponseStream.ReadAllAsync(cancellationToken))
{
var updateDto = new StockUpdateDto
{
ProductId = Guid.Parse(update.ProductId),
NewQuantity = update.NewQuantity,
ReservedQuantity = update.ReservedQuantity,
UpdateTime = update.UpdateTime.ToDateTime()
};
await onUpdate(updateDto);
}
}
private ProductDto MapToDto(Product product)
{
return new ProductDto
{
Id = Guid.Parse(product.Id),
SKU = product.Sku,
Name = product.Name,
Description = product.Description,
Price = new Money(product.Price.Amount, product.Price.Currency),
CategoryId = Guid.Parse(product.CategoryId),
StockQuantity = product.StockQuantity
};
}
}
// gRPC客户端注册
builder.Services.AddGrpcClient<ProductService.ProductServiceClient>(o =>
{
o.Address = new Uri(builder.Configuration["Services:ProductService:Grpc"]);
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new SocketsHttpHandler
{
PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
KeepAlivePingDelay = TimeSpan.FromSeconds(60),
KeepAlivePingTimeout = TimeSpan.FromSeconds(30),
EnableMultipleHttp2Connections = true
};
});
8.1.3 同步通信的挑战与解决方案
服务发现
// 使用Consul进行服务发现
public class ConsulServiceDiscovery : IServiceDiscovery
{
private readonly ConsulClient _consulClient;
private readonly ILogger<ConsulServiceDiscovery> _logger;
public async Task<ServiceEndpoint> DiscoverServiceAsync(string serviceName)
{
var services = await _consulClient.Health.Service(serviceName, tag: null, passingOnly: true);
if (services.Response.Length == 0)
{
throw new ServiceNotFoundException($"No healthy instances found for service {serviceName}");
}
// 简单的轮询负载均衡
var serviceEntry = services.Response[_random.Next(services.Response.Length)];
return new ServiceEndpoint
{
Address = serviceEntry.Service.Address,
Port = serviceEntry.Service.Port,
Tags = serviceEntry.Service.Tags
};
}
public async Task RegisterServiceAsync(string serviceName, string serviceId, string address, int port)
{
var registration = new AgentServiceRegistration
{
ID = serviceId,
Name = serviceName,
Address = address,
Port = port,
Check = new AgentServiceCheck
{
HTTP = $"http://{address}:{port}/health",
Interval = TimeSpan.FromSeconds(10),
Timeout = TimeSpan.FromSeconds(5),
DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1)
}
};
await _consulClient.Agent.ServiceRegister(registration);
}
}
负载均衡
// 自定义负载均衡策略
public interface ILoadBalancer
{
Task<ServiceEndpoint> GetNextEndpointAsync(string serviceName);
}
public class RoundRobinLoadBalancer : ILoadBalancer
{
private readonly IServiceDiscovery _serviceDiscovery;
private readonly ConcurrentDictionary<string, int> _counters = new();
public async Task<ServiceEndpoint> GetNextEndpointAsync(string serviceName)
{
var endpoints = await _serviceDiscovery.GetServiceEndpointsAsync(serviceName);
if (endpoints.Count == 0)
throw new ServiceNotFoundException($"No endpoints found for service {serviceName}");
var counter = _counters.AddOrUpdate(serviceName, 0, (key, value) => (value + 1) % endpoints.Count);
return endpoints[counter];
}
}
public class WeightedLoadBalancer : ILoadBalancer
{
private readonly IServiceDiscovery _serviceDiscovery;
private readonly Random _random = new();
public async Task<ServiceEndpoint> GetNextEndpointAsync(string serviceName)
{
var endpoints = await _serviceDiscovery.GetServiceEndpointsAsync(serviceName);
// 根据权重选择服务端点
var totalWeight = endpoints.Sum(e => e.Weight);
var randomValue = _random.NextDouble() * totalWeight;
var currentWeight = 0.0;
foreach (var endpoint in endpoints)
{
currentWeight += endpoint.Weight;
if (randomValue <= currentWeight)
return endpoint;
}
return endpoints.Last();
}
}
容错机制
// 使用Polly实现重试和熔断
public class ResilientServiceClient
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IAsyncPolicy<HttpResponseMessage> _resilientPolicy;
public ResilientServiceClient(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
// 组合策略:重试 + 熔断 + 超时
_resilientPolicy = Policy
.WrapAsync(
Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)),
Policy
.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryCount, context) =>
{
var logger = context.Values["logger"] as ILogger;
logger?.LogWarning("Retry {RetryCount} after {Timespan}s", retryCount, timespan.TotalSeconds);
}),
Policy
.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (exception, duration) =>
{
// 熔断器打开,可以在这里发送告警
},
onReset: () =>
{
// 熔断器重置
})
);
}
public async Task<T> GetAsync<T>(string serviceName, string endpoint)
{
var httpClient = _httpClientFactory.CreateClient(serviceName);
var response = await _resilientPolicy.ExecuteAsync(async () =>
{
return await httpClient.GetAsync(endpoint);
});
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<T>(content);
}
}
8.2 异步通信:解耦的艺术
异步通信就像发邮件,你不需要等待对方立即回复。这种模式在微服务架构中特别重要,可以有效解耦服务。
8.2.1 消息队列的选择
// 使用RabbitMQ实现异步通信
public class RabbitMqEventBus : IEventBus
{
private readonly IConnection _connection;
private readonly IModel _channel;
private readonly ILogger<RabbitMqEventBus> _logger;
private readonly string _exchangeName = "events";
public RabbitMqEventBus(IConnectionFactory connectionFactory, ILogger<RabbitMqEventBus> logger)
{
_logger = logger;
_connection = connectionFactory.CreateConnection();
_channel = _connection.CreateModel();
// 声明交换器
_channel.ExchangeDeclare(
exchange: _exchangeName,
type: ExchangeType.Topic,
durable: true,
autoDelete: false);
}
public async Task PublishAsync<T>(T integrationEvent) where T : IntegrationEvent
{
var eventName = typeof(T).Name;
var message = JsonSerializer.Serialize(integrationEvent);
var body = Encoding.UTF8.GetBytes(message);
var properties = _channel.CreateBasicProperties();
properties.Persistent = true; // 消息持久化
properties.MessageId = integrationEvent.Id.ToString();
properties.Timestamp = new AmqpTimestamp(DateTimeOffset.UtcNow.ToUnixTimeSeconds());
_channel.BasicPublish(
exchange: _exchangeName,
routingKey: eventName,
mandatory: true,
basicProperties: properties,
body: body);
_logger.LogInformation("Published event {EventName}: {EventId}", eventName, integrationEvent.Id);
}
public void Subscribe<T, THandler>()
where T : IntegrationEvent
where THandler : IEventHandler<T>
{
var eventName = typeof(T).Name;
var queueName = $"{typeof(THandler).Name}.{eventName}";
// 声明队列
_channel.QueueDeclare(
queue: queueName,
durable: true,
exclusive: false,
autoDelete: false);
// 绑定队列到交换器
_channel.QueueBind(
queue: queueName,
exchange: _exchangeName,
routingKey: eventName);
var consumer = new AsyncEventingBasicConsumer(_channel);
consumer.Received += async (model, ea) =>
{
try
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
var @event = JsonSerializer.Deserialize<T>(message);
var handler = ActivatorUtilities.CreateInstance<THandler>(serviceProvider);
await handler.HandleAsync(@event);
_channel.BasicAck(ea.DeliveryTag, multiple: false);
_logger.LogInformation("Processed event {EventName}: {EventId}", eventName, @event.Id);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing event {EventName}", eventName);
_channel.BasicNack(ea.DeliveryTag, multiple: false, requeue: false);
}
};
_channel.BasicConsume(
queue: queueName,
autoAck: false,
consumer: consumer);
}
}
8.2.2 事件驱动架构
// 定义领域事件
public abstract record IntegrationEvent
{
public Guid Id { get; }
public DateTime OccurredOn { get; }
protected IntegrationEvent()
{
Id = Guid.NewGuid();
OccurredOn = DateTime.UtcNow;
}
}
public record OrderCreatedEvent : IntegrationEvent
{
public Guid OrderId { get; }
public Guid CustomerId { get; }
public List<OrderItemDto> Items { get; }
public decimal TotalAmount { get; }
public DateTime CreatedAt { get; }
public OrderCreatedEvent(Guid orderId, Guid customerId, List<OrderItemDto> items, decimal totalAmount)
{
OrderId = orderId;
CustomerId = customerId;
Items = items;
TotalAmount = totalAmount;
CreatedAt = DateTime.UtcNow;
}
}
public record StockReservedEvent : IntegrationEvent
{
public Guid OrderId { get; }
public Dictionary<Guid, int> ReservedItems { get; }
public DateTime ReservedAt { get; }
public StockReservedEvent(Guid orderId, Dictionary<Guid, int> reservedItems)
{
OrderId = orderId;
ReservedItems = reservedItems;
ReservedAt = DateTime.UtcNow;
}
}
// 事件处理器
public class OrderCreatedEventHandler : IEventHandler<OrderCreatedEvent>
{
private readonly IInventoryService _inventoryService;
private readonly IEventBus _eventBus;
private readonly ILogger<OrderCreatedEventHandler> _logger;
public async Task HandleAsync(OrderCreatedEvent @event)
{
_logger.LogInformation("Processing OrderCreatedEvent: {OrderId}", @event.OrderId);
try
{
var reservedItems = new Dictionary<Guid, int>();
// 为每个订单项预留库存
foreach (var item in @event.Items)
{
var reserved = await _inventoryService.ReserveStockAsync(
item.ProductId,
item.Quantity);
if (!reserved)
{
// 库存不足,发布库存不足事件
await _eventBus.PublishAsync(new StockInsufficientEvent(
@event.OrderId,
item.ProductId,
item.Quantity
));
_logger.LogWarning("Insufficient stock for product {ProductId}", item.ProductId);
return;
}
reservedItems[item.ProductId] = item.Quantity;
}
// 库存预留成功,发布事件
await _eventBus.PublishAsync(new StockReservedEvent(
@event.OrderId,
reservedItems
));
_logger.LogInformation("Stock reserved for order: {OrderId}", @event.OrderId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing OrderCreatedEvent: {OrderId}", @event.OrderId);
throw;
}
}
}
// 事件总线抽象
public interface IEventBus
{
Task PublishAsync<T>(T integrationEvent) where T : IntegrationEvent;
void Subscribe<T, THandler>() where T : IntegrationEvent where THandler : IEventHandler<T>;
}
public interface IEventHandler<in T> where T : IntegrationEvent
{
Task HandleAsync(T @event);
}
8.2.3 Saga模式:管理分布式事务
// Saga实现 - 订单处理流程
public class OrderProcessingSaga : Saga<OrderProcessingSagaData>,
IAmStartedByMessages<OrderCreatedEvent>,
IHandleMessages<StockReservedEvent>,
IHandleMessages<PaymentCompletedEvent>,
IHandleMessages<StockInsufficientEvent>,
IHandleMessages<PaymentFailedEvent>
{
private readonly IOrderRepository _orderRepository;
private readonly IEventBus _eventBus;
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<OrderProcessingSagaData> mapper)
{
mapper.ConfigureMapping<OrderCreatedEvent>(m => m.OrderId).ToSaga(s => s.OrderId);
mapper.ConfigureMapping<StockReservedEvent>(m => m.OrderId).ToSaga(s => s.OrderId);
mapper.ConfigureMapping<PaymentCompletedEvent>(m => m.OrderId).ToSaga(s => s.OrderId);
mapper.ConfigureMapping<StockInsufficientEvent>(m => m.OrderId).ToSaga(s => s.OrderId);
mapper.ConfigureMapping<PaymentFailedEvent>(m => m.OrderId).ToSaga(s => s.OrderId);
}
public async Task Handle(OrderCreatedEvent message, IMessageHandlerContext context)
{
Data.OrderId = message.OrderId;
Data.CustomerId = message.CustomerId;
Data.TotalAmount = message.TotalAmount;
Data.Status = OrderStatus.Pending;
// Saga开始,等待库存预留结果
_logger.LogInformation("OrderProcessingSaga started for order: {OrderId}", message.OrderId);
}
public async Task Handle(StockReservedEvent message, IMessageHandlerContext context)
{
Data.StockReserved = true;
Data.ReservedItems = message.ReservedItems;
// 库存预留成功,开始支付流程
await _eventBus.PublishAsync(new ProcessPaymentCommand(
Data.OrderId,
Data.CustomerId,
Data.TotalAmount
));
_logger.LogInformation("Stock reserved for order: {OrderId}, proceeding to payment", message.OrderId);
}
public async Task Handle(PaymentCompletedEvent message, IMessageHandlerContext context)
{
Data.PaymentCompleted = true;
Data.PaymentTransactionId = message.TransactionId;
// 支付成功,确认订单
var order = await _orderRepository.GetByIdAsync(new OrderId(Data.OrderId));
order.Confirm();
await _orderRepository.UpdateAsync(order);
// 发布订单确认事件
await _eventBus.PublishAsync(new OrderConfirmedEvent(
Data.OrderId,
Data.PaymentTransactionId
));
MarkAsComplete(); // Saga完成
_logger.LogInformation("OrderProcessingSaga completed for order: {OrderId}", message.OrderId);
}
public async Task Handle(StockInsufficientEvent message, IMessageHandlerContext context)
{
// 库存不足,取消订单
var order = await _orderRepository.GetByIdAsync(new OrderId(Data.OrderId));
order.Cancel("Insufficient stock");
await _orderRepository.UpdateAsync(order);
await _eventBus.PublishAsync(new OrderCancelledEvent(
Data.OrderId,
"Insufficient stock"
));
MarkAsComplete();
_logger.LogWarning("OrderProcessingSaga cancelled for order {OrderId} due to insufficient stock", message.OrderId);
}
public async Task Handle(PaymentFailedEvent message, IMessageHandlerContext context)
{
// 支付失败,释放库存并取消订单
await _eventBus.PublishAsync(new ReleaseStockCommand(
Data.OrderId,
Data.ReservedItems
));
var order = await _orderRepository.GetByIdAsync(new OrderId(Data.OrderId));
order.Cancel("Payment failed");
await _orderRepository.UpdateAsync(order);
await _eventBus.PublishAsync(new OrderCancelledEvent(
Data.OrderId,
"Payment failed"
));
MarkAsComplete();
_logger.Warning("OrderProcessingSaga cancelled for order {OrderId} due to payment failure", message.OrderId);
}
}
public class OrderProcessingSagaData : ContainSagaData
{
public Guid OrderId { get; set; }
public Guid CustomerId { get; set; }
public decimal TotalAmount { get; set; }
public bool StockReserved { get; set; }
public bool PaymentCompleted { get; set; }
public Dictionary<Guid, int> ReservedItems { get; set; }
public string PaymentTransactionId { get; set; }
public OrderStatus Status { get; set; }
}
8.3 API网关:统一的入口
API网关是微服务架构中的重要组件,它提供了统一的入口点,处理横切关注点。
8.3.1 Ocelot网关配置
// ocelot.json
{
"Routes": [
{
"DownstreamPathTemplate": "/api/v1/products/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "product-service",
"Port": 80
}
],
"UpstreamPathTemplate": "/products/{everything}",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [ "product_api" ]
},
"RateLimitOptions": {
"ClientWhitelist": [ "admin-client-id" ],
"EnableRateLimiting": true,
"Period": "1s",
"PeriodTimespan": 1,
"Limit": 10
},
"FileCacheOptions": {
"TtlSeconds": 300,
"Region": "products"
}
},
{
"DownstreamPathTemplate": "/api/v1/orders/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "order-service",
"Port": 80
}
],
"UpstreamPathTemplate": "/orders/{everything}",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [ "order_api" ]
},
"AddHeadersToRequest": {
"X-User-Id": "Claims[sub] > value",
"X-User-Email": "Claims[email] > value"
}
}
],
"GlobalConfiguration": {
"BaseUrl": "https://api.yourdomain.com",
"RequestIdKey": "X-Request-ID",
"AdministrationPath": "/admin"
}
}
8.3.2 自定义中间件
// 自定义网关中间件
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
// 记录请求
_logger.LogInformation("Incoming request: {Method} {Path} from {IP}",
context.Request.Method,
context.Request.Path,
context.Connection.RemoteIpAddress);
await _next(context);
stopwatch.Stop();
// 记录响应
_logger.LogInformation("Request completed: {Method} {Path} - {StatusCode} in {ElapsedMs}ms",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
stopwatch.ElapsedMilliseconds);
}
}
public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;
private readonly ITokenValidationService _tokenValidationService;
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Headers.TryGetValue("Authorization", out var authHeader))
{
if (authHeader.ToString().StartsWith("Bearer "))
{
var token = authHeader.ToString().Substring(7);
var validationResult = await _tokenValidationService.ValidateTokenAsync(token);
if (validationResult.IsValid)
{
var claimsIdentity = new ClaimsIdentity(validationResult.Claims, "Bearer");
context.User = new ClaimsPrincipal(claimsIdentity);
}
else
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
}
}
await _next(context);
}
}
8.4 小结
微服务通信是分布式系统的核心挑战。记住几个关键原则:
- 选择合适的通信方式:内部通信用gRPC,外部API用REST,异步用消息队列
- 设计容错机制:重试、熔断、超时、降级一个都不能少
- 避免同步调用链:尽量使用异步通信解耦服务
- 监控和追踪:分布式追踪是定位问题的利器
- 版本兼容性:接口演进要考虑向后兼容
最重要的是,通信方式的选择要基于业务需求。不要为了技术而技术,选择最适合当前场景的解决方案。
在下一章中,我们将探讨数据一致性和分布式事务,这是微服务架构中的另一个核心挑战。

浙公网安备 33010602011771号