轻松掌握阿里Blink SQL关键技术及实现原理

内容来源:2018 年 6 月 15 日,阿里巴巴高级技术专家孙金城(金竹)在“阿里云流计算杭州峰会”进行《Blink SQL关键技术及实现原理》演讲分享。IT 大咖说(微信id:itdakashuo)作为独家视频合作方,经主办方和讲者审阅授权发布。

获取嘉宾演讲视频及PPT:suo.im/4ZZ9PO

摘要

本次演讲主要介绍Blink SQL的几个关键技术,以及实现原理,并通过两个实例来阐述具体的功能效果。

六个特点和背景介绍

(Blink架构)

Blink作为一个纯流式的计算平台,具备秒级甚至毫秒级的延时,能够快速容错,比如在某个计算节点挂掉的时候可以快速恢复。它还支持动态扩容,针对流计算场景做了大量优化,具备高吞吐和高资源利用率的极致性能,同时解决了非常明显的数据乱序的问题,并且支持大数据量的流计算请求。

什么是BlinkSQL

BlinkSQL是在FlinkSQL基础上新增了大量的丰富功能和性能优化。SQL作为声明式的可优化的语言具备很大的优势,Blink的SQL查询优化器会对用户SQL进行优化,制定最优的执行计划以获取高性能。同时SQL易于理解,用它编写的业务逻辑清晰明了。

虽然SQL被更多的用于批处理,但是在流计算场景下同样也适用。从处理的数据集合来看,流处理的数据是无穷的,而批处理面对的是有限集合。从内部处理逻辑来看,批处理是单次作业,流处理的运作持续不断,并且会对历史数据进行修正。

上图展示的是一个携带时间戳和用户名的点击事件流,我们先对这些事件流进行流式统计,同时在最后的流事件上触发批计算。流计算中每接收一个数据都会触发一个结果,可以看到最后的事件无论是在流还是批上计算结果都是6。这说明只要SQL语句和数据源相同,流和批的计算结果就不会存在差异。相同的SQL在流和批这两种模式下,语义相同,最终结果就会一致。

五个概念和一个实现

流表对偶性

流表对偶性是指流表转换信息无损,具有对偶性。

传统数据库表有两个最明显的特征schema和data,流同样也有schema和data,另外还有一个重要属性time,随着时间的推移数据会不断涌入流中。

上图中在对一个用户名关系表进行DML操作,每insert一条语句后会触发一个统计查询,按user维度分组做count统计。图中插入第一条数据后,输入的是(mary , 1),接着再插入数据就会输出(Bob , 2)、(mary , 1),在进行操作的时候虽然没有显示的涉及到时间,但是其实都隐含了一个操作时间,从本质上讲,传统数据库表中也有time属性。

这样的话单就属性来看数据库表和流式就是一样的,我们可以把一个具体的表可以看做是流上某一时间切片的数据。

动态表

随着时间变化不断变化的表可以称之为动态表,这个概念不适用于传统数据库表。因为传统数据库有着事务控制,在执行查询的那一刻表的内容不会有变化,所以是静态表。而流上的数据即使在查询的时候也还是在变化,数据进行回放的时候,可以Replay成一张动态表,而动态表的changelog又形成了流。

持续查询 & 增量计算

持续查询是流计算特有的,不断更新结果,执行运行的查询。它是保证低延迟和数据更新的基础。

增量计算是一种可以提高流计算性能的计算实现,流上的每一条数据都会利用上一条计算结果进行聚合和统计计算。

Early Emit & Retraction

Early Emit是为了保证流计算的低延时需求,将计算结果尽早的流到下游。由于流计算的结果会随着时间改变,因此即使上游的计算结果在发出的那一刻是正确的,但是随后又会产生新的结果,这样下游当初接收的数据就是陈旧的。Retraction解决的就是这一问题,为了保障流计算语义正确性,将Early Emit的计算结果撤回并发出正确的计算结果。

双流JOIN实现

对于双流join,由于流上的流速不一定相同,有可能会出现查询的时候数据不存在,后续才出现数据的情况。上图是Blink的join方案,将左边的数据存储在一个state中,并且对右边的state做join查询。同理右边的数据处理也是一样,先存储在一个state中,然后join查询另一个state。

这样的流程存在一个问题,即在join另一边的state时可能没有数据,传统数据库的做法是将它留空,而流上的数据会实时变化,此刻没有join数据不代表下一刻还没有。这就需要利用Retraction机制,在正确数据到来的时候撤回之前的空数据并重新填入新数据,如上图所示。

总结

