
"宇宙的尽头是SQL!"
相信从 MapReduce 时代探索至今的开发者们,在第一次接触分布式 SQL 引擎之后,都会如此感叹。低代码化的潮流,让 SQL 语言快速蔓延到更多的基础设施上面。但不得不说,SQL 也存在它的短板,首先它最早为了关系型数据库设计的,适合查询而非 ETL。但是现在人们慢慢把它扩展到 ETL、批流处理,甚至 AI 上,它就有点吃力了。第二个问题是,他是声明式的,导致缺乏可编程性。
“一只通晓万物的白泽诞生”,Byzer 开创性的定制了一套 DATA+AI 的语言作为核心 API,语法中融合 类 SQL 语法和宏命令,简单易用又不失灵活性。
从数据处理的角度看,它具备丰富、多样性的数据源扩展和 ET 扩展;
从特征工程和机器学习的角度看,语言原生支持模型的训练、部署、预测全流程(train/register/predict),几行代码完成一个模型开发。
通过 Byzer,开发人员可以只关注业务逻辑,学习成本低,容易理解,而且内置了很多扩展插件,还有功能强大的 Web UI,可以简化开发复杂度。
Byzer-lang项目地址 : https://github.com/byzer-org/byzer-lang
我们在之前的系列文章中演示了 ET 的设计和如何自定义一个 ET,并详细的介绍了一些 ET 如何在真实场景中解决我们数据和 ML 的业务问题:
在惊叹于 Byzer-lang 如此灵活的同时,作为开发者一定会很好奇其内部的实现原理,接下来让我们一探其究吧!
我们先来看一下解析器生成器的一些基本概念。
抽象语法树 (Abstract Syntax Tree,AST) 抽象语法树是源代码结构的一种抽象表示,它以树的形状表示语言的语法结构。抽象语法树一般可以用来进行
代码语法的检查
,代码风格的检查
,代码的格式化
,代码的高亮
,代码的错误提示
以及代码的自动补全
等等。
语法解析器 (Parser) 语法解析器通常作为
编译器
或解释器
出现。它的作用是进行语法检查,并构建由输入单词(Token
)组成的数据结构(即抽象语法树)。语法解析器通常使用词法分析器(Lexer)
从输入字符流中分离出一个个的单词(Token
),并将单词(Token
)流作为其输入。实际开发中,语法解析器可以手工编写,也可以使用工具自动生成。
词法分析器 (Lexer)
词法分析
是指在计算机科学中,将字符序列
转换为单词(Token
)的过程。执行词法分析
的程序便称为词法分析器。词法分析器(Lexer)
一般是用来供语法解析器(Parser)
调用的。
我们再看下上述概念如何在 Byzer-lang 的语法解析器中运用。
在 Byzer-lang 中我们将一套完整的 SQL 词法和语法规则定义在 DSLSQL.G4 文件中,内容是使用标准的 Antlr4 语法定义的 DSL,Antlr4 会基于我们的语法规则 codegen 成相应的代码。
在 Byzer-lang 中解析一条代码的大概流程如下:
一条代码经过词法解析器 DSLSQLLexer 生成一系列的 TokenStream,传递到语法解析器 DSLSQLParser 中生成 AST。DSLSQLParser 部分我们其实没有使用 AST,本质上是 codegen 生成的 。而 Byzer expression(就是if/ese 里面的表达式则完整走了整个流程,里面是有 AST 部分的,并且 AST 会被 codegen 成 SQL 引擎能执行的 SQL。
另外,Byzer-lang 中的语法提示复用了Byzer-lang 的 lexer 和 Spark SQL 的 lexer,重写了parser 部分。因为代码提示有一个特点是在书写过程中,大部分情况下语法都是错误的、未书写完成的,无法使用严格的 parser 来进行解析。因此我们结合了 spark 的 schema infer,以及重写了 SQL 的解析器,所以可以做到很好的效果。
架构图释义:
Byzer-lang 通过控制台( Byzer Notebook 或 Byzer Desktop)来完成任务开发、数据湖管理、系统配置、运行记录等操作。任务开发支持任务执行(query)、语法解析(analyze)、语法提示(suggestion);
Byzer-lang 支持多种 API 交互方式,可以和上层 Byzer Notebook 通信,包括的方式有:JDBC、HTTP、LSP 协议(language server protocol)、CLI;
Byzer-lang 核心的包含了 Byzer-lexer, Byzer-parser, Byzer-codegen 三个部分。其中 Byzer-lexer/Byzer-parser 使用的解析器生成器为 Antlr4,完全兼容 Spark SQL语法;另外
!if
分支语法使用的自研的 Byzer expression,这样可以不局限于 antlr4 框架的 DSL 限制,实现比如谓词下推的一些语法层面优化;codegen 是指 Byzer-lang 中的 adaptor 系列,比如:TrainAdaptor、SelectAdaptor 等,Adaptor 的具体功能,会在原理解析中介绍。开发者们提交过来执行的类 SQL 代码,最终会执行在 Byzer 的运行时引擎上面,根据代码的调用方式,分别提交到 Spark 和 Ray 引擎上面执行,除了这两个,Byzer 还支持 Java 或者 Scala 代码的本地执行。
我们还以 《利用 Byzer 的10行代码轻松实现企业级邮件服务》 中的邮件服务为例,其 Byzer 语句为:
run command as SendMessage.``
where method="mail"
and content="${EMAIL_BODY}"
and from = "userAccountNumber@qq.com"
and to = "${EMAIL_TO}"
and subject = "${EMAIL_TITLE}"
and contentType="text/html"
and attachmentContentType="text/csv"
and attachmentPaths="${savePath}"
and smtpHost = "smtp.qq.com"
and smtpPort="587"
and `userName`="userAccountNumber@qq.com"
and password="***"
;
复制
SendMessage 是我们提供的一个内置 ET,Byzer-lang 在语法设计的时候预留了足够的扩展性,我们可以很容易的使用 ET 的接口新增新的 ET 满足我们的定制化需求。
用户在 Byzer Notebook 中编写完一个代码后,点击执行(run)会提交到运行态引擎Byzer-lang 的 RestController,根据代码处理类型等配置交给 JobManager 进行同步或异步管理任务。代码会传递给 ScriptSQLExec 执行器,在执行器中会调用 DSLSQLLexer 和 DSLSQLParser 做语法解析。
由于我们使用的是观察者模式(Antlr4 支持观察者模式被动触发语法解析,还支持访问者模式由开发者主动触发语法解析),需要传递我们生成好的 ScriptSQLExecListener到 Antlr4 解析器中。
语法解析后会触发 Listener 的回调函数 exitSql。在 exitSql 我们根据 AST 中 Token 的关键词确定当前执行的语法,run 语法会使用 TrainAdapto r执行对应 ET SendMessage的 train 方法,这样就访问到了我们在 ET 中定义的 train 逻辑进行邮件发送了。由于Byzer 中的 ET,实际上就是定义了对表操作的扩展接口(SQLAlg),通过实现接口,我们可以在代码中定义具体的 ET 处理逻辑,实现表进表出的概念 —— 输入一张表,通过 ET 加工后输出一张表。在邮件工具中,我们的输出就是邮件内容和邮件的附件。
上面就是如何在 Byzer 中实现 SQL 扩展的全部流程,基于这种扩展方式,我们可以很方便地集成一个通用能力到 Byzer 中,也可以规范化扩展插件的接入,并将这种规范化拓展到增强自动代码提示、ET 管理工具、权限控制等功能上。是不是很有趣呢,快加入我们一起探索吧!
以上为 Byzer-lang 中底层扩展的详细讲解,希望对社区的小伙伴们有所帮助~ 欢迎大家加入 Byzer Slack 社区开发讨论组,参与 Byzer 社区的前沿技术话题讨论
https://join.slack.com/t/byzer-org/shared_invite/zt-10qgl60dg-lX4fFggaHyHB6GtUmer_xw
了解更多信息,欢迎大家关注我们的微信公众号
👇 👇 👇
也欢迎大家扫码添加 Byzer 小助手,
加入 Byzer 用户微信群讨论、交流问题哦~

点击阅读原文,访问 Byzer 官网