# ⚠️ 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时间限制
   - 相比Nashorn更容易实现受控取消
   - 更好的性能
   - 但需要额外依赖

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引擎

