fix: 强制设置有效 locale 修复 vnpy_ctp CTP 登录崩溃

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-24 11:38:19 +08:00
parent f73d436077
commit d368317c1b
8 changed files with 85 additions and 16 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
import os
# vnpy_ctp C++ 扩展需要有效 locale,否则 CTP 登录后可能崩溃
os.environ.setdefault("LANG", "C.UTF-8")
os.environ.setdefault("LC_ALL", "C.UTF-8")
from locale_fix import ensure_process_locale
ensure_process_locale()
import sqlite3
import time
+2 -2
View File
@@ -29,8 +29,8 @@ need_install git git
# vnpy_ctp 在 Linux 上需本地编译(Meson + pkg-config 查找 python3-dev
echo "==> 安装 vnpy_ctp 编译依赖..."
apt-get install -y build-essential python3-dev pkg-config locales
locale-gen en_US.UTF-8 2>/dev/null || true
update-locale LANG=C.UTF-8 LC_ALL=C.UTF-8 2>/dev/null || true
locale-gen en_US.UTF-8 C.UTF-8 2>/dev/null || true
update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 2>/dev/null || true
if ! command -v pm2 &>/dev/null; then
echo "==> 安装 PM2..."
+1 -1
View File
@@ -346,7 +346,7 @@ pm2 restart qihuo
|-----------|------|
| `pip install vnpy_ctp` 编译失败 / `Python dependency not found` | 安装 `build-essential python3-dev pkg-config` 后重试 |
| CTP 连接超时 | 检查前置 IP、端口、SimNow 是否维护、是否在允许连接时段 |
| 连接后立即崩溃 `locale::facet::_S_create_c_locale` | 系统 locale 未配置;`export LANG=C.UTF-8 LC_ALL=C.UTF-8``pm2 restart qihuo`,或 `git pull` 使用最新 `ecosystem.config.cjs` |
| 连接后立即崩溃 `locale::facet::_S_create_c_locale` | 安装 locale`apt install -y locales && locale-gen en_US.UTF-8``git pull``pm2 restart qihuo --update-env` |
| 服务器 `180.168.146.187` 超时 | 换 SimNow 备用前置 `182.254.243.31:30001/30011`(见 [SIMNOW.md](./SIMNOW.md) |
| 已连接但下单拒单 | 检查合约代码、价格精度、是否有足够保证金 |
+1 -1
View File
@@ -183,7 +183,7 @@ python scripts/test_simnow.py
| 报错 **4097** / 握手失败 | `pip install -U vnpy vnpy_ctp``.env``SIMNOW_ENV=实盘` |
| **不合法的登录** | 投资者代码/密码错,或未在快期改过一次密码 |
| 快期能登、脚本不能 | 多为网络或前置地址,换 SimNow 官网其他组前置试 |
| 连上后进程崩溃 `locale::facet::_S_create_c_locale` | 执行 `export LANG=C.UTF-8 LC_ALL=C.UTF-8``pm2 restart qihuo --update-env` |
| 连上后进程崩溃 `locale::facet::_S_create_c_locale` | `apt install -y locales && locale-gen en_US.UTF-8``git pull``pm2 restart qihuo --update-env` |
### 提示「未安装 vnpy / vnpy_ctp」
+3 -3
View File
@@ -11,9 +11,9 @@ module.exports = {
max_memory_restart: "300M",
env: {
NODE_ENV: "production",
LANG: "C.UTF-8",
LC_ALL: "C.UTF-8",
LC_CTYPE: "C.UTF-8",
LANG: "en_US.UTF-8",
LC_ALL: "en_US.UTF-8",
LC_CTYPE: "en_US.UTF-8",
},
error_file: "/opt/qihuo/logs/pm2-error.log",
out_file: "/opt/qihuo/logs/pm2-out.log",
+65
View File
@@ -0,0 +1,65 @@
"""Linux 上 vnpy_ctp 连接 CTP 前须设置有效 locale(否则 C++ 层 abort)。"""
from __future__ import annotations
import locale
import logging
import os
import subprocess
logger = logging.getLogger(__name__)
_LOCALE_DONE = False
_LOCALE_NAME = ""
def _list_locale_candidates() -> list[str]:
names: list[str] = []
for item in (
"C.UTF-8",
"en_US.UTF-8",
"en_US.utf8",
"POSIX",
"C",
):
if item not in names:
names.append(item)
try:
out = subprocess.check_output(["locale", "-a"], text=True, stderr=subprocess.DEVNULL)
for line in out.splitlines():
loc = line.strip()
if not loc:
continue
low = loc.lower()
if "utf" in low and loc not in names:
names.insert(0, loc)
except (OSError, subprocess.SubprocessError):
pass
return names
def ensure_process_locale() -> str:
"""强制设置进程 locale,覆盖系统里无效的旧值。"""
global _LOCALE_DONE, _LOCALE_NAME
if _LOCALE_DONE:
return _LOCALE_NAME
last_err: locale.Error | None = None
for name in _list_locale_candidates():
try:
locale.setlocale(locale.LC_ALL, name)
os.environ["LANG"] = name
os.environ["LC_ALL"] = name
os.environ["LC_CTYPE"] = name
_LOCALE_DONE = True
_LOCALE_NAME = name
logger.info("进程 locale 已设置: %s", name)
return name
except locale.Error as exc:
last_err = exc
continue
raise RuntimeError(
"未找到可用 localevnpy_ctp 会在 CTP 登录后崩溃。"
"请执行: apt install -y locales && locale-gen en_US.UTF-8 && "
"update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8"
) from last_err
+5 -3
View File
@@ -6,12 +6,13 @@ import os
import socket
import sys
os.environ.setdefault("LANG", "C.UTF-8")
os.environ.setdefault("LC_ALL", "C.UTF-8")
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, BASE)
from locale_fix import ensure_process_locale
ensure_process_locale()
from dotenv import load_dotenv
load_dotenv(os.path.join(BASE, ".env"))
@@ -38,6 +39,7 @@ def main() -> int:
env = os.getenv("SIMNOW_ENV", "实盘")
print("=== SimNow 配置 ===")
print(f"locale = {ensure_process_locale()}")
print(f"SIMNOW_USER = {user or '(未设置)'}")
print(f"SIMNOW_PASSWORD = {'*' * 8 if os.getenv('SIMNOW_PASSWORD') else '(未设置)'}")
print(f"SIMNOW_TD = {td}")
+5 -3
View File
@@ -7,9 +7,9 @@ import threading
import time
from typing import Any, Optional
# vnpy_ctp C++ 在部分 Linux 上缺 locale 会抛 std::runtime_error
os.environ.setdefault("LANG", "C.UTF-8")
os.environ.setdefault("LC_ALL", "C.UTF-8")
from locale_fix import ensure_process_locale
ensure_process_locale()
from ctp_symbol import ths_to_vnpy_symbol, to_vnpy_exchange
@@ -89,6 +89,7 @@ class CtpBridge:
self._init_engine()
def _init_engine(self) -> None:
ensure_process_locale()
try:
from vnpy.event import EventEngine
from vnpy.trader.engine import MainEngine
@@ -163,6 +164,7 @@ class CtpBridge:
self._ee.register(EVENT_LOG, _on_log)
try:
ensure_process_locale()
logger.info(
"CTP 连接 [%s] user=%s td=%s env=%s",
mode,