3878 字
19 分钟
WSL2 JetBrains Gateway 终端日志渲染消失排查与解决

环境信息

  • Windows: 11 24H2 (build 26200.8875)
  • WSL: 2.2.4.0(Store 版),内核 5.15.153.1-2,WSLg 1.0.61
  • 发行版: Ubuntu 24.04,自定义导入位置 F:\WSL\Ubuntu\ext4.vhdx(56.78 GB)
  • IDE: GoLand 2026.2,通过 JetBrains Gateway 远程连接 WSL
  • 物理内存: 31.2 GB
  • 排查日期: 2026-07-26
  • 最终结论: 通过切换 Gateway 连接模式为 SSH 解决

一、问题现象#

通过 JetBrains Gateway 用 WSL 直连模式 打开 WSL 内的项目时,Run 工具窗口的运行日志偶尔会突然消失、面板空白闪一下就没内容了。

症状细节(通过交互式确认):

维度表现
后端进程状态还在正常运行,服务能响应、日志文件还在写,只是 IDE Console 面板不刷新
Console 面板表现面板空白/闪一下内容全没了(不是”不来新输出”那种停在最后,而是内容整个消失)
触发时机无明显规律,什么都没干也可能出现,不是大量输出时才触发
恢复方式重新运行 Run 配置(Stop + Run)能恢复;Gateway 重连也行
频率经常遇到,影响正常开发

之前尝试过但无效的措施:

  • 把 WSL 内存调到 8GB → 没解决
  • 在 Gateway 客户端禁用 JCEF GPU 加速(ide.browser.jcef.gpu.disable=true)→ 没解决

二、根因诊断过程#

2.1 初步环境检查与第一轮优化(降低频率,未根治)#

2.1.1 检查 .wslconfig 与系统资源#

当前 .wslconfig(修改前):

[wsl2]
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true

发现三个问题:

  1. 没有设内存上限 → WSL 默认占 50%(~15GB),Windows 侧瘦客户端可能被饿死
  2. 没有开内存回收 → vmmemWSL 拿到内存不还,Linux 习惯占住 cache 不放,长期累积导致 Windows 侧响应变慢
  3. inotify watch 上限是瞬态值 → 详见 2.1.2

2.1.2 发现 inotify watch 持久化缺失(隐藏病根)#

Terminal window
# 检查当前运行时值
$ sysctl fs.inotify.max_user_watches
fs.inotify.max_user_watches = 524288
# 检查是否写入了配置文件
$ grep -rsi inotify /etc/sysctl.conf /etc/sysctl.d/
(没有任何输出 —— 说明这个值是临时的)

关键发现:运行时值是 524288,但没有任何配置文件持久化它。这意味着每次 wsl --shutdown 重启 WSL,inotify 都会回到内核默认值 8192,大项目的文件监听器会耗尽 → 编辑器后端 hang → 终端日志渲染不出来。

2.1.3 第一轮修复#

(1) 更新 .wslconfig:

⚠️ 重要陷阱:autoMemoryReclaim 和 sparseVhd 是 [experimental] 段的设置,不是 [wsl2]。放错段 WSL 会报”无法识别设置”并忽略。第一版放错了,已修正。

[wsl2]
memory=12GB # 明确上限,给 Windows 留 ~19GB
swap=4GB
networkingMode=mirrored # 保留原有
dnsTunneling=true # 保留原有
firewall=true # 保留原有
autoProxy=true # 保留原有
[experimental]
autoMemoryReclaim=gradual # 持续把 WSL 闲置内存还给 Windows
sparseVhd=true # VHDX 自动稀疏化,防膨胀致 IO 退化

(2) 持久化 inotify:

Terminal window
# 用 wsl -u root 绕过 sudo 密码
wsl -d Ubuntu -u root bash -c \
"echo 'fs.inotify.max_user_watches=524288' > /etc/sysctl.d/99-wsl-inotify.conf && \
sysctl -p /etc/sysctl.d/99-wsl-inotify.conf"

(3) 转换现有 VHDX 为稀疏格式(需 WSL 处于停止状态):

Terminal window
wsl --shutdown
wsl --manage Ubuntu --set-sparse true

(4) Windows Defender 排除项 —— 本环境不适用:

通过注册表诊断发现,本机的 Windows Defender 已经被策略禁用:

  • HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender 下 DisableAntiSpyware=1(AV 引擎关闭)
  • DisableLocalAdminMerge=1(即使管理员也无法改本地设置)
  • HKLM:\SOFTWARE\Microsoft\Windows Defender\ 整个注册表树缺失(彻底卸载/禁用)

因此 Defender 根本没在扫描 WSL 文件,不存在 Defender 导致的 IO 争抢,排除项无可添加。

2.1.4 第一轮修复验证#

wsl --shutdown # 让 .wslconfig 生效
# 重新启动 WSL 后验证
$ sysctl fs.inotify.max_user_watches
fs.inotify.max_user_watches = 524288 ✅ 从配置文件加载
$ free -h | grep -i mem
Mem: 11Gi 1.1Gi 10Gi # memory=12GB 生效 ✅
$ free -h | grep -i swap
Swap: 4.0Gi 0B 4.0Gi # swap=4GB 生效 ✅

无 “unrecognized setting” 告警,所有配置正确加载。

2.1.5 第一轮修复效果#

频率明显下降,但症状仍会复现。说明第一轮解决的是环境层面的恶化因素,没动到根本触发机制。

2.2 精准定位:日志诊断揪出真凶#

2.2.1 收集 Console 冻结时的关键细节#

通过交互式问答确认了四个关键维度:

  1. 后端还在跑,只是 Console 不刷新 → 问题在输出管线或客户端渲染层,不是后端进程
  2. 面板空白/闪一下没了 → 不是”不来新输出”(那会停在最后),而是内容整个消失 → 指向 ConsoleView 状态被重置
  3. 无明显规律 → 排除了输出量触发的缓冲区问题
  4. Stop + Run 恢复 → 新建 ConsoleView 修复了状态,证实问题在 Console 实例本身

2.2.2 曾误判为 JCEF 渲染崩溃(已纠正)#

