一个一站式流式处理云平台解决方案

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ZYC88888/article/details/83013146

随着互联网、IT、大数据等技术的爆发式发展,企业系统产生的大量爆发数据。对于保存在数据库中的业务数据,可以通过DBus数据总线+Wormhole流式处理平台的日志方式实时地无侵入同步和落地到任意sink端,提供下游系统分析使用;对于业务系统产生的日志数据,这些包含了业务高低峰、用户轨迹、系统异常/错误信息、调用链等诸多信息,也蕴含着无价的宝藏。一些公司通过埋点等方式和手段,往日志数据里输出他们想要监控和跟踪的信息,以便提供客观的数据支撑,做出更高效、更准确的决策。

DBus-https://github.com/bridata/dbus

Wormhole-https://github.com/edp963/wormhole

在这种背景下,各种各样的日志收集、结构化、分析工具如雨后春笋般出现,业界目前已有不少发展得比较成熟的方案,例如:Logstash、Filebeat、Flume、Fluentd、Chukwa. scribe、Splunk等,更有整合了日志采集、转换分析和展示的ELK集成方案。这些方案各有所长。在结构化日志这个方面,大多采用配置正则表达的方式:用于提取日志中模式比较固定、通用的部分,例如日志时间、日志类型、行号等。对于真正的和业务比较相关的信息,我们权且称之为message部分,原因主要是这部分千变万化,很难有一个特定、通用的模式来一劳永逸地描述及囊括所有情形。然而,这部分内容对于企业来说,比前面相对固定的部分更有价值。

本文就是介绍一种通过简单、可视化、所见即所得即席验证的方式,为用户提供高效、灵活地采集和加工利用数据日志的工具。

常见的日志处理方案

目前业界常见的日志分析代表为ELK:

  1. 通过Logstash实时收集和转换数据,保存到Eleaticsearch中,通过Kibana进行展示。

  2. 或者通过Filebeat 作为前端抓取数据,Logstash进行数据转换,保存到Eleaticsearch中,通过Kibana进行展示。

                                             

结构化日志的原理

DBUS设计的数据日志同步方案如下:

  1. 日志抓取端采用业界流行的组件(例如Logstash、Flume、Filebeat等)。一方面便于用户和业界统一标准,方便用户的整合;另一方面也避免无谓的重造轮子。抓取数据称为原始数据日志(raw data log)放进Kafka中,等待处理。

  2. 提供可视化界面,配置规则来结构化日志。用户可配置日志来源和目标。同一个日志来源可以输出到多个目标。每一条“日志源-目标”线,中间数据经过的规则处理用户根据自己的需求来自由定义。最终输出的数据是结构化的,即:有schema约束,可以理解为类似数据库中的表。

  3. 所谓规则,在DBUS中,即“规则算子”。DBUS设计了丰富易用的过滤、拆分、合并、替换等算子供用户使用。用户对数据的处理可分多个步骤进行,每个步骤的数据处理结果可即时查看、验证;可重复使用不同算子,直到转换、裁剪得到自己需要的数据。

  4. 将配置好的规则算子组运用到执行引擎中,对目标日志数据进行预处理,形成结构化数据,输出到Kafka,供下游数据使用方使用。

系统流程图如下所示:

根据配置,我们支持同一条原始日志,能提取为一个表数据,或者可以提取为多个表数据。

每个表是结构化的,满足相同的schema。

  • 每个表是一个规则 算子组的合集,可以配置1个到多个规则算子组

  • 每个规则算子组,由一组规则算子组合而成

拿到一条原始数据日志, 它最终应该属于哪张表呢?

每条日志需要与规则算子组进行匹配:

  • 符合条件的进入规则算子组的,最终被规则组转换为结构化的表数据。

  • 不符合的尝试下一个规则算子组。

  • 都不符合的,进入unknown_table表。

规则算子

规则算子是对数据进行过滤、加工、转换的基本单元。常见的规则算子如下:

算子之间是独立的,通过组合不同的算子达到更复杂的功能,对算子进行迭代使用最终达到对任意数据进行加工的目的。

我们试图使得算子尽量满足正交性或易用性(虽然正则表达式很强大,但我们仍然开发一些简单算子例如trim算子来完成简单功能,以满足易用性),算子的开发也可以随意扩充,比如可以开发提取JSON节点值或XML节点值的算子。算子的开发也很容易,只要遵循基本接口原则,就可以开发任意自定义算子。

结构化日志的例子

下面以提取heart_beat_status表为例子进行配置说明,整个数据日志结构化过程如下:

使用通用抓取端抓取原始日志

使用Logstash 读取log4j文件作为数据输入源,输出到Kafka中(这里具体配置就不说了,可参考Logstash配置)

这里抓取端不限制,你可以使用Flume、Filebeat,甚至你自己写的端,只要输出到Kafka中就可以。

使用可视化方式进行规则算子的配置

首先配置一个输出表的规则组, 查看heartbeat_log_new这个topic中,我们直接实时的可视化操作配置。

1、读取原始数据日志

可以看到由Logstash 预先提取已经包含了log4j的基本信息,例如path、@timestamp、level等。但是数据日志的详细信息在字段log中。由于不同的数据日志输出是不一样的,因此可以看到log列数据是同的。

需要指出的是:使用logstash预先提取其它列是可选的,其实对于Flume这样直接抓取到的就是raw data log,这对我们后面的提取没有影响

2、提取感兴趣列

例如我们提取timestamp、log 原始信息等,可以添加一个toindex算子,提取感兴趣的字段,如下:

这里需要指出,我们考虑使用数组下标方式,是有原因的:

  • 并不是所有列天生就有列名(例如flume抽取的原始数据,或者split算子处理后的数据列);

  • 下表方式对可以使用数组方式指定列(类似python方式, 例如:1:3 表示1,2,3列);

因此后续操作全部基于数组下标方式访问。

特别说明一下:如下?号所在的地方,鼠标移上去就会显示一个在线帮助,告诉你这个算子怎么使用,每个算子怎么用不需要记。

执行一下,就可以看到被提取后的字段情况:

3、过滤不相关数据

在这个例子中,我们只对插入心跳包的数据感兴趣。因此添加一个filter算子,对第2列进行过滤”插入心跳包”:

执行后,不符合条件的日志行就被过滤了。

4、以切分方式进行提取

添加一个split算子,我们对“数据源”,“插入心跳包”,还有后面的“node”进行切分。

可以看到切分后,原来的第1列变新的1,2,3,4,5列了。

特别需要说明的是:提取的方式非常多,Split只是一种常见方式。我们可以substring提取,replace掉不需要的数据等,我们还也可以配置正则表示式提取。我们不积极推荐用正则表示的原因是正则表达式很容易写错,并不是最简单可视化的选择。

5、以trim方式出来数据

我们想提取4列的值,使用trim算子进行过滤掉不需要的数据。

执行后,这样新的5列就拿到干净的值。

6、选择输出列

最后我们把感兴趣的列输出,使用saveAs算子进行输出, 指定列名和类型。

执行后,这就是处理好的最终输出数据样本。

查看结构化输出结果

保存上一步配置好的规则组,运用到DBus执行算子引擎,就可以生成相应的结构化数据了。目前根据项目实际,DBus输出的数据是UMS格式。UMS是DBus开源项目(https://github.com/bridata/dbus)定义并使用的,通用的数据交换格式,是标准的JSON。其中同时包含了schema和数据信息。更多UMS介绍请参考DBus开源项目主页的介绍。

输出结果的数据格式和结构,不想使用UMS的,可经过简单的开发,实现定制化。

以下是测试案例,输出的结构化UMS数据的例子:

{

    "payload": [

        {

            "tuple": [

                "127046516736228867",

                "2017-12-17 13:57:30.000",

                "i",

                "320171788",

                "2017/12/17 13:57:30.877",

                "edpdb",

                "成功",

                "/DBus/HeartBeat/Monitor/edpdb/TEST1/T1000"

            ]

        },

        {

            "tuple": [

                "127046516736228869",

                "2017-12-17 13:57:30.000",

                "i",

                "320171790",

                "2017/12/17 13:57:30.946",

                "edpdb",

                "成功",

                "/DBus/HeartBeat/Monitor/edpdb/TEST4/ONEYI"

            ]

        },

        {

            "tuple": [

                "127046520930532871",

                "2017-12-17 13:57:31.000",

                "i",

                "320171792",

                "2017/12/17 13:57:31.026",

                "edpdb",

                "成功",

                "/DBus/HeartBeat/Monitor/edpdb/TEST3/USER_REGISTER"

            ]

        }

    ],

    "protocol": {

        "type": "data_increment_data",

        "version": "1.3"

    },

    "schema": {

        "batchId": 0,

        "fields": [

            {

                "encoded": false,

                "name": "ums_id_",

                "nullable": false,

                "type": "long"

            },

            {

                "encoded": false,

                "name": "ums_ts_",

                "nullable": false,

                "type": "datetime"

            },

            {

                "encoded": false,

                "name": "ums_op_",

                "nullable": false,

                "type": "string"

            },

            {

                "encoded": false,

                "name": "ums_uid_",

                "nullable": false,

                "type": "string"

            },

            {

                "encoded": false,

                "name": "event_time",

                "nullable": false,

                "type": "datetime"

            },

            {

                "encoded": false,

                "name": "datasource",

                "nullable": false,

                "type": "string"

            },

            {

                "encoded": false,

                "name": "heartbeat_state",

                "nullable": false,

                "type": "string"

            },

            {

                "encoded": false,

                "name": "heartbeat_node",

                "nullable": false,

                "type": "string"

            }

        ],

        "namespace": "heartbeat_log.heartbeat_log_schema.heartbeat_table.3.host1.0"

    }

}

查看监控

为了便于掌握数据抽取及规则匹配等情况,我们提供了日志数据提取的可视化实时监控界面,如下图所示,可随时了解:

  • 实时数据条数

  • 错误条数情况(错误条数是指:执行算子时出现错误的情况,帮助发现算子与数据是否匹配,用于修改算子)

  • 数据延时情况

监控中还有一张表叫做__unkown_table__ 表明所有没有被匹配上的数据条数。例如:logstash抓取的日志中有5种不同模式的数据,我们只捕获了其中3种模式,其它没有被匹配上的数据,全部在_unkown_table_计数中。

总结

DBus日志同步方案总结如下:

1、集成整合现有日志抓取工具(Flume、filebeat、logstash等),方便用户接入及整合;

2、DBUS提供了丰富的算子及可视化配置界面,供用户结构化数据日志使用。

  • 通过可视化的,自由地使用各种规则算子对日志进行处理,生成结构化的数据日志;

  • 每个算子配置过程都可以看到原始数据和加工后的数据;

  • 算子可以随意增减,处理顺序可以随意调换,每个步骤的算子相互叠加使用;

  • 用户也可以扩展开发自己需要的算子;

  • 提供源到目标一对多的配置,让用户想怎么玩就怎么玩,从而可从任意多个角度挖掘数据价值;

  • 监控让用户更直观地了解到数据结构化实时转换的情况,一切尽在掌控;

3、将原始数据日志转换为结构化数据,输出到kafka中提供给下游数据使用费进行使用。

最终使得日志业务数据结构化的过程,变得简单、可视化、配置集中化, 使得大家都能轻松地玩转日志数据。

本文提到的日志结构化方案计划于2018年1月发布到开源项目DBus 0.4版本中,项目地址https://github.com/bridata/dbus

系统架构和工作原理

DBUS主要分为两个部分:貼源数据采集和多租户数据分发。两个部分之间以Kafka为媒介进行衔接。无多租户资源、数据隔离需求的用户,可以直接消费源端数据采集这一级输出到kafka的数据,无需再配置多租户数据分发。

GlobalOverview

1 DBUS源端数据采集

DBUS源端数据采集大体来说分为2部分:

  • 读取RDBMS增量日志的方式来 实时获取增量数据日志,并支持全量拉取;
  • 基于logtash,flume,filebeat等抓取工具来实时获得数据,以可视化的方式对数据进行结构化输出;

以下为具体实现原理 system arch

主要模块如下:

  • 日志抓取模块:从RDBMS的备库中读取增量日志,并实时同步到kafka中;
  • 增量转换模块:将增量数据实时转换为UMS数据,处理schema变更,脱敏等;
  • 全量抽取程序:将全量数据从RDBMS备库拉取并转换为UMS数据;
  • 日志算子处理模块:将来自不同抓取端的日志数据按照算子规则进行结构化处理;
  • 心跳监控模块:对于RDMS类源,定时向源端发送心跳数据,并在末端进行监控,发送预警通知;对于日志类,直接在末端监控预警。
  • web管理模块:管理所有相关模块。

2 多租户数据分发

对于不同租户对不同源端数据有不同访问权限、脱敏需求的情形,需要引入Router分发模块,将源端貼源数据,根据配置好的权限、用户有权获取的源端表、不同脱敏规则等,分发到分配给租户的Topic。这一级的引入,在DBUS管理系统中,涉及到用户管理、Sink管理、资源分配、脱敏配置等。不同项目消费分配给他的topic。

route2Project

主要功能:

  • 无侵入方式接入多种数据源: 业务系统无需任何修改,以无侵入性读取数据库系统的日志获得增量数据实时变化。目前RDBMS支持mysql,oracle数据源(Oracle数据源请参考Oracle相关协议), 日志方面支持基于logstash,flume和filebeat的多种数据日志抽取方案。
  • 海量数据实时传输: 使用基于Storm的流式计算框架,秒级延时,整体无单点保证高可用性。
  • 多租户支持: 提供用户管理、资源分配、Topology管理、租户表管理等丰富的功能,可根据需求,为不同租户分配不同的源端表数据访问权限,应用不同的脱敏规则,从而实现多租户资源隔离、差异化数据安全。

grafana

2intr_proj_table

2intr_router_topo

  • 感知源端schema变更: 当源端发生schema变更时,能自动感知schema变化,调整UMS版本号,并通过Kafka消息和邮件通知下游diff

  • 数据实时脱敏: 可根据需求对指定列数据进行实时脱敏。脱敏策略包括:直接替换、MD5、murmur等脱敏算法,脱敏加盐,正则表达式替换等。支持用户开发jar包实现DBUS未覆盖的个性化脱敏策略。 docs/encode

  • 初始化加载: 支持高效的初始化加载和重新加载,支持任意指定输出topic,灵活应对客户需求。 docs/fuller

  • 统一标准化消息传输协议: 使用统一的UMS(JSON格式)消息schema格式输出便于消费,提供数据线级ums_id保证数据顺序性,输出insert,Update(before/after),Delete event数据。 ums

  • 可靠多路消息订阅分发: 使用Kafka存储和传递消息保证可靠性和便捷的多用户订阅

  • 支持分区表/系列表数据汇集: 支持分区表的数据汇集到一个“逻辑表” 。也可将用户自定义的系列表数据汇集到一个“逻辑表“。例:

    grafana

  • 实时监控&预警: 可视化监控系统能随时查看各数据线实时流量和延时状况;当数据线发生异常时,根据配置策略自动发邮件或短信通知相关负责人

    grafana

Wormhole  Architecture

设计理念

  • 统一 DAG 高阶分形抽象
    • 构建由 Source DataSys,Kafka Topic,Spark Stream(Flink Stream),Sink DataSys 组成的物理 DAG
    • 每个物理 DAG 里可以并行处理多个由 Source Namespace,Flow,Sink Namespace 组成的逻辑 DAG
    • 每个 Flow 本身是典型的 Spark RDD DAG
  • 统一通用流消息 UMS 协议抽象
    • UMS 是 Wormhole 定义的流消息协议规范
    • UMS 试图抽象统一所有结构化消息
    • UMS 自身携带结构化数据 Schema 信息
    • Wh4 支持用户自定义半结构化 JSON 格式
  • 统一数据逻辑表命名空间 Namespace 抽象
    • Namespace 唯一定位所有数据存储所有结构化逻辑表
    • [Data System].[Instance].[Database].[Table].[Table Version].[Database Partition].[Table Partition]

主要特性

  • 支持可视化,配置化,SQL 化开发实施流式项目
  • 支持指令式动态流式处理的管理,运维,诊断和监控
  • 支持统一结构化 UMS 消息和自定义半结构化 JSON 消息
  • 支持处理增删改三态事件消息流
  • 支持单个物理流同时并行处理多个逻辑业务流
  • 支持流上 Lookup Anywhere,Pushdown Anywhere
  • 支持基于业务策略的事件时间戳流式处理
  • 支持 UDF 的注册管理和动态加载
  • 支持多目标数据系统的并发幂等入库
  • 支持多级基于增量消息的数据质量管理
  • 支持基于增量消息的流式处理和批量处理
  • 支持 Lambda 架构和 Kappa 架构
  • 支持与三方系统无缝集成,可作为三方系统的流控引擎
  • 支持私有云部署,安全权限管控和多租户资源管理

猜你喜欢

转载自blog.csdn.net/ZYC88888/article/details/83013146