Skip to content

Commit add61c1

Browse files
committed
refactor(msgAudit): 优化SDK实例管理和资源清理逻辑
- 移除 managedSdks 集合及相关代码,不再统一追踪所有SDK实例 - 采用ThreadLocal懒初始化,线程内部复用SDK实例 - closeAllSdks() 方法改为调用 closeThreadLocalSdk(),只清理当前线程资源 - 保留旧API方法,内部调用统一替换为 getOrInitThreadLocalSdk() - 文档更新,明确多线程和多实例场景下的SDK管理及DestroySdk调用规范 - 删除手动调用 Finance.DestroySdk() 的旧测试代码示例,防止误用造成内存泄漏
1 parent 03c5e8e commit add61c1

File tree

2 files changed

+13
-31
lines changed

2 files changed

+13
-31
lines changed

docs/giggly-pondering-turtle.md

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,8 @@ Thread C: init SDK_C → ...
5252

5353
**新增字段:**
5454
```java
55-
/** 每个线程持有独立SDK实例 */
55+
/** 每个线程持有独立 SDK 实例,懒初始化,线程内跨调用复用 */
5656
private final ThreadLocal<Long> threadLocalSdk = new ThreadLocal<>();
57-
58-
/** 跟踪所有已创建SDK,用于统一清理 */
59-
private final Set<Long> managedSdks = ConcurrentHashMap.newKeySet();
6057
```
6158

6259
**废弃字段/方法:**
@@ -77,7 +74,6 @@ private long getOrInitThreadLocalSdk() throws WxErrorException {
7774
}
7875
long newSdk = createSdk();
7976
threadLocalSdk.set(newSdk);
80-
managedSdks.add(newSdk);
8177
log.info("线程 [{}] 初始化会话存档SDK成功,sdk={}", Thread.currentThread().getName(), newSdk);
8278
return newSdk;
8379
}
@@ -99,22 +95,17 @@ public void closeThreadLocalSdk() {
9995
Long sdk = threadLocalSdk.get();
10096
if (sdk != null && sdk > 0) {
10197
Finance.DestroySdk(sdk);
102-
managedSdks.remove(sdk);
10398
threadLocalSdk.remove();
10499
log.info("线程 [{}] 关闭会话存档SDK,sdk={}", Thread.currentThread().getName(), sdk);
105100
}
106101
}
107102

