首页/新闻资讯/正文详情

Apache Thrift 的 Common Lisp 客户端/服务端开发完全指南:从 IDL 翻译到 RPC 实战

发布时间:2026/9/24 16:39:56 来源:云帆数科 栏目:资讯中心
Apache Thrift 的 Common Lisp 客户端/服务端开发完全指南:从 IDL 翻译到 RPC 实战
Apache Thrift 的 Common Lisp 客户端/服务端开发完全指南从 IDL 翻译到 RPC 实战【免费下载链接】thriftApache Thrift项目地址: https://gitcode.com/gh_mirrors/thrift2/thrift导读本文基于 Apache Thrift 仓库中 lib/cl/README.md 官方文档系统讲解如何在 Common Lisp 中消费 Thrift 协议从理解.thriftIDL 文件被翻译为 Lisp 源码后生成的包package、类型、def-struct/def-service形态到实现服务端逻辑、用 thrift 编译器生成 Lisp 接口、加载 ASDF 系统、启动服务端并用客户端远程调用。读完本文你将掌握with-client、serve两大核心宏的用法以及如何基于仓库自带的 tutorial/cl 教程代码跑通一个完整的 Calculator 示例并理解生成代码与手写实现之间的分工边界。一、Thrift Common Lisp 库概述Thrift 是一套语言无关的进程间通信协议与库合作的进程之间通过请求/响应消息进行通信消息的结构在使用前通过一份共享的**接口定义IDLInterface Definition Language**预先约定。在 Common Lisp 场景下这份.thrift定义文件会被翻译成若干 Lisp 源文件翻译产物包含以下几类定义三个包package一个用于实现操作符的命名空间另外两个分别用于请求操作符与响应操作符各种类型定义作为 Thrift 的typedef与enum定义的 Lisp 实现def-struct与def-exception形态对应 Thrift 的struct与exception定义def-service形态对应 Thrift 的service定义。其中def-service是最核心的产物。每个服务定义会展开为一组泛型函数generic function定义对于服务中的每一个操作op会生成两个函数函数使用方签名要点职责op-request客户端接受一个额外的、位于首位的protocol参数充当客户端代理通过 Thrift 编码的传输流与远程进程交互op-response服务端只接受一个protocol参数解码请求消息、调用基函数op并传入消息参数、编码并回送结果、处理异常需要特别指出的是翻译生成的代码只负责消息的编解码与传输真正执行op业务逻辑的函数需要由程序员实现其命名空间与生成的请求/响应函数相隔离详见下一节。库本身不提供从服务实现反向生成 IDL 的能力因此如果你要新建服务必须自己编写 IDL 文件参见官方流程说明中 no facility to generate them from a service implementation 的表述见 lib/cl/README.md。二、客户端与服务端的两大核心接口2.1 客户端接口with-client客户端侧唯一的操作符是一个宏(with-client (variable location) . body)它的语义是在动态上下文中建立一个连接并在退出时关闭它。variable被绑定到一个客户端代理的流/协议实例上该实例将底层的 I/O 流socket、文件等用实现 Thrift 协议与传输机制的算子包装起来。location则是形如#uthrift://127.0.0.1:9091的 Thrift URI由puri库提供的 uri 类解析。典型用法如下取自文档原样可运行(in-package :cl-user) (macrolet ((show (form) (format *trace-output* ~%~s ~{ ~s~} ,form (multiple-value-list (ignore-errors ,form))))) (with-client (protocol #uthrift://127.0.0.1:9091) (show (tutorial.calculator:ping protocol)) (show (tutorial.calculator:add protocol 1 2)) (show (tutorial.calculator:add protocol 1 4)) (let ((task (make-instance tutorial:work :op operation.subtract :num1 15 :num2 10))) (show (tutorial.calculator:calculate protocol 1 task)) (setf (tutorial:work-op task) operation.divide (tutorial:work-num1 task) 1 (tutorial:work-num2 task) 0) (show (tutorial.calculator:calculate protocol 1 task))) (show (shared.shared-service:get-struct protocol 1)) (show (zip protocol))))注意这里每个生成的请求函数都以protocol作为第一个参数这正是文档所强调的接受一个额外的初始protocol参数的体现macrolet中的show只是一个辅助打印宏用于把返回的多个值与可能的异常一并显示。2.2 服务端接口serve服务端接口组合了服务端对象server与服务对象service(serve (location service))它会在指定端口上接受连接并响应服务所声明操作的请求。service参数是def-service生成的一个全局变量所绑定的服务实例见下文运行服务端小节。三、构建与加载库BuildingThrift Common Lisp 库以ASDF系统thrift的形式发布。要构建它需要先把以下依赖系统注册给 ASDF依赖系统用途puri提供 Thrift 的 uri 类用于解析#uthrift://...这样的 locationcloser-mop提供类元数据class metadata支持trivial-utf-8字符串编解码usocketsocket 传输transportieee-floatsint 与 float 之间的转换trivial-gray-streamsgray streams 的抽象层alexandria常用工具函数依赖已随库打包用于本地构建测试与教程二进制也可以直接使用这些打包好的依赖来加载库本身。注册完成后执行(asdf:load-system :thrift)这行代码会编译并加载四类内容Thrift 定义文件的 Lisp 编译器即生成器运行时支持传输transport与协议protocol实现客户端与服务端的接口函数若要实际使用还需编写并加载远程服务的接口定义如果是实现服务方还需定义 Thrift 要代理调用的真实业务函数。3.1 仓库中的加载脚本仓库在 lib/cl/load-locally.lisp 中提供了一份使用捆绑依赖加载库本身的脚本它是构建自测与跨语言测试二进制的基础(require asdf) (load (merge-pathnames externals/bundle.lisp *load-truename*)) (asdf:load-asd (merge-pathnames lib/de.setf.thrift-backport-update/thrift.asd *load-truename*)) (asdf:load-system :thrift)依赖是通过 lib/cl/ensure-externals.sh 脚本从 Quicklisp 打包下来的——它会下载 quicklisp、调用quicklisp:bundle-systems把puri、usocket、closer-mop、trivial-utf-8、ieee-floats、trivial-gray-streams、alexandria、bordeaux-threads、cl-ppcre、fiasco、net.didierverna.clon等系统打包到externals/再拉取de.setf.thrift的backport-update分支作为库本体的来源。教程目录下的 tutorial/cl/load-locally.lisp 与该脚本内容一致保证测试与教程使用同一套加载路径。四、按照 Thrift 教程实现一个服务文档其余部分以 Thrift 官方教程为线索演示完整流程的五步实现服务业务逻辑翻译 Thrift IDL加载翻译后的 Lisp 服务接口运行服务端用客户端远程访问服务。4.1 第一步实现服务教程服务包含若干函数add、ping、zip和calculate。每一个被翻译的 IDL 文件都会为每个服务生成三个包。以教程文件为例相关的包是tutorial.calculator—— 请求包生成tutorial.calculator-implementation—— 实现包由程序员填写tutorial.calculator-response—— 响应包生成这种划分的目的是把 Thrift 方法的请求函数生成、**响应函数生成与实现函数程序员编写**分离开。文档建议在tutorial-implementation包中实现服务逻辑因为它import了common-lisp包而服务专属的那些包不导入common-lisp——这是为了避免 Thrift 方法名与common-lisp中的函数名发生冲突。仓库里对应的实现文件是 tutorial/cl/tutorial-implementation.lisp。文档给出的是基函数定义示例注意函数名带包限定符;; define the base operations (in-package :tutorial-implementation) (defun tutorial.calculator-implementation:add (num1 num2) (format t ~Asked to add ~A and ~A. num1 num2) ( num1 num2)) (defun tutorial.calculator-implementation:ping () (print :ping)) (defun tutorial.calculator-implementation:zip () (print :zip)) (defun tutorial.calculator-implementation:calculate (logid task) (calculate-op (work-op task) (work-num1 task) (work-num2 task)))其中calculate的实现借助了一个defgeneric分派把operation枚举值分派到对应的算术实现上并通过:around方法记录日志(defgeneric calculate-op (op arg1 arg2) (:method :around (op arg1 arg2) (let ((result (call-next-method))) (format t ~Asked to calculate: ~d on ~A and ~A ~d. op arg1 arg2 result) result)) (:method ((op (eql operation.add)) arg1 arg2) ( arg1 arg2)) (:method ((op (eql operation.subtract)) arg1 arg2) (- arg1 arg2)) (:method ((op (eql operation.multiply)) arg1 arg2) (* arg1 arg2)) (:method ((op (eql operation.divide)) arg1 arg2) (/ arg1 arg2)))仓库中的实际实现 tutorial/cl/tutorial-implementation.lisp 还展示了更完整的边界处理calculate用handler-case捕获division-by-zero并抛出 Thrift 异常tutorial:invalidoperation带有:why与:what-op关键字参数这样客户端就能收到结构化的远程异常。shared服务的实现见 tutorial/cl/shared-implementation.lisp它用一个 hash table 作为日志存储get-struct负责按键取值add-log负责把计算结果以shared:sharedstruct实例的形式存入。4.2 第二步翻译 Thrift IDLIDL 文件使用.thrift扩展名。教程场景有两个文件需要翻译tutorial.thriftshared.thrift由于前者include了后者只需用前者即可生成全部接口$THRIFT/bin/thrift -r --gen cl $THRIFT/tutorial/tutorial.thrift-r表示递归翻译被 include 的文件--gen cl指定目标语言为 Common Lispcl是 thrift 编译器注册的语言名。从编译器源码 compiler/cpp/src/thrift/generate/t_cl_generator.cc 可以看到CL 生成器会按 IDL 的namespace cl声明来决定输出的包前缀第 164 行program-get_namespace(cl)并据此输出三类文件。4.3 第三步加载 Lisp 翻译后的服务接口翻译器为每个 IDL 文件生成三个文件生成器源码第 129/130/142 行印证了这一点tutorial-types.lisp—— 类型定义def-struct、def-exception、枚举与 typedef 的 Lisp 实现tutorial-vars.lisp—— 变量与常量定义一个.asd文件ASDF 系统定义形如thrift-gen-tutorial.asd用来加载以上两者并把其他 include如教程中的shared作为依赖拉进来。仓库教程里的 tutorial/cl/make-tutorial-server.lisp 演示了完整加载序列加载本地依赖、加载 CLON 命令行框架、依次asdf:load-asd两个生成系统gen-cl/shared/thrift-gen-shared.asd与gen-cl/tutorial/thrift-gen-tutorial.asd最后加载手写实现所在的 tutorial/cl/thrift-tutorial.asd。这个教程级 ASDF 系统把shared-implementation与tutorial-implementation两个手写文件串行编译进thrift-tutorial系统依赖thrift-gen-tutorial即生成代码。4.4 第四步运行服务端服务在def-service形态中声明的实际名称教程里是calculator。每个服务定义都会定义一个以服务名命名的全局变量并将其绑定到一个描述该服务操作的服务实例上。启动服务只需指定一个 location 与服务实例(in-package :tutorial) (serve #uthrift://127.0.0.1:9091 calculator)仓库教程服务端入口 tutorial/cl/make-tutorial-server.lisp 使用thrift:serve绑定#uthrift://127.0.0.1:9090并通过clon:dump TutorialServer main生成可执行二进制。4.5 第五步用客户端远程访问在另一个进程中运行客户端前文的with-client代码段即此环节即可依次发起ping、add、calculate含触发除零异常与shared.shared-service:get-struct等调用。仓库客户端入口 tutorial/cl/make-tutorial-client.lisp 展示了异常路径的预期行为调用5 / 0时会捕获tutorial:invalidoperation并打印异常信息随后调用15 - 10正常返回最后读取服务端日志中的sharedstruct。五、已知问题与设计取舍Issues文档明确列出了该库当前的一些实现限制与设计讨论理解它们有助于规避陷阱5.1 optional 字段的歧义当 IDL 把某个字段声明为optional时生成的def-struct形态不会为该 slot 提供 initform编码算子也会跳过未绑定的 slot。这会在布尔bool字段上留下歧义无法区分字段未设置与字段显式设置为 NIL。5.2 实例化协议struct类是标准类standard classexception类则由具体实现决定解码器对 initargs 列表应用make-struct来构造实例文档指出在服务端一侧复用resourcingstruct 并直接对 slot-value 做副作用式解码是有优势的——这暗示未来可能的优化方向。5.3 map 的表示方式map 目前用hash table表示。由于通过 call/reply 接口传输的数据全部是静态类型的对象本身无需自描述编码形式用association listalist其实就已足够。文档还论证了为什么 property list 并不更优虽然 key 类型任意但getf使用eq比较若要支持任意 key 就得新建访问接口且 plist 无法直接用于函数应用如作为泛型函数分派依据。六、测试与回归仓库为 CL 库提供了自动化测试框架lib/cl/Makefile.am 中的run-tests目标调用 SBCL 执行 lib/cl/test/make-test-binary.lisp 生成测试二进制再由check-local运行./run-tests。构建产物run-tests、quicklisp、依赖目录等可通过clean-local清理。此外 lib/cl/READMES/readme-cassandra.lisp 还附带了一个 Cassandra 场景的补充说明可作为扩展阅读。总结Apache Thrift 的 Common Lisp 支持遵循与其他语言一致的先定义 IDL、再生成骨架、后填写实现的开发范式但又有鲜明的 Lisp 特色命名空间三分请求包 / 响应包 / 实现包相互隔离从机制上避免 Thrift 方法名污染common-lisp泛型函数驱动每个操作展开为一对op-request /op-response 泛型函数配合with-client与serve两个顶层算子客户端与服务端代码极度精简声明式传输#uthrift://host:port这样的 URI 即完整描述了连接目标底层 socket、协议编解码全部由库封装。若要在自己的项目中使用完整的落地路径是编写.thrift文件 → 用thrift -r --gen cl翻译 → 用 ASDF 加载生成系统与手写实现系统 → 服务端调用thrift:serve、客户端在thrift:with-client中发起调用。需要注意 optional 字段、map 表示等已知限制并在实现服务时始终把业务函数写在*-implementation包中。【免费下载链接】thriftApache Thrift项目地址: https://gitcode.com/gh_mirrors/thrift2/thrift创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Genex词法分析器深度解析:状态机如何精准切分复杂规则文本
Genex词法分析器深度解析:状态机如何精准切分复杂规则文本

