Conversation
Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e50d1c2782
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Thread.sleep(1000); | ||
| } catch (InterruptedException e) { | ||
| // Ignore | ||
| Thread.currentThread().interrupt(); |
There was a problem hiding this comment.
Avoid re-interrupting inside lock retry loop
When lock() is waiting and Thread.sleep is interrupted, setting the interrupt flag immediately inside the loop causes the next iteration to run with Thread.currentThread().isInterrupted()==true. In production with Lettuce/Spring Redis, subsequent tryLock() Redis calls can then fail immediately with RedisCommandInterruptedException (or loop without effective sleeping), so a single interrupt while contending for the lock can make lock acquisition fail continuously instead of retrying normally.
Useful? React with 👍 / 👎.
🤖 Augment PR Summary总结:该 PR 主要修复长时间批量操作时线程被中断导致 Redis 命令中断,从而引发 accessToken / ticket 刷新链路整体失败的问题。 改动点:
技术说明:该修复更偏向“容错与自愈”策略:当 Redis TTL 读取不可用/被中断时,系统优先保证 token 刷新链路可继续执行,从而提升长时间运行任务的稳定性。 🤖 Was this summary useful? React with 👍 or 👎 |
| Thread.sleep(1000); | ||
| } catch (InterruptedException e) { | ||
| // Ignore | ||
| Thread.currentThread().interrupt(); |
| } catch (Exception e) { | ||
| log.warn("获取access_token过期时间时发生异常,将视为已过期以触发刷新,异常信息: {}", e.getMessage()); | ||
| // 清除中断标志,确保后续的锁获取和token刷新操作能够正常执行 | ||
| Thread.interrupted(); |
There was a problem hiding this comment.
这里无条件调用 Thread.interrupted() 会清除调用方设置的中断信号(例如任务取消/应用关闭),即使异常并非由中断导致也会吞掉中断语义;建议至少仅在确认当前线程已被中断或异常链明确为“命令被中断”时再清除。
Severity: medium
Other Locations
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpInRedisConfigImpl.java:163weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpInRedisConfigImpl.java:200
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| Long expire = redisOps.getExpire(this.accessTokenKey); | ||
| return expire == null || expire < 2; | ||
| } catch (Exception e) { | ||
| log.warn("获取access_token过期时间时发生异常,将视为已过期以触发刷新,异常信息: {}", e.getMessage()); |
There was a problem hiding this comment.
log.warn 目前只记录了 e.getMessage(),会丢失堆栈与根因类型信息,后续排查 Redis 中断/连接问题可能不够。建议确认是否需要保留异常对象信息以便定位。
Severity: low
Other Locations
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpInRedisConfigImpl.java:162weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpInRedisConfigImpl.java:199
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
There was a problem hiding this comment.
Pull request overview
该 PR 旨在提升 WxJava 在长时间批量运行场景下对 Redis “命令被中断”异常的容错能力,避免因线程中断标志残留导致 accessToken / ticket 刷新链路持续失败,从而引发整条调用链崩溃。
Changes:
- 在
AbstractWxCpInRedisConfigImpl的 token/ticket 过期判断中增加异常捕获:Redis 异常时视为过期并触发刷新,同时清理线程中断标志 - 修正
RedisTemplateSimpleDistributedLock.lock()对InterruptedException的处理,新增中断相关测试 - 补充单测覆盖 Redis 异常场景下的 token/ticket 过期判断行为与线程中断标志处理
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpInRedisConfigImpl.java | 为 accessToken/jsapiTicket/agentJsapiTicket 的过期检查增加异常容错与日志,并处理线程中断标志 |
| weixin-java-cp/src/test/java/me/chanjar/weixin/cp/config/impl/AbstractWxCpInRedisConfigImplTest.java | 新增单测验证 Redis 异常时过期判断返回 true 且中断标志被清理 |
| weixin-java-common/src/main/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLock.java | 调整 lock() 捕获 InterruptedException 时的中断处理行为 |
| weixin-java-common/src/test/java/me/chanjar/weixin/common/util/locks/RedisTemplateSimpleDistributedLockInterruptTest.java | 新增单测覆盖 lock() 在 sleep 被中断时的中断标志处理与 tryLock 基本行为 |
| return expire == null || expire < 2; | ||
| } catch (Exception e) { | ||
| log.warn("获取agent_jsapi_ticket过期时间时发生异常,将视为已过期,异常信息: {}", e.getMessage()); | ||
| Thread.interrupted(); |
|
|
||
| boolean expired = config.isAccessTokenExpired(); | ||
|
|
||
| Assert.assertTrue(expired, "Redis异常时应将token视为已过期"); | ||
| // 中断标志应该被清除,允许后续操作正常进行 | ||
| Assert.assertFalse(Thread.currentThread().isInterrupted(), "处理异常后线程中断标志应被清除"); |
| public void lock() { | ||
| while (!tryLock()) { | ||
| try { | ||
| Thread.sleep(1000); | ||
| } catch (InterruptedException e) { | ||
| // Ignore | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
| } |
| public boolean isAccessTokenExpired() { | ||
| Long expire = redisOps.getExpire(this.accessTokenKey); | ||
| return expire == null || expire < 2; | ||
| try { | ||
| Long expire = redisOps.getExpire(this.accessTokenKey); | ||
| return expire == null || expire < 2; | ||
| } catch (Exception e) { | ||
| log.warn("获取access_token过期时间时发生异常,将视为已过期以触发刷新,异常信息: {}", e.getMessage()); | ||
| // 清除中断标志,确保后续的锁获取和token刷新操作能够正常执行 | ||
| Thread.interrupted(); | ||
| return true; | ||
| } |
| public boolean isJsapiTicketExpired() { | ||
| Long expire = redisOps.getExpire(this.jsapiTicketKey); | ||
| return expire == null || expire < 2; | ||
| try { | ||
| Long expire = redisOps.getExpire(this.jsapiTicketKey); | ||
| return expire == null || expire < 2; | ||
| } catch (Exception e) { | ||
| log.warn("获取jsapi_ticket过期时间时发生异常,将视为已过期,异常信息: {}", e.getMessage()); | ||
| Thread.interrupted(); | ||
| return true; | ||
| } |
在长时间批量操作场景下,线程被中断后 Lettuce 抛出
RedisCommandInterruptedException(Spring Data Redis 包装为RedisSystemException),该异常从isAccessTokenExpired()向上传播至调用链顶端,导致整个操作崩溃。由于中断标志未被清除,后续所有 Redis 操作也会连续失败。变更
AbstractWxCpInRedisConfigImplisAccessTokenExpired()、isJsapiTicketExpired()、isAgentJsapiTicketExpired()添加异常捕获Thread.interrupted()清除中断标志(否则后续锁获取/token 刷新也会失败),返回true触发 token 刷新RedisTemplateSimpleDistributedLocklock()捕获InterruptedException时原为// Ignore,改为Thread.currentThread().interrupt()正确恢复中断标志Original prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.