EMQX 消息转换表达式中的非 ASCII Unicode 崩溃修复:`iolist_to_binary` 与 `unicode:characters_to_binary` 的正确取舍
后端物联网消息队列通信【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址https://gitcode.com/gh_mirrors/em/emqx点击查看免费下载导读本文聚焦 EMQX 开源仓库中一项针对消息转换Message Transformation功能的缺陷修复当用户在转换表达式中使用中文、日文、emoji 等非 ASCII Unicode 字符串时模块的错误处理路径可能触发进程崩溃。文章将结合变更记录 fix-16847.en.md、核心实现与回归测试从 Erlang 字节列表与 Unicode 码点列表的本质差异出发剖析崩溃根因、修复方案及其验证方式帮助开发者在编写转换表达式或扩展类似功能时规避同类问题。一、变更记录与问题概述仓库 changes/ee/fix-16847.en.md 用一句话记录了本次修复的核心内容Fix a crash when non-ASCII unicode string is used in message transformation expression.即在消息转换Message Transformation表达式中使用了非 ASCII 的 Unicode 字符串时会导致崩溃crash——而不是简单地返回转换失败。这意味着一个本该优雅降级按failure_action丢弃消息或断开客户端的场景演变成了运行时异常这是需要被修复的。二、功能背景消息转换与 variform 表达式在展开根因前先明确这个崩溃发生在哪个功能链路中。EMQX 的emqx_message_transformation应用封装了发布消息转换能力对入站或内部触发的发布消息负载payload按用户配置的转换规则进行改写失败时可以丢弃消息或断开违规客户端应用 README。从 emqx_message_transformation.erl 的源码结构可以还原核心链路应用通过emqx_hooks:put(message.publish, ...)注册发布钩子将on_message_publish/1挂到message.publish钩子上register_hooks/0消息发布时按 topic 在注册表中匹配命中的转换规则on_message_publish/1每条转换规则由若干operation组成每个 operation 包含key如payload.msg与value一段variform 表达式依次对消息上下文求值run_transformation/2。其中表达式使用的是一门名为variform的模板语言由 emqx_variform.erl 实现支持concat([你好世界])这类字符串拼接、模板插值等能力。问题恰恰出在表达式含非 ASCII 字符串时。三、根因分析Unicode 码点列表不是 iolist3.1 编译期表达式被存为 Unicode 码点列表variform 的compile/1把用户提交的二进制表达式转换为内部编译结构。关键在 compile/1compile(Expression) when is_binary(Expression) - compile(unicode:characters_to_list(Expression));unicode:characters_to_list/1会把 UTF-8 二进制按字符拆解成Unicode 码点codepoint列表。例如你好世界/utf8被编译后expr字段里存放的是[20320, 22909, 19990, 30028]这样一个整数列表——这些整数的值都远大于 255。而decompile/1只是原样返回该列表decompile/1不做任何转换。3.2 错误处理期prettify_operation 触雷崩溃发生在转换执行失败后的美化输出环节。在 eval_operation/2 中表达式求值被try...catch包裹一旦抛出异常会把operation prettify_operation(Operation)写入失败上下文用于日志与跟踪catch Class:Error:Stacktrace - FailureContext1 #trace_failure_context{ ... context #{ ... operation prettify_operation(Operation) } },而修复前的prettify_operation/1当前版本见 prettify_operation/1对value字段的处理方式是maps:update_with( value, fun(V) - unicode:characters_to_binary(emqx_variform:decompile(V)) end, Operation0 ),关键点在于这里必须使用unicode:characters_to_binary/1。原因如下emqx_variform:decompile(V)返回的是码点列表可能含大于 255 的整数iolist_to_binary/1只接受0255 范围内的字节组成的深层列表iolist遇到大于 255 的码点会直接抛出badarg异常如果错误处理路径中再次抛出badarg且该异常未被捕获就会演变成进程崩溃——这正是本次修复要消灭的二次崩溃一次表达式求值失败本应按规则优雅处理结果却在记录失败原因时又崩了一次。修复前若写成iolist_to_binary(emqx_variform:decompile(V))那么当表达式含你好世界、emoji、日文假名等任意码点大于 255 的字符时prettify_operation/1必然崩溃。修复后改用unicode:characters_to_binary/1它会将码点列表正确编码回 UTF-8 二进制从而稳定输出如concat([你好世界])/utf8这样的可读文本。补充说明该函数不仅服务于错误上下文还负责把 operation 的key码点路径列表通过lists:join(., Path)与iolist_to_binary/1拼接成payload.msg形式的字符串。key路径本身只由 ASCII 的字段名组成因此key一侧使用iolist_to_binary是安全的风险仅在value表达式本体一侧。四、回归测试用中文表达式验证修复仓库为该修复配套了专门的回归测试位于 emqx_message_transformation_tests.erl%% Variform expressions containing non-ASCII unicode characters should not %% crash prettify_operation/1. emqx_variform:compile/1 stores the expression %% as a unicode codepoint list, and emqx_variform:decompile/1 returns it as-is. %% iolist_to_binary/1 cannot handle codepoints 255, so prettify_operation %% must use unicode:characters_to_binary/1 instead. prettify_unicode_operation_test() - Expr concat([你好世界])/utf8, {ok, Compiled} emqx_variform:compile(Expr), Operation #{key [payload, msg], value Compiled}, Result emqx_message_transformation:prettify_operation(Operation), ?assertMatch(#{key : payload.msg, value : concat([你好世界])/utf8}, Result).该测试用例自身就注释了完整的故障模型可以拆解为三层验证构造复现场景用 UTF-8 中文表达式concat([你好世界])走emqx_variform:compile/1得到内部编译结构——其中表达式已被存储为 Unicode 码点列表断言输出稳定调用prettify_operation/1后value必须被还原为concat([你好世界])/utf8且key被拼接为payload.msg隐含验证不崩溃若实现误用iolist_to_binary/1该用例会在prettify_operation/1内部抛出badarg测试直接失败从而把回归牢牢锁死。测试还覆盖了转换名称等字段的 Unicode 校验场景如nãme/utf8、test_哈哈/utf8属于非法名称见同文件invalid_names_test_/0说明该模块对 Unicode 输入的处理是系统性关注点而本次修复补齐了表达式求值失败路径上的最后一环。五、修复影响与同类隐患排查建议5.1 对用户行为的影响修复后使用中文等非 ASCII 字符编写转换表达式成为被显式支持的行为表达式含中文、日文、emoji 时concat([温度传感器])、${payload.name}等写法可正常求值即便求值失败例如字段不存在、类型不匹配失败上下文中的operation字段也能以可读的 UTF-8 文本形式展示供日志与 trace 分析使用失败后的处理仍然遵循规则的failure_actionignore/drop/disconnect见 run_transformation/2 与 run_transformations/2不会再出现失败处理自身崩溃的连锁反应。5.2 给 Erlang 开发者的排查建议本次修复背后是一条通用 Erlang 经验iolist_to_binary/1与unicode:characters_to_binary/1的输入域并不相同。凡是由unicode:characters_to_list/1、unicode:characters_to_binary/1或字符串字面量产生的字符列表其元素可能是任意 Unicode 码点含大于 255 的值一律不能直接交给iolist_to_binary/1反之由binary_to_list/1产生的字节列表元素恒为 0255才可以。排查同类型崩溃时可以优先搜索代码中先decompile/characters_to_list得到列表、再iolist_to_binary收尾的模式并确认是否需要对 Unicode 输入做转码。六、小结本次修复通过将prettify_operation/1中的列表转二进制操作从iolist_to_binary/1替换为unicode:characters_to_binary/1解决了消息转换表达式含非 ASCII Unicode 字符串时的崩溃问题并配套中文表达式的回归测试防止复发。变更虽小但它同时涉及 variform 编译期的数据表示码点列表、求值失败期的错误上下文构建以及 Erlang Unicode 处理的基础语义是理解 EMQX 消息转换模块内部实现与 Erlang 字符处理边界的一个极佳切入点。相关代码均可继续在 emqx_message_transformation.erl、emqx_variform.erl 与 emqx_message_transformation_tests.erl 中查阅。赞分享后端物联网消息队列通信【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址https://gitcode.com/gh_mirrors/em/emqx点击查看免费下载相关推荐EMQX Logger 格式化器崩溃修复解析深度嵌套非 ASCII Term 引发的 FORMATTER CRASHEMQX Logger 格式化器崩溃修复解析深度嵌套非 ASCII Term 引发的 FORMATTER CRASH 导读 EMQX 在 debug 级别日志后端物联网消息队列通信linuxdeployqt与CMake集成非qmake项目的完整部署方案linuxdeployqt与CMake集成非qmake项目的完整部署方案 linuxdeployqt是一款强大的Linux应用部署工具能够将应用程序及其依赖开发工具CLI构建工具JavaScript 正则表达式中的 Unicode 处理修饰符 u 和 \p{...} 类JavaScript 正则表达式中的 Unicode 处理修饰符 u 和 \p{...} 类 在 JavaScript 中处理 Unicode 字符时正文档教程前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

