第十六章 电商系统微服务实战(完结,总结以及之前所有内容会发到github)

news/2026/1/22 21:53:07/文章来源:https://www.cnblogs.com/qxnm/p/19519102

第十六章 电商系统微服务实战

理论学得再多,不如亲手做一个项目。这一章,我将带你从零开始构建一个真实的电商微服务系统。这不是玩具项目,而是考虑了生产环境各种复杂情况的真实案例。

16.1 需求分析与架构设计

16.1.1 真实电商系统的复杂性

很多人以为电商系统就是"商品+购物车+订单",但真实的企业级电商系统要复杂得多:

核心业务流程

graph TD用户[用户注册/登录] --> 浏览[浏览商品]浏览 --> 搜索[搜索/筛选]搜索 --> 详情[商品详情]详情 --> 加购物车[加入购物车]加购物车 --> 结算[结算页面]结算 --> 创建订单[创建订单]创建订单 --> 支付[支付处理]支付 --> 库存[库存扣减]支付 --> 物流[物流发货]物流 --> 完成[订单完成]创建订单 --> 优惠券[优惠券校验]创建订单 --> 会员[会员折扣]支付 --> 积分[积分抵扣]支付 --> 营销[营销活动]

非功能性需求

  • 高并发:双11期间每秒数万订单
  • 高可用:99.99%可用性,任何环节都不能单点故障
  • 数据一致性:订单、库存、支付必须一致
  • 性能:页面加载<2秒,接口响应<500ms
  • 安全:防刷单、防SQL注入、防XSS攻击

16.1.2 领域识别与服务拆分

基于DDD的战略设计,我们识别出以下核心领域:

// 用户限界上下文
namespace UserContext.Domain
{public class User : AggregateRoot<UserId>{public string Email { get; private set; }public string PasswordHash { get; private set; }public UserProfile Profile { get; private set; }public List<Address> Addresses { get; private set; }public UserLevel Level { get; private set; }public int Points { get; private set; }// 业务行为public void Register(string email, string password) { /* ... */ }public void AddAddress(Address address) { /* ... */ }public void UpgradeLevel() { /* ... */ }public void AddPoints(int points) { /* ... */ }}public class Address : Entity{public string Province { get; private set; }public string City { get; private set; }public string District { get; private set; }public string Detail { get; private set; }public string Phone { get; private set; }public bool IsDefault { get; private set; }public void SetAsDefault() { /* ... */ }}
}// 商品限界上下文
namespace ProductContext.Domain
{public class Product : AggregateRoot<ProductId>{public string Name { get; private set; }public string Description { get; private set; }public Money Price { get; private set; }public CategoryId CategoryId { get; private set; }public List<ProductImage> Images { get; private set; }public ProductStatus Status { get; private set; }public ProductAttributes Attributes { get; private set; }// 复杂业务逻辑public void UpdatePrice(Money newPrice, string reason) { /* ... */ }public void AddImage(string url, bool isMain = false) { /* ... */ }public void Publish() { /* ... */ }public void AddSku(Sku sku) { /* ... */ }}public class Sku : Entity{public string Code { get; private set; }public Money Price { get; private set; }public Dictionary<string, string> Specifications { get; private set; }public int StockQuantity { get; private set; }public void ReserveStock(int quantity) { /* ... */ }public void ReleaseStock(int quantity) { /* ... */ }}
}// 订单限界上下文
namespace OrderContext.Domain
{public class Order : AggregateRoot<OrderId>{public CustomerId CustomerId { get; private set; }public List<OrderItem> Items { get; private set; }public Money TotalPrice { get; private set; }public Money DiscountAmount { get; private set; }public OrderStatus Status { get; private set; }public ShippingAddress ShippingAddress { get; private set; }public PaymentInfo PaymentInfo { get; private set; }// 复杂订单逻辑public void ApplyCoupon(Coupon coupon) { /* ... */ }public void UsePoints(int points) { /* ... */ }public void Confirm() { /* ... */ }public void Ship(string trackingNumber) { /* ... */ }public void Cancel(string reason) { /* ... */ }}public class OrderItem : Entity{public ProductId ProductId { get; private set; }public SkuId SkuId { get; private set; }public string ProductName { get; private set; }public Money UnitPrice { get; private set; }public int Quantity { get; private set; }public Money SubTotal => UnitPrice * Quantity;public void ChangeQuantity(int newQuantity) { /* ... */ }}
}

16.1.3 服务架构设计

graph TBsubgraph "前端层"WEB[Web商城]APP[移动App]ADMIN[管理后台]endsubgraph "API网关层"GATEWAY[API网关/Ocelot]AUTH[认证中心/IdentityServer4]endsubgraph "业务服务层"USER[用户服务UserService]PRODUCT[商品服务ProductService]CART[购物车服务CartService]ORDER[订单服务OrderService]PAYMENT[支付服务PaymentService]INVENTORY[库存服务InventoryService]SHIPPING[物流服务ShippingService]MARKETING[营销服务MarketingService]SEARCH[搜索服务SearchService]endsubgraph "基础设施层"MQ[RabbitMQ]CACHE[Redis集群]SEARCH_ENGINE[Elasticsearch]FILE[MinIO文件存储]endsubgraph "数据存储层"USER_DB[(用户数据库)]PRODUCT_DB[(商品数据库)]ORDER_DB[(订单数据库)]INVENTORY_DB[(库存数据库)]PAYMENT_DB[(支付数据库)]endWEB --> GATEWAYAPP --> GATEWAYADMIN --> GATEWAYGATEWAY --> AUTHGATEWAY --> USERGATEWAY --> PRODUCTGATEWAY --> CARTGATEWAY --> ORDERGATEWAY --> SEARCHORDER --> USERORDER --> PRODUCTORDER --> INVENTORYORDER --> PAYMENTORDER --> MARKETINGORDER --> SHIPPINGCART --> PRODUCTCART --> USERPAYMENT --> MQINVENTORY --> MQSHIPPING --> MQUSER --> USER_DBPRODUCT --> PRODUCT_DBORDER --> ORDER_DBINVENTORY --> INVENTORY_DBPAYMENT --> PAYMENT_DBPRODUCT --> CACHEORDER --> CACHESEARCH --> SEARCH_ENGINEPRODUCT --> FILE

16.2 核心业务实现

16.2.1 用户服务实现

// 用户服务 - 领域层
namespace ECommerce.UserService.Domain
{public class User : AggregateRoot<UserId>{private readonly List<Address> _addresses = new();public string Email { get; private set; }public string PasswordHash { get; private set; }public UserProfile Profile { get; private set; }public IReadOnlyCollection<Address> Addresses => _addresses.AsReadOnly();public UserLevel Level { get; private set; }public int Points { get; private set; }public DateTime CreatedAt { get; private set; }public DateTime? LastLoginAt { get; private set; }private User() { }public static User Create(string email, string passwordHash, string name){if (string.IsNullOrWhiteSpace(email))throw new BusinessRuleException("邮箱不能为空");if (!IsValidEmail(email))throw new BusinessRuleException("邮箱格式不正确");var user = new User{Id = new UserId(Guid.NewGuid()),Email = email.ToLower(),PasswordHash = passwordHash,Profile = new UserProfile(name),Level = UserLevel.Bronze,Points = 0,CreatedAt = DateTime.UtcNow};user.AddDomainEvent(new UserRegisteredEvent(user.Id, user.Email, name));return user;}public void UpdateProfile(string name, string phone, string avatar){Profile = Profile.Update(name, phone, avatar);AddDomainEvent(new UserProfileUpdatedEvent(Id, name, phone, avatar));}public void AddAddress(string province, string city, string district, string detail, string phone, string contactName, bool isDefault = false){var address = new Address(province, city, district, detail, phone, contactName);if (isDefault){foreach (var addr in _addresses){addr.SetAsDefault(false);}}_addresses.Add(address);AddDomainEvent(new AddressAddedEvent(Id, address.Id, contactName));}public void AddPoints(int points, string reason){if (points <= 0)throw new BusinessRuleException("积分必须为正数");Points += points;// 检查等级升级var newLevel = CalculateLevel(Points);if (newLevel > Level){Level = newLevel;AddDomainEvent(new UserLevelUpgradedEvent(Id, Level, Points));}AddDomainEvent(new PointsAddedEvent(Id, points, reason));}private static UserLevel CalculateLevel(int points){return points switch{< 1000 => UserLevel.Bronze,< 5000 => UserLevel.Silver,< 20000 => UserLevel.Gold,_ => UserLevel.Diamond};}private static bool IsValidEmail(string email){return Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase);}}public class Address : Entity{public string Province { get; private set; }public string City { get; private set; }public string District { get; private set; }public string Detail { get; private set; }public string Phone { get; private set; }public string ContactName { get; private set; }public bool IsDefault { get; private set; }internal Address(string province, string city, string district, string detail, string phone, string contactName){Province = province;City = city;District = district;Detail = detail;Phone = phone;ContactName = contactName;IsDefault = false;}internal void SetAsDefault(bool isDefault){IsDefault = isDefault;}}
}// 用户服务 - 应用层
namespace ECommerce.UserService.Application
{public class UserApplicationService{private readonly IUserRepository _userRepository;private readonly IUnitOfWork _unitOfWork;private readonly IPasswordHasher<User> _passwordHasher;private readonly IEventBus _eventBus;private readonly ILogger<UserApplicationService> _logger;public async Task<RegisterUserResult> RegisterUserAsync(RegisterUserCommand command){_logger.LogInformation("用户注册开始: {Email}", command.Email);// 检查邮箱是否已存在var existingUser = await _userRepository.GetByEmailAsync(command.Email);if (existingUser != null){return RegisterUserResult.Fail("邮箱已被注册");}// 创建用户var passwordHash = _passwordHasher.HashPassword(null, command.Password);var user = User.Create(command.Email, passwordHash, command.Name);await _userRepository.AddAsync(user);await _unitOfWork.CommitAsync();_logger.LogInformation("用户注册成功: {UserId}", user.Id.Value);return RegisterUserResult.Success(user.Id);}public async Task<LoginResult> LoginAsync(LoginCommand command){var user = await _userRepository.GetByEmailAsync(command.Email);if (user == null){return LoginResult.Fail("邮箱或密码错误");}var result = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, command.Password);if (result == PasswordVerificationResult.Failed){return LoginResult.Fail("邮箱或密码错误");}// 生成JWT令牌var token = GenerateJwtToken(user);// 更新最后登录时间user.LastLoginAt = DateTime.UtcNow;await _userRepository.UpdateAsync(user);await _unitOfWork.CommitAsync();_logger.LogInformation("用户登录成功: {UserId}", user.Id.Value);return LoginResult.Success(token, user);}private string GenerateJwtToken(User user){var claims = new List<Claim>{new Claim(ClaimTypes.NameIdentifier, user.Id.Value.ToString()),new Claim(ClaimTypes.Email, user.Email),new Claim(ClaimTypes.Name, user.Profile.Name),new Claim("level", user.Level.ToString()),new Claim("points", user.Points.ToString())};var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"));var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);var token = new JwtSecurityToken(issuer: "ecommerce",audience: "ecommerce-users",claims: claims,expires: DateTime.UtcNow.AddHours(24),signingCredentials: creds);return new JwtSecurityTokenHandler().WriteToken(token);}}
}

16.2.2 商品服务实现

// 商品服务 - 领域层
namespace ECommerce.ProductService.Domain
{public class Product : AggregateRoot<ProductId>{private readonly List<ProductImage> _images = new();private readonly List<Sku> _skus = new();public string Name { get; private set; }public string Description { get; private set; }public Money Price { get; private set; }public CategoryId CategoryId { get; private set; }public BrandId BrandId { ge服务架构设计t; private set; }public IReadOnlyCollection<ProductImage> Images => _images.AsReadOnly();public IReadOnlyCollection<Sku> Skus => _skus.AsReadOnly();public ProductStatus Status { get; private set; }public ProductAttributes Attributes { get; private set; }public float Rating { get; private set; }public int ReviewCount { get; private set; }private Product() { }public static Product Create(string name, string description, Money price, CategoryId categoryId, BrandId brandId){if (string.IsNullOrWhiteSpace(name))throw new BusinessRuleException("商品名称不能为空");if (price.Amount <= 0)throw new BusinessRuleException("商品价格必须大于0");var product = new Product{Id = new ProductId(Guid.NewGuid()),Name = name.Trim(),Description = description?.Trim(),Price = price,CategoryId = categoryId,BrandId = brandId,Status = ProductStatus.Draft,Attributes = new ProductAttributes(),Rating = 0,ReviewCount = 0};product.AddDomainEvent(new ProductCreatedEvent(product.Id, name, price));return product;}public void UpdatePrice(Money newPrice, string reason, UserId operatorId){if (newPrice.Amount <= 0)throw new BusinessRuleException("商品价格必须大于0");var oldPrice = Price;Price = newPrice;AddDomainEvent(new ProductPriceChangedEvent(Id, oldPrice, newPrice, reason, operatorId));}public void AddImage(string url, string altText = "", bool isMain = false){if (string.IsNullOrWhiteSpace(url))throw new BusinessRuleException("图片URL不能为空");if (isMain){foreach (var img in _images){img.SetAsMain(false);}}var image = new ProductImage(url, altText, isMain);_images.Add(image);AddDomainEvent(new ProductImageAddedEvent(Id, image.Id, url));}public void AddSku(string code, Money price, Dictionary<string, string> specifications, int stockQuantity){if (_skus.Any(s => s.Code == code))throw new BusinessRuleException($"SKU编码 {code} 已存在");var sku = new Sku(code, price, specifications, stockQuantity);_skus.Add(sku);AddDomainEvent(new SkuAddedEvent(Id, sku.Id, code, price));}public void Publish(UserId operatorId){if (Status != ProductStatus.Draft)throw new BusinessRuleException("只有草稿状态的商品才能发布");if (!_images.Any())throw new BusinessRuleException("商品必须至少有一张图片");if (!_skus.Any())throw new BusinessRuleException("商品必须至少有一个SKU");Status = ProductStatus.Published;AddDomainEvent(new ProductPublishedEvent(Id, operatorId));}public void UpdateReviewStats(float newRating){var totalRating = Rating * ReviewCount + newRating;ReviewCount++;Rating = totalRating / ReviewCount;AddDomainEvent(new ProductReviewStatsUpdatedEvent(Id, Rating, ReviewCount));}}public class Sku : Entity{public string Code { get; private set; }public Money Price { get; private set; }public Dictionary<string, string> Specifications { get; private set; }public int StockQuantity { get; private set; }public int ReservedQuantity { get; private set; }public int AvailableQuantity => StockQuantity - ReservedQuantity;internal Sku(string code, Money price, Dictionary<string, string> specifications, int stockQuantity){Code = code;Price = price;Specifications = specifications ?? new Dictionary<string, string>();StockQuantity = stockQuantity;ReservedQuantity = 0;}public void ReserveStock(int quantity){if (quantity <= 0)throw new BusinessRuleException("预留数量必须为正数");if (AvailableQuantity < quantity)throw new BusinessRuleException($"库存不足,可用库存: {AvailableQuantity}");ReservedQuantity += quantity;}public void ReleaseStock(int quantity){if (quantity <= 0)throw new BusinessRuleException("释放数量必须为正数");if (ReservedQuantity < quantity)throw new BusinessRuleException($"预留库存不足,已预留: {ReservedQuantity}");ReservedQuantity -= quantity;}public void ConfirmStockDeduction(int quantity){if (quantity <= 0)throw new BusinessRuleException("确认数量必须为正数");if (ReservedQuantity < quantity)throw new BusinessRuleException($"预留库存不足,已预留: {ReservedQuantity}");ReservedQuantity -= quantity;StockQuantity -= quantity;}public void AddStock(int quantity){if (quantity <= 0)throw new BusinessRuleException("增加数量必须为正数");StockQuantity += quantity;}}
}

16.2.3 订单服务实现

// 订单服务 - Saga编排器
namespace ECommerce.OrderService.Application
{public class OrderSagaOrchestrator{private readonly IOrderRepository _orderRepository;private readonly IProductServiceClient _productServiceClient;private readonly IInventoryServiceClient _inventoryServiceClient;private readonly IPaymentServiceClient _paymentServiceClient;private readonly IMarketingServiceClient _marketingServiceClient;private readonly IEventBus _eventBus;private readonly ILogger<OrderSagaOrchestrator> _logger;public async Task<CreateOrderResult> CreateOrderAsync(CreateOrderCommand command){var sagaId = Guid.NewGuid();_logger.LogInformation("开始订单创建Saga: {SagaId}", sagaId);try{// 步骤1:验证和准备工作var preparationResult = await PrepareOrderAsync(command);if (!preparationResult.Success){return CreateOrderResult.Fail(preparationResult.Error);}// 步骤2:创建订单(本地事务)var order = Order.Create(command.CustomerId,preparationResult.Items,preparationResult.TotalPrice,preparationResult.ShippingAddress);// 应用优惠券if (command.CouponId.HasValue){order.ApplyCoupon(preparationResult.Coupon, preparationResult.DiscountAmount);}// 使用积分if (command.UsePoints > 0){order.UsePoints(command.UsePoints, preparationResult.PointsDiscount);}await _orderRepository.AddAsync(order);await _orderRepository.UnitOfWork.CommitAsync();// 步骤3:预留库存(跨服务调用)var stockReservationResult = await ReserveInventoryAsync(order);if (!stockReservationResult.Success){await CancelOrderAsync(order, stockReservationResult.Error);return CreateOrderResult.Fail(stockReservationResult.Error);}// 步骤4:发布订单创建事件await _eventBus.PublishAsync(new OrderCreatedEvent(order.Id,order.CustomerId,order.Items.Select(i => new OrderItemDto(i.ProductId, i.SkuId, i.Quantity, i.UnitPrice)).ToList(),order.TotalPrice,order.FinalPrice,DateTime.UtcNow));_logger.LogInformation("订单创建Saga成功: {OrderId}", order.Id.Value);return CreateOrderResult.Success(order.Id, order.FinalPrice);}catch (Exception ex){_logger.LogError(ex, "订单创建Saga失败: {SagaId}", sagaId);return CreateOrderResult.Fail("系统错误,请稍后重试");}}private async Task<PreparationResult> PrepareOrderAsync(CreateOrderCommand command){// 验证商品信息var productChecks = command.Items.Select(async item =>{var product = await _productServiceClient.GetProductAsync(item.ProductId);if (product == null)return (false, $"商品 {item.ProductId} 不存在");if (product.Status != ProductStatus.Published)return (false, $"商品 {product.Name} 已下架");return (true, string.Empty);});var results = await Task.WhenAll(productChecks);var failed = results.FirstOrDefault(r => !r.Item1);if (failed != default){return PreparationResult.Fail(failed.Item2);}// 计算价格var items = new List<OrderItem>();var totalPrice = Money.Zero;foreach (var item in command.Items){var product = await _productServiceClient.GetProductAsync(item.ProductId);var sku = product.Skus.FirstOrDefault(s => s.Id == item.SkuId);if (sku == null)return PreparationResult.Fail($"SKU不存在");var orderItem = new OrderItem(item.ProductId,item.SkuId,product.Name,sku.Price,item.Quantity);items.Add(orderItem);totalPrice += orderItem.SubTotal;}// 验证优惠券Money discountAmount = Money.Zero;Coupon coupon = null;if (command.CouponId.HasValue){coupon = await _marketingServiceClient.GetCouponAsync(command.CouponId.Value);if (coupon == null || !coupon.IsValidFor(totalPrice)){return PreparationResult.Fail("优惠券无效");}discountAmount = coupon.CalculateDiscount(totalPrice);}// 验证积分Money pointsDiscount = Money.Zero;if (command.UsePoints > 0){pointsDiscount = Money.Create(command.UsePoints / 100m); // 100积分=1元if (pointsDiscount > totalPrice * 0.3m) // 最多抵扣30%{return PreparationResult.Fail("积分抵扣超出限制");}}return PreparationResult.Success(items, totalPrice, discountAmount, pointsDiscount, coupon, command.ShippingAddress);}private async Task<ReservationResult> ReserveInventoryAsync(Order order){var reservationRequests = order.Items.Select(item =>new StockReservationRequest(item.ProductId, item.SkuId, item.Quantity)).ToList();var reservationResult = await _inventoryServiceClient.ReserveStockAsync(order.Id, reservationRequests);if (!reservationResult.Success){return ReservationResult.Fail(reservationResult.Error);}return ReservationResult.Success();}private async Task CancelOrderAsync(Order order, string reason){order.Cancel(reason);await _orderRepository.UpdateAsync(order);await _orderRepository.UnitOfWork.CommitAsync();// 发布订单取消事件await _eventBus.PublishAsync(new OrderCancelledEvent(order.Id,reason,DateTime.UtcNow));}}
}

16.3 技术选型与实现

16.3.1 项目结构

src/
├── Services/
│   ├── UserService/
│   │   ├── src/
│   │   │   ├── UserService.Api/          # API层
│   │   │   ├── UserService.Application/  # 应用层
│   │   │   ├── UserService.Domain/       # 领域层
│   │   │   ├── UserService.Infrastructure/# 基础设施层
│   │   │   └── UserService.Shared/       # 共享组件
│   │   ├── tests/
│   │   │   ├── UserService.UnitTests/
│   │   │   └── UserService.IntegrationTests/
│   │   ├── Dockerfile
│   │   └── UserService.csproj
│   ├── ProductService/
│   ├── OrderService/
│   ├── PaymentService/
│   └── InventoryService/
├── ApiGateways/
│   └── MainGateway/
├── Shared/
│   ├── SharedKernel/           # 共享内核
│   ├── Messaging/              # 消息传递
│   └── Monitoring/             # 监控组件
tests/
├── IntegrationTests/
└── EndToEndTests/
docker-compose.yml
docker-compose.override.yml
k8s/
├── namespaces/
├── deployments/
├── services/
└── ingresses/

16.3.2 关键配置

// Program.cs - 用户服务配置
var builder = WebApplication.CreateBuilder(args);// 服务配置
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();// 数据库配置
builder.Services.AddDbContext<UserDbContext>(options =>options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));// 身份认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>{options.TokenValidationParameters = new TokenValidationParameters{ValidateIssuer = true,ValidateAudience = true,ValidateLifetime = true,ValidateIssuerSigningKey = true,ValidIssuer = builder.Configuration["Jwt:Issuer"],ValidAudience = builder.Configuration["Jwt:Audience"],IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"]))};});// 依赖注入
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
builder.Services.AddScoped<UserApplicationService>();
builder.Services.AddSingleton<IPasswordHasher<User>, PasswordHasher<User>>();// 消息队列
builder.Services.AddMassTransit(x =>
{x.UsingRabbitMq((context, cfg) =>{cfg.Host(builder.Configuration["RabbitMQ:Host"], h =>{h.Username(builder.Configuration["RabbitMQ:Username"]);h.Password(builder.Configuration["RabbitMQ:Password"]);});cfg.ConfigureEndpoints(context);});
});// 监控
builder.Services.AddOpenTelemetry().WithTracing(traceBuilder =>{traceBuilder.AddAspNetCoreInstrumentation().AddHttpClientInstrumentation().AddEntityFrameworkCoreInstrumentation().AddSource("UserService").AddJaegerExporter();});builder.Services.AddHealthChecks().AddDbContextCheck<UserDbContext>().AddRedis(builder.Configuration["Redis:ConnectionString"]);// 缓存
builder.Services.AddStackExchangeRedisCache(options =>
{options.Configuration = builder.Configuration["Redis:ConnectionString"];
});var app = builder.Build();// 管道配置
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();app.MapControllers();
app.MapHealthChecks("/health");app.Run();

16.3.3 Docker配置

# Dockerfile - 用户服务
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS base
WORKDIR /app
EXPOSE 80FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
COPY ["Services/UserService/UserService.csproj", "Services/UserService/"]
COPY ["Shared/SharedKernel/SharedKernel.csproj", "Shared/SharedKernel/"]
COPY ["Shared/Messaging/Messaging.csproj", "Shared/Messaging/"]
RUN dotnet restore "Services/UserService/UserService.csproj"COPY . .
WORKDIR "/src/Services/UserService"
RUN dotnet build "UserService.csproj" -c Release -o /app/buildFROM build AS publish
RUN dotnet publish "UserService.csproj" -c Release -o /app/publishFROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .# 创建非root用户
RUN addgroup -g 1000 -S appuser && \adduser -u 1000 -S appuser -G appuser
USER appuserENTRYPOINT ["dotnet", "UserService.dll"]

16.4 部署与运维

16.4.1 Kubernetes部署

# 用户服务部署配置
apiVersion: apps/v1
kind: Deployment
metadata:name: user-servicenamespace: ecommerce
spec:replicas: 3selector:matchLabels:app: user-servicetemplate:metadata:labels:app: user-serviceversion: v1spec:containers:- name: user-serviceimage: ecommerce/user-service:1.0.0ports:- containerPort: 80env:- name: ASPNETCORE_ENVIRONMENTvalue: "Production"- name: ConnectionStrings__DefaultvalueFrom:secretKeyRef:name: user-service-secretskey: database-connectionresources:requests:memory: "256Mi"cpu: "250m"limits:memory: "512Mi"cpu: "500m"livenessProbe:httpGet:path: /healthport: 80initialDelaySeconds: 30periodSeconds: 10readinessProbe:httpGet:path: /health/readyport: 80initialDelaySeconds: 5periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:name: user-servicenamespace: ecommerce
spec:selector:app: user-serviceports:- port: 80targetPort: 80type: ClusterIP

16.4.2 监控配置

# Prometheus监控配置
apiVersion: v1
kind: ServiceMonitor
metadata:name: ecommerce-servicesnamespace: ecommerce
spec:selector:matchLabels:monitoring: enabledendpoints:- port: metricsinterval: 30spath: /metrics
---
# Grafana仪表盘配置
apiVersion: v1
kind: ConfigMap
metadata:name: ecommerce-dashboardnamespace: monitoring
data:dashboard.json: |{"dashboard": {"title": "电商系统监控","panels": [{"title": "订单量","type": "graph","targets": [{"expr": "rate(orders_created_total[5m])","legendFormat": "订单/秒"}]},{"title": "响应时间","type": "graph","targets": [{"expr": "histogram_quantile(0.95, http_request_duration_seconds_bucket)","legendFormat": "95分位"}]}]}}

16.5 小结

通过这个电商系统的实战案例,我们展示了:

  1. 如何从需求分析到架构设计:基于DDD的服务拆分方法
  2. 如何实现核心业务逻辑:用户、商品、订单等核心服务的实现
  3. 如何处理复杂业务场景:Saga模式处理分布式事务
  4. 如何构建生产级系统:包含监控、部署、运维的完整方案

这个案例虽然简化了一些复杂场景,但保留了微服务架构的核心挑战和解决方案。希望这个实战案例能帮助你更好地理解和应用微服务架构。

记住,微服务不是银弹,它需要团队的成熟度和技术的支撑。在选择微服务架构时,要充分考虑团队的实际情况和项目的具体需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1201847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java计算机毕设之基于springboot的日报管理系统设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

Hello World:一个后端程序员的碎碎念与新开始

Hello World! 敲下这行字的时候,感觉就像当年第一次在控制台打印出 Hello World 一样,既熟悉又带着一点仪式感。 作为一个在代码世界里摸爬滚打有些年头的 Java 后端开发,我想是时候搭建一个属于自己的“独立服务器…

计算机Java毕设实战-基于springboot的饰品商城系统基于Java Web的饰品商城系统的设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

从零开始学电脑:新手入门完全指南 - 指南

从零开始学电脑:新手入门完全指南 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

2025.12.20 作业 - # P1246 编码

2025.12.20 作业 - # P1246 编码题目描述 编码工作常被运用于密文或压缩传输。这里我们用一种最简单的编码方式进行编码:把一些有规律的单词编成数字。 字母表中共有 \(26\) 个字母 \(\mathtt{a,b,c,\cdots,z}\),这些…

【Da】调色面板

--本篇导航--界面各个调色工具调色节点复制/应用调色Lut界面重置调色抓取静态帧(截图)光箱 就是一个筛选器,会对素材进行各种条件的筛选,方便调色片段的管理。各个调色工具 波形图校色轮HDR色轮RGB混合器运动特效曲…

学习AI编程

habit-tracker 一个不错的AI编程示例。

打开CMD的方法和常用Dos命令

打开CMD的方法开始+系统+命令提示符 Win建+R 输入cmd打开控制台(通常使用) 在任意文件夹下面,按住shift键+鼠标右键点击,在此处打开命令行窗口 资源管理器的地址栏前面加上cmd路径管理员方式运行:选择以管理员方式…

基于 YOLOv8 的水稻病害智能检测系统实战源码【从农田到模型】

基于 YOLOv8 的水稻病害智能检测系统实战源码【从农田到模型】 一、项目背景&#xff1a;为什么要做“水稻病害检测系统”&#xff1f; 在传统农业生产中&#xff0c;病害识别严重依赖人工经验。实际问题主要集中在三点&#xff1a; 巡田成本高&#xff1a;大面积农田靠人工…

【HTML】前端工具箱建立【文本处理/JSON程序/加解密/校验和/ASCII/时间戳转换等】【附完整源代码】

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025.12.20 作业 - # P11951 [科大国创杯初中组 2023] 数数

2025.12.20 作业 - # P11951 [科大国创杯初中组 2023] 数数题目描述 小可可和小多在拼木棍。 他们现在拿到了 \(n\) 根木棍,第 \(i\) 根木棍的长度是 \(a_i\)。他们现在想知道,有多少种在里面选三根木棍的方案,使得…

HRCSA项目结课

一&#xff0c;项目准备1、安装RHEL9系统2、配置系统修改主机名并清除yum数据库配置本地光盘为yum源测试网络连通性二、部署项目1、安装LNMP环境2、配置环境&#xff08;数据库配置&#xff09;&#xff08;2&#xff09;论坛安装文件3、安装论坛软件

Java毕设选题推荐:基于springboo酒店客房管理系统民宿管理系统基于springboot的民宿客房管理系统【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

send-proxy vs send-proxy-v2 vs send-proxy-v2-ssl --2

send-proxy vs send-proxy-v2 vs send-proxy-v2-ssl --2下面给出完整工程级文档(含:TOA→内核→HAProxy→PROXY、send-proxy/v2/v2-ssl 字节级行为、SSL TLV 作用、以及 Nginx 支持与配置差异)。该文档不引入任何新…

计算机Java毕设实战-基于Java+Springboot+Vue的民宿管理系统基于springboot的民宿客房管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

第九章 数据一致性与分布式事务

第九章 数据一致性与分布式事务 在微服务架构中,数据一致性是最具挑战性的问题之一。当你把一个单体应用拆分成多个服务时,原本简单的数据库事务变成了复杂的分布式事务。这一章,我想分享一些实战经验,帮助你在这个…

第八章 微服务通信实现

第八章 微服务通信实现 微服务之间的通信是分布式系统中最具挑战性的部分。我见过太多团队因为通信设计不当而导致系统性能低下、故障频发。这一章,我想分享一些实战经验,帮助你设计高效可靠的微服务通信方案。 8.1 …

Java计算机毕设之基于springboot的民宿客房管理系统酒店客房管理系统设计(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…