闲鱼的实时触达系统是如何玩转跨端开发的?

简介: 系列文章更新中

作者:闲鱼技术——骆彬

  在Omega实时触达系统的系列技术文章中,已经对行为采集中心CEP规则中心用户触达中心三个子系统进行了详细介绍。闲鱼定义了自己的DSL语言(领域特定语言),它把复杂的代码开发转换成了一种类SQL形式的简练表达,而在底层具体实现上,端侧、前端和云端可以使用的不同高级语言,如:python、c++、javascript、java等。使用DSL的表达方式这样降低了不仅可以降低技术门槛,还提升了研发效率这就带来了本文所要介绍的问题:如何实现自定义DSL语言到多种底层高级语言的翻译?

DSL语言翻译中遇到问题

  Omega系统中不仅实现了云端的复杂事件计算(CEP)引擎,端侧和前端也实现了各自的复杂事件计算引擎,相较于云端可以计算跨用户行为,端侧和前端CEP则更关注于单用户行为的计算,其更加实时和安全。因为各端CEP计算引擎的实现差异,导致了开发人员只能局限在各自领域内做开发,对于跨端开发有较高的技术门槛,另外时间成本也会不可控。因此,我们提出用自定义DSL语言来屏蔽各端的技术差异,在理想的情况下,开发人员应该只关注业务逻辑,其他技术细节不应该花费精力,如下图所示红色部分。

翻译流程

  各端CEP计算引擎在实现上的技术差异主要包括输入数据、CEP计算API、执行容器、结果输出等。在输入数据方面,由于端侧、前端和云端处理的输入数据是有差异的,如:端侧/前端可以处理用户在某个页面停留5s的数据,而云端无法感知,这就要求自定义DSL语言必须在数据输入层面兼容各端输入数据的差异;在CEP计算API方面,各端设计的CEP计算基础API可以是不同的,如:i=i+1和i++,没有统一的一套协议规范很容易导致横行野蛮扩张,还会增加后期统一翻译的难度;在执行容器方面,端侧和前端是在阿里的端计算容器Walle上计算,云端则是在阿里的流计算容器blink上计算;在结果输出方面,由于各端对应的是同一个用户触达中心,在计算结果协议上各端基本是一致的。在明确了各端之间的差异之后,可以梳理出以下几部分核心内容:

输入数据协议:兼容各端输入数据的差异;
CEP计算API:便于统一翻译的实现和基础协议的管控;
翻译框架选型及翻译:兼容各端高级语言的翻译工作,便于统一各端的能力升级迭代;
屏蔽执行容器差异:解决执行容器和各端CEP计算引擎的映射关系;

DSL语言翻译的设计实现
输入数据的统一

  对于各端输入数据的差异,业界比较通用的做法是,构建一层通用数据模版层来屏蔽各端输入数据的差异,各端根据需要注册自己的输入数据实例。这样做的好处是自定义DSL语言的输入可以统一起来,在后续翻译到各端语言的过程中,再根据已注册的符合模版规范的各端具体实例进行转换。我们也采用了这种方式来处理各端输入数据的差异,如下是我们定义的输入数据协议模版:

{
    "eventAlias":"事件别名",
    "eventCode":"PUBLISH_ITEM",
    "eventDesc":"卖家的详情被浏览",
    "eventTime":"事件发生时间",
    "updateTime":"事件更新时间",
    "partitionId":"分区id",
    "userId":"用户id",
    "extraInfo":{
        "itemId":"商品id",
        "buyerId":"买家id",
        "sellerId":"卖家id",
        "itemType":"商品类型",
        "itemStatus":"商品状态",
        "categoryId":"类目id",
        "latitude":"经度",
        "longitude":"纬度",
          ...:...
    },
    "scene":"场景",
    "fromScene":"上一个场景",
    "toScene":"下一个场景",
    "isFirstEnter":"是否首次进入",
      "bizId":"唯一Id",
    "sessionId":"会话id",
    "actionType":"行为类型",
    "actionName":"行为标识",
    "ownerName":"骆彬"
}
CEP计算API的统一

  对于各端CEP计算API的统一,业界比较成熟的协议规范是Flink CEP的协议规范,其基础计算API拆分的更加合理,各端的接受度更高。因此,我们以Flink CEP的协议规范为基础,定义了一套闲鱼CEP计算引擎通用的计算API协议规范,各端根据协议去实现具体的API即可,协议规范如下所示:

public static <X> Pattern<X, X> begin(final String name); 
public static <X> Pattern<X, X> begin(final String name, 
                              final AfterMatchSkipStrategy afterMatchSkipStrategy);
public Pattern<T, F> where(IterativeCondition<F> condition);
public Pattern<T, F> or(IterativeCondition<F> condition);
public Pattern<T, F> until(IterativeCondition<F> untilCondition);
public Pattern<T, F> within(Time windowTime);
public Pattern<T, T> next(final String name);
public Pattern<T, T> notNext(final String name);
public Pattern<T, T> followedBy(final String name);
public Pattern<T, T> notFollowedBy(final String name);
public Pattern<T, T> followedByAny(final String name);
public Pattern<T, F> optional();
public Pattern<T, F> oneOrMore();
public Pattern<T, F> greedy();
public Pattern<T, F> times(int times);
public Pattern<T, F> times(int from, int to);
public Pattern<T, F> timesOrMore(int times);
public Pattern<T, F> allowCombinations();
public Pattern<T, F> consecutive();
public static <T, F extends T> GroupPattern<T, F> begin(final Pattern<T, F> group, 
                             final AfterMatchSkipStrategy afterMatchSkipStrategy);
public static <T, F extends T> GroupPattern<T, F> begin(Pattern<T, F> group);
public GroupPattern<T, F> followedBy(Pattern<T, F> group);
public GroupPattern<T, F> followedByAny(Pattern<T, F> group);
public GroupPattern<T, F> next(Pattern<T, F> group);
翻译框架及实现

  在统一了输入数据和CEP计算API之后,就可以开始自定义DSL语言到统一的CEP计算API的翻译设计。由于CEP计算引擎有各端的实现,使得翻译框架必须能够支持多种目标语言的翻译。目前业界使用的较多的翻译框架有Antlr V4、parboiled、Apache Calcite,其各自的特点如下表所示:

- Antlr V4 Apache Calcite parboiled
支持的语言 ActionScript、Csharp2、Delphi、JavaScript、Perl5、Ruby、C、CSharp3、Java、ObjC、Python Java Java、scala
使用案例 Hibernate、Apache、Hive、TOra、Esper、StreamBase、spark Hive、Drill、Flink、Phoenix、Storm -
功能范围 词法解析,语法解析,中间语法树生成 是一款开源SQL解析工具, 非自定义DSL解析工具 一个解析框架,需要自己开发Parser,没有AST概念
其他 idea有Antrl V4的插件,开发方便 - -

  结合以上各种翻译框架的特点,Antlr V4的翻译框架可以友好的支持我们对于多种语言翻译的需求,且开发更为方便,最终我们选择了Antlr V4的翻译框架。根据自定义DSL语法和统一的CEP计算API,可以设计一套语法解析文件,然后由Antlr V4生成DSL语法解析器和AST语法树,最后,结合各端特点完成由AST树节点到高级语言的翻译,大致流程如下图所示:

翻译流程

执行容器的屏蔽

  对于执行容器与各端CEP计算引擎之间一对多的映射关系,我们添加了一个DSL规则类型的概念。使用DSL规则类型去关联相应的执行容器,进而屏蔽了开发人员对于底层执行容器的感知。另外,我们设计了DSL编辑器,并提供了语法和事件提示、审核流、资源管理、结果查询等辅助功能,相信会给开发人员提供一个友好的体验。

编辑器