腾讯和360图解原理:3步解决Java报错堆栈看不懂

腾讯和360图解原理:3步解决Java报错堆栈看不懂

腾讯和360图解原理:3步解决Java报错堆栈看不懂 刚拿到腾讯或360的面试通知,或者刚入职发现线上日志里全是红字?别慌。很多人卡住不是因为代码写不出来,而是面对满屏的 StackTrace 像看天书。报错一堆看不懂…

2026/9/23 9:59:41 阅读更多 →
IronClaw Skills 系统深度指南:SKILL.md 格式、信任模型与确定性激活管线

IronClaw Skills 系统深度指南:SKILL.md 格式、信任模型与确定性激活管线

IronClaw Skills 系统深度指南:SKILL.md 格式、信任模型与确定性激活管线 【免费下载链接】ironclaw IronClaw is an Agent OS focused on privacy, security and extensibility 项目地址: https://gitcode.com/gh_mirrors/iro/ironclaw 本指南系统讲解 Iron…

2026/9/23 9:59:41 阅读更多 →
3个步骤搞定headstrong,附完整示例避坑指南

3个步骤搞定headstrong,附完整示例避坑指南

3个步骤搞定headstrong,附完整示例避坑指南 很多刚入行的同学,对着文档里的 headstrong 语法能背得滚瓜烂熟,但真到动手搭项目时,代码一跑就报错,或者性能直接拉胯。这种“懂原理却写不出项目”的断层感,是不是让你抓狂?别急,…

