Files
jsowell-charger-web/improve_record.md

213 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 充电桩频繁登录问题优化记录
## 问题背景
**发生时间**: 2026-03-21 06:00 左右
**问题描述**: 充电桩出现频繁登录的情况
**初步原因**: 项目没有及时回复桩端的心跳消息,未及时回复超过 3 次后桩端会发起重连
---
## 问题分析
### 问题 1: Pipeline 配置存在潜在阻塞点(最关键)
**文件**: `jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java:41`
**问题描述**:
- `nettyServerHandler` 绑定到 `businessGroup` 线程池32 线程)
-`echoServerHandler` **没有绑定到任何 EventExecutorGroup**,它会在 **IO 线程**Worker 线程)中执行
- 如果 IO 线程被其他任务阻塞,心跳回复就无法及时发送
**影响**: 这是导致心跳回复不及时的最主要原因!
---
### 问题 2: 心跳处理中存在阻塞的数据库操作
**文件**: `jsowell-netty/src/main/java/com/jsowell/netty/handler/yunkuaichong/HeartbeatRequestHandler.java:54`
**问题描述**:
- `saveLastTimeAndCheckChannel()` 是**同步**执行的,包含 Redis 写操作
- 如果 Redis 响应慢,会阻塞心跳响应
---
### 问题 3: `PileChannelEntity.checkChannel()` 使用同步阻塞遍历
**文件**: `jsowell-common/src/main/java/com/jsowell/common/enums/ykc/PileChannelEntity.java:108-115`
**问题描述**:
- `getPileSnByChannelId()` 使用线性遍历整个 `manager.entrySet()`
- 当连接数很多时,这个遍历操作会很慢,并且会阻塞执行线程
---
### 问题 4: 线程池配置可能导致任务堆积
**文件**: `jsowell-framework/src/main/java/com/jsowell/framework/config/ThreadPoolConfig.java:63`
**问题描述**:
- 使用 `CallerRunsPolicy` 拒绝策略,当队列满时,任务会在调用者线程中执行
- 如果 Netty 的 IO 线程或 businessGroup 线程作为调用者,会**阻塞这些关键线程**
---
## 优化方案
### 优化 1: 修复 Pipeline 配置优先级P0
**文件**: `jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerChannelInitializer.java:41`
**修改内容**:
```java
// 修改前
pipeline.addLast(businessGroup, nettyServerHandler);
pipeline.addLast(echoServerHandler);
// 修改后
pipeline.addLast(businessGroup, nettyServerHandler);
pipeline.addLast(businessGroup, echoServerHandler); // 回复Handler也绑定到业务线程池
```
**效果**: 避免心跳回复被 IO 线程阻塞
---
### 优化 2: 优化 PileChannelEntity 使用双向映射优先级P0
**文件**: `jsowell-common/src/main/java/com/jsowell/common/enums/ykc/PileChannelEntity.java`
**修改内容**:
1. 新增反向映射 `channelIdToPileSnMap`
2. `getPileSnByChannelId()` 从 O(n) 优化到 O(1)
3. 同步维护反向映射checkChannel、removeByPileSn、removeByChannelId
**效果**: 当连接数多时,查询性能大幅提升
---
### 优化 3: 修改线程池拒绝策略优先级P1
**文件**: `jsowell-framework/src/main/java/com/jsowell/framework/config/ThreadPoolConfig.java`
**修改内容**:
```java
// 修改前
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
private final int queueCapacity = 2000;
// 修改后
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
private final int queueCapacity = 5000; // 增大队列容量
```
**效果**: 避免阻塞关键线程Netty IO 线程、业务线程池)
---
### 优化 4: 心跳处理完全异步化优先级P1
**文件**: `jsowell-netty/src/main/java/com/jsowell/netty/handler/yunkuaichong/HeartbeatRequestHandler.java:67-85`
**修改内容**:
```java
// 修改前saveLastTimeAndCheckChannel 同步执行
saveLastTimeAndCheckChannel(pileSn, channel);
CompletableFuture.runAsync(() -> updateStatus(...), executor);
// 修改后:先返回心跳应答,再异步处理
byte[] response = getResult(ykcDataProtocol, messageBody);
CompletableFuture.runAsync(() -> {
saveLastTimeAndCheckChannel(pileSn, channel);
pileBasicInfoService.updateStatus(...);
}, executor);
return response;
```
**效果**: 心跳回复不受 Redis/数据库操作影响
---
### 优化 5: 增加心跳处理耗时监控优先级P2
**文件**: `jsowell-netty/src/main/java/com/jsowell/netty/server/yunkuaichong/NettyServerHandler.java:174-180`
**修改内容**:
```java
long elapsed = System.currentTimeMillis() - startTime;
int warnThreshold = "0x03".equals(frameTypeStr) ? 50 : 200;
if (elapsed > warnThreshold) {
log.error("【性能警告】消息处理耗时过长: {}ms, 帧类型: {}, pileSn: {}", elapsed, frameTypeStr, pileSn);
}
```
**效果**: 便于后续排查性能问题
---
## 修改文件清单
| 序号 | 文件路径 | 修改内容 |
|------|----------|----------|
| 1 | `jsowell-netty/.../NettyServerChannelInitializer.java` | EchoServerHandler 绑定到业务线程池 |
| 2 | `jsowell-common/.../PileChannelEntity.java` | 新增反向映射,优化查询性能 |
| 3 | `jsowell-framework/.../ThreadPoolConfig.java` | 修改拒绝策略,增大队列容量 |
| 4 | `jsowell-netty/.../HeartbeatRequestHandler.java` | 心跳处理完全异步化 |
| 5 | `jsowell-netty/.../NettyServerHandler.java` | 增加消息处理耗时监控 |
---
## 测试建议
### 1. 编译打包
```bash
mvn clean package -DskipTests
```
### 2. 部署后观察日志
关注以下指标:
- 是否还有桩频繁登录的情况
- 心跳处理耗时是否降低
- 是否出现 `【性能警告】` 日志
### 3. 监控 Redis 和数据库性能
- Redis 响应时间应 < 5ms
- 数据库连接池应充足
- 检查 Redis 服务性能
### 4. 持续监控
部署后持续观察 24 小时,确认问题是否彻底解决
---
## 预期效果
| 指标 | 优化前 | 优化后 |
|------|--------|--------|
| 心跳回复延迟 | 可能被阻塞 | < 10ms |
| 桩频繁登录 | 存在 | 消除 |
| 连接查询性能 | O(n) | O(1) |
| 线程阻塞风险 | 高 | 低 |
---
## 相关文档
- `CLAUDE.md` - 项目架构文档
- `jsowell-netty/CLAUDE.md` - Netty 模块文档
- `jsowell-common/CLAUDE.md` - 通用模块文档
---
## 记录信息
| 项目 | 内容 |
|------|------|
| 记录人 | Claude Code |
| 记录时间 | 2026-03-21 |
| Git 分支 | dev |
| 提交建议 | 建议创建新分支提交,便于回滚 |