背景
面向数据加工领域的DSL(特定领域语言,Domain-Specific Language)无需使用者编写较为复杂的通常程序语言,具有较好的用户体验,应用较为广泛。如何将DSL翻译为机器可执行的程序是每种DSL均需面对的问题,并且传统的DSL翻译通常采用直译的方式,运行时执行效率较低。
本文,提出一种面向数据加工领域语言的代码翻译算法,针对不同的DSL函数分别设计了代码翻译方案,不仅保证了语义的正确性,而且翻译得到的运行时代码执行效率较高。
怎么实现的?
本文提出了一种面向数据加工领域语言的代码翻译算法,函数功能丰富,具有较广的应用场景,翻译DSL时兼顾了运行时的执行速度,数据处理能力好,吞吐率较高。系统模块设计分为三部分:语言设计、代码翻译、发布运行。
A)语言设计,用于设计面向数据加工服务的特定领域语言。本步骤通过包含的5类基本函数(事件处理函数、值处理函数、操作符函数、资源池函数、类型相关函数)构成功能丰富的DSL,函数是此语言的基本组成单元。
事件处理函数表示处理当前事件的函数,此类函数均以e_开头,并且省略了表示“当前事件”的默认参数,比如:操作整个事件函数、操作事件某个属性的函数、字段提取函数和事件检查函数等等。
值处理函数表示处理值的函数,值可以是任意数据类型,此类函数的第一个参数均为“处理值”,比如:json提取函数、json转换函数、ip信息富化函数、正则表达式函数、
操作符函数表示通用的流程控制、算术计算、逻辑、数值比较等操作相关的函数,此类函数均以op_开头。
资源池函数表示可以将客户可以维护的外部资源加载到数据加工服务,同时提供了加载间隔时间以满足客户的实时性要求,加载的外部资源包含:数据库、阿里云OSS存储和阿里云SLS存储等等。
类型相关函数表示与数据类型紧密相关的函数,比如:类型转换函数、算术函数、字符串函数、日期时间函数、列表函数和字典函数等等。
B)代码翻译,分析各个函数的所属类型及参数并将其翻译为运行时代码。本步骤在保证语义正确性的前提下,将DSL翻译为执行效率较高的运行时代码,以提升数据加工服务的吞吐率。
¬¬¬1.将DSL经过词法语法解析成为AST(抽象语法树,Abstract Syntax Tree)。
2.预处理,遍历抽象语法树,如果当前节点是为名称“v”函数且参数有且只有一个,则将函数名替换为“e_single_field_value”。“v”函数实现了取属性值功能,使用频度较高,当参数只有一个时,无需循环遍历参数列表,性能提升了30%。
3.常量检查,遍历抽象语法树,根据名称和参数设置函数“是否为常量函数名称const_func_name”、“是否所有参数均为常量const_attr”、“是否部分参数均为常量part_const_attr”。
(1)函数Call
如果函数名称为“str_format”,且第一个参数为常量字符串,则设置“是否部分参数均为常量part_const_attr”为True,否则设置“是否部分参数均为常量part_const_attr”为False。
如果函数名称为"json_select\regex\regex_select\regex_findall\regex_match\regex_replace\regex_split",且第二个参数为常量字符串,后面的其他参数也均为常量,则设置“是否部分参数均为常量part_const_attr”为True,否则设置“是否部分参数均为常量part_const_attr”为False。
如果函数名称为"e_set \ dt_fromtimestamp ",且所有参数均为常量,则设置“是否部分参数均为常量part_const_attr”为True,否则设置“是否部分参数均为常量part_const_attr”为False。
执行完上面的步骤已可以正确设置“是否部分参数均为常量part_const_attr”。
如果函数是事件处理函数、与当前时间相关的函数和需要定时更新的资源池函数,则设置“是否为常量函数名称const_func_name”为False,否则设置“是否为常量函数名称const_func_name”为True。
如果函数的参数不全为常量,则返回False,否则将“是否为常量函数名称const_func_name”作为结果返回。
(2)键值对keyword
如果值为变量名称,则返回False,否则返回True。
(3)其他类型
遍历所有的参数,如果参数类型为列表,则需遍历列表元素。如果所有参数遍历结果均为True,则设置“是否所有参数均为常量const_attr”为True,并返回True,否则设置设置“是否所有参数均为常量const_attr”为False,并返回False。
4.代码翻译,遍历抽象语法树,根据节点属性翻译为运行时代码。
如果函数名称为“op_and\op_or”,为实现逻辑操作短路,将上述两个函数翻译为if操作。
如果函数“是否所有参数均为常量const_attr”为True,或者“是否部分参数均为常量part_const_attr”为True,则分别对不同的函数类型进行公共模式初始化抽取,此部分只会在模式匹配过程中执行一次。比如:对于具有定时刷新功能的资源池函数res_rds_mysql\res_log_logstore_pull\res_oss_file则返回结果指针,然后通过读写锁实时更新资源池;对于超高频函数e_set,针对参数的个数和常量属性分别进行处理,所有的键值对均为常量则转换为KeyValueConstSetTransformer,所有的键均为常量则转换为KeyConstSetTransformer,其他情况转换为SetTransformer。
如果函数为扩展类函数,比如:e_split、e_coutput,需要将此类函数转换为迭代器循环访问。
如果函数“是否为常量函数名称const_func_name”和“是否所有参数均为常量const_attr”均为True,则表明此函数结果为常量,则可以提取为全局共享变量。
如果函数为“dt_now\dt_today”,且没有参数,则无需进行进行时区处理,则转化为"DtNowExpression\DtTodayExpression"。
同时,所有的事件处理函数都需要加表示当前事件的参数event。
代码翻译参照:
C)发布运行,将代码翻译的结果和输入输出相结合,即可打包成为可以运行的应用程序。并且,此程序将定时发送运行日志到消息队列,继而监控程序读取消息队列里面的监控日志,最终生成方便客户使用的监控界面。
有哪些实际价值?
1)语义丰富:面向数据加工的领域语言支持二百多个函数,具有较为丰富的语义,能够应对广泛的数据加工应用需求。
2)执行速度快:针对不同的函数种类,分别进行了优化处理,尽量抽取数据加工只需执行一次的公共部分,提高了事件处理速度。