2026/9/23 9:59:41 阅读更多 →

最新新闻

14-02-对比-CSharp-dotNET-vs-Java-JVM数据结构对比

14-02-对比-CSharp-dotNET-vs-Java-JVM数据结构对比

C#/.NET 与 Java/JVM 数据结构对比:从契约、布局到运行时比较基线:以 .NET 8 BCL/CoreCLR 与 Java 21 标准集合/HotSpot 为主要语义参照;不同 JVM、GC、AOT 与第三方集合库不在统一结论中 比较原则:语言语法、标准库公开契约、固定…

2026/9/24 17:41:39 阅读更多 →
Java集合分类

Java集合分类

一. Java 集合:Collection 单列集合与 Map 双列集合 1. Collection 单列集合每次存储单个元素,只有值,没有对应的键。 例子:脉劫、康帅傅、粤利粤,每个元素独立存在。子接口: List:有序、可重复、有索引。代…

2026/9/24 17:41:39 阅读更多 →
ROS2 实战 | 手写 TF2 海龟实时坐标广播器(含完整避坑指南)

ROS2 实战 | 手写 TF2 海龟实时坐标广播器(含完整避坑指南)

ROS2 实战 | 手写 TF2 海龟实时坐标广播器(含完整避坑指南) 摘要:本文从零实现一个 ROS2 Python 节点,实时订阅 turtlesim 海龟位姿并广播 TF 坐标变换。重点修正了官方教程中常见的 Odometry/Pose 混用、__name__ 入口错误、--symlink-install 开发技巧等高频踩坑点,附完…

2026/9/24 17:41:39 阅读更多 →
SqueezeNet:Fire模块驱动,极致参数压缩的初代轻量化CNN标杆

SqueezeNet:Fire模块驱动,极致参数压缩的初代轻量化CNN标杆

一、时代痛点:早期CNN的体积与部署困境 2016年之前的经典CNN模型,普遍存在模型臃肿、参数量爆炸、部署门槛极高的问题,严重限制了深度学习的落地应用,也是SqueezeNet诞生的核心动因。 第一,33卷积参数冗余严重。AlexNe…

2026/9/24 17:41:39 阅读更多 →
deepagents之任务规划与分解

deepagents之任务规划与分解

前言 agent或llm第一个蜕变,是它可以执行工具了。 但是agent开始变的强大,其实是从LLM能力提升,agent可以根据任务,自主规划任务与执行开始的。 今天我们就看下deepagents如何进行任务的规划与分解。 为什么agent需要规划能力 简单…

2026/9/24 17:41:39 阅读更多 →
前端工程师转型AI Agent开发:收藏这份完整学习路线,小白也能轻松入门!

前端工程师转型AI Agent开发:收藏这份完整学习路线,小白也能轻松入门!

本文为前端工程师提供了转型AI Agent开发的完整学习路线。首先介绍了AI基础概念,如LLM、RAG和Agent,然后补充了后端能力,包括Python、API和Backend。接着,深入探讨了AI工程能力,如Prompt Engineering、Tool Calling和M…

2026/9/24 17:40:39 阅读更多 →

日新闻

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

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

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

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →