mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 20:33:03 +00:00
190 lines
4.4 KiB
Markdown
190 lines
4.4 KiB
Markdown
# ⚠️ Nashorn引擎限制说明
|
||
|
||
## 问题描述
|
||
|
||
Nashorn JavaScript引擎(Java 8-14自带)**无法中断正在执行的JavaScript代码**。
|
||
|
||
这是Nashorn引擎的一个已知限制,无法通过编程方式解决。
|
||
|
||
## 具体表现
|
||
|
||
### 症状
|
||
当JavaScript代码包含无限循环时:
|
||
```javascript
|
||
while(true) {
|
||
var x = 1 + 1;
|
||
}
|
||
```
|
||
|
||
会出现以下情况:
|
||
1. ✅ 30秒后客户端收到超时错误
|
||
2. ❌ Worker线程继续执行无限循环
|
||
3. ❌ 线程被永久阻塞,无法释放
|
||
4. ❌ 日志持续输出线程阻塞警告
|
||
|
||
### 日志示例
|
||
```
|
||
WARN -> [-thread-checker] i.vertx.core.impl.BlockedThreadChecker:
|
||
Thread Thread[playground-executor-1,5,main] has been blocked for 60249 ms, time limit is 60000 ms
|
||
```
|
||
|
||
## 为什么无法中断?
|
||
|
||
### 尝试过的方案
|
||
1. ❌ `Thread.interrupt()` - Nashorn不响应中断信号
|
||
2. ❌ `Future.cancel(true)` - 无法强制停止Nashorn
|
||
3. ❌ `ExecutorService.shutdownNow()` - 只能停止整个线程池
|
||
4. ❌ `ScriptContext.setErrorWriter()` - 无法注入中断逻辑
|
||
5. ❌ 自定义ClassFilter - 无法过滤语言关键字
|
||
|
||
### 根本原因
|
||
- Nashorn使用JVM字节码执行JavaScript
|
||
- 无限循环被编译成JVM字节码级别的跳转
|
||
- 没有安全点(Safepoint)可以插入中断检查
|
||
- `while(true)` 不会调用任何Java方法,完全在JVM栈内执行
|
||
|
||
## 现有防护措施
|
||
|
||
### 1. ✅ 客户端超时(已实现)
|
||
```java
|
||
executionFuture.toCompletionStage()
|
||
.toCompletableFuture()
|
||
.orTimeout(30, TimeUnit.SECONDS)
|
||
```
|
||
- 30秒后返回错误给用户
|
||
- 用户知道脚本超时
|
||
- 但线程仍被阻塞
|
||
|
||
### 2. ✅ 前端危险代码检测(已实现)
|
||
```javascript
|
||
// 检测无限循环模式
|
||
/while\s*\(\s*true\s*\)/gi
|
||
/for\s*\(\s*;\s*;\s*\)/gi
|
||
```
|
||
- 执行前警告用户
|
||
- 需要用户确认
|
||
- 依赖用户自觉
|
||
|
||
### 3. ✅ Worker线程池隔离
|
||
- 使用独立的 `playground-executor` 线程池
|
||
- 最多16个线程
|
||
- 不影响主服务的事件循环
|
||
|
||
### 4. ✅ 代码长度限制
|
||
- 最大128KB代码
|
||
- 减少内存消耗
|
||
- 但无法防止无限循环
|
||
|
||
## 影响范围
|
||
|
||
### 最坏情况
|
||
- 16个恶意请求可以耗尽所有Worker线程
|
||
- 后续所有Playground请求会等待
|
||
- 主服务不受影响(独立线程池)
|
||
- 需要重启服务才能恢复
|
||
|
||
### 实际影响
|
||
- 取决于使用场景
|
||
- 如果是公开服务,有被滥用风险
|
||
- 如果是内部工具,风险较低
|
||
|
||
## 解决方案
|
||
|
||
### 短期方案(已实施)
|
||
1. ✅ 前端检测和警告
|
||
2. ✅ 超时返回错误
|
||
3. ✅ 文档说明限制
|
||
4. ⚠️ 监控线程阻塞告警
|
||
5. ⚠️ 限流(已有RateLimiter)
|
||
|
||
### 中期方案(建议)
|
||
1. 添加IP黑名单机制
|
||
2. 添加滥用检测(同一IP多次触发超时)
|
||
3. 考虑添加验证码
|
||
4. 定期重启被阻塞的线程池
|
||
|
||
### 长期方案(需大量工作)
|
||
1. **迁移到GraalVM JavaScript引擎**
|
||
- 支持CPU时间限制
|
||
- 可以强制中断
|
||
- 更好的性能
|
||
- 但需要额外依赖
|
||
|
||
2. **使用独立进程执行**
|
||
- 完全隔离
|
||
- 可以强制杀死进程
|
||
- 但复杂度高
|
||
|
||
3. **代码静态分析**
|
||
- 分析AST检测循环
|
||
- 注入超时检查代码
|
||
- 但可能被绕过
|
||
|
||
## 运维建议
|
||
|
||
### 监控指标
|
||
```bash
|
||
# 监控线程阻塞告警
|
||
tail -f logs/*/run.log | grep "Thread blocked"
|
||
|
||
# 监控超时频率
|
||
tail -f logs/*/run.log | grep "JavaScript执行超时"
|
||
```
|
||
|
||
### 告警阈值
|
||
- 单个IP 1小时内超时 >3次 → 警告
|
||
- Worker线程阻塞 >80% → 严重
|
||
- 持续阻塞 >5分钟 → 考虑重启
|
||
|
||
### 应急方案
|
||
```bash
|
||
# 重启服务释放被阻塞的线程
|
||
./bin/stop.sh
|
||
./bin/run.sh
|
||
```
|
||
|
||
## 用户建议
|
||
|
||
### ✅ 建议的代码模式
|
||
```javascript
|
||
// 使用有限循环
|
||
for(var i = 0; i < 1000; i++) {
|
||
// 处理逻辑
|
||
}
|
||
|
||
// 使用超时保护
|
||
var maxIterations = 10000;
|
||
var count = 0;
|
||
while(condition && count++ < maxIterations) {
|
||
// 处理逻辑
|
||
}
|
||
```
|
||
|
||
### ❌ 禁止的代码模式
|
||
```javascript
|
||
// 无限循环
|
||
while(true) { }
|
||
for(;;) { }
|
||
|
||
// 无退出条件的循环
|
||
while(someCondition) {
|
||
// someCondition永远为true
|
||
}
|
||
|
||
// 递归炸弹
|
||
function boom() { return boom(); }
|
||
```
|
||
|
||
## 相关链接
|
||
|
||
- [Nashorn Engine Issues](https://github.com/openjdk/nashorn/issues)
|
||
- [GraalVM JavaScript](https://www.graalvm.org/javascript/)
|
||
- [Java Script Engine Comparison](https://benchmarksgame-team.pages.debian.net/benchmarksgame/)
|
||
|
||
---
|
||
|
||
**最后更新**: 2025-11-29
|
||
**状态**: ⚠️ 已知限制,已采取缓解措施
|
||
**建议**: 如需更严格的控制,考虑迁移到GraalVM JavaScript引擎
|
||
|