这里我们对这5个概念做下总结。首先有了流表对偶性,流表转换才有理论支撑,流表才能互转;有了动态表的概念,SQL才能作用于流上,才能进行持续查询;有了持续查询的概念,才能真正解释流式SQL计算永不结束,结果永远在更新;增量计算配合查询和引擎优化,才有了Blink的高性能;Early Emit和Retraction,一个使用Blink有了无限逼近秒级毫秒级的低延时,一个保证了流式语义的正确性。

两个案例和总结

案例1 – 无key热点

对于有多区块的数据源,每个区块都会由对应的节点来处理。理想情况下每个节点的处理量相同,负载是均衡的,但是实际场景下可能会出现不均衡处理的情况,比如某个节点无法处理所负载的量,对此我们要将该节点无法处理的部分分配给其他节点。

Blink为此提供了Dynamic Rebalance,它会感知下游节点的压力情况,进而将繁忙的节点处理不过来的数据分发给空闲节点。需要注意的是目前的这种情况是无key节点,这意味着数据在哪个节点处理都可以。

案例2 – keyed热点

Keyed就是对数据按key分组,相同的key必须到同一个节点上。这时候虽然框架也能感知到反压,但是不可以使用反压机制再分配节点上的数据。

这时候要用到Local-Global Aggregation,数据热点优化的另一个方案,也是由系统内部的查询优化器来完成,让用户无感知。它会先将每个节点的数据在本地进行Local Agg聚合,图中不同的颜色表示不同的key,他们在本地会做局部的聚合,这样原先上游的4条记录就变成的1条,很大程度上减轻了下游节点的压力。

总结

Blink SQL拥有标准的语义,完全支持标准的ANSI SQL,同时有丰富的功能,比如UDF、JOIN、双流JOIN等。有更优的性能,在ANSI SQL的各个语法功能基础上做了大量的性能优化。提供了一站式平台,集开发、测试、部署、监控、升级于一体,让用户专注于自己的业务。

原文链接:https://juejin.im/post/5b8609a6e51d4538d12ca085

java 泛型详解

对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。
本文参考java 泛型详解、Java中的泛型方法、 java泛型详解

1. 概述

泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。

什么是泛型?为什么要使用泛型?

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

2. 一个栗子

一个被举了无数次的例子:

毫无疑问,程序的运行结果会以崩溃结束:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。

我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。

3. 特性

泛型只在编译阶段有效。看下面的代码:

输出结果:D/泛型测试: 类型相同。

通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。

对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

4. 泛型的使用

泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法。

4.1 泛型类
泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。

泛型类的最基本写法(这么看可能会有点晕,会在下面的例子中详解):

一个最普通的泛型类:

定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。

看一个例子:

注意:
泛型的类型参数只能是类类型,不能是简单类型。
不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。

4.2 泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:

当实现泛型接口的类,未传入泛型实参时:

当实现泛型接口的类,传入泛型实参时:

4.3 泛型通配符
我们知道Ingeter是Number的一个子类,同时在特性章节中我们也验证过Generic与Generic实际上是相同的一种基本类型。那么问题来了,在使用Generic作为形参的方法中,能否使用Generic的实例传入呢?在逻辑上类似于Generic和Generic是否可以看成具有父子关系的泛型类型呢?

为了弄清楚这个问题,我们使用Generic这个泛型类继续看下面的例子:

通过提示信息我们可以看到Generic不能被看作为`Generic的子类。由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。

回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理Generic类型的类,这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示同时是Generic和Generic父类的引用类型。由此类型通配符应运而生。

我们可以将上面的方法改一下:

类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。

可以解决当具体类型不确定的时候,这个通配符就是 ?  ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。

4.4 泛型方法
在java中,泛型类的定义非常简单,但是泛型方法就比较复杂了。

尤其是我们见到的大多数泛型类中的成员方法也都使用了泛型,有的甚至泛型类中也包含着泛型方法,这样在初学者中非常容易将泛型方法理解错了。

泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。

4.4.1 泛型方法的基本用法
光看上面的例子有的同学可能依然会非常迷糊,我们再通过一个例子,把我泛型方法再总结一下。

4.4.2 类中的泛型方法
当然这并不是泛型方法的全部,泛型方法可以出现杂任何地方和任何场景中使用。但是有一种情况是非常特殊的,当泛型方法出现在泛型类中时,我们再通过一个例子看一下:

4.4.3 泛型方法与可变参数
再看一个泛型方法和可变参数的例子:

4.4.4 静态方法与泛型
静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。

即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。

4.4.4 泛型方法总结
泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则:

无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。另外对于一个static的方法而已,无法访问泛型类型的参数。所以如果static方法要使用泛型能力,就必须使其成为泛型方法。

4.4.5 泛型上下边界
在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。为泛型添加上边界,即传入的类型实参必须是指定类型的子类型

如果我们把泛型类的定义也改一下:

再来一个泛型方法的例子:

通过上面的两个例子可以看出:泛型的上下边界添加,必须与泛型的声明在一起 。

4.4.6 关于泛型数组要提一下
看到了很多文章中都会提起泛型数组,经过查看sun的说明文档,在java中是”不能创建一个确切的泛型类型的数组”的。

也就是说下面的这个例子是不可以的:

而使用通配符创建泛型数组是可以的,如下面这个例子:

这样也是可以的:

下面使用Sun的一篇文档的一个例子来说明这个问题:

这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList而不会出现异常,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。

而对泛型数组的声明进行限制,对于这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。

下面采用通配符的方式是被允许的:数组的类型不可以是类型变量,除非是采用通配符的方式,因为对于通配符的方式,最后取出数据是要做显式的类型转换的。

5. 最后

本文中的例子主要是为了阐述泛型中的一些思想而简单举出的,并不一定有着实际的可用性。另外,一提到泛型,相信大家用到最多的就是在集合中,其实,在实际的编程过程中,自己可以使用泛型去简化开发,且能很好的保证代码质量。

原文:https://blog.csdn.net/s10461/article/details/53941091

使用Emacs敲出UML,PlantUML快速指南

前言

本文多数内容引用自官网文档,并非本人原创,也谈不上翻译,只是把自己 理解的东西用中文写出来。

编写本文的目的旨在记录个人在学习PlantUML时对官网上一些内容的理解,以 及总结学习过程中遇到的问题,并将其分享。文章中如有不对之处欢迎大家直 言指正,以免造成误导。

版权:本文可自由转载,但不能商用,不能衍生,保持署名。转载请注明作者及出处.

Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

什么是PlantUML

PlantUML是一个快速创建UML图形的组件,官网上之所以称它是一个组件,我 想主要是因为多数情况下我们都是在Eclipse、NetBenas、Intellijidea、 Emacs、Word等软件里来使用PlantUML(参看各软件相关配置)。

  • PlantUML支持的图形有:
    • sequence diagram,
    • use case diagram,
    • class diagram,
    • activity diagram (here is the new syntax),
    • component diagram,
    • state diagram,
    • object diagram,
    • wireframe graphical interface
  • PlantUML通过简单和直观的语言来定义图形,它可以生成PNG、SVG和二进制 图片。下面是一个简单的示例:

    orgmode-babel-sequenceuml.png

    在官网上有一个简单的在线Demo服务, 有兴趣的朋友可以上去看下。

在Emacs里配置PlantUML(参考:Run it from Emacs

  1. 下载 plantuml.jar 到你的硬盘上(官网下载页面
  2. 安装生成图片用的软件Graphviz

  3. 在 .emacs 里添加配置,把 plantuml 添加到 org-babel-load-languages加载语言列表里。

    然后把刚下载到的 plantuml.jar 文件的存放路径也添加到 .emacs 文件中,以方便Emacs调用。

重启Emacs,复制上面的示例代码试一下吧!

如何使用

  • 顺序图(Sequence Diagram)
    • 简单示例

      顺序图用 -> , --><-<-- 来绘制参与者(Participants)之 间的消息(Message)。

      plantuml-quickstart-s1.png

    • 注释语句

      以单引号开始头行,即是一个单行注释。多行注释可以使用"'"表 示注释内容的开始,然后使用"'"来表示注释内容的结束。

    • 申明参与者

      申明参与者,可以使用 participant 关键词,也可以使用下面的参与者 分类关键词来申明参与者:

      • actor
      • boundary
      • control
      • entity
      • database

      不同的参与者类型,其图标也是不一样的。

      plantuml-quickstart-s2.png

      使用 as 关键词可以为参与者起一个别名,这样在对引用长名的参与者时, 会方便很多。在参与者申明语句后行尾可以追加背景色的设置,只要把标准 的HTML颜色值 写在后面就行了。

      plantuml-quickstart-s3.png

    • 使用非字母的参与者名称(Use non-letters in participants)

      针对非字母的参与者名,我们可以使用双引号,同样也可以为比较长的名字 起个别名,方法同上使用 as 关键词。

      使用上面的关键词来申明参与者,是一种显示申明;而采用引号来申明参与 者则是一种隐示申明方法,它不需要专门的位置去定义。

      plantuml-quickstart-s4.png

    • 发送消息给自己(Message to Self)

      一个参与者可以给自己发送消息,消息名如果需要有多行文本,可以用 \n 来表示换行。

      plantuml-quickstart-s5.png

    • 改变箭头的样式(Change arrow style)

      在用例图里可以通过以下方式来改变箭头的样式:

      • 使用 \ 或 / 来替换 < 或 > 可以让箭头只显示上半部分或下半 部分。
      • 重复输入箭头或斜杠( >> // ),用来绘制空心箭头。
      • 使用双横线 -- 替代 - 可以用来绘制点线。
      • 在箭头后面加个 o 可以在箭头前绘制一个圆圈。
      • 使用 <-> 可用来绘制双向箭头。

      plantuml-quickstart-s6.png

    • 改变箭头的颜色(Change arrow color)

      要改变箭头的颜色,可以使用HTML颜色符号,参看下面的例子:

      plantuml-quickstart-s7.png

    • 消息序号(Message sequence numbering)

      关键词 autonumber 用来给自动的给消息添加上序号。

      plantuml-quickstart-s8.png

      如果需要指定一个起始号码,可以直接在 autonumber 后面加个数字就行 了,如果要设置自增量,再在后面加一个数字就行了( autonumber start increment )。

      plantuml-quickstart-s9.png

      我们也可以为序号指定数字格式,这个格式化的过程实际上是Java类DecimalFormat 来执行的( 0 表示数字, # 缺省补零位数)。

      同样的,也可以使用一些HTML标签来控制数字的样式。

      plantuml-quickstart-s10.png

    • 标题(Title)

      要给图形加一个标题可以用 title 关键词来设置。

      plantuml-quickstart-s11.png

    • 图形图例(Legend the diagram)

      使用 legend 和 end legend 关键词可以设置图形的图例。图例可以设 为左对齐、右对齐和居中对齐。

      plantuml-quickstart-s12.png

    • 分割图形(Splitting diagrams)

      关键词 newpage 是用来把图形分割成几个图片的。每一个被分割出来的 图片可以看作是一个新的页面( new page ),如果要给新的页面添加一 个标题,可以紧跟在关键词 newpage 之后来设置。

      使用这个方法可以方便的在Word里把比较长的图形分别打印到几个不同的页 面上(有点分页符的概念)。

    • 消息分组(Grouping message)

      有时候可能需要对消息进行分组,那么可以使用下面的关键词来实现:

      • alt/else
      • opt
      • loop
      • par
      • break
      • critical
      • group, 这个关键词后面的文字会作为组名显示在图形上

      上面的关键词后可以添加一些文本用来显示在头部(注: group 除外,因 为它后面的文本用来显示在组名称的位置)。在组嵌套组的结构里可以用关 键词 end 来关闭组或者说是表示一个组符号的结束符(类似 if/endif )。

      plantuml-quickstart-s14.png

    • 消息注解(Notes on messages)

      我们可能经常会在消息的左边或右边使用注解,要添加注解,只要使用 note left 或 note right 关键词就可以了。

      plantuml-quickstart-s15.png

    • 一些其他的注解方式(Some other notes)

      通过使用关键词 note left of , note right of 或 note over , 我们还可以把注解放置在与之相关的参与者的左边或右边,或下方。

      通过改变注解的背景色,我们还可以高亮一个注解文本块。

      如果要使用多行注解,可以使用关键词 end note 来表示注解的结束。

      plantuml-quickstart-s16.png

    • 使用HTML进行格式化(Formatting using HTML)
    • 我们可以使用少量的HTML标签来格式化文本:

      •  加粗文本
      •  或  或  用来加下划线
      •  斜体
      •  或  或  用来加删除线
      •  或  或  用来加波浪线
      •  或  用来设置文本颜色
      •  或  用来设置背景色
      •  设置字体大小
      •  或  用来添加图片,图片文件必须 是可以访问得到才行。
      •  或  用来添加一个互 联网图片,同样的图片地址必须是可用的才行。

      plantuml-quickstart-s17.png

  • 用例图(Use Case Diagram)
    • 用例(Usecase)

      用例可以用一对小括号括起来表示,也可以使用 usecase 关键词来定义。 用例也可以通过使用 as 关键词来设置别名,在建立关系的时候可以使用 别名。

      plantuml-quickstart-u1.png

    • 参与者(Actors)

      定义参与者时,可以把参与者的名称放在两个冒号的中间,也可以用 actor 关键词来定义参与者。同样参与着也可以使用别名。

      plantuml-quickstart-u2.png

    • 示例

      plantuml-quickstart-u99.png

  • 类图(Class Diagram)
    • 示例1

      plantuml-quickstart-c01.png

  • 活动图(Activity Diagram)
    • 简单活动(Simple Activity)

      在活动图中,你可以使用 (*) 来表示活动开始点和结束点。使用 --> 来表示箭头。

      plantuml-quickstart-a1.png

    • 带标注的箭头(Label on arrows)

      缺省情况下,活动图的箭头是没有标注的。但我们可以通过方括号 [labels]来设置标注,只要把它放在箭头定义的后面就可以了。

      plantuml-quickstart-a2.png

    • 改变箭头的方向(Changing arrow direction)

      我们可以使用 -> 创建一个水平箭头,也可以通过下面的方式来改变箭头 的方向:

      • -down-> 向下(这个是默认的,等同于 =–>=)
      • -right-> 向右
      • -left-> 向左
      • -up-> 向上

      plantuml-quickstart-a3.png

      在描述箭头时, up|down|left|right 这几个单词的写法可以简化, 用单词开头的一个或两个字母来替换就行了,比如 -down-> 也可以写成 -d-> 或者 -do-> 。

    • 分支(Branches)

      在PlantUML里,我们可以使用 if/then/else 关键词来定义分支。

      plantuml-quickstart-a4.png

    • 多分支(More on Branches)

      直接给例子:

      plantuml-quickstart-a5.png

    • 同步块(Synchronization)

      同步块可以用“=== code ===”来表示。

      plantuml-quickstart-a6.png

      一个小实例

      plantuml-quickstart-a7.png

    • 长文本的活动描述(Long activity description)

      在定义活动的时候,有时候需要用多行文字来描述这个活动,这时我们可以 在描述里添加换行符 \n ,也可以使用少量的HTML标签。

      以下是可以使用的HTML标签:

      针对较长文本描述活动,可以起一个较短别名(如:"long text" as A1), 在图形定义脚本中可以直接使用别名,参看下面的例子中的A1

      plantuml-quickstart-a8.png

    • 注释(Notes)

      PlantUML可以通过在脚本里使用 note 来添加注释文本块。

      note commands:

      • note left
      • note right
      • note top
      • note bottom

      PlantUML用上面列表里的命令来标注一个注释块的开始,然后用 end note来标注注释块的结束。同时note命令也允许使用单行定义一个文本块, 详见下面的例子。

      plantuml-quickstart-a9.png

    • 分区(Partition)

      通过分区关键词 partition 可以定义一个分区,并且可以使用HTML的 颜色码或颜色名来设置分区的背景色。在你申明一个活动时,PlantUML会自动 的把这个活动对象放置到最后使用的分区中。当然,也可以使用 end partitio 关闭分区定义。

      plantuml-quickstart-a10.png

    • 图形标题(Title the diagram)

      标题关键词 title 用来设置一个图形的标题文本,我们可以在 title 和 end title 两个关键词之间放置比较长的标题文本。

      plantuml-quickstart-a11.png

    • 皮肤(Skinparam)

      皮肤命令 skinparam 可以改变图形的颜色和字体。这些命令可以在以下 的位置中使用:

      • 在图形定义里使用,
      • 在包含的文件里使用,
      • 在一个配置文件里使用,这个配置文件一般由命令行或ANT的Task来提供。

      plantuml-quickstart-a12.png

      使用 skinparam activityShape octagon 命令可以把活动图形改成八角 形的。(好像没效果!)

    • 完整示例(Complete Example)

      plantuml-quickstart-a13.png

  • 活动图Beta

    Beta版本的活动图简化了活动图的符号定义,从 V7947 这个版本开始, PlantUML就开始引入了一些简化写法,当然到目前(20140627)为止还不是 很完善,但这个版本里的一些简化写法已经是PlantUML后续版本的发展方向。

    下文中将会用几个简单的示例来介绍Beta版活动图的新功能,有兴趣的朋友 也可以试一下,在使用新的写法之前需要把 GraphViz 更新到最新版本。

    关于更多的PlantUML版本更新信息可以参考官网页面(What's New?

    • 简单活动(Simple Activity)

      在这个例子里,活动元素从一个 : 开始,然后到一个 ; 结束。 开始和结束符号,可以用 start 和 end 两个关键词来表示。之前版 本的开始和结束符都是用同一个符号 (*) 来表示的,个人觉得新的写法 逻辑更清晰,代码可读性更高。

      至于更多的文本格式,大家可以参考:Creole engine

      plantuml-quickstart-ab1.png

    • 条件符号(Conditional)

      和之前一样,还是使用 if , then 和 else 关键词,但分支条件的 标签 Labels 可以直接写在关键词 then 和 else 的后面,并用小括 号括起来就可以了(如: (Labels) )。

      plantuml-quickstart-ab2.png

      在新版本里除了使用 else 外,还新加了一个 elseif 关键词,有了这 个语法,我们就可以绘制一系列条件的活动图。

      plantuml-quickstart-ab3.png

    • 重复循环(Repeat Loop)

      通过 repeat 和 repeat while 关键词可以创建循环结构的图形。

      plantuml-quickstart-ab4.png

    • 条件循环(While Loop)

      “条件循环”和上面的“重复循环”不太一样,上面的“重复循环”是先执行一次 循环体里的内容,然后再执行断言条件,看是否重复执行循环体;而条件循 环则将断言放到了最前面,因此它是先判断是否满足条件再执行循环体里的 内容。

      要创建条件循环结构的图形可以通过使用 while 和 end while 两个关 键词来实现。如果要给条件分支加上标注,可以在 while 条件后加上一 个 is 关键词,然后用小括号括上要标注的内容;在 end while 后可 以直接用小括号括上要标注的内容。

      plantuml-quickstart-ab5.png

    • 并行处理(Parallel Processing)

      fork , fork again 和 end fork 三个关键词用来表示并行处理结 构。

      plantuml-quickstart-ab6.png

    • 注解的文本样式(Notes)

      注解里的文本样式是通过 Creole wiki syntax 来实现的。关于Creole引擎, 大家可以参考维基百科上的介绍。

      plantuml-quickstart-ab7.png

    • 颜色(Color)

      为活动元素指定背景色可以直接在活动开始标记 : 前加上颜色描述符:

      plantuml-quickstart-ab8.png

    • 完整示例(Complete Example)

      plantuml-quickstart-ab9.png

那些年我们用过的流计算框架

数据时代,从数据中获取业务需要的信息才能创造价值,这类工作就需要计算框架来完成。传统的数据处理流程中,总是先收集数据,然后将数据放到DB中。当人们需要的时候通过DB对数据做query,得到答案或进行相关的处理。这样看起来虽然非常合理,但是结果却非常紧凑,尤其是在一些实时搜索应用环境中的某些具体问题,类似于MapReduce方式的离线处理并不能很好地解决。

基于此,一种新的数据计算结构—流计算方式出现了,它可以很好地对大规模流动数据在不断变化的运动过程中实时地进行分析,捕捉到可能有用的信息,并把结果发送到下一计算节点。 什么是流计算? 时下,对信息高时效性、可操作性的需求不断增长,这要求软件系统在更少的时间内能处理更多的数据。传统的大数据处理模型将在线事务处理和离线分析从时序上将两者完全分割开来,但显然该架构目前已经越来越落后于人们对于大数据实时处理的需求。 流计算的产生即来源于对于上述数据加工时效性的严苛需求:数据的业务价值随着时间的流失而迅速降低,因此在数据发生后必须尽快对其进行计算和处理。而传统的大数据处理模式对于数据加工均遵循传统日清日毕模式,即以小时甚至以天为计算周期对当前数据进行累计并处理,显然这类处理方式无法满足数据实时计算的需求。在诸如实时大数据分析、风控预警、实时预测、金融交易等诸多业务场景领域,批量(或者说离线)处理对于上述对于数据处理时延要求苛刻的应用领域而言是完全无法胜任其业务需求的。而流计算作为一类针对流数据的实时计算模型,可有效地缩短全链路数据流时延、实时化计算逻辑、平摊计算成本,最终有效满足实时处理大数据的业务需求。 通常而言,流计算具备三大类特点:

  • 实时(realtime)且无界(unbounded)的数据流。流计算面对计算的 是实时且流式的,流数据是按照时间发生顺序地被流计算订阅和消费。且由于数据发生的持续性,数据流将长久且持续地集成进入流计算系统。例如,对于网站的访问点击日志流,只要网站不关闭其点击日志流将一直不停产生并进入流计算系统。因此,对于流系统而言,数据是实时且不终止(无界)的。
  • 持续(continuos)且高效的计算。流计算是一种”事件触发”的计算模式,触发源就是上述的无界流式数据。一旦有新的流数据进入流计算,流计算立刻发起并进行一次计算任务,因此整个流计算是持续进行的计算。
  • 流式(streaming)且实时的数据集成。流数据触发一次流计算的计算结果,可以被直接写入目的数据存储,例如将计算后的报表数据直接写入RDS进行报表展示。因此流数据的计算结果可以类似流式数据一样持续写入目的数据存储。

概念区分 明确了流处理概念的同时,一些其他的概念也需要了解:离线计算、批处理计算、实时计算。 离线计算 正如前文所述,离线计算就是在计算开始前已知所有输入数据,输入数据不会产生变化,且在解决一个问题后就要立即得出结果的前提下进行的计算。在大数据中属于数据的计算部分,在该部分中与离线计算对应的则是实时计算。一般来说,离线计算具有数据量巨大且保存时间长;在大量数据上进行复杂的批量运算;数据在计算之前已经完全到位,不会发生变化;能够方便的查询批量计算的结果等特点。 常用的离线计算框架包括有:

  • Hadoop,适用于离线大批量数据处理,不需要多次迭代。
  • Spark,适用于离线快速的处理,不能用于处理需要长期保存的数据;适用于多次迭代的计算模型。
  • MapReduce,Hadoop框架最核心的设计就是HDFS和MapReduce。HDFS为海量的数据提供了存储,MapReduce则为海量的数据提供了计算,它适用于大规模数据集的并行运算。
  • HDFS,这个Hadoop分布式文件系统能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。

批量计算 批量计算是一种批量、高时延、主动发起的计算。目前绝大部分传统数据计算和数据分析服务均是基于批量数据处理模型: 使用ETL系统或者OLTP系统进行构造数据存储,在线的数据服务(包括Ad-Hoc查询、DashBoard等服务)通过构造SQL语言访问上述数据存储并取得分析结果。这套数据处理的方法论伴随着关系型数据库在工业界的演进而被广泛采用。传统的批量数据处理模型传统的批量数据处理通常基于如下处理模型:

  • 使用ETL系统或者OLTP系统构造原始的数据存储,以提供给后续的数据服务进行数据分析和数据计算。
  • 用户/系统主动发起一个计算作业(例如=Hive的SQL作业)并向上述数据系统进行请求。
  • 计算结果返回,计算作业完成后将数据以结果集形式返回用户。

实时计算 实时计算一般都是针对海量数据进行的,一般要求为秒级。实时计算主要分为两块:数据的实时入库、数据的实时计算。主要应用的场景有:

  • 数据源是实时的不间断的,要求用户的响应时间也是实时的(比如对于大型网站的流式数据:网站的访问PV/UV、用户访问了什么内容、搜索了什么内容等,实时的数据计算和分析可以动态实时地刷新用户访问数据,展示网站实时流量的变化情况,分析每天各小时的流量和用户分布情况)。
  • 数据量大且无法或没必要预算,但要求对用户的响应时间是实时的。比如说:昨天来自每个省份不同性别的访问量分布,昨天来自每个省份不同性别不同年龄不同职业不同名族的访问量分布。

对于实时计算来说。首先需要解决数据的就是实时接收的问题,在网络带宽、接收性能、安全防控等情况下,如何实现海量并发数据平稳接收具有很大挑战。 离线=批量?实时=流式? 习惯上我们认为离线和批量等价;实时和流式等价,但其实这种观点并不完全正确。假设一种情况:当我们拥有一个非常强大的硬件系统,可以毫秒级的处理Gb级别的数据,那么批量计算也可以毫秒级得到统计结果(当然这种情况非常极端,目前不可能),那我们还能说它是离线计算吗? 所以说离线和实时应该指的是:数据处理的延迟;批量和流式指的是:数据处理的方式。两者并没有必然的关系。事实上Spark streaming就是采用小批量(batch)的方式来实现实时计算。

可以参考链接:https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-101。作者是Google实时计算的负责人,里面阐述了他对批量和实时的理解,并且作者认为批量计算只是流式计算的子集,一个设计良好的流式系统完全可以替代批量系统。

流计算产品盘点 目前流式计算是业界研究的一个热点。早期的代表系统有IBM的System S,它是一个完整的计算架构,通过“stream computing”技术,可以对stream形式的数据进行real-time的分析。“最初的系统拥有大约800个微处理器,但IBM称,根据需求,这个数字也有可能上万。研究者讲到,其中最关键的部分是System S软件,它可以将任务分开,比如分为图像识别和文本识别,然后将处理后的结果碎片组成完整的答案。IBM实验室高性能流运算项目的负责人Nagui Halim谈到:System S是一个全新的运算模式,它的灵活性和速度颇具优势。而与传统系统相比,它的方式更加智能化,可以适当转变,以适用其需要解决的问题。 近年来Twitter、LinkedIn等公司相继开源了流式计算系统Storm、Kafka等,加上Yahoo!之前开源的S4,流式计算研究在互联网领域持续升温。下面来盘点一些业界常见的流计算产品。 Storm Storm是一个分布式的、容错的实时计算系统,做作为最早的一个实时计算框架,早期应用于各大互联网公司。在Storm出现之前,进行实时处理是非常痛苦的事情,我们主要的时间都花在关注往哪里发消息,从哪里接收消息,消息如何序列化,真正的业务逻辑只占了源代码的一小部分。一个应用程序的逻辑运行在很多worker上,但这些worker需要各自单独部署,还需要部署消息队列。最大问题是系统很脆弱,而且不是容错的:需要自己保证消息队列和worker进程工作正常。Storm具有编程简单、高性能,低延迟、分布式、可扩展、容错、消息不丢失等特点。

但是,Storm没有提供exactly once的功能,并且开启ack功能后又会严重影响吞吐,所以会给大家一种印象:流式系统只适合吞吐相对较小的、低延迟不精确的计算;而精确的计算则需要由批处理系统来完成,所以出现了Lambda架构,同时运行两个系统:一个流式,一个批量,用批量计算的精确性来弥补流式计算的不足,但是这个架构存在一个问题就是需要同时维护两套系统,代价比较大。 Spark streaming

Spark streaming采用小批量的方式,提高了吞吐性能。Spark streaming批量读取数据源中的数据,然后把每个batch转化成内部的RDD。Spark streaming以batch为单位进行计算),而不是以record为单位,大大减少了ack所需的开销,显著满足了高吞吐、低延迟的要求,同时也提供exactly once功能。但也因为处理数据的粒度变大,导致Spark streaming的数据延时不如Storm,Spark streaming是秒级返回结果(与设置的batch间隔有关),Storm则是毫秒级。 Flink Flink是一个针对流数据和批数据的分布式处理引擎,主要由Java代码实现。对 Flink 而言,其所要处理的主要场景就是流数据,批数据只是流数据的一个极限特例而已。Flink 可以支持本地的快速迭代,以及一些环形的迭代任务,并且可以定制化内存管理。在这点,如果要对比 Flink 和 Spark 的话,Flink 并没有将内存完全交给应用层。这也是为什么 Spark 相对于 Flink,更容易出现 OOM 的原因(out of memory)。就框架本身与应用场景来说,Flink 更相似与 Storm。

Apache Flink的特点有:低延迟的流处理器;丰富的API能够帮助程序员快速开发流数据应用;灵活的操作状态和流窗口;高效的流与数据的容错。 Apache Kafka Kafka是一个分布式的、分区的、多复本的日志提交服务,它通过一种独一无二的设计提供了一个消息系统的功能。实现流处理最基本的方法是使用Kafka API读取输入数据流进行处理,并产生输出数据流。这个过程可以用任何编程语言实现。这种方法比较简单,易于操作,适应于任何有Kafka客户端的语言。

Apache Samza Samza处理数据流时,会分别按次处理每条收到的消息。Samza的流单位既不是元组,也不是Dstream,而是一条条消息。在Samza中,数据流被切分开来,每个部分都由一组只读消息的有序数列构成,而这些消息每条都有一个特定的ID。该系统还支持批处理,即逐次处理同一个数据流分区的多条消息。Samza的执行与数据流模块都是可插拔式的,尽管Samza的特色是依赖Hadoop的Yarn(另一种资源调度器)和Apache Kafka。

Heron Twitter由于本身的业务特性,对实时性有着强烈的需求。因此在流计算上投入了大量的资源进行开发。第一代流处理系统Storm发布以后得到了广泛的关注和应用。根据Storm在实践中遇到的性能、规模、可用性等方面的问题,Twitter又开发了第二代流处理系统——Heron,并在2016年将它开源。

目前的Heron支持Aurora、YARN、Mesos以及EC2,而Kubernetes和Docker等目前正在开发中。通过可扩展插件Heron Scheduler,用户可以根据不同的需求及实际情况选择相应的运行平台,从而达到多平台资源管理器的支持。 编译自互联网,参考资料:

  • http://www.csdn.net/article/2014-06-12/2820196-Storm
  • http://www.jianshu.com/p/16323566f3c6
  • http://www.jianshu.com/u/a9196e920278
  • https://help.aliyun.com/document_detail/49924.html
  • https://help.aliyun.com/document_detail/49926.html
  • https://baike.baidu.com/

原文发布于微信公众号 – CSDN技术头条(CSDN_Tech)

原文发表时间:2017-10-23