最初根据”闪一下空白”的症状,套用了 JetBrains 已知的 JCEF(Java Chromium Embedded Framework)GPU 渲染崩溃问题:

  • 搜索到多个 JetBrains 官方 issue(jcef#39、IJPL-211645)记录了”webview 空白闪一下”的崩溃
  • 尝试方案 → ide.browser.jcef.gpu.disable=true → 重启 Client
  • 结果无效,症状仍出现

诚实纠正:Run Console 输出面板其实是 Swing/AWT 组件,不是 JCEF 渲染的。JCEF 只管 Markdown 预览、内置浏览器、Jupyter 预览那些,Console 日志不走它。看到”空白闪一下”就套到 JCEF 上是误判。这一步的教训:症状相似不等于根因相同,必须看实际渲染栈。

2.2.3 查 Gateway 客户端日志(Windows 侧)#

日志位置:

C:\Users\<user>\AppData\Local\JetBrains\JetBrainsGateway2026.2\log\idea.log

搜 console|channel|freeze|disconnect|reconnect|reset|EDT|timeout|broken,找到两类关键证据:

证据一 调用 wsl.exe 反复超时(10 秒强制终止)

2026-07-26 19:44:06 WARN - RemoteCredentialsEx - Timeout of PT10S expired while waiting for
'wsl.exe --distribution Ubuntu -- /bin/bash -lc "... active-projects"' to terminate, forcefully terminating...

在 19:44–19:48 之间出现了十几次。Gateway 通过 wsl.exe 执行管理命令(列举活跃项目、最近项目),每次都卡 10 秒被强杀。说明 Windows 调用 WSL 的命令通道本身就不稳定。

证据二:客户端在反复强制重连(正是”闪一下”的来源)

2026-07-26 23:33:51 ThinClientHandle - Setting handle ... to reconnecting state
2026-07-26 23:33:57 Updating join link ... forceReconnect=true
2026-07-26 23:53:29 Updating join link ... forceReconnect=true
2026-07-26 23:55:41 Updating join link ... forceReconnect=true

forceReconnect=true 出现了 3 次。客户端被强制重连 → 重连时客户端 Console 状态会丢,这就是”面板空白闪一下没了”的直接原因。

连接走的是 tcp://127.0.0.1:5990 —— Gateway 和后端 IDE 之间的通信走 localhost TCP 5990 端口。

2.2.4 查后端 IDE 日志(WSL 侧)—— 铁证#

日志位置:

/home/helan/.cache/JetBrains/GoLand2026.2/log/idea.log

搜同样的关键词,找到了 Console 空白那一刻的完整日志(23:55:34):

2026-07-26 23:55:34.275 WARN FramedByteChannelImpl - Framed Channel (id: Tcp:/127.0.0.1:60607)
Failed to read next message LENGTH value.
2026-07-26 23:55:34.275 Read returns <NULL> result. Stop reading from underlying byte channel.
2026-07-26 23:55:34.275 Read thread is terminated. Closing underlying channel.
2026-07-26 23:55:34.285 TLS Transport went to terminal state 'CLOSED'
↓ (7 秒后客户端重连)
2026-07-26 23:55:41.170 新连接 54729 建立
2026-07-26 23:55:41.284 Transport Wrapper terminate
2026-07-26 23:55:42.681 Transport disconnected, closing incoming/outgoing channel
2026-07-26 23:55:42.682 BackendRpcNewSessionListener - Transport disconnected for: RemoteAppSession
↓ (重连后旧 session 被销毁,Console 状态丢失)
2026-07-26 23:55:45.011 WARN Framed Channel 54729 - Failed to read next message LENGTH value (又一次!)

根因确认:

关键错误是这一行:

WARN - FramedByteChannelImpl - Failed to read next message LENGTH value.

这是 TLS/TCP 帧解析失败 —— Gateway 和后端之间的 localhost TCP 5990 信道,在读取下一个消息的长度字段时读到了无效数据(null),于是判定信道坏了,主动关闭并重连。重连后旧的 RemoteAppSession 被销毁、新的建立 —— Console 的内容是绑定在旧 session 上的,session 一销毁内容就没了,这就是”闪一下空白”的完整机制。

而且日志显示这个断连是反复发生的(23:55:34 一次,23:55:45 紧接着又一次),和”偶尔出现”的描述一致。

2.2.5 根因本质#

不是内存、不是 JCEF、不是 inotify。是 localhost TCP 信道在传输中数据被破坏或中断。

在 WSL2 上,Windows↔WSL 的 localhost 通信走的是 Hyper-V 虚拟交换机,已知在某些 WSL 版本/Windows 版本组合下会偶发丢字节或连接重置。本环境是 WSL 2.2.4.0 + Windows 26200,命中了这个偶发问题。

2.3 排查路径上的一个意外发现 网络模式未生效#

虽然 .wslconfig 配了 networkingMode=mirrored,但实测 WSL 当前实际跑的还是 NAT 模式:

$ ip route | grep default
default via 10.119.246.60 dev eth0 # NAT 模式的典型网关地址
$ cat /etc/resolv.conf
nameserver 10.255.255.254 # WSL 的 NAT DNS 中继

mirrored 模式下应该直接镜像 Windows 的网络接口,不会出现 10.x 网段。可能是 Windows 版本/驱动不支持被静默降级了。

但这不是 Console 空白问题的根因 —— 无论 NAT 还是 mirrored,Gateway 和后端 IDE 之间都走 localhost,两个模式都有偶发帧错误的报告。换网络模式不是针对这个问题的解法。


三、解决方案:切换 Gateway 为 SSH 连接模式#

3.1 原理#

模式通信路径稳定性
WSL 直连模式(原)Gateway → wsl.exe 调用 + 裸 localhost TCP:5990 帧信道 → 后端 IDE偶发帧解析失败(无传输层保护)
SSH 模式(新)Gateway → SSH(端口 22) → WSL 的 sshd → 后端 IDE稳定(SSH 有 TCP 重传、keepalive、加密)

SSH 是可靠的字节流协议,自带 TCP 重传、keepalive、流量控制。TLS 帧信道那种”读到的 LENGTH 字段是 null”的情况,在 SSH 上基本不会发生 —— 因为 SSH 层会先保证数据完整性,帧解析层拿到的永远是完整数据。

3.2 对其他工作流的影响#

零影响。SSH 只改 Gateway 连 WSL 的方式,不改 WSL 的网络模式。WSL 访问 Windows 代理该怎么配还怎么配。

3.3 实施步骤#

步骤一:在 WSL 里安装并配置 openssh-server#

Terminal window
# 以 root 执行(wsl -u root 绕过 sudo 密码)
wsl -d Ubuntu -u root bash -c "apt-get update -qq && \
DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server"

安装后 OpenSSH 9.6p1(Ubuntu 24.04 用 socket-activated 模式)。

步骤二:配置 sshd#

Terminal window
# authorized_keys —— 把客户端公钥加进去
mkdir -p /home/helan/.ssh
chmod 700 /home/helan/.ssh
cat /home/helan/.ssh/id_rsa.pub >> /home/helan/.ssh/authorized_keys
sort -u -o /home/helan/.ssh/authorized_keys /home/helan/.ssh/authorized_keys
chown helan:helan /home/helan/.ssh/authorized_keys
chmod 600 /home/helan/.ssh/authorized_keys
# sshd_config —— 强制密钥认证、禁用密码认证(更安全)
sed -i 's/^PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
grep -q '^PasswordAuthentication' /etc/ssh/sshd_config || \
echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config
grep -q '^PubkeyAuthentication' /etc/ssh/sshd_config || \
echo 'PubkeyAuthentication yes' >> /etc/ssh/sshd_config
# 创建运行时目录
mkdir -p /run/sshd
chmod 0755 /run/sshd
# 校验配置
/usr/sbin/sshd -t && echo "config OK"

步骤三:把 Windows 侧的公钥也加进 authorized_keys#

关键 从 Windows 连接时,用的是 Windows 侧的 SSH 密钥,不是 WSL 里的密钥。两边密钥不一样,必须都授权。

查看 Windows 侧的公钥:

Terminal window
Get-Content "$env:USERPROFILE\.ssh\id_rsa.pub"
Get-Content "$env:USERPROFILE\.ssh\id_ed25519.pub"

把这两个公钥追加到 WSL 的 /home/helan/.ssh/authorized_keys 里(最终该文件有 3 个公钥:1 个 WSL 自己的 + 2 个 Windows 的)。

步骤四:启动 sshd 并设置开机自启#

Terminal window
# 启动(用 systemd,因为 wsl.conf 里 systemd=true)
systemctl enable ssh.socket
systemctl enable ssh.service
systemctl start ssh.socket
systemctl start ssh.service
# 验证
systemctl is-active ssh.socket ssh.service # 应输出 active active
ss -tlnp | grep ':22 ' # 应看到 0.0.0.0:22 LISTEN

步骤五:验证从 Windows 到 WSL 的 SSH 免密登录#

Terminal window
# 1. 端口可达性
Test-NetConnection -ComputerName 127.0.0.1 -Port 22 -InformationLevel Quiet
# 输出 True
# 2. 密钥免密登录
ssh.exe -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5 `
helan@localhost 'echo OK; whoami; hostname'
# 输出:
# OK
# helan
# Helan
# 3. 如果有多个密钥,显式指定 ed25519 也能通
ssh.exe -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5 `
-i "$env:USERPROFILE\.ssh\id_ed25519" helan@localhost 'echo ED25519_KEY_OK'
# 输出 ED25519_KEY_OK

步骤六:在 Gateway GUI 里新建 SSH 连接#

  1. 打开 JetBrains Gateway(不是直接打开 GoLand,是单独的 Gateway 应用)
  2. 首页选 SSH(不是 WSL)
  3. 填写连接信息:
    • Host: localhost(或 127.0.0.1)
    • Port: 22
    • User: helan
  4. 点 Check Connection and Continue,Gateway 会用 Windows 侧的 SSH 密钥(已授权)免密连接
  5. 连接成功后,选择要打开的项目路径(WSL 里的路径,比如 /home/helan/你的项目)
  6. Gateway 会在 WSL 里下载并启动后端 IDE,然后拉起 JetBrains Client

3.4 最终验证结果#

切换到 SSH 模式使用一段时间后,Console 日志渲染消失问题未再复现,确认通过 SSH 协议的传输层可靠性,从根本上消除了 localhost TCP 帧信道的偶发解析失败问题。


四、配置文件清单#

4.1 C:\Users\80945\.wslconfig(最终状态)#

[wsl2]
memory=12GB
swap=4GB
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true
[experimental]
autoMemoryReclaim=gradual
sparseVhd=true

4.2 WSL /etc/wsl.conf#

[boot]
systemd=true
[user]
default=helan
[interop]
appendWindowsPath = false

4.3 WSL /etc/sysctl.d/99-wsl-inotify.conf#

fs.inotify.max_user_watches=524288

4.4 WSL /etc/ssh/sshd_config 关键设置#

Port 22 # 默认值,未显式写
PasswordAuthentication no
PubkeyAuthentication yes

4.5 WSL /home/helan/.ssh/authorized_keys#

包含 3 个公钥:

  • WSL 内 helan 的 id_rsa.pub
  • Windows 侧的 id_rsa.pub
  • Windows 侧的 id_ed25519.pub

4.6 systemd 服务#

  • ssh.socket — enabled + active(开机自启 + 当前运行)
  • ssh.service — enabled + active

五、排查教训与防坑要点#

5.1 排查顺序的教训#

这次排查走了一个弯路:先从环境层面(内存、inotify、VHDX)下手,这些虽然降低了问题频率,但没动到根因。正确顺序应该是:

  1. 先看日志 客户端日志 + 后端 IDE 日志,直接定位”那一刻发生了什么”
  2. 从日志错误反推根因:Failed to read next message LENGTH value → 帧解析失败 → 信道层
  3. 再决定解法:换信道协议(SSH)而非调环境参数

环境优化(内存回收、inotify 持久化)作为降低恶化的措施有价值,但不能替代根因诊断。

5.2 “症状相似不等于根因相同”#

“闪一下空白”这个症状在 JetBrains 体系下至少有三种根因:

  • JCEF GPU 渲染崩溃(Markdown/Jupyter 预览、内置浏览器)→ 关 JCEF GPU
  • ConsoleView document 累积膨胀导致 EDT 阻塞(Run Console Swing 渲染)→ 调 console buffer
  • Gateway 信道帧解析失败导致重连(本次的根因)→ 换 SSH 模式

Run Console 输出面板是 Swing 组件,不走 JCEF。看到”空白”就套 JCEF 修法是误判。必须确认症状发生在哪个渲染层。

5.3 WSL 配置的几个坑#

  1. autoMemoryReclaim 和 sparseVhd 在 [experimental] 段,不在 [wsl2]。放错段 WSL 会报”无法识别设置”并忽略,而且不会让你立即知道(只在 stderr 输出告警,程序不报错)
  2. inotify 值如果只在运行时 sysctl -w 设置而不写配置文件,每次 wsl --shutdown 后都会重置。看似设了 524288,实际重启就回到 8192
  3. networkingMode=mirrored 在某些 Windows/驱动组合下会被静默降级为 NAT,需要实测 ip route 和 resolv.conf 确认实际生效的模式
  4. wsl --manage <distro> --set-sparse true 必须在 WSL 停止状态下执行,否则报 ERROR_SHARING_VIOLATION(VHDX 文件被占用)
  5. Add-MpPreference 在 Tamper Protection 开启或策略禁用 Defender 时会静默无效(不报错但不生效),需要查注册表 HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender 确认 Defender 实际状态

5.4 SSH 模式 vs WSL 直连模式的选择建议#

场景推荐模式
偶发 Console 空白/卡顿,日志里有 forceReconnect 或 Failed to read next message LENGTH valueSSH 模式
稳定无问题两种都行,WSL 直连稍快(少一层 SSH)
需要 Windows Defender 排除项(Defender 在跑)两种都需要,与连接模式无关
WSL 访问 Windows 代理/服务两种都支持,SSH 模式零影响

六、关键命令速查#

6.1 日志位置#

# Gateway 客户端日志(Windows 侧)
C:\Users\<user>\AppData\Local\JetBrains\JetBrainsGateway<version>\log\idea.log
# 后端 IDE 日志(WSL 侧)
~/.cache/JetBrains/<Product><version>/log/idea.log

6.2 快速诊断命令#

Terminal window
# 查 WSL 版本
wsl --version
# 查 WSL 实际网络模式(看网关 IP 网段)
wsl -d Ubuntu -u root bash -c "ip route | grep default; cat /etc/resolv.conf"
# 查 vmmemWSL 内存占用
Get-Process vmmemWSL | Select-Object Name, @{N='RAM_MB';E={[math]::Round($_.WorkingSet64/1MB)}}
Terminal window
# 查 inotify 是否持久化(在 WSL 内)
sysctl fs.inotify.max_user_watches # 运行时值
grep -rsi inotify /etc/sysctl.conf /etc/sysctl.d/ # 配置文件里有没有
# 如果运行时值非默认但配置文件没有 → 是瞬态,重启会丢
# 查 Defender 策略状态(在 Windows PowerShell)
Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender'
# 看 DisableAntiSpyware 和 DisableLocalAdminMerge

6.3 SSH 服务管理#

Terminal window
# 启动/重启/状态
sudo systemctl start ssh.socket ssh.service
sudo systemctl restart ssh.service
sudo systemctl status ssh.service
# 开机自启
sudo systemctl enable ssh.socket ssh.service

6.4 日志关键词(用于定位 Console 空白问题)#

在 Gateway 和后端 IDE 的 idea.log 里搜这些词:

console|channel|reconnect|disconnect|forceReconnect|EDT|freeze|broken|timeout|forcibly
Failed to read next message LENGTH
Transport disconnected
RemoteAppSession

如果看到 Failed to read next message LENGTH value + forceReconnect=true,就是本文档记录的根因,解法是切换到 SSH 模式。

WSL2 JetBrains Gateway 终端日志渲染消失排查与解决
https://shirone.mysqil.com/posts/linux/wsl2-jetbrains-gateway-终端日志渲染消失排查与解决/
作者
HikariTish
发布于
2026-07-27
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取