ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

twemproxy Memcached 协议支持详解:ASCII 命令矩阵、解析状态机与多键分片合并

twemproxy Memcached 协议支持详解:ASCII 命令矩阵、解析状态机与多键分片合并 后端【免费下载链接】twemproxyA fast, light-weight proxy for memcached and redis项目地址https://gitcode.com/gh_mirrors/twe/twemproxy点击查看免费下载本指南以 twemproxynutcracker仓库中的 notes/memcache.md 为骨架系统梳理 twemproxy 对 Memcached 协议的支持范围与实现原理仅实现 ASCII 文本协议、不包含二进制协议完整覆盖存储、检索、删除、算术、touch、version 等命令的请求/响应格式并深入 src/proto/nc_memcache.c 的逐字节状态机解析、多键get/gets的分片与响应合并逻辑以及测试与配置层面的实战验证。读完本文你将能准确判断 twemproxy 支持哪些 Memcached 命令、各命令的线上格式与返回值语义并理解其分片-转发-合并的底层工作方式。概览只做 ASCII不做 Binarytwemproxy 定位为 memcached 与 redis 的快速轻量代理见 README.md其 Memcached 协议实现遵循一条明确的边界仅实现 Memcached ASCII 文本命令二进制Binary协议目前不支持。这与 src/proto/nc_memcache.c 中纯字符流逐字节解析的实现方式完全一致——解析器直接对\r\n分隔的文本行和data数据块做状态机处理不存在任何二进制帧头解析。因此凡是依赖 Memcached Binary 协议如 SASL 认证、某些客户端库的默认二进制模式的场景需要将客户端切换为 ASCII 文本协议才能经由 twemproxy 访问后端。请求命令支持矩阵twemproxy 当前支持的 Memcached 请求命令共 17 种下面按类别完整列出沿用原文档的支持状态 标准格式表达格式中的\r\n为行结束符。ASCII 存储命令Storage Command命令支持格式set是set key flags expiry datalen [noreply]\r\ndata\r\nadd是add key flags expiry datalen [noreply]\r\ndata\r\nreplace是replace key flags expiry datalen [noreply]\r\ndata\r\nappend是append key flags expiry datalen [noreply]\r\ndata\r\nprepend是prepend key flags expiry datalen [noreply]\r\ndata\r\ncas是cas key flags expiry datalen cas [noreply]\r\ndata\r\n其中各字段类型为flagsuint32_t客户端自定义的数据相关标志位expiryuint32_t过期时间秒datalenuint32_t数据块大小字节datauint8_t[]数据块本体casuint64_tCAS 令牌。从源码看存储类命令在解析完成后进入专门的SW_VAL状态按datalen精确消费数据块见 src/proto/nc_memcache.c并允许datalen为 0此时数据块为空。memcache_storage()函数将set/add/replace/append/prepend/cas六类统一归为存储命令src/proto/nc_memcache.c。ASCII 检索命令Retrieval Command命令支持格式get是get key [key]\r\ngets是gets key [key]\r\nget支持一次请求携带多个 keygets则在返回时附带 CAS 令牌。这两者是 twemproxy 中唯一允许携带多键的 Memcached 命令也是唯一会被分片fragment处理的命令详见下文。ASCII 删除命令Delete命令支持格式delete是delete key [noreply]\r\nASCII 算术命令Arithmetic Command命令支持格式incr是incr key value [noreply]\r\ndecr是decr key value [noreply]\r\n其中value为uint64_t表示增量/减量。ASCII 其他命令Misc Command命令支持格式touch是touch key expiry[noreply]\r\ngat计划中gat expiry key\r\ngats计划中gats expiry key\r\nquit是quit\r\nflush_all否flush_all [delay] [noreply]\r\nversion是version\r\nverbosity否verbosity num [noreply]\r\nstats否stats\r\nstats否stats args\r\n需要特别留意计划中/不支持列gat/gatsget-and-touch标注为Planned当前版本尚未实现flush_all、verbosity、stats均标注Notwemproxy 不会转发这些命令客户端直接使用时会得到错误响应。命令识别的源码依据在 src/proto/nc_memcache.c 的memcache_parse_req()中命令名按字节长度 逐字符比对识别3 字符匹配get/set/add/cas4 字符匹配gets/incr/decr/quit5 字符匹配touch6 字符匹配append/delete7 字符匹配prepend/replace/version。长度比对使用 src/proto/nc_proto.h 定义的str4cmp~str12cmp宏小端平台上按 32 位整型一次比较 4 字节减少逐字符比较开销。所有消息类型统一在 src/nc_message.h 的MSG_TYPE_CODEC中声明如MSG_REQ_MC_SET、MSG_REQ_MC_GETS、MSG_RSP_MC_VALUE等。响应格式twemproxy 会解析后端 Memcached 返回的 ASCII 响应并原样转发给客户端。以下按类别完整列出各类响应的文本格式。错误响应Error ResponsesERROR\r\n CLIENT_ERROR [error]\r\n SERVER_ERROR [error]\r\n语义ERROR客户端发送了不存在的命令名CLIENT_ERROR客户端命令不符合协议规范SERVER_ERROR服务端处理命令时出错导致无法完成。对应地src/proto/nc_memcache.c 在解析响应时识别ERROR、CLIENT_ERROR、SERVER_ERROR三种错误类型MSG_RSP_MC_ERROR等并进入SW_RUNTO_CRLF状态吞掉错误详情直到行尾。存储命令响应Storage Command ResponsesSTORED\r\n NOT_STORED\r\n EXISTS\r\n NOT_FOUND\r\n语义STORED存储成功NOT_STORED因add或replace的条件未满足而未存储EXISTS执行cas时目标条目在你上次获取之后已被修改NOT_FOUND执行cas时目标条目不存在。删除命令响应Delete Command ResponsesNOT_FOUND\r\n DELETED\r\n检索命令响应Retrieval ResponsesEND\r\n VALUE key flags datalen [cas]\r\ndata\r\nEND\r\n VALUE key flags datalen [cas]\r\ndata\r\n[VALUE key flags datalen [cas]\r\ndata]\r\nEND\r\n单键命中时返回一行VALUE ...加数据块再加END多键命中时返回多个VALUE行最后统一以END\r\n收尾。[cas]仅在gets时出现。twemproxy 的响应解析器src/proto/nc_memcache.c通过MSG_RSP_MC_VALUE状态链SW_SPACES_BEFORE_KEY→SW_KEY→SW_FLAGS→SW_VLEN→SW_RUNTO_VAL→SW_VAL→SW_VAL_LF逐字段解析并校验数据长度。算术命令响应Arithmetic ResponsesNOT_FOUND\r\n value\r\n其中value为uint64_t即incr/decr操作后的新键值。解析时以纯数字行进入SW_RSP_NUM状态识别为MSG_RSP_MC_NUM。touch 命令响应Touch Command ResponsesNOT_FOUND\r\n TOUCHED\r\n统计响应Statistics Response[STAT name value\r\n]END\r\n需要说明stats命令本身在 twemproxy 侧标注为No不支持转发此处仅按 Memcached 协议标准列出其响应形态供了解协议完整性。其他响应Misc ResponsesOK\r\n VERSION version\r\n协议语义要点Notes原文档总结的 Memcached 语义细节在配置 twemproxy 后端时同样适用逐条列出如下set无条件创建映射无论条目是否存在add仅当映射不存在时才添加replace仅当映射存在时才替换append与prepend会忽略 flags 和 expiry 值noreply指示服务端即使出错也不发送回复decr到 0 则结果为 0incr超过UINT64_MAX则回绕为 0key 最大长度为250 字符该常量在 src/proto/nc_memcache.c 定义为MEMCACHE_MAX_KEY_LENGTH 250解析时超出即报错见 src/proto/nc_memcache.cexpiry为 0 表示条目永不过期但仍可能因缓存淘汰被驱逐非零expiry要么是 Unix 时间自 1970-01-01 起的秒数要么是相对当前时间的秒数偏移小于60 × 60 × 24 × 30秒 30 天expiry 以服务端时间为准而非客户端时间datalen可以为 0此时data数据块为空。noreply 的解析实现noreply选项在存储、算术、删除、touch 命令中均可选。解析器在SW_RUNTO_CRLF状态遇到n时进入SW_NOREPLY校验后续恰好是 7 字符的noreply并置r-noreply 1src/proto/nc_memcache.c。存储类命令在 noreply 之后仍需继续读取data数据块。多键 get/gets 的分片与响应合并这是 twemproxy 处理 Memcached 请求时最核心、也最能体现其分片代理价值的一环。何时分片memcache_should_fragment()src/proto/nc_memcache.c规定只有get/gets且携带多个 key 时才分片。原因有二分片需要额外分配数组跟踪哪个 key 去了哪台后端代价更高因此单键请求绝不分片、直接路由到一台后端多键get的各个 key 可能被哈希到不同后端必须按后端拆分后并发请求再合并响应。分片过程memcache_fragment_retrieval()src/proto/nc_memcache.c按以下步骤构造子请求为每台后端建立对应的子消息sub_msg遍历请求中的每个 key用msg_backend_idx()依据哈希/分布算法决定其归属后端并把 key 追加到该后端的子消息中依据原命令类型为每个子消息前置get4 字节或gets5 字节头并追加\r\n结尾记录frag_id、frag_owner用于后续合并并把子消息串入碎片队列。由此形如get k1 k2 k3的多键请求会被拆成若干get k1\r\n、get k2 k3\r\n等单后端请求并行发出。响应合并pre-coalescesrc/proto/nc_memcache.c每当收到一个碎片响应时执行。对VALUE/END类型响应会裁掉每台后端返回的END\r\n标记只保留VALUE行避免合并时出现多余的 END对异常响应则标记请求错误并返回SERVER_ERROR。post-coalescesrc/proto/nc_memcache.c当所有碎片响应到齐后执行遍历每个 key 对应的子响应通过memcache_copy_bulk()src/proto/nc_memcache.c把各VALUE key flags len [cas]\r\ndata\r\n块mbuf 层面直接搬移/切分接近零拷贝复制到合并响应中最后统一追加一个END\r\n收尾返回给客户端。这套分片请求 → 并行转发 → 裁剪 END → 合并 VALUE → 补 END的流程让客户端可以像访问单机 Memcached 一样发送多键get/gets而实际数据被透明地分布到多台后端。实战配置 Memcached 池并验证用配置声明 Memcached 池twemproxy 的每个池通过 conf/nutcracker.yml 中的 YAML 块定义。池默认即为 Memcached 协议只有显式写redis: true才会启用 Redis 协议该字段在 src/nc_conf.c 中被默认置为CONF_DEFAULT_REDIS即默认关闭。例如 conf/nutcracker.yml 中的gamma、delta、omega池没有写redis: true其监听端口对接的1121x正是 Memcached 常用端口而 conf/nutcracker.leaf.yml 中的leaf池同样面向11212/11213两台 Memcached并使用fnv1a_64哈希与ketama一致性分布leaf: listen: 127.0.0.1:22121 hash: fnv1a_64 distribution: ketama auto_eject_hosts: true server_retry_timeout: 2000 server_failure_limit: 1 servers: - 127.0.0.1:11212:1 - 127.0.0.1:11213:1启动方式详见 README.md$ src/nutcracker -c conf/nutcracker.leaf.yml -d # 指定配置文件并守护进程化 $ src/nutcracker -t -c conf/nutcracker.leaf.yml # 仅校验配置语法用 telnet 手工验证协议由于是 ASCII 协议可直接用 telnet 或 nc 手工敲命令验证这正是原文档ASCII 协议更易调试观点的实践$ telnet 127.0.0.1 22121 Trying 127.0.0.1... Connected to 127.0.0.1. set foo 0 0 3\r\n bar\r\n STORED\r\n get foo\r\n VALUE foo 0 3\r\n bar\r\n END\r\n incr counter 5\r\n 5\r\n version\r\n VERSION 1.4.x\r\n quit\r\n自动化测试佐证仓库的测试目录 tests/test_memcache/ 提供了可运行的验证脚本例如 tests/test_memcache/test_gets.py起两个 Memcached 后端127.0.0.1:2200/2201与一个 nutcracker 实例监听127.0.0.1:4100is_redisFalse用 python-memcache 客户端连接 nutcracker覆盖set/get/incr/delete基础用例test_mget_mset验证多键get_multi/set_multi经 twemproxy 分片合并后的正确性test_mget_mset_large将键数量从 179 递增到T_LARGE默认 1000覆盖大数量多键请求test_mget_mset_key_not_exists验证部分 key 不存在返回空时多键get_multi行为正确。这说明多键分片合并不仅是设计文档中的规划更是有自动化测试保障的既定能力。调试思路与未来方向原文档在 Notes 末尾记录了两点技术考量在此一并转述ASCII 协议更易调试相比二进制协议ASCII 文本协议在线上形态直观可以用strace、tcpdump抓包观察线上的字节流也可以用 telnet、netcat、socat 直接构造 Memcached 请求与响应来做实验——这也是上文手工验证方法的理论来源。meta-text 协议的演进规划twemproxy 计划在 meta-text 协议被标记为稳定、且使用它的 Memcached 服务端经过数个版本发布之后再支持这一更高效的协议。也就是说在本文所述的 ASCII 支持之外meta-text 属于文档明确记载的、面向未来的扩展方向。小结围绕 notes/memcache.md本文完整还原了 twemproxy 的 Memcached 协议支持全貌仅 ASCII、共支持 17 种命令存储 6、检索 2、删除 1、算术 2、其他 6其中gat/gats计划中、flush_all/verbosity/stats不支持各类请求与响应的文本格式与字段类型、noreply、250 字符 key 上限、30 天内的相对过期时间等语义细节均已列出并进一步结合 src/proto/nc_memcache.c 的状态机解析与多键分片合并实现、conf/nutcracker.yml 的 Memcached 池配置示例及 tests/test_memcache/test_gets.py 的测试用例形成了从命令格式到实现原理再到可运行验证的完整链路。若需对照 Redis 协议侧的能力可继续阅读 notes/redis.md。赞分享后端【免费下载链接】twemproxyA fast, light-weight proxy for memcached and redis项目地址https://gitcode.com/gh_mirrors/twe/twemproxy点击查看免费下载相关推荐memcached网络协议全解析从ASCII到二进制协议memcached网络协议全解析从ASCII到二进制协议 本文全面解析Memcached的网络协议体系从基础的ASCII文本协议到高性能的二进制协议深入探缓存后端高可用Cal.diy Embed 生命周期详解握手协议、命令队列与事件状态机Cal.diy Embed 生命周期详解握手协议、命令队列与事件状态机 导读 Cal.diy Scheduling infrastructure for a后端前端企业应用DiceDB PFMERGE 命令详解多 HyperLogLog 基数合并与跨分片实现DiceDB PFMERGE 命令详解多 HyperLogLog 基数合并与跨分片实现 PFMERGE 是 DiceDB 中用于合并多个 HyperLogLo数据库缓存后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表