Why make wheels ?

“宇宙的尽头是SQL!”,相信从 MapReduce 时代过渡过来的开发,在第一次接触分布式 SQL 引擎都会惊叹出这句话。低代码化的潮流,让 SQL 语言快速蔓延到更多的基础设施上面。但不得不说,SQL 也存在它的短板,首先它最早为了关系型数据库设计的,适合查询而非 ETL,但是现在人们慢慢把他扩展到 ETL,批流处理,甚至 AI 上,它就有点吃力了。 第二个问题是,他是声明式的,导致缺乏可编程性。

“一只通晓万物的白泽诞生”,Byzer 以 DATA+AI 为核心理念,语法中融合了类 SQL 语法和宏命令,开创性的制定了一套核心 API*,*简单易用又不失灵活性。

  • 从数据处理的角度看,它具备丰富、多样性的数据源扩展和 ET 扩展;

  • 从特征工程和机器学习的角度看,语言原生支持模型的训练、部署、预测全流程(train/register/predict),几行代码完成一个模型开发。

通过Byzer,开发人员可以只关注业务逻辑,学习成本低,容易理解,而且内置了很多扩展插件,还有功能强大的 Web UI,可以简化开发复杂度。

在这里插入图片描述

How is it designed ?

我们在前面文章《ET 开发指南》中演示了 ET 的设计和如何自定义一个 ET,并详细的介绍了一些 ET 如何在真实场景中解决我们数据和 ML 的业务问题。在惊叹于 Byzer-lang 如此灵活的同时,作为开发者一定会很好奇其内部的实现原理,接下来让我们一探其究吧!

基本概念

我们先来看一下解析器生成器的一些基本概念。

  1. 抽象语法树 (Abstract Syntax Tree,AST) 抽象语法树是源代码结构的一种抽象表示,它以树的形状表示语言的语法结构。抽象语法树一般可以用来进行代码语法的检查代码风格的检查代码的格式化代码的高亮代码的错误提示以及代码的自动补全等等。

  2. 语法解析器 (Parser) 语法解析器通常作为编译器解释器出现。它的作用是进行语法检查,并构建由输入单词(Token)组成的数据结构(即抽象语法树)。语法解析器通常使用词法分析器(Lexer)从输入字符流中分离出一个个的单词(Token),并将单词(Token)流作为其输入。实际开发中,语法解析器可以手工编写,也可以使用工具自动生成。

  3. 词法分析器 (Lexer) 词法分析是指在计算机科学中,将字符序列转换为单词(Token)的过程。执行词法分析的程序便称为词法分析器。词法分析器(Lexer)一般是用来供语法解析器(Parser)调用的。

我们再看下上述概念如何在 Byzer-lang 的语法解析器中运用。

在 Byzer-lang 中我们将一套完整的 SQ L词法和语法规则定义在 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 的解析器,所以可以做到很好的效果。

由于本篇文章重点是讲解 ET 处理流程,antlr4 就介绍到这里,我们会在后续文章中揭秘我们的语法解析的实现内幕。

技术架构

在这里插入图片描述

架构图释义:

  • 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 语句为:

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满足我们的定制化需求。如需了解ET的接入方式,请参考文章:

用户在 Byzer Notebook编写完一个代码后,点击执行(run)会提交到运行态引擎Byzer-lang的RestController,根据代码处理类型等配置交给JobManager进行同步或异步管理任务。代码会传递给ScriptSQLExec执行器,在执行器中会调用DSLSQLLexer和DSLSQLParser做语法解析。

由于我们使用的是观察者模式(Antlr4支持观察者模式被动触发语法解析,还支持访问者模式由开发者主动触发语法解析),需要传递我们生成好的ScriptSQLExecListener到Antlr4解析器中。

语法解析后会触发Listener的回调函数exitSql。 在exitSql我们根据AST中Token的关键词确定当前执行的语法,run语法会使用TrainAdaptor执行对应ET SendMessage的train方法,这样就访问到了我们在ET中定义的train逻辑进行发送邮件了。由于Byzer中的ET,实际上就是定义了对表操作的扩展接口(SQLAlg),通过实现接口,我们可以在代码中定义具体的ET处理逻辑,实现表进表出的概念 —— 输入一张表,通过ET加工后输出一张表。在邮件工具中,我们的输出就是邮件内容和邮件的附件。

上面就是如何在 Byzer 中实现 SQL 扩展的全部流程,基于这种扩展方式,我们可以很方便地集成一个通用能力到 Byzer 中,也可以规范化扩展插件的接入,并将这种规范化拓展到增强自动代码提示、ET 管理工具、权限控制等功能上。是不是很有趣呢,快加入我们一起探索吧!

Logo

更多推荐