随着Flink 1.10的发布,对SQL的支持也非常强大。Flink 还提供了 MySql, Hive,ES, Kafka等连接器Connector,所以使用起来非常方便。

  接下来咱们针对构建流式SQL应用文章的梗概如下:

  1. 搭建流式SQL应用所需要的环境准备。

  2. 构建一个按每小时进行统计购买量的应用。

  3. 构建每天以10分钟的粒度进行统计应用。

  4. 构建按分类进行排行,取出想要的结果应用。

1. 搭建流式应用所需要的环境准备

   Kafka,用于将数据写入到Kafka中,然后Flink通过读取Kafka的数据然后再进行处理。版本号:2.11。

     MySQL, 用于保存数据的分类。Flink从中读取分类进行处理和计算 。版本号:8.0.15。

   ElasticSearch, 用于保存结果数据和进行索引存储。下载的时候可以在搜索引擎里边搜索“elasticsearch 国内”,这样就可以从国内快速下载,要不然下载的太慢了。版本号:7.6.0。

   Kibana, 用于ES的结果展示,图形化的界面美观。 下载的时候也需要搜索“Kibana 国内”,比较快速。版本号:7.6.0。

     Flink, 核心的流处理程序,版本号:1.10。Flink支持国内镜像下载,这个到时候可以自行找一下。

     Zookeeper,  Kafka依赖这个应用,所以也会用到的,这个什么版本都是可以的。我的版本号:3.4.12。

   当然我的是mac电脑,如果是mac电脑的话,下载ES和Kibana的时候要下载文件中带“darwin”字样的,可以在Mac中使用其他的不能执行。应该是程序里边的编译不同,这个也是一个小坑。

     因为Flink需要连接Mysql, Elasticseratch , Kafka,所以也需要提前下载Flink所需要的Connector jar包到Flink的lib里边。

wget -P ./lib/ https://repo1.maven.org/maven2/org/apache/flink/flink-json/1.10.0/flink-json-1.10.0.jar | \
wget -P ./lib/ https://repo1.maven.org/maven2/org/apache/flink/flink-sql-connector-kafka_2.11/1.10.0/flink-sql-connector-kafka_2.11-1.10.0.jar | \
wget -P ./lib/ https://repo1.maven.org/maven2/org/apache/flink/flink-sql-connector-elasticsearch6_2.11/1.10.0/flink-sql-connector-elasticsearch6_2.11-1.10.0.jar | \
wget -P ./lib/ https://repo1.maven.org/maven2/org/apache/flink/flink-jdbc_2.11/1.10.0/flink-jdbc_2.11-1.10.0.jar | \
wget -P ./lib/ https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.48/mysql-connector-java-5.1.48.jar

  

   环境都准备好了,那么需要把环境都启动起来,进行检查。

   Elasticsearch启动好了之后需要访问这个网址没有问题,说明成功了:http://localhost:9200/_cluster/health?pretty

   Flink启动好之后需要访问 http://localhost:8081 会有界面展示。

   Kibana 启动好了之后访问:http://127.0.0.1:5601/ 会有界面展示。当然Kibana在目录conf/kibana.yml里边需要把ES的地址给打开。

   Zookeeper 这个相信很多同学都会配置了,如果有不会配置的,可以自己搜索一下。

   我们先看一下最后的效果图,可能不是特别好,是这么个意思。

2. 构建一个按每个小时统计购买量应用。 

  我们写一个程序,往Kafka里边写数据,模拟一些连续的数据源头。

  首先定义一个Pojo类。

package myflink.pojo;

public class UserBehavior {
//用户ID
public long userId;
//商品ID
public long itemId;
//商品类目ID
public int categoryId;
//用户行为,包括{"pv","buy","cart", "fav"}
public String behavior;
//行为发生的时间戳,单位秒
public String ts; public long getUserId() {
return userId;
} public void setUserId(long userId) {
this.userId = userId;
} public long getItemId() {
return itemId;
} public void setItemId(long itemId) {
this.itemId = itemId;
} public int getCategoryId() {
return categoryId;
} public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
} public String getBehavior() {
return behavior;
} public void setBehavior(String behavior) {
this.behavior = behavior;
} public String getTimestamp() {
return ts;
} public void setTimestamp(String ts) {
this.ts = ts;
}
}

  接着写一个往Kafka写数据的类。随机生成用于的行为,里边包括用户的id,类目id等。让程序运行起来。

package myflink.kafka;

