shm 数据服务(单机共享内存)
使用前需先完成配置:客户端进程需在 hikyuu.ini 的 [hikyuu] 节开启 use_shm_server,
具体配置项见下文 配置项。
同一台机器上运行 hikyuu 时,可执行 shmserver (或安装目录 gui 下的 shmserver.py)
单独拉起一个常驻的 共享内存数据服务(shm server) 进程,由其完成耗时的数据预加载,并将
K 线热数据与基础信息 发布为两类共享内存快照;其余 Jupyter 研究、策略回测、数据采集等进程
作为 客户端,经 IPC 接入后零拷贝读取快照,避免每个进程重复预加载、重复占用内存。
共享内存 K 线缓存 / 零拷贝 K 线视图在单进程场景下同样生效。
效果:只有服务端承担耗时的 K 线预加载与主要内存开销,客户端的启动时间与内存占用大幅下降, 且各进程看到的数据保持一致。
重要
服务端 不会自动产生:必须在某个进程内 显式调用
start_shm_server() 才会启动服务。任何 hikyuu 进程(包括调用者自身)都 永远不会
因为连接不到服务而自动变成服务端——连接不上时只会降级为独立模式,自行加载全部数据(行为与
未启用本特性时完全一致)。
本特性(服务端与客户端两侧)全部由独立的 VIP 插件 shmserver 提供,需要有效的 VIP
授权:
服务端:未安装插件或授权无效时
start_shm_server()返回False并打印告警;客户端:shm 的实现(协议、共享内存读写、代理驱动)同样在插件内,未安装或未授权时进程 静默 降级为独立模式——自行加载全部数据、功能完全正常,只是失去共享内存加速, 且启动日志不会产生报错噪音。
备注
服务地址为 IPC 运行时目录下的固定名,与 datadir 解耦;客户端与服务端须使用同一
datadir 以保证数据一致。IPC 运行时目录可通过 shm_ipc_dir 配置或环境变量
HIKYUU_IPC_DIR 指定,默认 ~/.hikyuu/ipc(见 配置项)。
本特性是"单机内多进程共享一份数据",与跨机器的 行情采集数据服务 (vip/dataserver、
get_data_from_buffer_server)以及行情采集地址(quotation_server)是相互独立的概念,
请勿混淆。
启动与停止服务端
服务端在其自身进程内以 Python API 控制启停(范式同 start_data_server / stop_data_server):
- start_shm_server(datadir: str = '', publish_shm: bool = True, recv_spot: bool = True) bool
在当前进程内启动 shm 数据服务,供其他 hikyuu 进程作为客户端零拷贝读取。须在 hikyuu 初始化之后调用(
import hikyuu默认完成初始化);早于初始化调用会因数据未就绪而返回False。- 参数:
datadir (str) -- 数据目录,为空时使用当前 StockManager 数据目录(兼容保留;服务地址已与 datadir 解耦,改由 IPC 运行时目录下的固定名构造)
publish_shm (bool) -- 是否发布两类共享内存快照(K 线热数据 + 基础信息)
recv_spot (bool) -- 是否由本进程接收实时行情(内部订阅
quotation_server并驱动实时更新)
- 返回:
启动成功返回
True;本进程已处于客户端模式、插件缺失或授权无效时返回False
- stop_shm_server() None
停止当前进程内的 shm 数据服务,释放共享内存段并注销相关挂钩。
- is_shm_server_running() bool
查询当前进程内的 shm 数据服务是否在运行。
典型用法:在常驻的服务端进程里启动服务,其余研究 / 回测进程显式开启 use_shm_server 后以
客户端身份接入:
# 进程 A —— 服务端(常驻)
import hikyuu as hku
if not hku.start_shm_server():
... # 插件缺失 / 授权无效,本进程将以独立模式运行
# 进程 B / C / … —— 客户端
import hikyuu as hku
hku.load_hikyuu(use_shm_server=True) # 显式接入进程 A 的服务,本进程进入客户端模式
备注
作为服务端且需要维持快照准实时时,通常保持 recv_spot=True,由服务端订阅
quotation_server 并驱动实时更新,把最新行情镜像写入共享内存段尾部;此时客户端无需
各自再接收行情,进一步节省资源。
备注
服务端进程只作快照发布者,并不需要接入其他 shm 服务。use_shm_server 默认关闭,服务端
进程按独立模式完成初始化与发布,无需额外处理;仅当配置文件中显式置 use_shm_server=True
时,才需在服务端进程内以 load_hikyuu(use_shm_server=False) 跳过客户端探测——否则该进程
会先当作客户端去连接既有服务,在等待 shm_server_wait_timeout (默认 600 秒)超时后打印
Failed connect to hikyuu shm server, fallback to standalone mode! 告警再降级为独立模式
(数据仍能正常加载与发布,仅多出启动等待与一条无害告警)。命令行工具 shmserver 已显式
关闭客户端探测。
命令行常驻启动
也可用随包安装的命令行工具 shmserver 单独拉起一个常驻服务端进程,等价于在 Python 中调用
start_shm_server() 后阻塞等待,按 Ctrl-C 优雅停止:
# pip 安装后可直接执行;尚未安装为命令时等价:python -m hikyuu.gui.shmserver
shmserver [选项]
Options:
--datadir TEXT 数据目录,为空时使用 hikyuu.ini 中 [hikyuu] datadir
--publish_shm BOOLEAN 是否发布共享内存快照(K 线热数据 + 基础信息),默认 True
--recv_spot BOOLEAN 本进程是否接收实时行情并镜像写入快照尾部,默认 True
--config TEXT 指定 hikyuu 配置文件路径,为空则使用默认 ~/.hikyuu/hikyuu.ini
服务端进程启动后,其余研究 / 回测进程若要以客户端身份接入(而非独立加载全部数据),需在配置
或 load_hikyuu 参数中显式开启 use_shm_server=True,并与服务端使用相同 datadir。
工作原理
显式启动服务端:服务端进程调用
start_shm_server()后,加载数据并在服务地址上 监听,供客户端连接。核心库不做任何主从选举、不竞争文件锁。客户端协商:普通进程初始化时,若
use_shm_server为真,则经 shmserver 插件的客户端 接口尝试连接既有服务并轮询其就绪状态(约 1 秒/次,覆盖服务端"正在启动"的窗口);等待时长 受shm_server_wait_timeout总预算约束,超时则 降级为独立模式,自行加载全部数据 ——绝不 在本进程拉起服务。连接成功后,插件创建的三类代理驱动(K 线 / 基础信息 / 板块) 被装配进 StockManager。关闭客户端预加载:客户端会把自身全部 K 线类型的预加载关闭(仅内存中生效,不修改配置 文件),改由服务端提供。
共享内存快照:服务端完成预加载后,把已加载数据发布为只读共享内存段,客户端零拷贝直接 读取(微秒级);服务端收到实时行情更新时会同步写入快照尾部,因此客户端读到的是准实时数据。
等待就绪:客户端连接后按约 1 秒间隔轮询服务端的加载进度。服务端在基础数据(证券列表、 市场、证券类型、节假日、板块等)加载完毕即对外提供服务,不等待 K 线预加载完成;客户端 在
shm_server_wait_timeout秒(协商总预算,含探测与就绪等待,0为无限)内等到就绪 即进入客户端模式,超时则降级为独立模式。容错降级:快照未覆盖的证券或 K 线类型自动改走 IPC;IPC 请求失败或响应无法解析时, 客户端回退到自己的本地数据驱动(日志中出现
Fallback to local driver),因此服务端退出后 客户端仍可继续工作。该本地驱动连接在客户端启动时即建立并全程持有(兜底用,非按需懒创建): 对 sqlite 开销可忽略,对 MySQL / ClickHouse 则会建立真实连接池。不过占大头的 K 线预加载已 免除,故前述启动时间与内存占用大幅下降的效果仍然成立。
配置项
均位于 hikyuu.ini 的 [hikyuu] 节。use_shm_server / shm_server_wait_timeout 作用于
客户端 (服务端由 start_shm_server() 的参数控制);shm_ipc_dir 对服务端与客户端均生效。
use_shm_server 默认为 False,仅当需要接入既有服务时才需显式开启:
[hikyuu]
tmpdir = /home/user/stock/tmp
datadir = /home/user/stock
; 本进程是否作为客户端连接既有 shm 服务;默认为 False(始终以独立模式运行),需要接入时置 True
use_shm_server = True
; 客户端接入协商的总时长预算(秒,含连接探测与就绪等待);0 表示无限等待,超时后降级为独立模式
shm_server_wait_timeout = 600
; shm 数据服务的 IPC 运行时目录(socket / 锁 / 段名记录落点),默认配置生成时写入用户家目录
; docker 共享场景改为宿主与容器挂载的相同绝对路径
shm_ipc_dir = /home/user/.hikyuu/ipc
配置项 |
默认值 |
说明 |
|---|---|---|
|
|
本进程是否作为客户端连接既有服务。置 |
|
|
客户端接入协商的总时长预算(秒,含连接探测与就绪等待); |
|
|
shm 数据服务的 IPC 运行时目录(socket、单实例锁、段名记录文件的落点),服务端与客户端均
生效;亦可经环境变量 |
docker 容器共享
跨容器共享服务时,客户端容器需能访问两类资源:IPC 运行目录中的 socket 文件 与 /dev/shm 中的 共享内存段。配置要点:
服务端与客户端使用 同一 datadir(连同数据目录一并挂载进容器),且插件版本一致;
在
hikyuu.ini中将shm_ipc_dir配置为 挂载范围内的绝对路径(不依赖容器内HOME);客户端容器以
--ipc=host运行(或--ipc=container:<服务端容器名>),与服务端共享 /dev/shm;若服务端运行于容器内且未使用
--ipc=host,须调大--shm-size(默认仅 64MB,K 线段可达 GB 级);Windows 宿主下的容器均为 Linux 容器(Docker Desktop / WSL2),而 Windows 原生进程使用命名管道 与 Windows 共享内存,无法与 Linux 容器互通,故服务端也应放入容器(或 WSL)内运行。
Linux 宿主示例(服务端运行于宿主,客户端运行于容器)
服务端在宿主上直接运行(shmserver 常驻亦可),hikyuu.ini 中配置:
shm_ipc_dir = /data/hikyuu-ipc
客户端容器将配置、数据与 IPC 运行目录按 相同路径 挂载:
docker run -it --rm \
--ipc=host \
-v /data/hikyuu-ipc:/data/hikyuu-ipc \
-v /home/user/.hikyuu:/home/user/.hikyuu \
-v /home/user/stock:/home/user/stock \
my-hikyuu-image
容器内使用的 hikyuu.ini 即挂载进来的同一份(shm_ipc_dir 指向挂载路径),开启
use_shm_server = True 即可接入宿主服务端。
服务端也可运行于容器内(与客户端同为 --ipc=host,共享内存段落在宿主 /dev/shm):
docker run -d --name shmserver \
--ipc=host \
-v /home/user/.hikyuu:/home/user/.hikyuu \
-v /home/user/stock:/home/user/stock \
my-hikyuu-image shmserver
Windows 宿主示例(Docker Desktop / WSL2,服务端与客户端均为 Linux 容器)
宿主目录 D:/hikyuu 下放置 .hikyuu(配置)与 stock(数据),hikyuu.ini 中配置
shm_ipc_dir = /home/user/.hikyuu/ipc(容器内路径,两个容器挂载点一致):
# 服务端容器
docker run -d --name shmserver --ipc=host `
-v D:/hikyuu/.hikyuu:/home/user/.hikyuu `
-v D:/hikyuu/stock:/home/user/stock `
my-hikyuu-image shmserver
# 客户端容器
docker run -it --rm --ipc=host `
-v D:/hikyuu/.hikyuu:/home/user/.hikyuu `
-v D:/hikyuu/stock:/home/user/stock `
my-hikyuu-image
数据获取路径
客户端的每一次 K 线查询按以下优先级选择路径:
路径 |
适用场景 |
|---|---|
共享内存快照 |
服务端已预加载、且该证券未被 |
本地数据驱动 |
服务端未预加载的 K 线类型,以及分时( |
IPC 请求 |
快照未覆盖的证券(如被 |
本地数据驱动 |
IPC 请求失败或响应异常时的最终降级 |
备注
服务端的 [preload] 配置决定了快照的内容:某个 K 线类型为 True 才会被服务端预加载、
才会进入共享内存快照;xxx_max 限制预加载条数,被截断的证券不进快照,由客户端改走 IPC
查询(两条路径的结果一致)。
客户端进程自身的 [preload] 配置会被自动忽略(全部置为 False)。
复权处理始终在客户端本地完成,服务端传输的都是不复权的原始数据。
客户端模式下的 权息与历史财务 改为按需懒加载并在本地缓存:首次访问某证券的权息 / 财务数据时才加载(服务端基础信息快照在初始发布时不含财务数据,待财务预加载完成后再刷新 快照),而非随预加载一次性载入,后续访问命中本地缓存,避免客户端为每个证券重复拉取。
客户端模式下的板块读写
客户端进程的板块数据同样由服务端提供一致视图:进入客户端模式时经 IPC(BLOCK_LOAD)从服务端
一次性拉取全量板块并缓存在本进程(会话期一次性,见下)。
读取 (
get_block/get_block_list等):命中上述本进程缓存;缓存不可用或未命中时回退 到本地板块驱动(客户端与服务端共用同一datadir,兜底结果一致)。保存与删除 (
save_block/remove_block):客户端模式下 不直接写本地板块库,而是经 IPC(BLOCK_SAVE/BLOCK_REMOVE)转发,由 服务端进程单点落库。原因:多个客户端共享 同一服务端时,若各自直写本地共享库会形成多写者、互相覆盖并绕过服务端缓存,故写操作一律收敛到 服务端。
板块写操作以服务端应答为准:连接不可用或服务端落库报错时,发起进程会抛出异常,且 不会 悄悄改走 本地驱动兜底写(写不适用读路径的"三层回退"语义,避免用户误以为修改已生效)。
警告
板块修改的传播存在 可见性延迟 (板块缓存在客户端会话内一次性拉取、不重拉所致),各方的 可见时点如下:
发起修改的进程:应答成功后立即就地同步本进程缓存,立即生效;
服务端 / 库:修改即时持久化;服务端进程重启或每日
reload重载数据后修改依然保留;其它已在运行的客户端:其板块缓存于会话建立时拉取、会话内不重拉,本次修改在当前会话内 不可见;需重启 / 重连客户端(新会话重新拉取),或启用了每日重载者等到下一次
reload后 才可见;之后新启动的客户端:连接时拉取到的即是最新板块,直接可见。
另外,板块写依赖 服务端配置的板块数据源可写:若服务端使用的是只读板块数据源(如钱龙板块目录 驱动的实现),保存 / 删除会失败并在发起进程抛出异常、板块保持不变,客户端不会绕过服务端去写 本地库。
若服务端不可用、客户端降级为独立模式运行,则板块读写与未启用本特性时完全一致——直接读写本地 板块库,不存在上述传播边界。
常见问题
多个进程之间的数据会不一致吗?
不会。客户端与服务端使用同一 datadir,快照与 IPC 返回的都是服务端已加载的数据,实时行情的
镜像写入保证了准实时一致。(例外:板块的 写 操作,见上文「客户端模式下的板块读写」——某客户端
保存 / 删除的板块,其它已在线客户端在当前会话内不会立即看到,需重启 / 重连或下一次 reload
后才可见。)
在服务端进程里直接保存板块,客户端能看到吗?
不能立即看到。若在 服务端进程自身 内直接调用 save_block / remove_block (不经 IPC
转发,直接操作服务端本地板块库),服务端用于应答客户端 BLOCK_LOAD 的板块缓存不会同步更新;
需对服务端进程执行 reload() 或重启服务端,之后新连接 / 重连的客户端才可见。因此板块写操作
应统一经由客户端经服务端转发进行,并保证同一时刻只有一个进程在写板块。
没有启动服务端会怎样?
各进程连不到服务,会各自以独立模式加载全部数据,功能与结果不受影响,只是无法享受启动时间与 内存的优化。
服务端运行在 Jupyter 中,客户端变慢?
服务端的 Python 主线程若长时间持有 GIL(例如一段未释放 GIL 的密集计算),服务端处理请求的线程 在写日志时会被阻塞,客户端表现为请求延迟被放大。建议把服务端放在独立的 Python 进程中运行。
如何完全关闭该功能?
use_shm_server 默认为 False:不启动服务端、也不在任何进程的配置或 load_hikyuu
参数中开启该选项,所有进程即完全按独立模式运行,行为与未启用本特性时一致。
IPC 运行目录下会残留哪些文件?
IPC 运行目录默认为 ~/.hikyuu/ipc(可经 [hikyuu] shm_ipc_dir 配置或环境变量
HIKYUU_IPC_DIR 覆盖)。socket / 命名管道文件名形如 hikyuu_shm_server.ipc
(Windows 下为同名命名管道),另有配套的 .lock 文件;服务端还会用 hikyuu_ks.last /
hikyuu_bi.last 记录当前共享内存段名,用于清理上一个异常退出的服务端残留段,并以
hikyuu_shmserver.lock 作为单实例文件锁(见下)。服务端正常退出时会删除共享内存段;上述
文件残留后均可安全手工删除(单实例锁在进程终止时由操作系统自动释放)。
备注
服务端启动时会对 hikyuu_shmserver.lock 加非阻塞排他文件锁,本机同一时刻只允许一个
shmserver 实例运行:已有实例存活时,新启动的实例将记录 FATAL 日志后直接退出;进程终止
(含 Ctrl-C、崩溃)时由操作系统自动释放锁,不影响下一次正常启动。共享内存段名与其记录文件
(hikyuu_ks.last / hikyuu_bi.last)为全局固定、未按 datadir 隔离,在单实例约束下
后启动者清理的必为上一个已退出服务端的残留段,不存在两个运行中服务端互删段的问题;如需切换
datadir,请先关闭当前服务端进程,否则新实例会因单实例锁拒绝启动并退出。