Apache Thrift JavaScript 库完全指南:浏览器 RPC 客户端、Node.js Web 服务器与 Grunt 构建实践

Apache Thrift JavaScript 库完全指南:浏览器 RPC 客户端、Node.js Web 服务器与 Grunt 构建实践 Apache Thrift JavaScript 库完全指南浏览器 RPC 客户端、Node.js Web 服务器与 Grunt 构建实践【免费下载链接】thriftApache Thrift项目地址: https://gitcode.com/GitHub_Trending/thr/thrift本文是 Apache Thrift JavaScript浏览器端库的实战指南基于仓库中 lib/js/README.md 展开结合 lib/js/src/thrift.js、lib/js/Gruntfile.js 与 lib/nodejs/lib/thrift/web_server.js 等源码深入讲解。读完你将掌握如何用thrift编译器从 IDL 生成 JS 代码、如何通过 XHR 与 WebSocket 两种传输在浏览器中发起 RPC 调用、如何用createWebServer在 Node.js 侧托管静态页面与 Thrift 服务、如何用 Grunt 一键完成构建与测试以及 64 位整数等关键边界问题的处理方式。一、库定位浏览器端的 Apache Thrift 实现Apache Thrift 是一套跨语言 RPC 框架而 JavaScript 分支位于 lib/js是其浏览器端实现。它支持 RPC 客户端以JSON 协议TJSONProtocol通过Http[s]XHR与WebSocket两种传输通道与服务端通信。在浏览器中你无需任何插件只要引入生成的 JS 文件与库文件即可直接调用 Thrift 服务。从源码注释可以确认thrift.js只创建一个全局对象Thrift所有特性都收敛在该命名空间内lib/js/src/thrift.js库中Version常量为0.25.0与 lib/js/package.json 中声明的版本一致。该命名空间内部又分为几个层次传输层TransportThrift.TXHRTransport别名Thrift.Transport负责基于 XMLHttpRequest 的字节级 I/OThrift.TWebSocketTransport负责基于 WebSocket 的字节级 I/O协议层ProtocolThrift.TJSONProtocol别名Thrift.Protocol负责消息的序列化与反序列化辅助类型Thrift.TException、Thrift.TApplicationException、Thrift.TProtocolException、Thrift.Multiplexer等。端到端的典型调用方式如下源码注释中的示例lib/js/src/thrift.jsvar transport new Thrift.Transport(http://localhost:8585); var protocol new Thrift.Protocol(transport); var client new MyThriftSvcClient(protocol); var result client.MyMethod();二、获取源码与 Grunt 构建本目录是 Apache Thrift JavaScript 库的根目录其中包含Gruntfile.js与package.json。构建与测试工具链依赖较新版本的 Node.js。2.1 安装构建依赖执行以下命令安装 Grunt 构建所需的支持文件npm install该命令读取 lib/js/package.json 并从互联网拉取相应依赖其中包括grunt^1.6.3、grunt-cli、grunt-contrib-concat用于拼接、grunt-contrib-uglify用于压缩、grunt-contrib-jshint用于代码检查、grunt-contrib-qunit用于运行测试、grunt-jsdoc用于生成文档、browserify、node-int64与json-int64用于 64 位整数支持等。2.2 执行构建npx grunt该命令运行项目本地安装的 Grunt位于./node_modules/.bin/执行的任务链定义在 lib/js/Gruntfile.js 的default任务中[test, concat, uglify, jsdoc]即test先执行installAndGenerate创建目录、复制thrift.js、安装 Node 依赖、调用编译器生成各变体的测试代码、安装测试依赖、用 browserify 打包 Int64 相关库随后执行jshint代码检查启动 HTTP/HTTPS/ES6 测试服务器并运行 QUnit 浏览器测试详见第六节concat将src/**/*.js拼接为dist/thrift.jsuglify压缩生成带版本横幅的dist/thrift.min.jsjsdoc基于src/*.js与./README.md生成doc/下的 HTML 文档。2.3 构建产物目录结构构建前及构建后的目录如下目录说明/srcJavaScript Apache Thrift 源码核心为 src/thrift.js/doc由 jsdoc 生成的 HTML 文档构建后出现/dist发行文件thrift.js与thrift.min.js构建后出现/test各类测试也是查阅示例代码的好去处/node_modules由npm install安装的构建支持文件三、从 IDL 生成 JavaScript 代码Thrift 编译器thrift源码在 compiler/cpp支持针对 JS 的多种代码生成选项。以官方测试套件为例lib/js/Gruntfile.js 中展示了各类变体的生成命令thrift -gen js --out test/gen-js ../../test/v0.16/ThriftTest.thrift thrift -gen js:jquery --out test/gen-js-jquery ../../test/v0.16/ThriftTest.thrift thrift -gen js:node --out test/gen-nodejs ../../test/v0.16/ThriftTest.thrift thrift -gen js:es6 --out test/gen-js-es6 ../../test/v0.16/ThriftTest.thrift thrift -gen js:node,es6 --out ./test/gen-nodejs-es6 ../../test/v0.16/ThriftTest.thrift各生成选项的适用场景-gen js生成浏览器端普通 JavaScript配合 XHR 传输典型文件名xxx.jsxxxClient-gen js:jquery生成依赖 jQuery1.5的浏览器端代码使用jqRequest方式发起异步请求-gen js:node生成 Node.js 端代码典型为gen-nodejs/xxx.js配合thriftnpm 包使用-gen js:es6/-gen js:node,es6生成 ES6 语法版本测试服务器可通过--es6参数切换加载。其中-gen js生成的浏览器端代码如hello_svcClient依赖第二节所述的thrift.js运行时-gen js:node生成的 Node 端代码则依赖thriftnpm 包即 lib/nodejs 的 Node.js 分支二者的运行时是分开的。四、完整示例hello_svc 服务端到端跑通下面这个完整示例展示了一个简单的基于浏览器的 JavaScript Thrift 客户端与 Node.js JavaScript 服务端服务为hello_svc以下三个代码块完整引自 lib/js/README.md并补充运行说明。4.1 hello.thrift —— 服务 IDLservice hello_svc { string get_message(1: string name) }用以下命令生成浏览器端gen-js/与 Node 端gen-nodejs/代码thrift -gen js -gen js:node hello.thrift4.2 hello.html —— 浏览器客户端!DOCTYPE html html langen head meta charsetutf-8 titleHello Thrift/title /head body Name: input typetext idname_in input typebutton idget_msg valueGet Message div idoutput/div script srcthrift.js/script script srcgen-js/hello_svc.js/script script (function() { var transport new Thrift.TXHRTransport(/hello); var protocol new Thrift.TJSONProtocol(transport); var client new hello_svcClient(protocol); var nameElement document.getElementById(name_in); var outputElement document.getElementById(output); document.getElementById(get_msg) .addEventListener(click, function(){ client.get_message(nameElement.value, function(result) { outputElement.innerHTML result; }); }); })(); /script /body /html要点说明页面先引入运行时库thrift.js再引入编译器生成的gen-js/hello_svc.js传输层Thrift.TXHRTransport(/hello)指向服务端挂载 Thrift 服务的 URL 路径协议层Thrift.TJSONProtocol(transport)负责 JSON 序列化生成的客户端hello_svcClient(protocol)的get_message(name, callback)为异步调用回调参数即服务端返回的字符串结果。4.3 hello.js —— Node.js 服务器var thrift require(thrift); var hello_svc require(./gen-nodejs/hello_svc.js); var hello_handler { get_message: function(name, result) { var msg Hello name !; result(null, msg); } } var hello_svc_opt { transport: thrift.TBufferedTransport, protocol: thrift.TJSONProtocol, processor: hello_svc, handler: hello_handler }; var server_opt { staticFilePath: ., services: { /hello: hello_svc_opt } } var server thrift.createWebServer(server_opt); var port 9099; server.listen(port); console.log(Http/Thrift Server running on port: port);说明服务端使用 Node.js 分支的thrift包lib/nodejscreateWebServer是创建静态文件 Thrift 服务一体化 Web 服务器的工厂函数实现在 lib/nodejs/lib/thrift/web_server.jshello_svc_opt中processor与handler成对出现handler提供业务实现processor由 IDL 编译生成负责把请求路由到对应处理方法一个服务器可以通过services对象挂载多个服务路径浏览器客户端只需把TXHRTransport的 URL 指向对应路径即可。注意README 示例中服务端选项写为staticFilePath而当前源码 web_server.js 实际读取的选项键名是files。若选项缺失或为空字符串静态文件服务将被禁用见 web_server.js因此建议使用files指定静态资源根目录。另外README 示例中的Thrift.createWebServer应理解为thrift.createWebServer小写包名导出的函数与第五节正式配置示例保持一致。五、createWebServer 服务端配置详解createWebServer(options)返回一个原生 Node.js HTTP/HTTPS Server 实例可直接listen(port)。其完整选项在 lib/nodejs/lib/thrift/web_server.js 中有详细注释整理如下。5.1 ServerOptions服务器级配置选项类型说明corsarray/object允许跨域请求的来源Origin字符串数组含*或命中请求 Origin 时放行否则返回 403filesstring静态文件服务根目录缺省或为空字符串时禁用静态文件服务headersobject静态文件 GET 响应时附加的响应头键值对哈希servicesobject将服务 URI 字符串映射到 ServiceOptions 对象的哈希tlsobjectNode.js TLS 选项见 Node.js tls 文档不提供或为 null 时使用普通 HTTP启用 SSL/TLS 至少需要key与cert5.2 ServiceOptions服务级配置选项类型说明transportobject分层传输默认TBufferedTransportprotocolobject序列化协议默认TBinaryProtocol示例与 JS 浏览器端配套常用TJSONProtocolprocessorobjectIDL 编译器生成的服务类/处理器为兼容历史用法也可用cls键传入直接传入处理器对象或处理器类均可见 web_server.jshandlerobject服务的业务处理方法实现一个真实的配置范例来自测试服务器 lib/js/test/server_http.jsconst thrift require(../../nodejs/lib/thrift); const ThriftTestSvc require(./gen-nodejs/ThriftTest.js); const ThriftTestHandler require(./test_handler).ThriftTestHandler; const ThriftTestSvcOpt { transport: thrift.TBufferedTransport, protocol: thrift.TJSONProtocol, processor: ThriftTestSvc, handler: ThriftTestHandler }; const ThriftWebServerOptions { files: __dirname, services: { /service: ThriftTestSvcOpt } }; const server thrift.createWebServer(ThriftWebServerOptions); server.listen(8089);5.3 请求路由与 CORS 处理createWebServer创建的服务器在底层为不同 HTTP 方法挂接了不同的处理逻辑web_server.jsPOSTprocessPost按请求路径在services中查找服务将请求体交给svc.transport.receiver(...)累积数据再以new svc.protocol(transportWithData)构造输入协议、new svc.protocol(new svc.transport(...))构造输出协议最终调用svc.processor.process(input, output)完成 RPCweb_server.jsGETprocessGet提供静态文件服务。它会对路径做规范化校验防止../逃逸出baseDirweb_server.js目录请求自动回退到index.html并按扩展名映射 Content-Type如.html→text/html、.js→application/javascript、.json→application/json等见 web_server.jsOPTIONS处理 CORS 预检放行时返回 204并设置access-control-allow-origin、access-control-allow-methods: GET, POST, OPTIONS、access-control-allow-headers: content-type, accept等头web_server.jsupgradeWebSocket服务端手工完成 RFC 6455 握手校验Sec-WebSocket-Key并计算Sec-WebSocket-Accept随后按帧协议解析客户端消息并路由到services中的第一个服务web_server.js。帧编码支持文本TJSONProtocol与二进制TBinaryProtocol两种 opcode。六、传输层与协议层源码级解析6.1 TXHRTransport基于 XHR 的 HTTP 传输Thrift.TXHRTransport别名Thrift.Transport在 lib/js/src/thrift.js 中定义构造函数接受 URL 与可选 optionsuseCORS、customHeaders。其flush(async, callback)方法通过getXmlHttpRequestObject()获取浏览器 XHR 对象兼容旧版ActiveXObject以POST方式发送send_buf并在请求头中设置Accept与Content-Type为application/vnd.apache.thrift.json; charsetutf-8异步模式下通过onreadystatechange在readyState 4 status 200时把responseText写入接收缓冲并回调同步模式下直接检查status 200并填充recv_bufthrift.js。此外jqRequest(client, postData, args, recv_method)提供了 jQuery 集成路径它要求 jQuery 1.5jQuery.Deferred通过jQuery.ajax发起 POST并利用自定义 convertertext thrift将响应文本交给recv_method反序列化thrift.js。6.2 TWebSocketTransport基于 WebSocket 的传输Thrift.TWebSocketTransportthrift.js维护callbacks待回调队列与send_pending连接未建立前缓存的发送请求flush(async, callback)连接已打开时立即socket.send(send_buf)并把回调压入队列未打开时先缓存待__onOpen时统一补发thrift.js__onMessage按先进先出顺序弹出回调并传入服务端消息数据open()仅在readyState为 CLOSED或无 socket时才创建new WebSocket(url)并绑定onopen/onmessage/onerror/onclose。6.3 TJSONProtocolJSON 序列化协议Thrift.TJSONProtocol别名Thrift.Protocol在 thrift.js 中定义。它把 Thrift 类型映射为 JSON 内的类型标记字符串thrift.jsThrift 类型JSON 类型标记BOOLtfBYTE/I08i8I16i16I32i32I64i64DOUBLEdblSTRING/UTF7strSTRUCTrecMAPmapLISTlstSETset序列化时通过writeMessageBegin(name, messageType, seqid)将消息组织为[version, name, messageType, seqid, ...]数组结构Thrift.Protocol.Version 1反序列化时通过readMessageBegin()读取并校验版本号不匹配则抛出Wrong thrift protocol versionthrift.js。反序列化会优先使用JSONInt64.parse当引入json-int64时以正确处理 64 位整数。协议层还内置了两道安全防线递归深度限制Thrift.DEFAULT_RECURSION_DEPTH 64incrementRecursionDepth()超过该值会抛出DEPTH_LIMIT类型的TProtocolExceptionthrift.js负数长度防御readMapBegin/readListBegin在 size 为负时抛出NEGATIVE_SIZE异常skip()深度超过 64 层抛出DEPTH_LIMIT防止恶意数据导致异常行为thrift.js。对应测试可参考 lib/js/test/test-protocol-negative-size.js。6.4 多路复用MultiplexerThrift.Multiplexerthrift.js允许在单条连接上承载多个服务createClient(serviceName, SCl, transport)为客户端生成自增seqid并通过Thrift.MultiplexProtocol在写消息时把方法名改写为serviceName : name服务端据此区分不同服务。七、测试体系如何验证你的 JS 分支7.1 浏览器端测试入口lib/js/test/README.md 说明测试结构如下服务端server_http.js是支持标准 Apache Thrift 测试套件test/v0.16/ThriftTest.thrift的 Node.js Web 服务器同时支持 XHR 与 WebSocket 客户端server_https.js是同一服务器的 SSL/TLS 版本证书取自 test/keys 目录HTTP 对应ws:HTTPS 对应wss:客户端三个基于 QUnit 的 HTML 驱动文件——test.html测试 jQuery 生成代码-gen js:jquery、test-nojq.html测试普通 JS 构建-gen js均使用 XHR 传输、testws.html测试 WebSocket 传输test*.js为实际测试逻辑。7.2 通过 Grunt 一键运行在 lib/js 目录执行npx grunt test或默认的npx grunt即可自动完成生成各类测试代码ThriftGen系列任务→jshint代码检查 → 启动四个测试服务器HTTP/HTTPS × 普通/ES6→ 用 headless Puppeteer 运行 QUnit 测试覆盖 XHR、jQuery、WebSocket、ES6、Int64、递归深度、双重渲染等场景见 Gruntfile.js→ 结束后杀掉服务器进程。7.3 与 Java 测试服务器联调如需以 Java 分支的测试服务器运行客户端测试可在 lib/js 目录执行make check要求已先构建 Apache Thrift Java 分支并在 lib/java 中执行过make check。八、TypeScript 支持浏览器端与 Node 端的 TypeScript 定义文件也可以通过编译器生成thrift --gen js:ts file.thrift生成的.d.ts定义文件可为 TypeScript 项目提供完整的类型提示。此外Node.js 分支的 TypeScript 相关测试与工具位于 lib/nodets可作为补充参考。九、64 位整数与 Breaking Changes从 0.13.0 版本起一个重要的破坏性变更影响了 64 位整数常量的生成方式64 位整数常量现在使用node-int64生成例如var x new Int64(7fffffffffffffff);在浏览器端lib/js/package.json 将node-int64与json-int64列为依赖Grunt 的ThriftBrowserifyNodeInt64任务会用 browserify 把node-int64、json-int64及 lib/nodejs/lib/thrift/int64_util.js 打包为浏览器可用的Int64、JSONInt64、Int64Util全局对象Gruntfile.js。协议层writeI64会调用Int64Util.toDecimalString(i64)进行十进制字符串转换readMessageBegin优先使用JSONInt64.parse解析thrift.js从而保证超过 JavaScript 安全整数范围的 64 位数值不会丢失精度。相关测试可见 lib/js/test/test-int64.js 与 lib/js/test/test-int64.html。十、实践要点小结浏览器端引入dist/thrift.js或thrift.min.js与编译器生成的gen-js/*.js用TXHRTransport(url)TJSONProtocol构造客户端即可异步调用 RPC传输选择需要双向/长连接用TWebSocketTransport普通请求/响应用 XHR 即可服务端createWebServer同时支持二者WebSocket 握手由服务端内置实现服务端createWebServer一个实例即可同时托管静态页面files与多个 Thrift 服务services并通过tls选项一键启用 HTTPS/WSS通过cors控制跨域64 位整数涉及大整数时务必配套json-int64解析库并按 0.13.0 之后的写法使用Int64包装常量构建与测试在 lib/js 下依次执行npm install与npx grunt即可完成代码检查、测试、拼接、压缩与文档生成全套流程。更完整的 IDL 语法可参考 doc/specs/idl.md 与 doc/specs/thrift-json-protocol.md文档目录下包含 JSON 协议规范官方测试套件 test/v0.16/ThriftTest.thrift 覆盖了全部数据类型与集合组合是编写 IDL 与验证客户端行为的最佳参照。【免费下载链接】thriftApache Thrift项目地址: https://gitcode.com/GitHub_Trending/thr/thrift创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考