108103
/**
109-
* 关闭所有线程持有的SDK。应用关闭时调用(如Spring @PreDestroy / Shutdown Hook)。
104+
* 等同于 closeThreadLocalSdk(),无法感知其他线程的SDK。
105+
* 不再遍历集合销毁所有SDK(已去掉 managedSdks 追踪)。
110106
*/
111107
public void closeAllSdks() {
112-
managedSdks.forEach(sdk -> {
113-
Finance.DestroySdk(sdk);
114-
log.info("关闭会话存档SDK,sdk={}", sdk);
115-
});
116-
managedSdks.clear();
117-
threadLocalSdk.remove();
108+
closeThreadLocalSdk();
118109
}
119110
```
120111

@@ -123,10 +114,10 @@ public void closeAllSdks() {
123114
- 移除 try-finally 中的 `releaseSdk(sdk)` 调用(SDK不再每次释放)
124115
- 方法变得更简洁:直接使用sdk,无需包装计数
125116

126-
**保留旧API方法不变(getChatDatas / getDecryptData / getChatPlainText / getMediaFile):**
117+
**保留旧API方法(getChatDatas / getDecryptData / getChatPlainText / getMediaFile):**
127118
- 保持 @Deprecated 标注
128-
- 内部调用改为 `getOrInitThreadLocalSdk()` 以保持一致性(旧方法也受益于ThreadLocal)
129-
- 移除对 `initSdk()` 的依赖
119+
- `getChatDatas` 仅将 `initSdk()` 替换为 `getOrInitThreadLocalSdk()`(因 initSdk 已移除,必须改),其余逻辑、注释、代码风格完全不动
120+
- `getDecryptData` / `getChatPlainText` / `getMediaFile` 两个重载:**完全不改动**(保留原注释、`e.printStackTrace()` 等)
130121

131122
### 2. WxCpMsgAuditService(接口新增)
132123

@@ -138,8 +129,8 @@ public void closeAllSdks() {
138129
void closeThreadLocalSdk();
139130

140131
/**
141-
* 关闭所有会话存档SDK实例
142-
* 适用于应用关闭时(如Spring Bean销毁阶段)统一释放资源
132+
* 等同于 closeThreadLocalSdk(),无法感知其他线程的SDK
133+
* 适用于应用关闭时(如Spring Bean销毁阶段)清理当前线程资源
143134
*/
144135
void closeAllSdks();
145136
```
@@ -191,6 +182,8 @@ try {
191182
2. **独立线程无需显式关闭**:线程销毁时JVM会回收,但 `Finance.DestroySdk()` 不会自动调用。若关注资源,建议加 finally 清理。
192183
3. **多企业(多CorpId)场景**`threadLocalSdk` 是实例字段(非static),不同 `WxCpMsgAuditServiceImpl` 实例(不同企业)的ThreadLocal独立,互不影响。
193184
4. **库加载幂等性**`Finance.loadingLibraries()` 底层调用 `System.load()`,JVM保证同一库不重复加载,多线程并发调用安全。
185+
5. **不要手动调用 `Finance.DestroySdk()`**:旧测试代码中的 `Finance.DestroySdk(chatDatas.getSdk())` 会直接销毁 ThreadLocal 管理的 SDK,必须删除。
186+
6. **`closeAllSdks()` 的局限性**:不追踪所有SDK,只清理当前线程,语义为"尽力清理"。非线程池场景若不调用 `closeThreadLocalSdk()`,原生SDK内存会泄漏到JVM退出,但进程退出后OS会回收。
194187

195188
---
196189

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
import java.util.Collections;
2424
import java.util.LinkedList;
2525
import java.util.List;
26-
import java.util.Set;
27-
import java.util.concurrent.ConcurrentHashMap;
2826
import java.util.function.Consumer;
2927

3028
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.MsgAudit.*;
@@ -42,9 +40,6 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
4240
/** 每个线程持有独立 SDK 实例,懒初始化,线程内跨调用复用 */
4341
private final ThreadLocal<Long> threadLocalSdk = new ThreadLocal<>();
4442

45-
/** 跟踪所有已创建的 SDK,用于 closeAllSdks() 统一清理 */
46-
private final Set<Long> managedSdks = ConcurrentHashMap.newKeySet();
47-
4843
@Override
4944
public WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, String passwd,
5045
@NonNull long timeout) throws Exception {
@@ -84,7 +79,6 @@ private long getOrInitThreadLocalSdk() throws WxErrorException {
8479
}
8580
long newSdk = createSdk();
8681
threadLocalSdk.set(newSdk);
87-
managedSdks.add(newSdk);
8882
log.info("线程 [{}] 初始化会话存档SDK成功,sdk={}", Thread.currentThread().getName(), newSdk);
8983
return newSdk;
9084
}
@@ -149,20 +143,15 @@ public void closeThreadLocalSdk() {
149143
Long sdk = threadLocalSdk.get();
150144
if (sdk != null && sdk > 0) {
151145
Finance.DestroySdk(sdk);
152-
managedSdks.remove(sdk);
153146
threadLocalSdk.remove();
154147
log.info("线程 [{}] 关闭会话存档SDK,sdk={}", Thread.currentThread().getName(), sdk);
155148
}
156149
}
157150

158151
@Override
159152
public void closeAllSdks() {
160-
managedSdks.forEach(sdk -> {
161-
Finance.DestroySdk(sdk);
162-
log.info("关闭会话存档SDK,sdk={}", sdk);
163-
});
164-
managedSdks.clear();
165-
threadLocalSdk.remove();
153+
// 无法感知其他线程的SDK,只清理当前线程
154+
closeThreadLocalSdk();
166155
}
167156

168157
@Override

0 commit comments

Comments
 (0)