import com.alibaba.fastjson.JSON;
import myflink.pojo.UserBehavior;
import org.apache.commons.lang3.RandomUtils;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord; import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.TimeUnit; /**
* @author huangqingshi
* @Date 2020-03-15
*/
public class KafkaWriter { //本地的kafka机器列表
public static final String BROKER_LIST = "localhost:9092";
//kafka的topic
public static final String TOPIC_USER_BEHAVIOR = "user_behaviors";
//key序列化的方式,采用字符串的形式
public static final String KEY_SERIALIZER = "org.apache.kafka.common.serialization.StringSerializer";
//value的序列化的方式
public static final String VALUE_SERIALIZER = "org.apache.kafka.common.serialization.StringSerializer"; private static final String[] BEHAVIORS = {"pv","buy","cart", "fav"}; private static KafkaProducer<String, String> producer; public static void writeToKafka() throws Exception{ //构建userBehavior, 数据都是随机产生的
int randomInt = RandomUtils.nextInt(0, 4);
UserBehavior userBehavior = new UserBehavior();
userBehavior.setBehavior(BEHAVIORS[randomInt]);
Long ranUserId = RandomUtils.nextLong(1, 10000);
userBehavior.setUserId(ranUserId);
int ranCate = RandomUtils.nextInt(1, 100);
userBehavior.setCategoryId(ranCate);
Long ranItemId = RandomUtils.nextLong(1, 100000);
userBehavior.setItemId(ranItemId);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
userBehavior.setTimestamp(sdf.format(new Date())); //转换为json
String userBehaviorStr = JSON.toJSONString(userBehavior); //包装成kafka发送的记录
ProducerRecord<String, String> record = new ProducerRecord<String, String>(TOPIC_USER_BEHAVIOR, null,
null, userBehaviorStr);
//发送到缓存
producer.send(record);
System.out.println("向kafka发送数据:" + userBehaviorStr);
//立即发送
producer.flush(); } public static void main(String[] args) { Properties props = new Properties();
props.put("bootstrap.servers", BROKER_LIST);
props.put("key.serializer", KEY_SERIALIZER);
props.put("value.serializer", VALUE_SERIALIZER); producer = new KafkaProducer<>(props); while(true) {
try {
//每一秒写一条数据
TimeUnit.SECONDS.sleep(1);
writeToKafka();
} catch (Exception e) {
e.printStackTrace();
} }
} }

  本地idea Console 输出的结果是这样的:

    向kafka发送数据:{"behavior":"buy","categoryId":7,"itemId":75902,"timestamp":"2020-03-15T11:35:11Z","ts":"2020-03-15T11:35:11Z","userId":4737}

  我们将Flink的任务数调整成10个,也就是同时执行的任务数。 位置在 conf/flink-conf.yaml,taskmanager.numberOfTaskSlots: 10,然后重启下。我的已经启动并且运行了3个任务,看下图:

  我们接下来运行Flink 内置的客户端。命令: bin/sql-client.sh embedded,这样我们就开始了Flink SQL之旅了。我们使用Flink的DDL,从Kafka里边读取数据,采用ProcessingTime的时间事件进行处理,为ts设置水位线,允许5秒延迟。更多参考 时间属性Flink DDL。里边的Kafka 连接以及相关的配置,相信大家都不是很陌生。

CREATE TABLE user_behavior (
userId BIGINT,
itemId BIGINT,
categoryId BIGINT,
behavior STRING,
ts TIMESTAMP(3),
proctime as PROCTIME(), -- 通过计算列产生一个处理时间列
WATERMARK FOR ts as ts - INTERVAL '' SECOND -- 在ts上定义watermark,ts成为事件时间列
) WITH (
'connector.type' = 'kafka', -- 使用 kafka connector
'connector.version' = 'universal', -- kafka 版本,universal 支持 0.11 以上的版本
'connector.topic' = 'user_behaviors', -- kafka topic
'connector.startup-mode' = 'earliest-offset', -- 从起始 offset 开始读取
'connector.properties.zookeeper.connect' = 'localhost:2181', -- zookeeper 地址
'connector.properties.bootstrap.servers' = 'localhost:9092', -- kafka broker 地址
'format.type' = 'json' -- 数据源格式为 json
);

  接下来我们使用select来看一下Flink的数据,执行语句:select * from user_behavior,会出现如下图。同时SQL上面还支持 show tables、describe user_behavior 等操作。

  我们需要将结果放入Elasticsearch,这样也比较简单,我们还通过DDL来创建一个表。我们只需要一个语句,就可以实现连接Elasticsearch(后边简称ES)并且创建相应的Type和Index了。不需要自己再去创建一次,是不是很简单,哈。里边有两个字段,一个是每天的小时数,一个是购买的统计量。当有数据写入这个表的时候,那么就会将数据写入到ES上,非常方便。

