微服务架构
一、微服务架构概述
1. 什么是微服务?
微服务架构是一种将单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,并通过轻量级机制(通常是 HTTP RESTful API)进行通信。
核心特点:
- ✅ 独立部署 - 每个服务可以独立部署和扩展
- ✅ 技术多样性 - 不同服务可以使用不同技术栈
- ✅ 去中心化 - 数据管理和治理去中心化
- ✅ 故障隔离 - 单个服务故障不影响整体系统
2. 微服务 vs 单体架构
| 特性 | 单体架构 | 微服务架构 |
|---|---|---|
| 部署 | 整体部署 | 独立部署 |
| 扩展 | 垂直扩展 | 水平扩展 |
| 技术栈 | 单一技术栈 | 多技术栈 |
| 数据管理 | 共享数据库 | 独立数据库 |
| 复杂度 | 低(初期) | 高(需要服务治理) |
| 适用场景 | 小型项目 | 大型复杂系统 |
3. 微服务的挑战
- ⚠️ 服务发现 - 如何找到服务?
- ⚠️ 配置管理 - 如何统一管理配置?
- ⚠️ 服务通信 - 如何高效通信?
- ⚠️ 分布式事务 - 如何保证数据一致性?
- ⚠️ 监控和日志 - 如何追踪问题?
- ⚠️ 测试 - 如何测试分布式系统?
二、服务发现 - Consul
一、为什么要使用consul?
想象一下,你有一个由 100 个微服务组成的系统,它们部署在几十台服务器或成百上千个容器中。
- 问题 1:服务在哪里? OrderService 想调用 PaymentService,但它怎么知道 PaymentService 的 IP 和端口?这些地址是动态变化的(容器重启、扩缩容)。 传统做法: 写死在配置文件里。但一旦 PaymentService 的地址变了,所有调用它的服务都得改配置、重启。极其脆弱且不可扩展。
- 问题 2:服务还活着吗? 如何知道某个 UserService 实例是宕机了,还是只是网络抖动? 传统做法: 手动检查或写脚本。效率低下,无法实时感知。
- 问题 3:配置怎么管理? 数据库连接字符串、功能开关(Feature Flag)、超时时间等配置,分散在各个服务的配置文件中。 传统做法: 修改一个配置,需要逐个服务更新。一致性差,易出错。
- 问题 4:如何安全通信? 在服务间传输敏感数据(如用户信息),如何防止被窃听或篡改? 传统做法: 自行实现加密,复杂且容易有安全漏洞。
二、Consul 如何解决这些问题?
- 1. 服务发现 (Service Discovery) - “它在哪?” 工作原理: 注册 (Register): 当一个服务(如 PaymentService)启动时,它会向 Consul 注册自己,告诉 Consul:“我叫 payment,我的 IP 是 10.0.1.10,端口是 8080,健康检查地址是 /health”。 发现 (Discover): 当另一个服务(如 OrderService)需要调用 payment 时,它向 Consul 查询:“payment 服务在哪?” Consul 返回一个或多个可用的 IP:Port 列表。 优势: 动态: 服务地址变化时,自动更新,调用方无感知。 解耦: 服务之间不需要知道对方的具体地址。 支持 DNS 和 HTTP API: 可以直接用 curl payment.service.consul:8080 或调用 Consul API。
- 2. 健康检查 (Health Checking) - “它还活着吗?” 工作原理: Consul 会定期(如每 10 秒)对每个注册的服务执行健康检查(通常是 HTTP 请求 /health 或 TCP 端口检查)。 如果检查失败,Consul 会将该服务实例标记为 critical(不健康)。 当 OrderService 查询 payment 服务时,Consul 只返回健康的服务实例。 优势: 自动故障转移: 避免将请求发送到已宕机的服务。 提高系统可用性: 结合负载均衡,实现高可用。
- 3. 配置管理 (Key/Value Store) - “配置在哪?” 工作原理: Consul 内置一个分布式的 KV (Key-Value) 存储。 你可以把配置(如 database.url, feature.flag.new-ui=true)存到 Consul 的 KV 中。 服务启动时,从 Consul 拉取配置,并可以监听配置变更,实现动态更新。 优势: 集中管理: 所有配置在一个地方。 动态生效: 修改配置后,服务可以自动感知并应用,无需重启。 版本控制与审计: 可以追踪配置变更历史。
- 4. 安全服务通信 (Connect) - “如何安全地说话?” 工作原理: Consul Connect 通过 mTLS (双向 TLS) 来加密服务间的通信。 每个服务都有一个由 Consul CA (证书颁发机构) 签发的证书。 服务间通信前先互相验证证书,确保身份真实,通信内容加密。 可以定义 Service Intentions(意图)来控制哪些服务可以调用哪些服务(类似防火墙规则)。
三、Consul 的典型应用场景
- 微服务架构: 这是 Consul 最主要的场景。管理成百上千个服务的发现、配置和安全。
- 混合云/多数据中心: Consul 支持多数据中心,可以跨 AWS、Azure、本地机房统一管理服务。
- 容器编排集成: 与 Kubernetes、Docker Swarm 集成,为容器化应用提供服务发现。
- 边缘计算: 在网络边缘的设备上运行 Consul,实现本地服务发现
四、替代方案
Consul 不是唯一的解决方案,常见的还有:
- ZooKeeper: 老牌的分布式协调服务,功能强大但较重,主要用于 Hadoop 生态。
- etcd: CoreOS 开发,Kubernetes 的默认存储,轻量,主要用于配置存储和协调。
- Eureka (Netflix): 专注于服务发现,常用于 Spring Cloud 生态。
- Nacos (Alibaba): 集成了服务发现、配置管理、服务管理等功能,国内流行。
- Consul 的优势在于: 功能全面(发现+配置+安全+多数据中心)、易于部署、文档完善、社区活跃。
五、.NET 中使用 Consul
- 1.添加健康检查接口2.添加 Consul注册,一般启动一个后台任务
app.MapHealthChecks("/health"); //健康检查地址///Consul服务注册 public class ConsulService : IHostedService { private ConsulClient? _client; private string? _registrationId; /// <summary> /// 启动 /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task StartAsync(CancellationToken cancellationToken) { try { var builder = WebApplication.CreateBuilder(); var config = builder.Configuration; var consulAddress = AppSettings.ConsulConfig.Address; var serviceName = AppSettings.ConsulConfig.Service; var serviceHost = AppSettings.ConsulConfig.Host; var servicePort = AppSettings.ConsulConfig.Port; _client = new ConsulClient(c => c.Address = new Uri(consulAddress)); _registrationId = $"{serviceName}-{serviceHost}-{servicePort}"; var registration = new AgentServiceRegistration { ID = _registrationId, Name = serviceName, Address = serviceHost, Port = servicePort, Tags = new[] { "urlprefix-/" }, Check = new AgentServiceCheck { HTTP = $"http://{serviceHost}:{servicePort}/health", Interval = TimeSpan.FromSeconds(10), Timeout = TimeSpan.FromSeconds(5), DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1) } }; SerilogHelper.Log(LogCategoryEnum.System, LogEventLevel.Information, $" Consul服务已注入..."); await _client.Agent.ServiceRegister(registration, cancellationToken); } catch (Exception ex) { SerilogHelper.Log(LogCategoryEnum.System, LogEventLevel.Error, $" Consul服务测试失败:{ex.Message}{ex.StackTrace}"); } } /// <summary> /// 结束 /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> public Task StopAsync(CancellationToken cancellationToken) => _client != null && _registrationId != null ? _client.Agent.ServiceDeregister(_registrationId, cancellationToken) : Task.CompletedTask; }
三、API 网关 - Ocelot
一、什么是 API 网关?为什么需要它?
- 在微服务架构中,前端(Web、App)可能需要调用多个后端服务。直接让前端调用每个服务会带来问题:
- 复杂性: 前端需要知道每个服务的地址。
- 安全性: 每个服务都暴露在公网,风险高。
- 重复代码: 认证、限流、日志等功能需要在每个服务中重复实现。
- 协议转换: 前端用 HTTP,后端可能用 gRPC。
- Ocelot 是一个基于 .NET 的微服务网关,用于 API 网关和边缘路由。
- API 网关就是解决这些问题的“守门人”。它集中处理:
- 路由 (Routing): 把 /api/users 转发到 UserService,/api/orders 转发到 OrderService。
- 认证与授权 (Authentication & Authorization): 统一校验 JWT Token。
- 限流 (Rate Limiting): 防止某个用户或 IP 滥用接口。
- 熔断与重试 (Circuit Breaker & Retry): 服务不可用时降级或重试。
- 日志与监控 (Logging & Monitoring): 记录所有请求日志。
- 请求/响应聚合 (Aggregation): 一个请求返回多个服务的数据。
二、Ocelot 核心功能
Ocelot 提供了上述所有功能,其核心是通过一个 JSON 配置文件 (ocelot.json) 来定义路由规则和中间件行为。
三、详细使用步骤
- 创建项目并安装 Ocelot
// 创建一个新的 ASP.NET Core Web API 项目作为网关 dotnet new webapi -n OcelotGateway cd OcelotGateway // 安装 Ocelot 包 dotnet add package Ocelot - 创建 Ocelot 配置文件 ocelot.json
{"Routes": [ { "DownstreamPathTemplate": "/api/users/{everything}", // 后端服务的实际路径 "DownstreamScheme": "http", // 后端协议 (http/https) "DownstreamHostAndPorts": [ { "Host": "localhost", // 后端服务主机 "Port": 5001 // 后端服务端口 } ], "UpstreamPathTemplate": "/users/{everything}", // 客户端访问的路径 "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ], // 支持的 HTTP 方法 "Key": "UserService", // 路由唯一标识 "AuthenticationOptions": { "AuthenticationProviderKey": "Bearer", "AllowedScopes": [] }, "RateLimitOptions": { "ClientWhitelist": [], "EnableRateLimiting": true, "Period": "1m", "PeriodTimespan": 60, "Limit": 100 }, "LoadBalancerOptions": { "Type": "RoundRobin" // 轮询负载均衡 }, "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 10, "TimeoutValue": 5000 }, "UseServiceDiscovery": false // 是否使用服务发现 }, { "DownstreamPathTemplate": "/api/orders/{everything}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5002 } ], "UpstreamPathTemplate": "/orders/{everything}", "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ], "Key": "OrderService" } ], "GlobalConfiguration": { "BaseUrl": "http://localhost:5000", // 网关的基地址 "ServiceDiscoveryProvider": { "Host": "localhost", "Port": 8500, "Type": "Consul" } } } - 配置 Program.cs
using Ocelot.DependencyInjection; using Ocelot.Middleware; var builder = WebApplication.CreateBuilder(args); // 1. 添加 Ocelot 服务 builder.Configuration.SetBasePath(builder.Environment.ContentRootPath) .AddJsonFile("ocelot.json", optional: false, reloadOnChange: true); // 重要:启用热重载 builder.Services.AddOcelot(builder.Configuration); var app = builder.Build(); // 2. 使用 Ocelot 中间件 await app.UseOcelot(); // 这是 Ocelot 的核心中间件 app.Run();
四、核心配置详解
路由配置
- DownstreamPathTemplate: 后端服务的真实路径。{everything} 是通配符,匹配所有子路径。
- UpstreamPathTemplate: 客户端访问网关的路径。/users/123 会被转发到
http://localhost:5001/api/users/123。 - UpstreamHttpMethod: 指定哪些 HTTP 方法可以触发此路由
负载均衡 (Load Balancer)
"LoadBalancerOptions": { "Type": "RoundRobin" // 轮询 "Type": "LeastConnection" // 最少连接 "Type": "NoLoadBalance" // 无负载均衡 }当 DownstreamHostAndPorts 有多个节点时,Ocelot 会根据策略选择一个。
服务质量 (QoS) - 熔断器
"QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, // 允许3次异常后熔断 "DurationOfBreak": 10, // 熔断持续10秒 "TimeoutValue": 5000 // 请求超时5秒 }防止一个慢服务拖垮整个系统。
限流 (Rate Limiting)
"RateLimitOptions": { "EnableRateLimiting": true, "Period": "1m", // 每分钟 "Limit": 10 // 最多10次请求 }保护后端服务不被刷爆。
认证 (Authentication)
"AuthenticationOptions": { "AuthenticationProviderKey": "Bearer" }这表示此路由需要 JWT Bearer Token 认证。你还需要在项目中配置 JWT:
// 在 AddOcelot() 之前 builder.Services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "https://your-auth-server.com"; // IdentityServer 地址 options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; }); builder.Services.AddOcelot(builder.Configuration);服务发现 (Service Discovery) 让 Ocelot 从 Consul 获取服务地址,而不是写死。
- 修改 ocelot.json
"GlobalConfiguration": { "ServiceDiscoveryProvider": { "Host": "localhost", "Port": 8500, "Type": "Consul" } }, "Routes": [ { "DownstreamPathTemplate": "/api/users/{everything}", "DownstreamScheme": "http", // 移除 DownstreamHostAndPorts "UpstreamPathTemplate": "/users/{everything}", "UpstreamHttpMethod": [ "GET" ], "ServiceName": "UserService", // Consul 中注册的服务名 "UseServiceDiscovery": true // 启用服务发现 } ]- 安装 Consul 包:
dotnet add package Ocelot.Provider.Consul- 在 Program.cs 中启用:
builder.Services.AddOcelot(builder.Configuration) .AddConsul(); // 启用 Consul 支持
五、高级功能
- 请求聚合 (Request Aggregation)
"Aggregates": [ { "RouteKeys": [ "UserService", "OrderService" ], "UpstreamPathTemplate": "/summary" } ] - 自定义中间件 你可以编写自定义的 Ocelot 中间件来处理特殊逻辑。
- 配置中心 可以将 ocelot.json 存储在 Consul KV 或数据库中,实现动态配置。
六、总结
| 功能 | Ocelot配置项 | 说明 |
|---|---|---|
| 路由 | Routes, DownstreamPathTemplate, UpstreamPathTemplate | 定义请求转发规则 |
| 负载均衡 | LoadBalancerOptions | 支持轮询、最少连接等 |
| 熔断 | QoSOptions | 防止级联故障 |
| 限流 | RateLimitOptions | 保护后端服务 |
| 认证 | AuthenticationOptions | 集成 JWT、IdentityServer |
| 服务发现 | UseServiceDiscovery, ServiceName | 与 Consul/Eureka 集成 |
| 动态配置 | reloadOnChange: true | 配置文件修改后自动重载 |
四、微服务通信
1. 同步通信
HTTP/REST:
- 简单易用
- 浏览器友好
- 性能相对较低
gRPC:
- 高性能
- 强类型
- 支持流式传输
- 详见 gRPC 远程调用文档
2. 异步通信
消息队列:
- RabbitMQ
- Kafka
- Azure Service Bus
事件驱动:
- 发布/订阅模式
- 事件溯源
五、微服务治理
1. 服务发现
Consul:
- 服务注册与发现
- 健康检查
- 配置管理
- 详见本文档 Consul 章节
其他方案:
- Eureka(Spring Cloud)
- Nacos(Alibaba)
- etcd(Kubernetes)
2. API 网关
Ocelot:
- 路由转发
- 认证授权
- 限流熔断
- 详见 Ocelot 网关文档
其他方案:
- Kong
- Zuul
- Nginx
3. 认证授权
IdentityServer4:
- OAuth 2.0 / OpenID Connect
- 单点登录(SSO)
- 令牌管理
- 详见 IdentityServer4 文档
4. 容错机制
熔断:
- 防止级联故障
- 快速失败
- 详见 熔断-限流文档
限流:
- 保护后端服务
- 防止系统过载
- 详见 熔断-限流文档
重试:
- 指数退避
- 带抖动重试
降级:
- 返回默认值
- 使用缓存数据
六、微服务最佳实践
- ✅ 服务拆分原则 - 按业务领域拆分(DDD)
- ✅ API 设计 - RESTful 或 gRPC,保持一致性
- ✅ 服务发现 - 使用 Consul、Eureka 等
- ✅ 配置管理 - 集中配置管理(Consul KV、Apollo)
- ✅ 监控和日志 - 分布式追踪(Jaeger、Zipkin)
- ✅ 容错机制 - 熔断、降级、重试
- ✅ 安全 - 服务间认证和授权(IdentityServer4)
- ✅ 测试 - 单元测试、集成测试、契约测试
- ✅ 数据管理 - 每个服务独立数据库
- ✅ 部署 - 容器化部署(Docker、Kubernetes)
七、微服务挑战和解决方案
1. 分布式事务
问题: 跨服务的数据一致性
解决方案:
- Saga 模式 - 分布式事务编排
- 最终一致性 - 接受最终一致性
- 事件溯源 - 通过事件保证一致性
2. 服务间通信
问题: 网络延迟、故障
解决方案:
- 异步通信 - 使用消息队列
- 熔断器 - 快速失败
- 重试机制 - 指数退避
3. 数据一致性
问题: 跨服务数据同步
解决方案:
- 事件驱动 - 发布/订阅
- CQRS - 命令查询职责分离
- 最终一致性 - 接受延迟一致性
4. 监控和调试
问题: 分布式系统难以追踪
解决方案:
- 分布式追踪 - Jaeger、Zipkin
- 集中日志 - ELK Stack
- 指标监控 - Prometheus、Grafana
八、微服务架构模式
1. API 网关模式
作用: 统一入口,路由转发
实现: Ocelot、Kong
2. 服务发现模式
作用: 动态发现服务地址
实现: Consul、Eureka
3. 配置中心模式
作用: 集中管理配置
实现: Consul KV、Apollo、Nacos
4. 断路器模式
作用: 防止级联故障
实现: Polly、Hystrix
5. 服务网格模式
作用: 服务间通信基础设施
实现: Istio、Linkerd
九、常见面试题
Q1: 微服务和单体架构如何选择?
选择微服务:
- ✅ 大型复杂系统
- ✅ 团队规模大
- ✅ 需要独立部署和扩展
- ✅ 技术栈多样化
选择单体:
- ✅ 小型项目
- ✅ 团队规模小
- ✅ 快速迭代
- ✅ 简单业务
Q2: 如何拆分微服务?
拆分原则:
- 按业务领域 - DDD 领域驱动设计
- 高内聚低耦合 - 服务内部高内聚,服务间低耦合
- 数据独立 - 每个服务独立数据库
- 团队结构 - 按团队拆分(康威定律)
Q3: 如何处理分布式事务?
方案:
- Saga 模式 - 分布式事务编排
- 两阶段提交(2PC) - 性能较差,不推荐
- 最终一致性 - 接受延迟一致性
- 事件溯源 - 通过事件保证一致性
Q4: 微服务的监控和日志?
监控:
- 指标监控 - Prometheus、Grafana
- 链路追踪 - Jaeger、Zipkin
- 健康检查 - 定期检查服务状态
日志:
- 集中日志 - ELK Stack(Elasticsearch、Logstash、Kibana)
- 结构化日志 - JSON 格式
- 日志聚合 - 统一收集和查询
Q5: 微服务的部署策略?
容器化:
- Docker 容器化
- Kubernetes 编排
CI/CD:
- 自动化构建和部署
- 蓝绿部署
- 金丝雀发布
服务治理:
- 服务发现
- 负载均衡
- 健康检查