Genex词法分析器深度解析:状态机如何精准切分复杂规则文本 【免费下载链接】genex-cj 生成表达式(Generate Expression,简称:Genex或GE)是一款用于按照指定语法规则随机或固定生成数据的功能库。主要适用于依赖规则数据… · 2026/9/24 16:39:56

PaddleHub 语言模型(Language Model)模块实战指南:词嵌入、语义匹配与预训练语义模型全解析
PaddleHub 语言模型(Language Model)模块实战指南:词嵌入、语义匹配与预训练语义模型全解析

人工智能大模型微调模型推理服务 【免费下载链接】PaddleFormers PaddleFormers is an easy-to-use library of pre-trained large language model zoo based on PaddlePaddle. 项目地址: https://gitcode.com/gh_mirrors/pa/PaddleFormers 点击查看 免费下载 本指… · 2026/9/24 16:39:56

Regal 规则解析:non-raw-regex-pattern——用 Raw String 编写 Rego 正则模式
Regal 规则解析:non-raw-regex-pattern——用 Raw String 编写 Rego 正则模式

后端认证鉴权云原生 【免费下载链接】opa Open Policy Agent (OPA) is an open source, general-purpose policy engine. 项目地址: https://gitcode.com/gh_mirrors/op/opa 点击查看 免费下载 本文是 Regal 规则库 Idiomatic(惯用法)分类下… · 2026/9/24 16:39:50