CREATE TABLE buy_cnt_per_hour (
hour_of_day BIGINT,
buy_cnt BIGINT
) WITH (
'connector.type' = 'elasticsearch', -- 使用 elasticsearch connector
'connector.version' = '', -- elasticsearch 版本,6 能支持 es 6+ 以及 7+ 的版本
'connector.hosts' = 'http://localhost:9200', -- elasticsearch 地址
'connector.index' = 'buy_cnt_per_hour', -- elasticsearch 索引名,相当于数据库的表名
'connector.document-type' = 'user_behavior', -- elasticsearch 的 type,相当于数据库的库名
'connector.bulk-flush.max-actions' = '', -- 每条数据都刷新
'format.type' = 'json', -- 输出数据格式 json
'update-mode' = 'append'
);

  每个小时的购买量,那么我们需要的是使用滚动窗口,Tumbling Window,那么使用TUMBLE_START函数,另外我们还需要获取ts中的小时数,那么需要使用HOUR函数。将所有behavior为buy的写入到这个表中。

INSERT INTO buy_cnt_per_hour
SELECT HOUR(TUMBLE_START(ts, INTERVAL '' HOUR)), COUNT(*)
FROM user_behavior
WHERE behavior = 'buy'
GROUP BY TUMBLE(ts, INTERVAL '' HOUR);

  这个时候看Flink里边的任务中会出现这个任务,因为是持续不断的进行处理的。执行过程中如果有数据的话,那么会将数据写到表 buy_cnt_per_hour,同时也会将数据写到ES里边。

  下面我们来配置一下Kinbana来将结果进行展示,访问 http://localhost:5601, 然后选择左边菜单的“Management”,然后选择 “Index Patterns” -> “Create Index Pattern”, 输入我们刚才创建的Index: “buy_cnt_per_hour”。可以通过左侧的“Discover”按钮就可以看到我们的数据了。

  

  我们继续点击左侧的“Dashboard”按钮,创建一个“用户行为日志分析”的Dashboard。 进入左侧的 “Visualize” - “Create Visualization" 选择“Area”图,Bucket的按我下边截图左下进行配置和选择。

  保存后添加到Dashboard即可。这样就从数据源头到数据展示就构建完成了,是不是很快~

3. 构建每天以10分钟的粒度进行统计独立用户数应用。

  

  我们继续使用DDL创建Flink的表以及对应的ES的Index。

CREATE TABLE cumulative_uv (
time_str STRING,
uv BIGINT
) WITH (
'connector.type' = 'elasticsearch',
'connector.version' = '',
'connector.hosts' = 'http://localhost:9200',
'connector.index' = 'cumulative_uv',
'connector.document-type' = 'user_behavior',
'format.type' = 'json',
'update-mode' = 'upsert'
);

  创建好了需要将ts进行分解出来小时和分钟,通过一个视图,这个视图和数据库的视图类似,不存储数据,也不占用Flink的执行Task。首先将ts格式化,然后转换成时间:小时:分钟,分钟后边没有0,结尾需要补个0。然后统计不同的用户数需要使用DISTINCT函数和COUNT函数。还有使用Over Window功能,也就是从之前的数据到现在,以处理时间升序把数据按Window的功能来进行统计。直白的将就是有一条数据的话就会将数据处理, 然后有一条数据比当前最大值大的话会保留最大值。当前窗口是以每10分钟为一个窗口。

CREATE VIEW uv_per_10min AS
SELECT
MAX(SUBSTR(DATE_FORMAT(ts, 'HH:mm'),1,4) || '') OVER w AS time_str,
COUNT(DISTINCT userId) OVER w AS uv
FROM user_behavior
WINDOW w AS (ORDER BY proctime ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW);

  这个视图主要是数据比较多,只需要每10分钟一个点其实就满足要求了,那么现在我们需要做的就是再将数据处理一下即可写入ES。

INSERT INTO cumulative_uv
SELECT time_str, MAX(uv)
FROM uv_per_10min
GROUP BY time_str;

  这样ES里边就会有新的index产生,下一步我们在kibana里边创建一个 index pattern, 输入index “cumulative_uv”,接下来到 “Visualize”里边创建一个 Visualization ,名为“累计独立用户数”,表选择“Line”类型的图标,其他指标和我下图配置的一样即可。

  

  累计独立用户数也创建好了。