实际应用效果

  目前Omega的翻译方案经过双十一实践的检验,在降低技术门槛和提高开发效率方面效果显著。通过自定义DSL语言屏蔽各端语言实现的差异性,使得稍有SQL使用经验的人都可以快速进入开发,开发的技术门槛直线下降,开发人员可以专注于业务逻辑的实现。经过双十一活动的实践,通过自定义DSL语言开发业务规则可以把之前一周的开发量压缩到1-2个小时,平均开发一个DSL业务规则耗时在10分钟左右,开发效率成倍提升。

结果展示

后续开发计划

  Omega的开发生态已经具备了一定规模,输出了一系列核心协议标准,提供了简洁、高效的集成开发和运维环境。目前,端侧和云端已经接入,后续要还要接入前端,向更广的领域发展,对于各端翻译的技术细节后面也会详细介绍。另外,基于翻译器的核心协议标准,进一步深化闲鱼DSL语言能力,对外输出协议标准和成熟的翻译器产品也在规划之列。

相关文章
uniapp的form表单自定义验证规则
uniapp的form表单自定义验证规则
813 0
|
前端开发 JavaScript Go
VSCode 配置本地服务器
VSCode 配置本地服务器
1380 0
VSCode 配置本地服务器
|
前端开发 Java 数据安全/隐私保护
深入理解 Spring MVC Controller —— 请求参数获取
前言 接上篇《深入理解 Spring MVC Controller —— 请求映射》,上篇主要介绍了处理器方法及请求映射的定义。有了处理器方法 Spring MVC 就可以对请求进行处理,有了请求映射 Spring MVC 就能知道哪些请求应该由哪些处理器方法来处理。
1464 0
深入理解 Spring MVC Controller —— 请求参数获取
|
存储 弹性计算 Linux
2022年阿里云服务器租用价格表(最新收费标准及活动价格表)
本文介绍了阿里云服务器价格的组成,官方报价工具和价格计算器的使用,以及最新活动报价等信息。
40495 0
2022年阿里云服务器租用价格表(最新收费标准及活动价格表)
|
人工智能 编解码 物联网
设计师集体破防!UNO:字节跳动创新AI图像生成框架,多个参考主体同框生成,位置/材质/光影完美对齐
UNO是字节跳动开发的AI图像生成框架,通过渐进式跨模态对齐和通用旋转位置嵌入技术,解决了多主体场景下的生成一致性问题。该框架支持单主体特征保持与多主体组合生成,在虚拟试穿、产品设计等领域展现强大泛化能力。
855 4
设计师集体破防!UNO:字节跳动创新AI图像生成框架,多个参考主体同框生成,位置/材质/光影完美对齐
|
数据采集 供应链 数据可视化
小白在做京东项目数据采集并实现可视化的过程中需要的步骤要点
根据业务需求和数据反馈,不断优化数据采集和分析流程。 引入新的数据源、算法和技术,提高分析效率和准确性。 通过以上步骤,京东可以利用数据采集和可视化分析来洞察市场趋势、优化库存管理、提升用户体验和增强竞争力。
|
8月前
|
存储 数据处理 数据库
公募REITs公告PDF文档处理项目
本项目是一个专门用于处理基础设施公募REITs(Real Estate Investment Trusts)公告PDF文件的完整RAG数据处理管道,也适用于其他公告PDF文件,应用多模态大模型,可高效提升文本提取内容。系统能够自动化地将PDF公告文档转换为结构化数据,能够检测表格、实现跨页表格拼接,并将表格内容还原为便于检索的文本信息。并构建向量数据库和 Elasticsearch 以支持智能检索与问答系统。
公募REITs公告PDF文档处理项目
|
Linux 数据库
ntp如何配置同步服务器
【5月更文挑战第19天】ntp如何配置同步服务器
2555 2
|
消息中间件 Dubbo Java
阿里云中间件开源往事
分布式架构和云原生重塑了中间件的游戏规则,这给国内开发者提供了重新定义中间件的历史机遇。本文讲述了阿里云在应用中间件领域核心开源项目的过去、现在和未来,篇幅较长。
839 83
阿里云中间件开源往事
|
安全 Linux 虚拟化
0基础教你安装VM 17PRO-直接就是专业许可证版
0基础教你安装VM 17PRO-直接就是专业许可证版