微服务架构与Nacos-OpenFeign-Sentinel-Gateway入门笔记
微服务架构入门:Nacos、OpenFeign、Sentinel 与 Gateway
当一个系统拆成多个独立服务后,会遇到一组新的问题:服务地址怎么找?服务之间怎么调用?流量突然增大怎么办?前端应该访问哪个入口?
Nacos、OpenFeign、Sentinel 和 Spring Cloud Gateway 分别帮助处理这些问题。这篇笔记通过一个电商系统的例子,介绍微服务架构、组件分工、请求流程和基本接入思路。
本文是概念与接入片段笔记,不是完整可运行项目。示例采用 Java 17、Spring Boot 3.2 / Spring Cloud 2023 这一代配置方式;实际接入应按项目版本核对官方文档。文中的地址、端口、阈值和超时时间均为演示值。
一、什么是微服务架构
**微服务架构是一种将系统按业务能力划分为多个可独立部署服务的设计方式。**服务运行在不同进程中,通过 HTTP、RPC 或消息等方式协作。微服务架构介绍
1. 从单体系统开始理解
一个电商单体应用可能包含:
电商应用
├── 用户模块
├── 商品模块
├── 订单模块
├── 库存模块
└── 支付模块
整体构建、整体部署
模块之间通常可以直接调用 Java 方法。单体也可以组织得很清晰,并通过部署多个实例扩展容量。
2. 拆成微服务后
用户服务 user-service
商品服务 product-service
订单服务 order-service
库存服务 inventory-service
支付服务 payment-service
各自运行、通过接口或消息协作
例如订单服务需要查询商品信息时,就要向商品服务发起网络请求。
拆分应围绕业务边界,而不是简单把 Controller、Service、DAO 分成三个远程服务。独立部署、明确接口和数据归属,是拆分时需要考虑的目标。
3. 好处与代价
| 方面 | 单体应用 | 微服务架构 |
|---|---|---|
| 部署 | 通常作为一个整体部署 | 可分别发布各服务 |
| 扩容 | 通常扩容整个应用 | 可以扩容热点服务 |
| 调用 | 进程内方法调用较多 | 增加网络调用和失败场景 |
| 数据一致性 | 单库事务通常更直接 | 跨服务一致性需要额外设计 |
| 运维 | 初期较简单 | 需要更多部署、监控和排障能力 |
微服务能改善独立发布与扩容能力,但也增加网络延迟、超时、配置管理和协作成本。故障是否能隔离,还取决于超时、限流、隔离等机制是否落实。
对于业务边界尚不清晰、规模较小的项目,可以先做好模块化,再根据实际需求拆分服务。
二、四个组件分别解决什么问题
| 组件 | 核心职责 | 解决的问题 |
|---|---|---|
| Nacos | 服务注册发现、配置管理 | 服务在哪里?配置从哪里读取? |
| OpenFeign | 声明式 HTTP 客户端 | 怎样方便地调用其他服务? |
| Sentinel | 流量控制、熔断与保护 | 请求过多或依赖异常时如何保护服务? |
| Spring Cloud Gateway | 请求路由与过滤 | 外部请求从哪里进入,转发到哪个服务? |
| Spring Cloud LoadBalancer | 客户端负载均衡 | 同一个服务有多个实例时,选择哪一个? |
可以用这个关系理解:
客户端 → Gateway → 订单服务 → OpenFeign → 商品服务
│ │
└──按集成位置接入 Sentinel──┘
Nacos:向服务和网关提供服务实例信息,并提供配置管理
LoadBalancer:在需要按服务名调用时选择具体实例
这些组件不是微服务的唯一实现方式,也不是每个系统都必须全部使用。
三、Nacos:服务地址簿与配置管理
Nacos 提供服务发现和配置管理能力。在这里重点学习两个用途:注册中心和配置中心。Nacos 官方介绍
1. 为什么需要注册中心
如果订单服务将商品服务地址写死:
http://192.168.1.10:8082/products/1001
当商品服务换机器、换端口或者增加实例时,调用方的地址配置也要调整。
接入服务发现后,可以用服务名识别目标:
product-service
├── 192.168.1.10:8082
└── 192.168.1.11:8082
基本过程:
- 商品服务启动,将自己的服务名、地址等信息注册到 Nacos。
- 订单服务通过客户端发现或订阅商品服务的实例信息。
- 客户端负载均衡组件从可用实例中选一个。
- 订单服务直接向商品服务发起请求。
**Nacos 通常不转发业务请求。**调用方可以使用客户端缓存的实例列表,并接收后续更新,不是每次请求都必须访问 Nacos 服务端。
实例健康变化的传播需要时间。即使注册中心认为某实例可用,调用也仍然可能失败,因此还需要超时和故障处理。
2. 配置中心有什么用
可以将多个服务的配置集中管理,例如业务开关、功能参数等,而不是到每台机器上分别修改文件。
Nacos 配置常见标识:
| 名称 | 用途 | 示例 |
|---|---|---|
| Namespace | 隔离不同环境或租户 | dev、test、prod 对应的命名空间 |
| Group | 对配置进行分组 | DEFAULT_GROUP |
| Data ID | 标识一份配置 | order-service.yaml |
在客户端设置命名空间时,需要核对所用版本要求的命名空间 ID,不要将显示名称直接当作 ID。
**发布配置不代表所有运行参数都会立即生效。**需要客户端监听更新,而且对应属性或 Bean 支持刷新;端口、底层连接等配置可能仍需要重启或专门处理。
3. 服务注册的基本片段
加入依赖,版本由匹配的 Spring Cloud Alibaba BOM 管理:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
商品服务的 application.yml 示例:
server:
port: 8082
spring:
application:
name: product-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
前提是 Nacos 服务端已运行,地址可达,并根据其鉴权配置设置客户端凭据。注册后应在控制台检查服务名、实例地址和健康状态。
4. 导入远程配置的基本片段
使用配置中心时,还需要 spring-cloud-starter-alibaba-nacos-config。在采用 Config Data 导入方式的版本中,可使用:
spring:
application:
name: order-service
config:
import:
- "nacos:order-service.yaml?group=DEFAULT_GROUP&refreshEnabled=true"
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
需要提前在对应命名空间和 Group 中创建 order-service.yaml。这里没有添加 optional:,表示远程配置是启动所需的配置。
注册发现与配置管理是两种能力,需要分别接入。旧教程中的 bootstrap.yml、默认配置加载方式与新版本可能不同,应根据使用的版本核对。Spring Cloud Alibaba Nacos 接入指南
四、OpenFeign:像调用方法一样调用远程 HTTP 接口
OpenFeign 允许通过 Java 接口和注解描述远程 HTTP 请求,再由框架生成客户端代理。OpenFeign 官方文档
1. 调用示例
假设商品服务提供:
GET /products/1001
响应为:
{
"id": 1001,
"name": "机械键盘"
}
订单服务中定义客户端接口:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "product-service")
public interface ProductClient {
@GetMapping("/products/{id}")
ProductDTO getProduct(@PathVariable("id") Long id);
}
对应 DTO,使用 Java 17 的 record 表示:
public record ProductDTO(Long id, String name) {
}
启动类开启客户端扫描:
@EnableFeignClients(basePackages = "com.example.order.client")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
将接口放在对应扫描包中,然后在业务类中通过构造器注入 ProductClient,即可调用:
ProductDTO product = productClient.getProduct(1001L);
虽然写法类似普通方法调用,背后执行的是 HTTP 网络请求,会发生超时、连接失败、响应解析失败等情况。
2. 需要的依赖
订单服务一般需要:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
还需要前面提到的 Nacos Discovery 接入,使 product-service 能解析成实际实例地址。Feign 不会自动启动商品服务,也不会单独完成服务注册。
3. 为远程调用设置超时
spring:
cloud:
openfeign:
client:
config:
product-service:
connectTimeout: 1000
readTimeout: 2000
上述单位为毫秒,分别控制建立连接和等待响应的超时;具体行为还受底层 HTTP 客户端影响。示例值需要结合实际延迟和业务预算调整。
自动重试需要谨慎设计。对于创建订单、支付等操作,应先保证幂等性,避免重复执行;多层重试还可能放大故障期间的请求量。
Spring Cloud OpenFeign 的官方项目目前定位为功能完整、主要维护的项目,并建议新开发需求关注 Spring HTTP Service Clients;学习和维护已有 Feign 项目仍然需要理解其调用机制。项目状态说明
五、Sentinel:流量控制与故障保护
Sentinel 通过对受保护资源统计和执行规则,提供流量控制、熔断等能力。资源可以是 HTTP 接口、某段业务方法或一次远程调用。Sentinel 官方介绍
1. 为什么需要保护机制
假设订单服务依赖商品服务,而商品服务响应突然变慢:
商品服务变慢
↓
订单服务中的调用长时间等待
↓
线程和连接被占用
↓
更多请求积压,故障影响扩大
治理机制的目标是限制影响范围,让服务在压力或故障下仍能提供可控的行为。
2. 区分限流、熔断、降级和隔离
| 概念 | 目的 | 示例 |
|---|---|---|
| 限流 | 限制进入受保护资源的请求量或并发量 | 超过阈值后拒绝或按策略等待 |
| 熔断 | 依赖持续异常时暂时停止正常调用 | 商品查询错误比例过高后快速失败 |
| 降级 | 以受控的替代行为处理异常或保护事件 | 返回“服务繁忙,请稍后重试” |
| 隔离 | 限制某类调用占用的资源 | 限制同时执行的依赖调用数量 |
QPS 指每秒请求数;并发数量指某时刻正在执行的请求数,两者不能混为一谈。Sentinel 的线程数控制属于并发控制思路,并不等于自动为每个依赖创建独立线程池。
3. 熔断器的状态
关闭:允许调用并统计结果
↓ 达到异常或慢调用条件
打开:暂时拦截正常调用
↓ 熔断时间结束
半开:允许探测调用
├──探测正常 → 关闭
└──探测异常 → 再次打开
Sentinel 支持按慢调用比例、异常比例和异常数配置熔断规则,通常还要设置最小请求数、统计窗口和熔断时间。应结合真实指标确定阈值,而不是直接照搬演示数值。Sentinel 熔断文档
4. 定义一个受保护资源
接入 spring-cloud-starter-alibaba-sentinel 后,可以使用注解定义资源。下面是读取演示订单信息的片段:
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Service;
@Service
public class OrderQueryService {
@SentinelResource(
value = "queryOrder",
blockHandler = "handleBlocked",
fallback = "handleFailure"
)
public String queryOrder(Long id) {
return "订单:" + id;
}
public String handleBlocked(Long id, BlockException ex) {
return "请求过多或服务保护中,请稍后重试";
}
public String handleFailure(Long id, Throwable ex) {
return "订单查询暂不可用";
}
}
blockHandler处理 Sentinel 规则触发的拦截,例如流控和熔断。fallback处理未被排除的业务异常等情况。- 处理方法需要满足参数与返回类型要求。
还要确保注解切面集成生效,并通过 Spring 代理调用该方法;同类内部直接调用可能绕过代理。**仅添加注解不会自动产生限流规则。**需要为 queryOrder 配置并加载规则。Sentinel 注解说明
示例返回字符串用于理解机制。真实 API 应按业务协议明确表达失败,不应把“没有执行”的操作伪装成成功。
5. Dashboard 与规则持久化
Sentinel Dashboard 用于观察指标、管理规则,保护逻辑由应用侧客户端执行。常见演示配置:
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
Dashboard 需要单独运行,并确认应用与控制台所需通信地址可达。应用可能在产生请求后才出现监控数据。
控制台演示方式修改的规则通常保存在客户端内存中,不等于完成持久化。生产接入应设计规则数据源和发布方式,例如通过适配的数据源从 Nacos 加载规则,并明确控制台的发布链路。Sentinel 接入指南
Feign 接入 Sentinel 还需要对应版本支持和显式启用;不能认为加入两个 Starter 后所有调用就自动具备熔断和 fallback。
六、Gateway:外部请求的统一入口
这里的 Gateway 指 Spring Cloud Gateway。它根据路由规则将请求转发到服务,并允许通过过滤器处理请求与响应。Gateway 官方文档
1. 为什么需要网关
如果前端直接调用多个后端服务,就要了解各服务地址,并分别处理入口层逻辑。
使用网关后:
前端 → Gateway
├── /api/products/** → 商品服务
├── /api/orders/** → 订单服务
└── /api/users/** → 用户服务
网关可以集中实现路由、令牌校验、跨域配置、日志和入口流量控制,但这些能力需要相应配置或代码。
服务仍应负责业务授权,例如用户能否读取某个订单。网关也不会自动阻止客户端绕过它访问服务,需要部署网络和访问控制配合。
2. 三个核心概念
| 概念 | 含义 |
|---|---|
| Route | 路由:目标地址和匹配、过滤规则的组合 |
| Predicate | 断言:判断请求是否匹配,例如路径或请求头 |
| Filter | 过滤器:在转发前后处理请求,例如修改路径 |
3. 路由配置示例
下面是 Spring Cloud Gateway 4.1 这一代 WebFlux 网关的 YAML 风格。较新版本的 Starter 名称和属性前缀可能调整,应按实际版本选择文档。
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: product-route
uri: lb://product-service
predicates:
- Path=/api/products/**
filters:
- StripPrefix=1
含义:
- 匹配
/api/products/**。 StripPrefix=1去掉第一个路径段api。lb://product-service表示通过负载均衡解析商品服务实例。
因此:
客户端访问:/api/products/1001
后端收到: /products/1001
示例网关需要匹配版本的 Gateway Starter、Nacos Discovery 和 LoadBalancer,以及可发现的商品服务实例。Gateway 配置文档
Gateway 有不同运行栈。本文这一代 WebFlux 网关使用响应式模型,应与普通 Servlet 业务应用分开配置,避免混入不匹配的 MVC 依赖。过滤器中执行阻塞数据库或 Feign 调用,也需要考虑线程模型,不能随意阻塞事件循环。WebFlux 网关说明
七、一次请求如何经过这些组件
以下以“查询订单详情,同时获取商品信息”为例。使用查询场景,可以先理解调用链,再学习写入操作的数据一致性。
flowchart LR
C[客户端] --> G[Gateway]
G --> O[订单服务]
O --> F[OpenFeign 与 LoadBalancer]
F --> P[商品服务]
N[Nacos] -.服务发现与配置.-> G
N -.服务发现与配置.-> O
N -.服务注册与配置.-> P
S[Sentinel 客户端规则] -.按集成位置保护.-> O
S -.按集成位置保护.-> P
实线表示业务请求方向,虚线表示管理信息或保护关系。Sentinel 客户端运行在接入它的应用中,图中的节点用于表达职责。
请求流程:
- 客户端请求
GET /api/orders/2001。 - Gateway 根据路由和过滤器,将请求转发为订单服务的
/orders/2001。 - 订单服务读取自己管理的订单数据。
- 订单服务通过
ProductClient请求商品信息。 - 发现客户端提供商品实例列表,LoadBalancer 选择目标实例,Feign 发送 HTTP 请求。
- 在已接入的保护点,Sentinel 根据规则决定放行还是拦截。
- 订单服务组合结果,经 Gateway 返回客户端;依赖失败时按业务要求返回错误或降级结果。
**内部服务调用通常可以直接进行,不需要每次绕回外部网关。**具体是否走内部网关或服务网格,取决于架构设计。
八、其他相关能力
微服务不仅需要这四个组件,还可能需要:
| 能力 | 用途 |
|---|---|
| 消息队列,例如 RocketMQ、RabbitMQ、Kafka | 异步通信、削峰、事件传播 |
| 数据一致性机制,例如 Saga、事务消息、Outbox,或适用的 Seata 模式 | 处理跨服务操作的一致性 |
| 日志、指标与链路追踪,例如 OpenTelemetry 及配套后端 | 定位一次请求涉及哪些服务、哪里失败 |
| Docker 与部署编排 | 打包和运行服务实例 |
| Knife4j | 展示和调试各服务接口 |
创建订单并扣减库存是写入场景。远程扣库存成功后,订单本地事务仍可能失败;超时也可能表示对方已执行、只是响应未到达。需要状态机、幂等、补偿、消息或合适的事务方案,不能仅靠 Feign 和本地 @Transactional 保证整个链路原子性。
九、常见误区与排查方向
| 误区或问题 | 正确理解与排查方向 |
|---|---|
| 把项目拆成多个 JAR 就完成微服务设计 | 还需要业务边界、独立发布、接口契约和数据归属 |
| Nacos 会转发所有业务请求 | 通常提供实例信息,业务请求由调用方直接发送 |
| Feign 调用与本地方法一样可靠 | 它是网络调用,必须处理超时和失败 |
| 加上 Sentinel 就自动防止雪崩 | 需要保护点、规则、合理的超时与容量设计 |
| 配置中心修改任何参数都即时生效 | 取决于刷新机制和参数是否支持动态更新 |
| Gateway 自带完整登录与权限体系 | 路由与过滤器提供实现入口,鉴权需要配置和业务设计 |
| 发现不到服务 | 检查服务名、注册地址、命名空间、Group 和健康状态 |
| 能发现服务但调用失败 | 检查注册 IP 是否对调用方可达、端口、路径和网络 |
| 网关转发后 404 | 对照 Path 匹配、StripPrefix 和后端实际接口路径 |
| Sentinel 页面没有数据 | 检查客户端集成、控制台连接,并访问受保护资源 |
| 重启后保护规则丢失 | 检查规则是否只在内存,是否接入持久化数据源 |
容器部署时尤其要注意:应用容器中的 127.0.0.1 指它自己,不能直接代表另一个 Nacos 容器或主机上的服务。
记忆各组件职责时,可以抓住四句话:Nacos 帮你找服务和配置;OpenFeign 帮你调用服务;Gateway 帮你接收并路由外部请求;Sentinel 帮你按规则控制流量和故障影响。