4.   构建按分类进行排行,取出想要的结果应用。

    接下来我们需要按主类目进行统计和排序。因为子类目非常多。

  首先我们需要准备一个mysql, 然后创建好表。简单些几条对应的类目关系,当然可以根据自己所生成的数据进行自行写入一些对应的关系表。

create table category (
sub_category_id bigint(20),
parent_category_id bigint(20)
);
insert into category(sub_category_id, parent_category_id) values(1038, 1);
insert into category(sub_category_id, parent_category_id) values(91244, 1);
insert into category(sub_category_id, parent_category_id) values(44712, 1);
insert into category(sub_category_id, parent_category_id) values(2,2);
insert into category(sub_category_id, parent_category_id) values(3,3);
insert into category(sub_category_id, parent_category_id) values(4,4);
insert into category(sub_category_id, parent_category_id) values(5,5);
insert into category(sub_category_id, parent_category_id) values(6,6);
insert into category(sub_category_id, parent_category_id) values(7,7);
insert into category(sub_category_id, parent_category_id) values(8,8);
insert into category(sub_category_id, parent_category_id) values(9,9);

  定义一个Flink表,数据从Mysql获取,用于进行类目关系关联。

CREATE TABLE category_dim (
sub_category_id BIGINT, -- 子类目
parent_category_id BIGINT -- 顶级类目
) WITH (
'connector.type' = 'jdbc',
'connector.url' = 'jdbc:mysql://localhost:3306/flink',
'connector.table' = 'category',
'connector.driver' = 'com.mysql.jdbc.Driver',
'connector.username' = 'root',
'connector.password' = 'root',
'connector.lookup.cache.max-rows' = '',
'connector.lookup.cache.ttl' = '10min'
);

  创建ES的index,用于存储统计后的结果。

CREATE TABLE top_category (
category_name STRING, -- 类目名称
buy_cnt BIGINT -- 销量
) WITH (
'connector.type' = 'elasticsearch',
'connector.version' = '',
'connector.hosts' = 'http://localhost:9200',
'connector.index' = 'top_category',
'connector.document-type' = 'user_behavior',
'format.type' = 'json',
'update-mode' = 'upsert'
);

  接下来还是创建一个视图,将表和类目关联起来,方便后边的统计结果。使用的是 Temporal Join

CREATE VIEW rich_user_behavior AS
SELECT U.userId, U.itemId, U.behavior,
CASE C.parent_category_id
WHEN 1 THEN '服饰鞋包'
WHEN 2 THEN '家装家饰'
WHEN 3 THEN '家电'
WHEN 4 THEN '美妆'
WHEN 5 THEN '母婴'
WHEN 6 THEN '3C数码'
WHEN 7 THEN '运动户外'
WHEN 8 THEN '食品'
ELSE '其他'
END AS category_name
FROM user_behavior AS U LEFT JOIN category_dim FOR SYSTEM_TIME AS OF U.proctime AS C
ON U.categoryId = C.sub_category_id;

  将类型为“buy”的写入到表,同时也就是写入了ES里边,然后ES里边的index-top_category 也就有了数据了。

INSERT INTO top_category
SELECT category_name, COUNT(*) buy_cnt
FROM rich_user_behavior
WHERE behavior = 'buy'
GROUP BY category_name;

  我们继续在Kibana里边创建一个index pattern,输入“top_category”,然后visualize里边创建一个visualization 名为类目排行榜。详细的配置可参考如下。

   好了整个的过程计算创建完了。

  

  通过使用Flink 1.10以及对应的Connector, 实现了对Mysql, Kafka, Elasticsearch 的快速连接,更快的达到的我们想要实现的效果。

  里边涉及到往kafka里边写数据可参考工程:https://github.com/stonehqs/flink-demo

  