263页 2024年“数据要素×”优秀项目案例集
263页 2024年“数据要素×”优秀项目案例集

适应人群适配政府大数据管理部门、各行业数字化负责人、数据治理与产业研究人员; also 适合大数据服务商、咨询顾问、政企信息化架构师、科研院校研究学者及产业链从业从业者。 重要性总结本案例集共计 263 页,覆盖工业制造、现代农业、交通物流、商贸流… · 2026/9/24 17:12:20

django-allauth 集成 Figma 第三方登录:应用注册、回调配置与 OAuth2 源码实现解析
django-allauth 集成 Figma 第三方登录:应用注册、回调配置与 OAuth2 源码实现解析

后端认证鉴权身份认证 【免费下载链接】django-allauth Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication. 🔁 Mirror of https://codeberg.org/allauth… · 2026/9/24 17:12:14

YOLOv11模型Docker镜像构建与部署全攻略:从基础镜像选型到Kubernetes集群实战
YOLOv11模型Docker镜像构建与部署全攻略:从基础镜像选型到Kubernetes集群实战

🎬 Clf丶忆笙:个人主页 🔥 个人专栏:《YOLOv11全栈指南:从零基础到工业实战》 ⛺️ 努力不一定成功,但不努力一定不成功! 文章目录 一、Docker容器化对YOLOv11部署的价值 1.1 环境一致性:告别"在我机器上能跑"的噩梦 1.2 依赖隔离:让YOLOv11服务互不干… · 2026/9/24 17:12:07

