嘿,欢迎来到 Apache ShenYu 的学习之旅! 👋
这不是一份枯燥的技术文档,而是你的私人教练笔记。我会像朋友一样,带你从零开始,逐步理解这个高性能 API 网关的设计精髓。
第一部分:项目架构深度解析 🔍
1. 项目架构概览
🎯 用通俗的类比理解 ShenYu
想象一个繁忙的国际机场 ✈️:
- ShenYu Gateway(网关) = 机场的航站楼,所有乘客(请求)必须经过这里
- Plugin(插件) = 安检、海关、检票等各个环节,每个环节各司其职
- Admin(管理后台) = 机场调度中心,实时配置航班、闸口规则
- Selector & Rule(选择器与规则) = 航班指示牌,告诉每位乘客该去哪个登机口
- Data Sync(数据同步) = 实时广播系统,让所有地方都知道最新的航班信息
📊 核心设计特征
ShenYu 是一个高性能、可扩展的微服务 API 网关,它的设计哲学是:
- 插件化架构 - 一切皆插件,按需加载
- 责任链模式 - 请求像流水线一样依次经过各个插件
- 动态配置 - 无需重启即可实时更新规则
- 多协议支持 - HTTP、Dubbo、gRPC、WebSocket...一网打尽
- 响应式编程 - 基于 Spring WebFlux + Reactor 的异步非阻塞
🆚 与其他网关的对比
如果你了解 Kong 或 Zuul,那么:
- Kong(基于 Nginx + Lua) → ShenYu(基于 Java + Netty)
- Kong 的插件需要 Lua 编程 → ShenYu 的插件用熟悉的 Java
- Zuul 1.x 是同步阻塞 → ShenYu 是异步响应式,性能更好
- Spring Cloud Gateway 功能较轻 → ShenYu 提供完整的治理能力
🛠️ 技术栈核心组件
框架层:
- Spring Boot: 3.3.1
- Spring WebFlux: 响应式Web框架
- Reactor: 响应式编程库
- Netty: 高性能网络通信
存储层:
- H2/MySQL/PostgreSQL/Oracle: 元数据存储
- ZooKeeper/Nacos/Etcd/Consul: 配置中心(可选)
通信层:
- WebSocket: 默认数据同步方式
- gRPC: RPC通信支持
- Dubbo/Motan/SOFA: 多种RPC协议
工具层:
- MyBatis: 数据访问
- Disruptor: 高性能队列
- Caffeine: 本地缓存
- Guava: 工具库
🌐 外部系统集成
ShenYu 可以对接:
- 注册中心:Zookeeper、Nacos、Eureka、Consul、Etcd
- 配置中心:Apollo、Nacos、Polaris
- 监控系统:Prometheus(通过 metrics 插件)
- 链路追踪:集成 OpenTelemetry(需配置)
- 消息队列:Kafka、RocketMQ(用于日志收集)
🔄 架构流程描述
一个典型的 HTTP 请求在 ShenYu 中的旅程:
graph LR
A[客户端请求] --> B[ShenYu Bootstrap:9195]
B --> C{GlobalPlugin<br/>全局处理}
C --> D{Selector匹配<br/>粗粒度路由}
D --> E{Rule匹配<br/>细粒度规则}
E --> F[插件链执行]
F --> G[RateLimiter<br/>限流]
G --> H[Sign<br/>鉴权]
H --> I[Divide/Dubbo<br/>路由转发]
I --> J[后端服务]
J --> K[Response<br/>响应处理]
K --> L[返回客户端]
M[Admin:9095] -.配置推送.-> B
M --> N[(MySQL/H2<br/>元数据)]
M -.WebSocket/ZK.-> B
关键点说明:
- 请求进入
ShenyuWebHandler(核心处理器) - 按
order顺序执行插件链 - 每个插件可选择是否处理当前请求(通过
skip()) Selector决定大方向(如匹配到/api/**路由)Rule决定具体策略(如负载均衡、超时时间)- 代理插件(如 DividePlugin)完成最终转发
2. 目录结构与核心流程
📁 项目目录组织逻辑
ShenYu 采用 Maven 多模块 结构,按功能模块清晰划分:
shenyu/
├── shenyu-admin/ # 🎛️ 管理后台(9095端口)
│ ├── mapper/ # MyBatis 数据访问层
│ ├── service/ # 业务逻辑层
│ ├── controller/ # REST API 接口
│ └── listener/ # 数据变更监听器
│
├── shenyu-bootstrap/ # 🚀 网关启动模块(9195端口)
│ └── application.yml # 【第一个要看的配置文件】
│
├── shenyu-web/ # 🌐 Web处理核心
│ ├── handler/ # ShenyuWebHandler(请求总入口)
│ ├── filter/ # 前置过滤器(跨域、健康检查等)
│ └── loader/ # 插件动态加载器
│
├── shenyu-plugin/ # 🔌 插件生态(40+插件)
│ ├── shenyu-plugin-api/ # 插件接口定义
│ ├── shenyu-plugin-base/ # 插件基础类
│ ├── shenyu-plugin-divide/ # HTTP代理插件
│ ├── shenyu-plugin-dubbo/ # Dubbo代理插件
│ ├── shenyu-plugin-ratelimiter/ # 限流插件
│ └── shenyu-plugin-sign/ # 签名认证插件
│
├── shenyu-sync-data-center/ # 🔄 数据同步中心
│ ├── shenyu-sync-data-websocket/ # WebSocket同步(默认)
│ ├── shenyu-sync-data-zookeeper/ # ZooKeeper同步
│ └── shenyu-sync-data-nacos/ # Nacos同步
│
├── shenyu-client/ # 📲 客户端SDK(业务系统接入)
│ ├── shenyu-client-http/ # HTTP服务接入
│ ├── shenyu-client-dubbo/ # Dubbo服务接入
│ └── shenyu-client-grpc/ # gRPC服务接入
│
├── shenyu-common/ # 🛠️ 公共工具模块
│ └── dto/ # 核心数据传输对象(RuleData、SelectorData)
│
├── shenyu-spi/ # 🔧 SPI机制实现
├── shenyu-register-center/ # 📝 服务注册中心
├── shenyu-loadbalancer/ # ⚖️ 负载均衡算法
└── shenyu-examples/ # 💡 示例代码(强烈推荐学习)
├── shenyu-examples-http/ # HTTP服务接入示例
├── shenyu-examples-dubbo/ # Dubbo服务接入示例
└── shenyu-examples-grpc/ # gRPC服务接入示例
👀 关键文件定位表:
| 文件路径 | 作用 | 阅读优先级 |
|---|---|---|
shenyu-bootstrap/src/main/resources/application.yml |
网关核心配置 | ⭐⭐⭐⭐⭐ 必读 |
shenyu-web/...//ShenyuWebHandler.java |
请求处理总入口 | ⭐⭐⭐⭐⭐ |
shenyu-plugin-api/.../ShenyuPlugin.java |
插件接口定义 | ⭐⭐⭐⭐ |
shenyu-common/dto/RuleData.java |
规则数据结构 | ⭐⭐⭐⭐ |
shenyu-admin/.../ShenyuAdminBootstrap.java |
管理后台启动类 | ⭐⭐⭐ |
db/init/schema.sql |
数据库表结构 | ⭐⭐⭐ |
📦 模块依赖关系
graph TB
A[shenyu-bootstrap] --> B[shenyu-web]
B --> C[shenyu-plugin-base]
C --> D[shenyu-plugin-api]
B --> E[shenyu-sync-data-center]
A --> F[shenyu-spring-boot-starter]
G[shenyu-admin] --> H[shenyu-common]
G --> I[shenyu-admin-listener]
C --> H
E --> H
J[业务服务] --> K[shenyu-client]
K --> H
依赖说明:
- 核心依赖是 单向的,从上层到下层
shenyu-common被所有模块依赖,是基础模块shenyu-admin和shenyu-bootstrap是两个独立的应用,互不依赖
🔥 典型业务流程:HTTP请求代理
让我们追踪一个最常见的场景:将 /api/user/123 代理到后端服务
步骤拆解:
// ① 请求到达 ShenyuWebHandler
// 位置: shenyu-web/src/main/java/org/apache/shenyu/web/handler/ShenyuWebHandler.java:123
public Mono<Void> handle(ServerWebExchange exchange) {
before(exchange); // 记录开始时间
return new DefaultShenyuPluginChain(plugins).execute(exchange);
}
// ② 插件链开始执行
// 位置: ShenyuWebHandler.java:289
public Mono<Void> execute(ServerWebExchange exchange) {
if (this.index < plugins.size()) {
ShenyuPlugin plugin = plugins.get(this.index++);
if (!plugin.skip(exchange)) {
// 判断是否跳过
return plugin.execute(exchange, this);
}
}
return Mono.empty();
}
// ③ GlobalPlugin - 构建 ShenyuContext
// 位置: shenyu-plugin/shenyu-plugin-global/.../GlobalPlugin.java
// 作用: 解析请求,生成上下文对象,存入 exchange.attributes
// ④ DividePlugin - HTTP代理插件
// 位置: shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-divide/.../DividePlugin.java
protected Mono<Void> doExecute(ServerWebExchange exchange,
ShenyuPluginChain chain,
SelectorData selector,
RuleData rule) {
// 1. 获取后端服务列表
List<Upstream> upstreams = loadUpstreams(selector);
// 2. 负载均衡选择一个
Upstream upstream = loadBalancer.select(upstreams, exchange);
// 3. 构建目标 URL
String targetUrl = buildRealURL(upstream, exchange);
// 4. 发起 HTTP 调用
return httpClient.request(targetUrl, exchange);
}
数据流动路径:
客户端请求
└→ Netty接收(端口9195)
└→ Spring WebFlux DispatcherHandler
└→ ShenyuWebHandler.handle()
└→ GlobalPlugin(生成ShenyuContext)
└→ SignPlugin(验证签名)
└→ WafPlugin(安全检查)
└→ RateLimiterPlugin(限流)
└→ DividePlugin(HTTP代理)
└→ WebClient发起请求
└→ 后端服务(如 127.0.0.1:8080)
📊 核心数据结构流程图
sequenceDiagram
participant Client as 客户端
participant Gateway as ShenYu Gateway
participant Plugin as 插件链
participant Backend as 后端服务
participant Admin as ShenYu Admin
Admin->>Gateway: ①推送配置(WebSocket)
Note over Gateway: 更新内存缓存
Client->>Gateway: ②发送请求 /api/user/123
Gateway->>Plugin: ③执行插件链
Plugin->>Plugin: ④GlobalPlugin: 构建上下文
Plugin->>Plugin: ⑤SignPlugin: 鉴权
Plugin->>Plugin: ⑥RateLimiterPlugin: 限流
Plugin->>Plugin: ⑦DividePlugin: 选择后端
Plugin->>Backend: ⑧代理转发请求
Backend-->>Plugin: ⑨返回响应
Plugin-->>Gateway: ⑩处理响应
Gateway-->>Client: ⑪返回结果
🎯 实现文件索引
| 流程阶段 | 核心文件 |
|---|---|
| 请求接收 | shenyu-web/handler/ShenyuWebHandler.java |
| 全局处理 | shenyu-plugin/shenyu-plugin-global/GlobalPlugin.java |
| 鉴权签名 | shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/SignPlugin.java |
| 限流控制 | shenyu-plugin/shenyu-plugin-fault-tolerance/shenyu-plugin-ratelimiter/RateLimiterPlugin.java |
| HTTP代理 | shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-divide/DividePlugin.java |
| 负载均衡 | shenyu-loadbalancer/src/main/java/...//LoadBalancer.java |
| 数据同步 | shenyu-sync-data-center/shenyu-sync-data-websocket/.../WebsocketSyncDataService.java |
3. 代码结构观察
🏗️ 代码组织模式
ShenYu 的代码组织遵循以下模式:
1. 插件采用模板方法模式
// 抽象插件基类
public abstract class AbstractShenyuPlugin implements ShenyuPlugin {
@Override
public Mono<Void> execute(ServerWebExchange exchange, ShenyuPluginChain chain) {
// ① 模板方法:定义执行骨架
SelectorData selector = matchSelector(exchange); // 匹配选择器
if (selector == null) {
return chain.execute(exchange);
}
RuleData rule = matchRule(exchange, selector); // 匹配规则
if (rule == null) {
return chain.execute(exchange);
}
// ② 子类实现具体逻辑
return doExecute(exchange, chain, selector, rule);
}
// 留给子类实现
protected abstract Mono<Void> doExecute(...);
}
2. 数据缓存采用单例模式
// 位置: shenyu-plugin/shenyu-plugin-base/cache/BaseDataCache.java
public final class BaseDataCache {
private static final BaseDataCache INSTANCE = new BaseDataCache();
// 插件数据缓存
private final ConcurrentMap<String, PluginData> PLUGIN_MAP = new ConcurrentHashMap<>();
// 选择器数据缓存
private final ConcurrentMap<String, List<SelectorData>> SELECTOR_MAP = new ConcurrentHashMap<>();
public static BaseDataCache getInstance() {
return INSTANCE;
}
}
3. 数据同步采用观察者模式
// 监听数据变化
public interface DataChangedListener {
void onPluginChanged(List<PluginData> changed);
void onSelectorChanged(List<SelectorData> changed);
void onRuleChanged(List<RuleData> changed);
}
// Admin 推送数据时通知所有监听器
publisher.publishEvent(new DataChangedEvent(...));
🎨 设计模式识别
| 设计模式 | 应用场景 | 代码位置 |
|---|---|---|
| 责任链模式 | 插件链执行 | ShenyuPluginChain |
| 模板方法模式 | 插件抽象基类 | AbstractShenyuPlugin |
| 单例模式 | 数据缓存 | BaseDataCache |
| 观察者模式 | 数据变更通知 | DataChangedListener |
| 工厂模式 | 负载均衡器创建 | LoadBalancerFactory |
| 策略模式 | 多种负载均衡算法 | LoadBalancer 接口 |
| Builder模式 | DTO对象构建 | RuleData.Builder |
| SPI机制 | 插件扩展 | @SPI 注解 |
🔍 代码质量观察
✅ 优秀实践:
- 清晰的职责划分 - 每个模块功能单一,边界清晰
- 充分的抽象 - 接口与实现分离,易于扩展
- 完善的单元测试 - 核心模块测试覆盖率高
- 统一的代码风格 - 遵循 Checkstyle 规范
- 丰富的示例代码 -
shenyu-examples目录提供大量实例
⚠️ 需要关注的点:
- 响应式编程学习曲线 - 大量使用 Reactor,新手可能不适应
- 配置项较多 -
application.yml配置项众多,需逐步理解 - 模块间通信复杂 - Admin 与 Gateway 的数据同步有多种方式
💡 学习机会(潜在改进点)
这些点不是批评,而是你深入学习的好方向:
TODO 注释探索:
# 搜索 TODO/FIXME grep -r "TODO\|FIXME" shenyu-*/src --include="*.java"性能优化思考点:
- 插件链的执行性能(是否可以并行处理?)
- 选择器和规则的匹配算法(Trie树优化)
- 本地缓存的过期策略
代码重构练习:
- 尝试提取重复的判空逻辑
- 将长方法(>50行)拆分为小方法
- 优化嵌套层级深的代码
第二部分:技能需求清单 📚
1. 基础技能要求
🎓 编程语言和框架
Java 核心(必须):
- ✅ Java 17 语法特性(Records、Switch表达式、Text Blocks)
- ✅ Lambda 表达式与 Stream API
- ✅ 并发编程(CompletableFuture、线程池)
- ✅ 反射与注解
Spring 生态(必须):
- ✅ Spring Boot 3.x 自动配置原理
- ✅ Spring WebFlux 响应式编程(重点!)
- ✅ Spring AOP 切面编程
- ✅ Spring 依赖注入(DI)和控制反转(IoC)
响应式编程(重点难点):
// Reactor 核心概念必须掌握
Mono<String> mono = Mono.just("Hello"); // 0-1个元素
Flux<Integer> flux = Flux.range(1, 5); // 0-N个元素
// 常用操作符
mono.map(s -> s.toUpperCase()) // 转换
.flatMap(s -> callAnotherService(s)) // 异步调用
.filter(s -> s.length() > 5) // 过滤
.doOnNext(System.out::println) // 副作用
.subscribe(); // 触发执行
🔧 具体版本要求
| 依赖 | 版本 | 说明 |
|---|---|---|
| JDK | 17+ | 必须,使用了 Java 17 特性 |
| Spring Boot | 3.3.1 | 核心框架 |
| Reactor | (由Spring Boot管理) | 响应式编程库 |
| Netty | (由Spring Boot管理) | 网络通信层 |
| MyBatis | 3.0.3 | Admin 数据访问 |
| Dubbo | 3.2.14 | Dubbo协议支持 |
| gRPC | 1.65.1 | gRPC协议支持 |
⚠️ 版本兼容性提示:
- ShenYu 2.7.x 要求 JDK 17+,不兼容 JDK 8
- 如果你的业务系统是 JDK 8,只需升级 ShenYu Gateway 即可,业务系统无需升级
🛠️ 基础工具和概念
必备工具:
- ✅ Maven(项目构建)
- ✅ Git(版本控制)
- ✅ Docker(容器部署)
- ✅ Postman/Curl(接口测试)
- ✅ IDEA(推荐IDE)
数据库知识:
- ✅ MySQL 基本操作
- ✅ SQL 查询与索引优化
- ✅ 数据库连接池(HikariCP)
2. 进阶技能要求
🏛️ 架构模式和设计原则
必须理解的模式:
- 微服务架构 - ShenYu 本身就是微服务基础设施
- 插件化架构 - 核心功能以插件形式组织
- 事件驱动架构 - 数据变更通过事件传播
设计原则(SOLID):
- 单一职责 - 每个插件只做一件事
- 开闭原则 - 通过插件扩展,而不是修改核心代码
- 依赖倒置 - 依赖抽象(ShenyuPlugin接口),而非具体实现
🌐 领域特定知识
API 网关领域:
- 路由转发、负载均衡、限流熔断
- 灰度发布、A/B测试
- API 鉴权与签名
- 请求/响应转换
RPC 协议理解:
- HTTP/HTTPS、HTTP/2
- Dubbo 序列化与通信机制
- gRPC Protobuf
- WebSocket 长连接
服务治理概念:
- 服务注册与发现
- 配置中心
- 链路追踪
- 监控告警
3. 技能掌握程度建议
🌱 初学者(1-2周目标)
你应该能:
- 成功启动 Admin 和 Bootstrap
- 通过 Admin 界面配置一个简单的 HTTP 路由
- 理解 Selector 和 Rule 的区别
- 阅读
GlobalPlugin和DividePlugin源码
学习路径:
- 跟着 Quick Start 运行起来
- 阅读官方文档的"核心概念"部分
- 尝试修改
application.yml配置 - 在 IDEA 中打断点,跟踪一次完整请求
🚀 有经验的开发者(1-2月目标)
你应该能:
- 自定义开发一个简单插件
- 理解数据同步的多种实现方式
- 配置 Dubbo/gRPC 代理
- 排查常见问题(端口冲突、配置错误)
学习路径:
- 深入理解插件链执行机制
- 阅读 3-5 个核心插件源码
- 参考
shenyu-examples实现业务接入 - 学习 SPI 机制和自定义扩展
🏆 进阶贡献者(3月+目标)
你应该能:
- 为 ShenYu 贡献代码(修复 Bug 或新功能)
- 理解整体架构设计权衡
- 优化性能瓶颈
- 设计企业级部署方案
学习路径:
- 完整阅读核心模块源码
- 参与社区讨论,了解设计决策
- 尝试提交 Pull Request
- 研究性能测试与调优
第三部分:学习路径规划 🎯
1. 项目运行入口定位(快速上手)
🚀 一键启动指南(预计 30 分钟)
方式一:Docker 快速启动(推荐新手)
# ① 创建 Docker 网络
docker network create shenyu
# ② 启动 Admin(管理后台)
docker pull apache/shenyu-admin
docker run -d --name shenyu-admin \
-p 9095:9095 \
--net shenyu \
apache/shenyu-admin
# ③ 启动 Bootstrap(网关)
docker pull apache/shenyu-bootstrap
docker run -d --name shenyu-bootstrap \
-p 9195:9195 \
-e "shenyu.local.enabled=true" \
-e "SHENYU_SYNC_WEBSOCKET_URLS=ws://shenyu-admin:9095/websocket" \
--net shenyu \
apache/shenyu-bootstrap
# ④ 验证成功
# 访问 http://localhost:9095 (账号/密码: admin/123456)
# 网关地址: http://localhost:9195
方式二:源码本地启动(推荐深入学习)
# ① 克隆代码
git clone https://github.com/apache/shenyu.git
cd shenyu
# ② 编译项目(跳过测试加速)
mvn clean install -DskipTests -Dmaven.javadoc.skip=true
# ③ 初始化数据库(选择 H2 或 MySQL)
# 使用 H2(无需额外安装):
# Admin 启动时会自动初始化
# 使用 MySQL:
mysql -uroot -p < db/init/mysql/schema.sql
# ④ 启动 Admin
cd shenyu-admin
mvn spring-boot:run
# 或用 IDEA 运行 ShenyuAdminBootstrap.main()
# ⑤ 启动 Bootstrap(新终端)
cd shenyu-bootstrap
mvn spring-boot:run
# 或用 IDEA 运行 ShenyuBootstrapApplication.main()
# ⑥ 验证成功
curl http://localhost:9095/actuator/health # Admin 健康检查
curl http://localhost:9195/actuator/health # Gateway 健康检查
⚙️ 环境配置清单
必备软件:
| 软件 | 版本要求 | 下载地址 |
|---|---|---|
| JDK | 17+ | https://adoptium.net/ |
| Maven | 3.6+ | https://maven.apache.org/ |
| MySQL | 5.7+(可选) | https://dev.mysql.com/downloads/ |
| Docker | 20+(可选) | https://www.docker.com/ |
配置检查清单:
# ✅ 检查 Java 版本
java -version # 应显示 17 或更高
# ✅ 检查 Maven
mvn -version
# ✅ 检查端口占用
netstat -an | findstr "9095" # Windows
lsof -i:9095 # Mac/Linux
# ✅ 配置 Maven 镜像加速(国内用户)
# 编辑 ~/.m2/settings.xml,添加阿里云镜像
⚠️ 常见配置陷阱及解决方案
| 问题 | 现象 | 解决方案 |
|---|---|---|
| 端口冲突 | Address already in use |
修改 application.yml 中的 server.port |
| JDK版本不对 | Unsupported class file major version |
确保使用 JDK 17+ |
| 数据库连接失败 | Could not connect to database |
检查 application-mysql.yml 配置 |
| WebSocket 连接失败 | Gateway 无法同步配置 | 确认 Admin 的 9095 端口可访问 |
| 内存不足 | OutOfMemoryError |
增加 JVM 参数 -Xmx2g |
✅ 验证成功标志
1. Admin 启动成功:
# 日志中看到:
Started ShenyuAdminBootstrap in xx seconds
# 浏览器访问:
http://localhost:9095
# 能看到登录界面 → 成功!
# 默认账号: admin / 123456
2. Bootstrap 启动成功:
# 日志中看到:
Netty Http Server start success, on port : 9195
# 命令行测试:
curl http://localhost:9195/actuator/health
# 返回 {"status":"UP"} → 成功!
3. 数据同步成功:
# Bootstrap 日志中看到:
websocket sync data completed
# 在 Admin 界面"系统管理 -> 插件管理"中,
# 能看到多个插件(divide、dubbo、sign等)→ 成功!
4. 完整链路测试:
# 启动一个测试后端服务(端口 8080)
# 在 Admin 中配置一个 divide 插件规则
# 通过网关访问:
curl http://localhost:9195/your-api-path
# 能正常返回后端响应 → 完整链路打通!
2. 循序渐进学习计划(四阶段法)
📅 阶段一:环境搭建和项目启动(1-2 天)
目标:成功运行项目并能打个断点 🎯
任务清单:
- [ ] 安装 JDK 17 和 Maven
- [ ] 克隆代码并编译成功
- [ ] 启动 Admin 和 Bootstrap
- [ ] 登录 Admin 后台,浏览功能菜单
- [ ] 在 IDEA 中给
ShenyuWebHandler.handle()打断点 - [ ] 发送一个请求,断点生效,观察调用栈
实战练习:
# 1. 启动项目后,运行示例服务
cd shenyu-examples/shenyu-examples-http
mvn spring-boot:run
# 2. 在 Admin 中配置路由
插件列表 -> divide -> 选择器 -> 新增
名称: test-selector
匹配方式: 前缀匹配
条件: uri = /http/**
规则列表 -> 新增
名称: test-rule
匹配条件: uri = /http/**
处理: {
"loadBalance":"random","retry":0}
# 3. 测试转发
curl http://localhost:9195/http/test/findByUserId?userId=1
# 4. 观察日志,理解请求流程
学习资源:
- 官方文档:https://shenyu.apache.org/zh/docs/quick-start/quick-start-http
- 视频教程:搜索"Apache ShenYu 入门"
📅 阶段二:核心流程理解(3-5 天)
目标:追踪一个完整业务流程,画出自己的流程图 🎯
任务清单:
- [ ] 理解 Selector 和 Rule 的作用
- [ ] 阅读
ShenyuWebHandler源码 - [ ] 阅读
GlobalPlugin和DividePlugin源码 - [ ] 使用调试器逐步跟踪请求
- [ ] 画出插件链执行流程图
- [ ] 理解数据同步机制(WebSocket)
核心文件阅读顺序:
// ① 请求入口
shenyu-web/handler/ShenyuWebHandler.java
├─ handle() // 总入口
└─ DefaultShenyuPluginChain.execute() // 插件链
// ② 插件接口
shenyu-plugin-api/ShenyuPlugin.java
├─ execute() // 执行逻辑
├─ skip() // 是否跳过
└─ getOrder() // 执行顺序
// ③ 插件基类
shenyu-plugin-base/AbstractShenyuPlugin.java
├─ execute() // 模板方法
├─ matchSelector() // 选择器匹配
├─ matchRule() // 规则匹配
└─ doExecute() // 子类实现
// ④ HTTP代理插件
shenyu-plugin-divide/DividePlugin.java
└─ doExecute() // 负载均衡+HTTP调用
// ⑤ 数据缓存
shenyu-plugin-base/cache/BaseDataCache.java
├─ PLUGIN_MAP // 插件缓存
├─ SELECTOR_MAP // 选择器缓存
└─ RULE_MAP // 规则缓存
实战练习:
// 1. 自己实现一个简单的"日志打印插件"
@Component
public class MyLogPlugin implements ShenyuPlugin {
@Override
public Mono<Void> execute(ServerWebExchange exchange, ShenyuPluginChain chain) {
String path = exchange.getRequest().getPath().value();
System.out.println("===== 请求路径: " + path + " =====");
return chain.execute(exchange); // 继续执行下一个插件
}
@Override
public int getOrder() {
return 0; // 最先执行
}
@Override
public String named() {
return "myLog";
}
}
// 2. 启动项目,观察日志输出
📅 阶段三:模块深入和定制开发(1-2 周)
目标:能修改或扩展一个现有功能 🎯
任务清单:
- [ ] 深入理解 3-5 个插件源码(如 Sign、RateLimiter、Dubbo)
- [ ] 理解 SPI 机制,查看
@SPI注解的使用 - [ ] 尝试自定义一个负载均衡算法
- [ ] 学习数据同步的其他方式(Zookeeper、Nacos)
- [ ] 接入一个 Dubbo 服务到网关
深入学习插件:
| 插件 | 功能 | 学习重点 |
|---|---|---|
| SignPlugin | API签名验证 | 参数提取、签名算法 |
| RateLimiterPlugin | 限流 | Redis+Lua脚本、令牌桶算法 |
| DubboPlugin | Dubbo代理 | 泛化调用、参数映射 |
| HystrixPlugin | 熔断 | Hystrix 命令封装 |
| WafPlugin | 安全防护 | 黑白名单、SQL注入检测 |
实战练习:自定义负载均衡器:
// 1. 实现 LoadBalancer 接口
@Join // SPI 标记
public class MyWeightedLoadBalancer implements LoadBalancer {
@Override
public Upstream select(List<Upstream> upstreams, String ip) {
// 实现加权随机算法
int totalWeight = upstreams.stream()
.mapToInt(Upstream::getWeight)
.sum();
int random = ThreadLocalRandom.current().nextInt(totalWeight);
for (Upstream upstream : upstreams) {
random -= upstream.getWeight();
if (random < 0) {
return upstream;
}
}
return upstreams.get(0);
}
}
// 2. 在 META-INF/shenyu/ 目录下创建 SPI 文件
// org.apache.shenyu.loadbalancer.spi.LoadBalancer
myWeighted=com.example.MyWeightedLoadBalancer
// 3. 在规则配置中使用
{
"loadBalance":"myWeighted"}
📅 阶段四:架构理解和贡献指南(2 周+)
目标:能理解技术选型原因,并尝试修复一个简单 issue 🎯
任务清单:
- [ ] 阅读完整架构设计文档
- [ ] 理解为什么选择 Reactor 而不是传统 Servlet
- [ ] 理解为什么使用 Disruptor 队列
- [ ] 参与社区讨论,提问和回答问题
- [ ] 在 GitHub 上找一个
good first issue并尝试修复 - [ ] 提交你的第一个 Pull Request
架构设计权衡:
| 技术选型 | 原因 | 权衡 |
|---|---|---|
| Reactor | 异步非阻塞,高并发 | 学习曲线陡峭 |
| Netty | 高性能网络通信 | 配置复杂 |
| Disruptor | 无锁队列,低延迟 | 内存占用较大 |
| ConcurrentHashMap | 本地缓存,极速读取 | 集群间需同步 |
| WebSocket | 实时推送配置 | 需保持长连接 |
贡献指南:
# 1. Fork 项目到你的 GitHub
# 2. 克隆你的 Fork
git clone https://github.com/YOUR_USERNAME/shenyu.git
cd shenyu
# 3. 创建分支
git checkout -b fix-issue-1234
# 4. 修改代码并测试
# 确保通过所有单元测试
mvn test
# 5. 提交代码
git add .
git commit -m "fix: 修复 issue #1234 的问题"
git push origin fix-issue-1234
# 6. 在 GitHub 上创建 Pull Request
# 7. 等待 Code Review
3. 学习路径流程图
graph TB
Start([开始学习 ShenYu]) --> Stage1{阶段一<br/>环境搭建}
Stage1 -->|1-2天| Check1{能成功启动?}
Check1 -->|否| Trouble1[查看常见问题]
Trouble1 --> Stage1
Check1 -->|是| Stage2{阶段二<br/>核心流程理解}
Stage2 -->|3-5天| Check2{能追踪请求流程?}
Check2 -->|否| Doc1[阅读官方文档]
Doc1 --> Stage2
Check2 -->|是| Stage3{阶段三<br/>模块深入}
Stage3 -->|1-2周| Check3{能自定义插件?}
Check3 -->|否| Code1[参考示例代码]
Code1 --> Stage3
Check3 -->|是| Stage4{阶段四<br/>架构理解}
Stage4 -->|2周+| Check4{能贡献代码?}
Check4 -->|否| Community[参与社区讨论]
Community --> Stage4
Check4 -->|是| End([成为 ShenYu 专家!])
style Start fill:#e1f5e1
style End fill:#ffe1e1
style Check1 fill:#fff9e1
style Check2 fill:#fff9e1
style Check3 fill:#fff9e1
style Check4 fill:#fff9e1
第四部分:实践建议和进阶指导 💡
1. 调试技巧和常见陷阱
🐛 调试技巧
技巧1:善用日志级别
# application.yml
logging:
level:
org.apache.shenyu: debug # ShenYu 核心日志
org.apache.shenyu.plugin: trace # 插件详细日志
reactor.netty: debug # Netty 网络日志
技巧2:关键断点位置
// ① 请求入口
ShenyuWebHandler.handle()
→ 观察请求开始
// ② 插件执行
DefaultShenyuPluginChain.execute()
→ 观察插件链
// ③ 选择器匹配
AbstractShenyuPlugin.matchSelector()
→ 观察路由匹配
// ④ 后端调用
DividePlugin.doExecute()
→ 观察代理转发
技巧3:Reactor 调试
// 开启 Reactor 调试模式(启动类中)
static {
Hooks.onOperatorDebug(); // 会显示完整的异步调用栈
}
⚠️ 常见陷阱(Top 5)
1. 配置未生效
❌ 问题: 修改了 Admin 配置,但 Gateway 没反应
✅ 排查:
- 检查 WebSocket 连接是否正常
- 查看 Gateway 日志是否有 "sync data completed"
- 检查插件是否启用(Admin -> 插件管理 -> 状态)
2. 端口冲突
❌ 问题: Address already in use: 9095
✅ 解决:
# Windows
netstat -ano | findstr "9095"
taskkill /PID <PID> /F
# Linux/Mac
lsof -i:9095
kill -9 <PID>
3. Selector/Rule 不匹配
❌ 问题: 请求返回 404,但后端服务正常
✅ 排查:
- 检查 Selector 的匹配条件(uri、header、method等)
- 查看 Rule 的条件是否覆盖当前请求
- 注意匹配顺序(sort字段)
4. Dubbo 泛化调用失败
❌ 问题: No provider available
✅ 排查:
- 确认 Dubbo 服务已注册到注册中心
- 检查 metadata(服务元数据)是否正确
- 确认网络连通性
5. 内存溢出
❌ 问题: OutOfMemoryError: Java heap space
✅ 解决:
# 增加堆内存
java -Xms2g -Xmx4g -jar shenyu-bootstrap.jar
# 或在 IDEA 中设置 VM Options:
-Xms2g -Xmx4g -XX:+HeapDumpOnOutOfMemoryError
2. 扩展练习建议
🌟 练习1:修改响应头(难度:⭐)
目标:为所有响应添加自定义 Header
@Component
public class CustomHeaderPlugin implements ShenyuPlugin {
@Override
public Mono<Void> execute(ServerWebExchange exchange, ShenyuPluginChain chain) {
return chain.execute(exchange).doFinally(signal -> {
exchange.getResponse().getHeaders().add("X-Powered-By", "ShenYu");
});
}
@Override
public int getOrder() {
return Integer.MAX_VALUE; // 最后执行
}
}
🌟 练习2:实现IP黑名单(难度:⭐⭐)
目标:拦截黑名单 IP 的请求
@Component
public class IpBlacklistPlugin implements ShenyuPlugin {
private final Set<String> blacklist = Set.of("192.168.1.100", "10.0.0.50");
@Override
public Mono<Void> execute(ServerWebExchange exchange, ShenyuPluginChain chain) {
String clientIp = getClientIp(exchange);
if (blacklist.contains(clientIp)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.execute(exchange);
}
private String getClientIp(ServerWebExchange exchange) {
return exchange.getRequest()
.getHeaders()
.getFirst("X-Forwarded-For");
}
}
🌟 练习3:添加数据库表(难度:⭐⭐⭐)
目标:为 API 调用添加计数统计功能
-- 1. 创建表
CREATE TABLE api_stat (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
api_path VARCHAR(255),
call_count INT DEFAULT 0,
last_call_time DATETIME
);
-- 2. 实现统计插件
@Component
public class ApiStatPlugin implements ShenyuPlugin {
@Autowired
private ApiStatMapper mapper;
@Override
public Mono<Void> execute(ServerWebExchange exchange, ShenyuPluginChain chain) {
String path = exchange.getRequest().getPath().value();
// 异步更新统计(不阻塞主流程)
Mono.fromRunnable(() -> mapper.incrementCount(path))
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
return chain.execute(exchange);
}
}
🌟 练习4:动态路由切换(难度:⭐⭐⭐⭐)
目标:根据请求头实现灰度发布
@Component
public class GrayReleasePlugin extends AbstractShenyuPlugin {
@Override
protected Mono<Void> doExecute(ServerWebExchange exchange,
ShenyuPluginChain chain,
SelectorData selector,
RuleData rule) {
String version = exchange.getRequest().getHeaders().getFirst("X-Version");
List<Upstream> upstreams = getUpstreams(selector);
// 根据版本选择后端
Upstream target = upstreams.stream()
.filter(u -> matchVersion(u, version))
.findFirst()
.orElseGet(() -> upstreams.get(0)); // 降级到默认版本
// 设置目标地址
exchange.getAttributes().put("targetUrl", buildUrl(target, exchange));
return chain.execute(exchange);
}
}
3. 参与贡献的途径
🤝 社区位置
- GitHub:https://github.com/apache/shenyu
- 官网:https://shenyu.apache.org
- 邮件列表:dev@shenyu.apache.org
- Slack:加入 Apache Slack #shenyu 频道
🔍 寻找 Good First Issue
# 1. 访问 GitHub Issues
https://github.com/apache/shenyu/issues
# 2. 筛选标签
Label: good first issue
# 3. 常见的新手任务类型
- 文档改进
- 单元测试补充
- 代码注释优化
- 简单的 Bug 修复
📋 代码规范要求
提交前检查清单:
# ✅ 代码格式检查
mvn checkstyle:check
# ✅ 单元测试
mvn test
# ✅ 许可证检查
mvn apache-rat:check
# ✅ 代码编译
mvn clean install -DskipTests
提交信息规范:
# 格式: <type>(<scope>): <subject>
# 示例:
feat(plugin): 添加新的限流插件
fix(admin): 修复选择器删除时的空指针异常
docs(readme): 更新快速开始文档
test(divide): 补充 DividePlugin 的单元测试
第五部分:技术栈学习指引 🌐
1. 官方文档定位
📚 核心技术栈文档
| 技术 | 官方文档 | 重点章节 |
|---|---|---|
| Spring Boot | https://spring.io/projects/spring-boot | Auto Configuration、Actuator |
| Spring WebFlux | https://docs.spring.io/spring-framework/reference/web/webflux.html | Reactive Core、WebClient |
| Project Reactor | https://projectreactor.io/docs | Mono/Flux、Operators |
| Netty | https://netty.io/wiki/ | User Guide、Best Practices |
| MyBatis | https://mybatis.org/mybatis-3/ | Dynamic SQL、Plugins |
| Apache Dubbo | https://dubbo.apache.org/zh-cn/ | 泛化调用、协议详解 |
📖 ShenYu 自身文档
必读章节(按优先级):
- Quick Start - 15分钟快速上手
- Plugin Center - 所有插件的使用说明
- Developer Guide - 自定义插件开发
- Deployment - 生产环境部署
- Design - 架构设计文档
文档地址:
2. 学习路径建议
🎯 技能学习顺序
graph LR
A[Java基础] --> B[Spring Boot]
B --> C[Spring WebFlux]
C --> D[Project Reactor]
D --> E[ShenYu 架构]
A --> F[设计模式]
F --> E
B --> G[微服务概念]
G --> E
时间分配建议(总计 2-3 月):
- Java 17 新特性 - 1周
- Spring WebFlux + Reactor - 2-3周(重点难点)
- ShenYu 核心概念 - 1周
- ShenYu 源码阅读 - 4-6周
- 实战项目练习 - 2-4周
🔑 核心概念优先级
立即掌握(⭐⭐⭐⭐⭐):
- Reactor 的 Mono/Flux
- WebFlux 的异步处理模型
- ShenYu 的 Selector & Rule
- 插件链执行机制
逐步理解(⭐⭐⭐):
- Netty 的 EventLoop
- MyBatis 的动态 SQL
- Disruptor 的无锁队列
- 各种数据同步方式
可选进阶(⭐):
- Dubbo 的底层通信
- gRPC 的 Protobuf
- Kubernetes Ingress
- 分布式追踪
3. 工具与环境配置指南
🛠️ IDEA 推荐配置
# 1. 安装插件
- Lombok Plugin
- Maven Helper
- Rainbow Brackets
- GitToolBox
# 2. 代码风格导入
File -> Settings -> Editor -> Code Style -> Import Scheme
导入 script/shenyu_checkstyle.xml
# 3. 运行配置模板
Run -> Edit Configurations -> Templates -> Spring Boot
Environment Variables: shenyu.local.enabled=true
VM Options: -Xms512m -Xmx2048m
🐳 Docker 快捷命令
# 查看日志
docker logs -f shenyu-admin
docker logs -f shenyu-bootstrap
# 进入容器
docker exec -it shenyu-admin bash
# 重启容器
docker restart shenyu-admin
# 清理环境
docker-compose down -v
docker system prune -af
4. 进阶拓展方向
📰 技术博客推荐
ShenYu 官方博客:
核心贡献者博客(搜索关键词):
- "Apache ShenYu 架构解析"
- "API 网关设计实践"
- "Spring WebFlux 性能优化"
🎤 相关技术大会
- ApacheCon - Apache 基金会年度大会
- QCon - 软件架构与实践大会
- ArchSummit - 全球架构师峰会
💬 社区与论坛
| 平台 | 链接 | 适用场景 |
|---|---|---|
| GitHub Discussions | https://github.com/apache/shenyu/discussions | 英文讨论,官方响应快 |
| Slack | Apache Slack #shenyu | 实时交流 |
| 邮件列表 | dev@shenyu.apache.org | 重要决策讨论 |
| 知乎/掘金 | 搜索"Apache ShenYu" | 中文教程和经验分享 |
结语 🎓
恭喜你!如果你读到这里,说明你已经掌握了学习 Apache ShenYu 的完整路线图。
记住几个关键点:
- 先跑起来,再深入学习 - 不要一开始就啃源码
- 动手实践 > 阅读文档 - 自己写一个插件胜过看十篇文章
- 响应式编程是难点 - 多花时间理解 Reactor
- 参与社区 - 提问、回答、贡献代码,你会学得更快
最后,记得享受学习的过程!ShenYu 是一个设计精良的项目,你会从中学到很多架构设计的精髓。
有任何问题,随时在社区提问。Good luck! 🚀