Flink系列之1.10版流式SQL应用的更多相关文章

  1. Apache Flink系列(1)-概述

    一.设计思想及介绍 基本思想:“一切数据都是流,批是流的特例” 1.Micro Batching 模式 在Micro-Batching模式的架构实现上就有一个自然流数据流入系统进行攒批的过程,这在一定 ...

  2. Java如何使用实时流式计算处理?

    我是3y,一年CRUD经验用十年的markdown程序员‍常年被誉为职业八股文选手 最近如果拉过austin项目代码的同学,可能就会发现多了一个austin-stream模块.其实并不会意外,因为这一 ...

  3. Flink系列之流式

    本文仅是自己看书.学习过程中的个人总结,刚接触流式,视野面比较窄,不喜勿喷,欢迎评论交流. 1.为什么是流式? 为什么是流式而不是流式系统这样的词语?流式系统在我的印象中是相对批处理系统而言的,用来处 ...

  4. 流式计算(三)-Flink Stream 篇一

    原创文章,谢绝任何形式转载,否则追究法律责任! ​流的世界,有点乱,群雄逐鹿,流实在太多,看完这个马上又冒出一个,也不知哪个才是真正的牛,据说Flink是位重量级选手,能流计算,还能批处理, 和其他伙 ...

  5. Flink系列(0)——准备篇(流处理基础)

    Apache Flink is a framework and distributed processing engine for stateful computations over unbound ...

  6. 流式处理新秀Flink原理与实践

    随着大数据技术在各行各业的广泛应用,要求能对海量数据进行实时处理的需求越来越多,同时数据处理的业务逻辑也越来越复杂,传统的批处理方式和早期的流式处理框架也越来越难以在延迟性.吞吐量.容错能力以及使用便 ...

  7. 使用flink Table &Sql api来构建批量和流式应用(2)Table API概述

    从flink的官方文档,我们知道flink的编程模型分为四层,sql层是最高层的api,Table api是中间层,DataStream/DataSet Api 是核心,stateful Stream ...

  8. 「Flink」理解流式处理重要概念

    什么是流式处理呢? 这个问题其实我们大部分时候是没有考虑过的,大多数,我们是把流式处理和实时计算放在一起来说的.我们先来了解下,什么是数据流. 数据流(事件流) 数据流是无边界数据集的抽象 我们之前接 ...

  9. Demo:基于 Flink SQL 构建流式应用

    Flink 1.10.0 于近期刚发布,释放了许多令人激动的新特性.尤其是 Flink SQL 模块,发展速度非常快,因此本文特意从实践的角度出发,带领大家一起探索使用 Flink SQL 如何快速构 ...

随机推荐

  1. 对“深入理解 Java 内存模型(六)——final”的学习

    转载自https://www.infoq.cn/article/java-memory-model-6/ 与前面介绍的锁和 volatile 相比较,对 final 域的读和写更像是普通的变量访问.对 ...

  2. 专利|Pct||

    专利:有些专利写的尽量模糊,为了不让别人检出,让别人能轻易侵犯专利权 优先权:在本国申请后,在他国也是同一个专利人申请,并也是同一个申请日. 发明20年:实用新型外观设计:20年 Pct:专利合作条约 ...

  3. By virtue of|sustain|post |scrape off |stretch|access to|take into account of|exploit|hasten|blur |idle|bored her to|account for|accused of|cruelty

    By virtue of this superior quality, this product is often sold out of stockin many areas. 我们的产品因其优秀的 ...

  4. HTML的img标签:alt属性和title属性

    当浏览器卖主扭曲了标准并且自顾自的不按规则去做一些事,他们可能会造成一些问题,或者至少产生了混淆.例子之一就是一些浏览器处理alt属性(一般会被错误的称作alt标签)的方式,比如拥有大量用户的Wind ...

  5. [LC] 674. Longest Continuous Increasing Subsequence

    Given an unsorted array of integers, find the length of longest continuous increasing subsequence (s ...

  6. Ionic3学习笔记(十六)上传头像至图床

    本文为原创文章,转载请标明出处 个人做的开源 Demo 登录注册模块采用的是 Wilddog 野狗通讯云的身份认证服务,不得不说各方面和 Google 收购的 Firebase 很像,十分简单易用.其 ...

  7. 吴裕雄--天生自然KITTEN编程:拾金币

  8. 【转载】Scrapy安装及demo测试笔记

    Scrapy安装及demo测试笔记 原创 2016年09月01日 16:34:00 标签: scrapy / python   Scrapy安装及demo测试笔记 一.环境搭建 1. 安装scrapy ...

  9. Spring学习笔记(七)模拟实际开发过程的调用过程XML版-Setter方式注入

    模拟实际开发过程的调用过程XML版-Setter方式注入 源码获取github [TOC] 1.项目结构 2.jar包跟上个一样 3.重写set方法 UserServiceImpl.java 1234 ...

  10. 烧光百亿的共享单车行业,ofo和摩拜到底该不该合并?

    共享经济领域可谓一地鸡毛,除了众多不靠谱的跟风项目外--共享马扎."老公寄存屋",更多的是不绝于耳的倒闭消息.尤其是在共享单车行业,暂且不提那些体量小的项目,单单是倒闭的大型共享单 ...