【SRC】EDU实战篇8:越权漏洞挖掘的页面联动与逻辑批量探测技巧
【SRC】EDU实战篇8:越权漏洞挖掘的页面联动与逻辑批量探测技巧

文章目录 思路 案例一(功能越权) 案例二(IDOR) 站点1 站点2 站点3 站点4 站点5 站点6 站点7 案例三(修改返回包) 站点1 站点2 案例四(JWT越权) 站点1 案例五(前端分析绕过) 站点1 站点2 站点3 站点4 总结 ⚠️本博文所涉安全渗透测试技术、方法及案例,仅用于网络安全… · 2026/9/24 17:12:07

mysql 专业笔记 -- 第 39 章:事件(EVENTS)
mysql 专业笔记 -- 第 39 章:事件(EVENTS)

第 39.1 节:创建事件 MySQL 提供了事件功能,避免在大部分调度工作与 SQL 相关、与文件相关较少时使用复杂的 cron 交互。更多信息请参阅手册。可将事件视为按重复间隔计划运行的存储过程。 为节省调试事件相关问题的时间,请记住全局事件处理程序必须开启才能处理事件。 S… · 2026/9/24 17:12:07

销售离职客户带不走?忆笙CRM用公海池、查重合并、数据权限把客户资产锁进公司系统
销售离职客户带不走?忆笙CRM用公海池、查重合并、数据权限把客户资产锁进公司系统

很多老板都有过这种担心:销售一提离职,手里那批客户会不会也跟着人走了。这不是杞人忧天。还在用 Excel、个人微信、个人手机管客户的公司,客户资料本质上是销售的个人私产,人一走,关系和资源往往一起带走。 这里停一… · 2026/9/24 17:12:07

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码