database/sql 本身并不暴露一个通用的 "dialer" 入口;是否能用 SSH 的 sshClient.Dial() 替换网络连接,取决于具体 driver 是否提供自定义 Dial hook。
因此正确做法是:
- 优先:Driver Dial(推荐):对 mysql/pg 分别用 driver 支持的 dial hook,把连接建立过程替换为
sshClient.Dial("tcp", "dbhost:dbport")。 - 回退:本地端口转发(ssh -L 语义):如果未来扩展到不支持自定义 dial 的数据库/driver,则启用本地监听端口转发方案保证通用性。
- Go 内建立 SSH client(
x/crypto/ssh)。 - 为 MySQL / PostgreSQL driver 配置自定义 dial:
- 由 driver 回调触发时,通过
sshClient.Dial("tcp", target)建立到 DB 的连接。 - 支持连接池:dial 可并发、安全复用 SSH client。
- 由 driver 回调触发时,通过
长生命周期的 SSH 连接(如 xsql proxy)可能因网络中断而变为"死连接"。此时通过 proxy 的新连接请求会超时失败,且用户无法感知 proxy 已不可用。
xsql 内置了 SSH 连接健康检测和自动重连机制:
-
SSH Keepalive:周期性发送
keepalive@openssh.com请求探测连接存活状态- 默认间隔:30 秒
- 默认最大失败次数:3(连续 3 次失败判定为死连接)
-
自动重连(ReconnectDialer):
- 当 keepalive 检测到连接死亡时,自动触发重连
- 当 dial 操作失败时,自动尝试重连并重试
- 带指数退避的重试策略
- 重连过程中输出状态日志:
[proxy] SSH connection lost: <error> [proxy] reconnecting to SSH server... [proxy] SSH reconnected successfully
xsql proxy:使用 ReconnectDialer,支持自动重连(长生命周期连接)xsql query/xsql schema dump:每次命令创建新连接,不需要重连
多个数据库可以共享同一个 SSH 代理配置:
ssh_proxies:
bastion:
host: bastion.example.com
port: 22
user: admin
identity_file: ~/.ssh/id_ed25519
passphrase: "keyring:ssh/passphrase"
known_hosts_file: ~/.ssh/known_hosts
profiles:
prod-db1:
db: mysql
host: db1.internal
ssh_proxy: bastion # 引用预定义的代理
prod-db2:
db: pg
host: db2.internal
ssh_proxy: bastion # 复用同一个代理| 字段 | 类型 | 说明 |
|---|---|---|
host |
string | SSH 跳板机地址 |
port |
int | SSH 端口(默认 22) |
user |
string | SSH 用户名 |
identity_file |
string | SSH 私钥路径 |
passphrase |
string | 私钥密码(支持 keyring: 引用) |
known_hosts_file |
string | known_hosts 文件路径 |
skip_host_key |
bool | 跳过主机密钥验证(危险) |
- 已支持:私钥(含 passphrase)
- 计划中:SSH agent 支持
- 默认启用
known_hosts校验;允许显式关闭(--ssh-skip-known-hosts-check)。
当前版本不支持自动解析 ssh_config,需显式配置 SSH 参数。
- 计划支持解析:
Host,HostName,User,Port,IdentityFile,ProxyJump。 - 解析失败时回退到显式参数(config/cli/env)。
当需要传统的 ssh -L 行为或 driver 不支持 dial hook 时,可使用 xsql proxy 命令启用本地端口转发:
- 监听
127.0.0.1:0(或指定端口)分配端口 - 将 DB 连接指向本地端口
- 输出支持 JSON/YAML 或终端表格(详见
docs/cli-spec.md) - 内置 SSH 自动重连:网络中断后自动恢复代理服务