Sonic 常用 CLI 命令与工具详解
Sonic Channel 文本协议全命令实测详解:PING/AUTH/PUSH/POP/FLUSH/COUNT/QUERY/SUGG/LIST 语法、真实回显、错误码表与 Python 最小客户端实现。
Sonic 常用 CLI 命令与工具详解
> 目标:逐一实测 Sonic Channel 文本协议的全部常用命令(PING/AUTH/PUSH/POP/FLUSH/COUNT/QUERY/SUGG/LIST/TRIGGER/INFO 等),给出协议语法表、错误码表与一个可直接复用的 Python 最小客户端;全部回显取自 Ubuntu 22.04 + sonic-server v1.10.2 真实会话。
一、环境信息
- 操作系统:Ubuntu 22.04(x86_64)
- 软件版本:sonic-server v1.10.2
- 服务端点:
127.0.0.1:1491(Sonic Channel,TCP 文本协议;配置inet = "0.0.0.0:1491") - 认证口令:
q1w2e3r4(channel.auth_password配置值) - 配置文件:
/opt/data/sonic/config.cfg(参数说明见《Sonic 配置参数详解》) - 数据目录:
/opt/data/sonic/store/ - 测试数据:
messages集合(default桶,对象conversation:1至conversation:7,4 英 3 中文本) - 客户端工具:
nc(netcat-openbsd)与 Python 3.10 内置 socket 库
二、前置条件
- Sonic 进程在运行且端口可达,用 nc 验证(实测回显):
printf 'QUIT\n' | nc -w 2 127.0.0.1 1491
CONNECTED <sonic-server v1.10.2>
ENDED quit
- 已知认证口令(配置
channel.auth_password,未设置则任意口令可过); - 准备一个交互工具:临时手测用 nc(
nc 127.0.0.1 1491进入交互,逐行输入命令);批量验证/接入业务用本文第七节的 Python 最小客户端,或选用官方生态客户端库(Go: github.com/valeriansaliou/go-sonic;Python: github.com/x0k/sonic-client 等社区库;Rust/PHP/JS 均有社区实现)。
三、协议总览与认证
3.1 连接生命周期与问候语
每次 TCP 连接建立后,服务端先推一行问候语(实测):
CONNECTED <sonic-server v1.10.2>
随后连接处于「未认证」状态,必须先 START;未认证发送其他命令会直接终止连接(实测):
>>> PING
<<< ENDED not_recognized
正常收尾用 QUIT(实测):
>>> QUIT
<<< ENDED quit
3.2 START 与三种模式
START <模式> <口令> 完成认证并锁定通道模式,实测:
>>> START search q1w2e3r4
<<< STARTED search protocol(1) buffer(20000)
回显三段含义:模式名、协议版本 protocol(1)、单命令缓冲上限 buffer(20000)(字节)。三种模式与可用命令:
| 模式 | 用途 | 命令集 |
|---|---|---|
| ingest | 写入/删除/计数/清空 | PING、PUSH、POP、COUNT、COUNTC、COUNTB、COUNTO、FLUSHC、FLUSHB、FLUSHO、HELP、QUIT |
| search | 查询/补全/列词 | PING、QUERY、SUGGEST、LIST、HELP、QUIT |
| control | 触发后台任务/状态 | PING、TRIGGER、INFO、HELP、QUIT |
模式约束实测:search 模式发 ingest 命令被拒:
>>> PUSH messages default conversation:1 "should fail here"
<<< ERR unknown_command
非法模式名直接终止连接(实测):
>>> START badmode q1w2e3r4
<<< ENDED invalid_mode
口令错误同样终止(实测):
>>> START search wrong-pass
<<< ENDED authentication_failed
3.3 同步响应与异步响应
- 同步命令(PUSH/POP/FLUSH/COUNT/TRIGGER/INFO/START/QUIT/PING)直接回一行结果;
- 异步命令(QUERY/SUGGEST/LIST)两段式:先回
PENDING <任务ID>,处理完成后推送EVENT <命令> <任务ID> [结果...],客户端凭 ID 匹配。实测一组完整交互:
>>> QUERY messages default "sonic"
<<< PENDING RHkjf3oe
<<< EVENT QUERY RHkjf3oe conversation:6 conversation:4 conversation:2 conversation:1
3.4 协议语法要素
| 语法要素 | 说明 | 示例 |
|---|---|---|
| 参数顺序 | 固定位置参数 | PUSH <collection> <bucket> <object> "<text>" |
| 引号 | 文本参数必须双引号包裹 | "Hello world" |
| 修饰符 | KEY(value) 形式,置于命令尾部 | LIMIT(10)、OFFSET(2)、LANG(cmn) |
| 换行 | 每条命令以 \n 结尾 | — |
| 大小写 | 命令与参数名大写,值原样保留 | QUERY/LIMIT |
文本不加引号会报错(实测):
>>> PUSH messages default conversation:1 Hello world unquoted
<<< ERR invalid_parameter("text", "unterminated quoted string")
四、数据写入与删除命令详解(ingest 模式)
4.1 PUSH:写入索引
语法:PUSH <collection> <bucket> <object> "<text>" [LANG(<locale>)]
实测写入英文与中文测试语料(回显逐行照抄):
>>> PUSH messages default conversation:1 "Hello world, this is Sonic search backend"
<<< OK
>>> PUSH messages default conversation:2 "Sonic is a fast, lightweight and schema-less search backend"
<<< OK
>>> PUSH messages default conversation:3 "Rust is a fast systems programming language"
<<< OK
>>> PUSH messages default conversation:4 "快速上手 Sonic 搜索引擎,轻量又高效"
<<< OK
>>> PUSH messages default conversation:6 "Sonic 支持中文搜索吗?实测一下就知道"
<<< OK
要点:
object是外部数据库主键(如conversation:1),查询返回它,正文仍存业务库;- 文本上限为 START 回显的
buffer(20000)字节(实测 25000 字节文本发送后服务端直接断开连接); - 更新对象 = 先 POP 旧文本再 PUSH 新文本;
- 中文建议显式
LANG(cmn)强制词级分词(实测对比见《Sonic 介绍与选型指南》4.2 节)。
4.2 POP:按文本删除
语法:POP <collection> <bucket> <object> "<text>"——必须提供与写入时相同的文本,服务端按词条反删。实测(回显为被删除的词条数):
>>> POP messages default conversation:1 "Hello world, this is Sonic search backend"
<<< RESULT 3
删除不存在的文本返回 0,不报错(实测):
>>> POP messages default conversation:1 "some other text that was never pushed"
<<< RESULT 0
删除效果复核(conversation:1 原有 3 词条,删后为 0):
>>> COUNTO messages default conversation:1
<<< RESULT 0
⚠️ 注意:POP 只删除「文本分词后命中的词条映射」。若原文因停用词过滤从未入索引的词,POP 无需也无法删除它们;文本前后空白差异可能造成删不干净,务必用原文。
4.3 FLUSHO / FLUSHB / FLUSHC:三级清空
| 命令 | 语法 | 作用范围 |
|---|---|---|
| FLUSHO | FLUSHO <collection> <bucket> <object> | 清除单个对象 |
| FLUSHB | FLUSHB <collection> <bucket> | 清除整个桶 |
| FLUSHC | FLUSHC <collection> | 清除整个集合 |
实测记录(FLUSHO 返回被清除的词条数):
>>> FLUSHO ftest2 def o1
<<< RESULT 3
(该对象文本 red green blue,3 个词条,数字精确对应。)
FLUSHB/FLUSHC 实测:
>>> FLUSHB ftest def
<<< RESULT 1
>>> FLUSHC ftest2
<<< RESULT 1
>>> COUNTC ftest2
<<< RESULT 0
💡 说明:官方协议文档将 FLUSHB/FLUSHC 返回值描述为清除计数,但实测中(无论桶内 4 个对象还是 2 个对象)均返回
1;建议把该返回值当作「操作已受理」的指示符,实际效果用 COUNT 系列命令复核(上例COUNTC归 0 即确认清空成功)。
4.4 COUNT / COUNTC / COUNTB / COUNTO:计数
| 命令 | 语法 | 返回含义(实测+源码确认) |
|---|---|---|
| COUNTC | COUNTC <collection> | 集合内桶数 |
| COUNTB | COUNTB <collection> <bucket> | 桶内对象数 |
| COUNTO | COUNTO <collection> <bucket> <object> | 对象词条数 |
实测(7 个对象、1 个桶、conversation:1 有 3 词条时):
>>> COUNTC messages
<<< RESULT 1
>>> COUNTB messages default
<<< RESULT 7
>>> COUNTO messages default conversation:1
<<< RESULT 3
老命令 COUNT(不带段名)在 1.10.2 已被官方弃用,且三种参数形态行为不一致(实测 COUNT messages 返回 0 而 COUNTB messages default 返回 7),新代码一律使用 COUNTC/COUNTB/COUNTO 三段式。
五、数据查询命令详解(search 模式)
5.1 QUERY:全文检索
语法:QUERY <collection> <bucket> "<terms>" [LIMIT(<number>)] [OFFSET(<number>)] [LANG(<locale>)]
英文多词检索(相关度降序,实测):
>>> QUERY messages default "sonic"
<<< PENDING RHkjf3oe
<<< EVENT QUERY RHkjf3oe conversation:6 conversation:4 conversation:2 conversation:1
>>> QUERY messages default "search backend"
<<< PENDING KHwJjB2h
<<< EVENT QUERY KHwJjB2h conversation:2 conversation:1
分页修饰符实测(LIMIT(2) 取前 2,OFFSET(2) 跳过前 2):
>>> QUERY messages default "sonic" LIMIT(2)
<<< EVENT QUERY 4GmsXQ3s conversation:6 conversation:4
>>> QUERY messages default "sonic" LIMIT(2) OFFSET(2)
<<< EVENT QUERY LNNwCfzR conversation:2 conversation:1
LIMIT 越界被拒(受 query_limit_maximum = 100 约束,实测):
>>> QUERY messages default "sonic" LIMIT(1000)
<<< ERR policy_reject(LIMIT out of minimum/maximum bounds)
无命中词返回空 EVENT(实测):
>>> QUERY nosuch nobucket "test"
<<< PENDING hDweLkYB
<<< EVENT QUERY hDweLkYB
中文检索实测要点:查询词分词路径与索引一致才能精确命中。实测 QUERY messages default "搜索引擎" 直接返回了以「搜索引擎」为词索引的对象:
>>> QUERY messages default "搜索引擎"
<<< PENDING lBPsXyUe
<<< EVENT QUERY lBPsXyUe conversation:4 conversation:6
而「今天天气」这类在索引中被逐字切开的查询词,精确命中为空、靠 FST 相似词兜底返回了包含 今天天气 词条的对象(实测):
>>> QUERY messages default "今天天气"
<<< PENDING 4bD69jt3
<<< EVENT QUERY 4bD69jt3 conversation:4
⚠️ 注意:这就是中文场景必须「写入与查询同用 LANG(cmn)」的原因——索引粒度(词/字)与查询粒度错位时,精确命中降级为模糊兜底,排序质量随之下降。
5.2 SUGGEST:搜索补全
语法:SUGGEST <collection> <bucket> "<word>" [LIMIT(<number>)]
基于 FST 词图的前缀补全(实测):
>>> SUGGEST messages default "son"
<<< PENDING RBt9H0YP
<<< EVENT SUGGEST RBt9H0YP sonic
>>> SUGGEST messages default "sea"
<<< PENDING j2CW2mCR
<<< EVENT SUGGEST j2CW2mCR search
无匹配前缀返回空 EVENT(实测):
>>> SUGGEST messages default "zzz"
<<< PENDING TZYugGVH
<<< EVENT SUGGEST TZYugGVH
⚠️ 注意:SUGGEST/LIST 依赖 FST 词图合并(默认 180 秒周期或手动 TRIGGER consolidate),刚 PUSH 的词可能暂不可补全(实测复现与解法见 6.1)。
5.3 LIST:列出索引词表
语法:LIST <collection> <bucket> [LIMIT(<number>)] [OFFSET(<number>)]
实测列出 messages 桶全部词条(LIMIT(500) 满页):
>>> LIST messages default LIMIT(500)
<<< PENDING ybeCU5Y0
<<< EVENT LIST ybeCU5Y0 sonic 不 不错 中文搜索 今 今天天气 北京 天 实测 快 快速 手 搜索引擎 支持 轻量 知道 高效 backend contact example.org fast language lightweight programming rust schema search support systems
(可读性起见按输出原样截取;可见中文逐字词条与英文词级词条并存、停用词已滤除、conversation:7 的 contact/example.org/support 三个词条在 POP 前存在。)
LIMIT 越界同样被拒(实测):
>>> LIST messages default LIMIT(5000)
<<< ERR policy_reject(LIMIT out of minimum/maximum bounds)
六、管理命令详解(control 模式)
6.1 TRIGGER:触发后台任务
语法:TRIGGER <action>,实测可用 action 为 consolidate(合并 FST 词图)。实测效果——合并前 LIST/SUGGEST 为空:
>>> LIST messages default
<<< PENDING Jy058Ddq
<<< EVENT LIST Jy058Ddq
>>> SUGGEST messages default "sea"
<<< PENDING Efx83Ib2
<<< EVENT SUGGEST Efx83Ib2
触发合并后立即可用(实测):
>>> TRIGGER consolidate
<<< OK
>>> LIST messages default LIMIT(50)
<<< PENDING p0cSlmwt
<<< EVENT LIST p0cSlmwt backend contact example.org fast language lightweight programming rust schema search sonic support systems 一下 不错 中文搜索 今天天气 出门 实测 快速 手 搜索引擎 支持 散步 知道 轻量 高效
批量写入后的固定动作建议:PUSH 批次结束 → control 模式 TRIGGER consolidate → 再开放查询流量,保证补全与纠错立即可用。
6.2 INFO:运行统计
语法:INFO,实测:
>>> INFO
<<< RESULT uptime(179) clients_connected(1) commands_total(35) command_latency_best(2) command_latency_worst(63) kv_open_count(1) fst_open_count(1) fst_consolidate_count(1)
字段含义:uptime 进程运行秒数;clients_connected 当前连接数(监控连接泄漏);commands_total 累计命令数;command_latency_best/worst 命令延迟极值(微秒);kv_open_count/fst_open_count 打开的 KV/FST 库句柄数(多集合部署排查句柄占用);fst_consolidate_count 累计合并次数。
6.3 HELP:命令自述
各模式均可 HELP 与 HELP commands(实测 search 模式):
>>> HELP
<<< RESULT PING QUIT
>>> HELP commands
<<< RESULT QUERY SUGGEST LIST PING HELP QUIT
(ingest 模式 HELP commands 实测回显 RESULT PUSH POP COUNT COUNTC COUNTB COUNTO FLUSHC FLUSHB FLUSHO PING HELP QUIT;control 模式回显 RESULT TRIGGER INFO PING HELP QUIT——HELP commands 就是该模式可用命令集的权威清单。)
七、Python 最小客户端实现
仅用标准库 socket 即可实现协议客户端,以下代码可直接保存为 sonic_client.py 使用(本专栏全部实测回显即出自它):
#!/usr/bin/env python3
"""Minimal Sonic Channel text-protocol client (raw TCP, no deps)."""
import re
import socket
import time
class SonicClient:
def __init__(self, host="127.0.0.1", port=1491, timeout=15):
self.sock = socket.create_connection((host, port), timeout=timeout)
self.buf = b""
self.greeting = self._read_line() # CONNECTED <sonic-server v1.10.2>
def _read_line(self):
while b"\n" not in self.buf:
chunk = self.sock.recv(4096)
if not chunk:
raise ConnectionError("connection closed by server")
self.buf += chunk
line, self.buf = self.buf.split(b"\n", 1)
return line.decode("utf-8", "replace").rstrip("\r")
def cmd(self, command, timeout=15):
"""Send one command; return list of response lines (verbatim)."""
self.sock.sendall((command + "\n").encode("utf-8"))
lines, deadline = [], time.time() + timeout
pending_id = None
while True:
line = self._read_line()
lines.append(line)
m = re.match(r"^PENDING\s+(\S+)", line)
if m:
pending_id = m.group(1) # async command: wait for EVENT
if line.startswith("EVENT ") or pending_id is None:
if re.match(r"^(PONG|OK|ERR|ENDED|STARTED|EVENT)\b", line):
break
if time.time() > deadline:
break
return lines
def close(self):
try:
self.cmd("QUIT", timeout=5)
except Exception:
pass
self.sock.close()
使用示例(写入 → 检索完整闭环,实测输出附后):
from sonic_client import SonicClient
# 写入侧
c = SonicClient()
print(c.greeting)
for line in c.cmd("START ingest q1w2e3r4"):
print(line)
for line in c.cmd('PUSH messages default conversation:1 "Hello world, this is Sonic search backend"'):
print(line)
c.close()
# 查询侧
s = SonicClient()
s.cmd("START search q1w2e3r4")
for line in s.cmd('QUERY messages default "sonic"'):
print(line)
s.close()
实测输出:
CONNECTED <sonic-server v1.10.2>
STARTED ingest protocol(1) buffer(20000)
OK
PENDING RHkjf3oe
EVENT QUERY RHkjf3oe conversation:6 conversation:4 conversation:2 conversation:1
实现要点:QUERY/SUGGEST/LIST 必须等待 EVENT <命令> <同ID> 行才算完成(本实现的 pending_id 匹配逻辑);生产化还需补上重连、口令配置化与并发事件分发。
八、命令速查与错误码
8.1 命令速查表
| 命令 | 模式 | 语法 | 同步/异步 | 实测成功回显 |
|---|---|---|---|---|
| PING | 全部 | PING | 同步 | PONG |
| START | 未认证 | START <mode> <password> | 同步 | STARTED <mode> protocol(1) buffer(20000) |
| QUIT | 全部 | QUIT | 同步 | ENDED quit |
| PUSH | ingest | PUSH <c> <b> <o> "<text>" [LANG(l)] | 同步 | OK |
| POP | ingest | POP <c> <b> <o> "<text>" | 同步 | RESULT <删除词条数> |
| COUNTC | ingest | COUNTC <c> | 同步 | RESULT <桶数> |
| COUNTB | ingest | COUNTB <c> <b> | 同步 | RESULT <对象数> |
| COUNTO | ingest | COUNTO <c> <b> <o> | 同步 | RESULT <词条数> |
| FLUSHC | ingest | FLUSHC <c> | 同步 | RESULT 1(见 4.3 说明) |
| FLUSHB | ingest | FLUSHB <c> <b> | 同步 | RESULT 1(见 4.3 说明) |
| FLUSHO | ingest | FLUSHO <c> <b> <o> | 同步 | RESULT <词条数> |
| QUERY | search | QUERY <c> <b> "<terms>" [LIMIT(n)] [OFFSET(n)] [LANG(l)] | 异步 | EVENT QUERY <id> <objects...> |
| SUGGEST | search | SUGGEST <c> <b> "<word>" [LIMIT(n)] | 异步 | EVENT SUGGEST <id> <words...> |
| LIST | search | LIST <c> <b> [LIMIT(n)] [OFFSET(n)] | 异步 | EVENT LIST <id> <words...> |
| TRIGGER | control | TRIGGER consolidate | 同步 | OK |
| INFO | control | INFO | 同步 | RESULT uptime(...) ... |
| HELP | 全部 | HELP [commands] | 同步 | RESULT <命令列表> |
8.2 错误响应表(实测汇总)
| 回显 | 触发场景(实测) | 处置 |
|---|---|---|
ENDED not_recognized | 未认证时发送任意命令 | 先 START 再发命令 |
ENDED authentication_failed | 口令错误 | 核对 channel.auth_password |
ENDED invalid_mode | START 模式名非法 | 用 ingest/search/control |
ERR unknown_command | 当前模式不支持该命令 | 核对命令与模式归属 |
ERR invalid_parameter("text", "unterminated quoted string") | 文本未加双引号 | 文本参数加双引号 |
ERR policy_reject(LIMIT out of minimum/maximum bounds) | LIMIT 超出 search 段上限 | 1 ≤ LIMIT ≤ maximum |
| 连接直接断开 | 单命令超过 buffer(20000) 字节 | 拆分文本分多次 PUSH |
通用规则:ERR 开头的行不终止连接,可继续发送后续命令;ENDED 开头的行意味着服务端已关闭本次连接,客户端必须重连并重新 START。
九、常见问题
Q1:PING 返回 ENDED not_recognized?
命令发在 START 之前。Sonic 通道必须先认证选模式,任何未认证命令都会结束连接(实测复现)。
Q2:QUERY 报 ERR unknown_command?
命令发错了模式——QUERY 属 search 模式,在 ingest 连接里不可用。按第八节速查表核对命令归属(实测在 search 连接里发 PUSH 同样报此错)。
Q3:PUSH 返回 OK 但 QUERY 查不到? 先查对象与桶拼写;再确认查询词没有踩停用词(如 hello/world/help 均在英文停用词表,实测不在索引词表中);中文场景检查索引与查询的分词粒度是否一致(LANG(cmn) 用法见 5.1)。
Q4:SUGGEST/LIST 返回空但 PUSH 成功?
FST 词图尚未合并。等默认 180 秒周期,或 control 模式 TRIGGER consolidate 后立即生效(实测复现并解决)。
Q5:发送长文本后连接消失? 超过 START 回显的 buffer 上限(20000 字节),服务端直接断开。客户端按 buffer 拆分文本、同一对象分多次 PUSH 即可(实测复现)。
Q6:POP 后 COUNTO 不为 0?
POP 按词条删除,传入文本与原文不一致(空白/标点差异)会漏删。用与 PUSH 完全一致的原文;顽固残留用 FLUSHO 直接清对象(实测 FLUSHO 后 COUNTC/COUNTO 立即归 0)。
Q7:如何确认服务端版本与运行状态?
问候语 CONNECTED <sonic-server v1.10.2> 带版本;control 模式 INFO 看连接数、命令延迟与句柄数(均为实测字段,见 6.2)。
十、参考链接
- 官方协议规范(PROTOCOL.md,本文语法依据):https://github.com/valeriansaliou/sonic/blob/master/PROTOCOL.md
- 官方仓库:https://github.com/valeriansaliou/sonic
- 官方配置参考:https://github.com/valeriansaliou/sonic/blob/master/CONFIGURATION.md
- 官方 Go 客户端:https://github.com/valeriansaliou/go-sonic
- 本专栏配套篇:《Sonic 介绍与选型指南》《Sonic Docker Compose 部署指南》《Sonic 配置参数详解》
- 相关文档:《MeiliSearch 1.55.0 进阶实战与运维》《Sonic 介绍与选型指南》