最近在学Flink,准备用Flink搭建一个实时的推荐系统。找到一个好的网站(也算作是flink创始者的官方网站),上面有关于Flink的上手教程,用来练练手,熟悉熟悉,下文仅仅是我的笔记。

1. 数据集

网站New York City Taxi & Limousine Commission提供了关于纽约市从2009-1015年关于出租车驾驶的公共数据集。

具体数据下载方法,可见# Taxi Data Streams,下载完数据后,不要解压缩。

我们的第一个数据集包含纽约市的出租车出行的信息,每一次出行包含两个事件:START和END,可以分别理解为开始和结束该行程。每一个事件又包括11个属性,详细介绍如下:

taxiId         : Long      // a unique id for each taxi
driverId : Long // a unique id for each driver
isStart : Boolean // TRUE for ride start events, FALSE for ride end events
startTime : DateTime // the start time of a ride
endTime : DateTime // the end time of a ride,
// "1970-01-01 00:00:00" for start events
startLon : Float // the longitude of the ride start location
startLat : Float // the latitude of the ride start location
endLon : Float // the longitude of the ride end location
endLat : Float // the latitude of the ride end location
passengerCnt : Short // number of passengers on the ride

另一个数据集包含出租车的费用信息,与每一次行程对应:

taxiId         : Long      // a unique id for each taxi
driverId : Long // a unique id for each driver
startTime : DateTime // the start time of a ride
paymentType : String // CSH or CRD
tip : Float // tip(小费) for this ride
tolls : Float // tolls for this ride
totalFare : Float // total fare collected

2. 生成数据流

首先定义TaxiRide事件,即数据集中的每一个record。

我们使用Flink的source函数(TaxiRideSource)读取TaxiRide流,这个source是基于事件时间进行的。同样的,费用事件TaxiFare的流通过函数TaxiFareSource进行传送。为了让生成的流更加真实,事件传送的时间是与timestamp成比例的。两个真实相隔十分钟发生的事件在流中也相差十分钟。此外,我们可以定义一个变量speed-up factor为60,该变量为加速因子,那么真实事件中的一分钟在流中只有1秒钟,缩短60倍嘛。不仅如此,我们还可以定义最大服务延时,这个延时使得每个事件在最大服务延时之内随机出现,这么做的目的是让这个流的事件产生与在real-world发生的不确定性更接近。

对于这个应用,我们设置speed-up factor为600(即10分钟相当于1秒),以及最大延时时间为60。

所有的行动都应使用事件时间(event time)(相对于处理时间(processing time))来实现。

Event-time decouples the program semantics from serving speed and guarantees consistent results even in case of historic data or data which is delivered out-of-order.

事件时间(event time)将程序语义与服务速度分离开,即使在历史数据或无序传送的数据的情况下也能保证一致的结果。简单来说就是,在数据处理的过程中,依赖的时间跟在流中出现的时间无关,只跟该事件发生的时间有关。

private void generateUnorderedStream(SourceContext<TaxiRide> sourceContext) throws Exception {  

  // 设置服务开始时间servingStartTime
long servingStartTime = Calendar.getInstance().getTimeInMillis(); // 数据开始时间dataStartTime,即第一个ride的timestamp
long dataStartTime; Random rand = new Random(7452); // 使用优先队列进行emit,其比较方式为他们的等待时间
PriorityQueue<Tuple2<Long, Object>> emitSchedule = new PriorityQueue<>(
32,
new Comparator<Tuple2<Long, Object>>() {
@Override
public int compare(Tuple2<Long, Object> o1, Tuple2<Long, Object> o2) {
return o1.f0.compareTo(o2.f0); }
}); // 读取第一个ride,并将第一个ride插入到schedule里
String line;
TaxiRide ride;
if (reader.ready() && (line = reader.readLine()) != null) {
// read first ride
ride = TaxiRide.fromString(line);
// extract starting timestamp
dataStartTime = getEventTime(ride);
// get delayed time,这个delayedtime是dataStartTime加一个随机数,随机数有最大范围,用来模拟真实世界情况
long delayedEventTime = dataStartTime + getNormalDelayMsecs(rand); // 将ride插入到schedule里
emitSchedule.add(new Tuple2<Long, Object>(delayedEventTime, ride));
// 设置水印时间
long watermarkTime = dataStartTime + watermarkDelayMSecs;
// 下一个水印时间是时间戳是 watermarkTime - maxDelayMsecs - 1
// 只能证明,这个时间一定是小于dataStartTime的 Watermark nextWatermark = new Watermark(watermarkTime - maxDelayMsecs - 1);
// 将该水印放入Schedule,且这个水印被优先队列移到了ride之前
emitSchedule.add(new Tuple2<Long, Object>(watermarkTime, nextWatermark)); } else {
return;
} // 从文件里读取下一个ride(peek)
if (reader.ready() && (line = reader.readLine()) != null) {
ride = TaxiRide.fromString(line);
} // read rides one-by-one and emit a random ride from the buffer each time
while (emitSchedule.size() > 0 || reader.ready()) { // insert all events into schedule that might be emitted next
// 在Schedule里的下一个事件的延时后时间 long curNextDelayedEventTime = !emitSchedule.isEmpty() ? emitSchedule.peek().f0 : -1;
// 当前从文件读取的ride的事件时间
long rideEventTime = ride != null ? getEventTime(ride) : -1;
// 这个while循环用来进行当前Schedule为空的情况
while(
ride != null && ( // while there is a ride AND
emitSchedule.isEmpty() || // and no ride in schedule OR
rideEventTime < curNextDelayedEventTime + maxDelayMsecs) // not enough rides in schedule
)
{
// insert event into emit schedule
long delayedEventTime = rideEventTime + getNormalDelayMsecs(rand);
emitSchedule.add(new Tuple2<Long, Object>(delayedEventTime, ride)); // read next ride
if (reader.ready() && (line = reader.readLine()) != null) {
ride = TaxiRide.fromString(line);
rideEventTime = getEventTime(ride);
}
else {
ride = null;
rideEventTime = -1;
}
} // 提取Schedule里的第一个ride,叫做head
Tuple2<Long, Object> head = emitSchedule.poll();
// head应该要到达的时间
long delayedEventTime = head.f0;
long now = Calendar.getInstance().getTimeInMillis(); // servingTime = servingStartTime + (delayedEventTime - dataStartTime)/ this.servingSpeed
long servingTime = toServingTime(servingStartTime, dataStartTime, delayedEventTime);
// 应该再等多久,才让这个ride发生呢?(哈哈,我好喜欢这个描述)
long waitTime = servingTime - now;
// 既然要等,那就睡着等吧
Thread.sleep( (waitTime > 0) ? waitTime : 0);
// 如果这个head是一个TaxiRide
if(head.f1 instanceof TaxiRide) {
TaxiRide emitRide = (TaxiRide)head.f1;
// emit ride
sourceContext.collectWithTimestamp(emitRide, getEventTime(emitRide));
}
// 如果这个head是一个水印标志
else if(head.f1 instanceof Watermark) {
Watermark emitWatermark = (Watermark)head.f1;
// emit watermark
sourceContext.emitWatermark(emitWatermark);
// 并设置下一个水印标志到Schedule中
long watermarkTime = delayedEventTime + watermarkDelayMSecs;
// 同样,保证这个水印的时间戳在下一个ride的timestamp之前
Watermark nextWatermark = new Watermark(watermarkTime - maxDelayMsecs - 1);
emitSchedule.add(new Tuple2<Long, Object>(watermarkTime, nextWatermark));
}
}
}

那么,如何在java中运行这些sources,下面是一个示例:

// get an ExecutionEnvironment
StreamExecutionEnvironment env = StreamExcutionEnvironment.getExecutionEnvironment();
// configure event-time processing
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
// get the taxi ride data stream
DataStream<TaxiRide> rides = env.addSource(
new TaxiRideSource("/path/to/nycTaxiRides.gz", maxDelay, servingSpeed));

另外,有一些应用需要我们使用加入检查点的机制。检查点(checkpoint)是从failure中恢复的一种机制。他也需要建立CheckpointedTaxiRideSource来在流中运行。

3. 数据清洗

Flink入门训练--以New York City Taxi为例的更多相关文章

  1. 入门训练 Fibonacci数列

      入门训练 Fibonacci数列   时间限制:1.0s   内存限制:256.0MB 问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时, ...

  2. 蓝桥杯 入门训练 Fibonacci数列(水题,斐波那契数列)

    入门训练 Fibonacci数列 时间限制:1.0s   内存限制:256.0MB 问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非 ...

  3. 入门训练 A+B问题

    http://lx.lanqiao.org/problemset.page?code=BEGIN-&userid=34549   入门训练 A+B问题   时间限制:1.0s   内存限制:2 ...

  4. 蓝桥杯 入门训练 Fibonacci数列

      入门训练 Fibonacci数列   时间限制:1.0s   内存限制:256.0MB        问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. ...

  5. 【蓝桥杯】入门训练 Fibonacci数列

      入门训练 Fibonacci数列   时间限制:1.0s   内存限制:256.0MB        问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. ...

  6. 入门训练 Fibonacci数列 (水题)

    入门训练 Fibonacci数列   时间限制:1.0s   内存限制:256.0MB        问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n ...

  7. lqb 入门训练 A+B问题

    入门训练 A+B问题 时间限制:1.0s   内存限制:256.0MB     问题描述 输入A.B,输出A+B. 说明:在“问题描述”这部分,会给出试题的意思,以及所要求的目标. 输入格式 输入的第 ...

  8. lqb 入门训练 序列求和 (PS:用长整数做数据的输入输出)

    入门训练 序列求和 时间限制:1.0s   内存限制:256.0MB     问题描述 求1+2+3+...+n的值. 输入格式 输入包括一个整数n. 输出格式 输出一行,包括一个整数,表示1+2+3 ...

  9. lqb 入门训练 圆的面积 (PS: PI的精确计算方法 atan(1.0) * 4)

    入门训练 圆的面积 时间限制:1.0s   内存限制:256.0MB     问题描述 给定圆的半径r,求圆的面积. 输入格式 输入包含一个整数r,表示圆的半径. 输出格式 输出一行,包含一个实数,四 ...

随机推荐

  1. REST-framework快速构建API--初体验

    一.快速上手 1.环境准备 安装restframework,注册app pip install djangorestframework INSTALLED_APPS = [ 'django.contr ...

  2. flask_admin 笔记三 客户化视图

    客户化视图1, model数据模型参数配置1)配置全局参数内置的ModelView类很适合快速入门. 但是,您需要配置其功能以适合您的特定型号. 这是通过设置ModelView类中提供的配置属性的值来 ...

  3. 解决SSH登录用户执行的命令部分环境变量参数不生效的问题

    问题概况 linux机器在/etc/profile配置完成环境变量后,SSH到目标机器执行命令,但是获取不到已配置的环境变量值. 例如场景: 在/etc/profile配置了http代理 export ...

  4. linux重启tomcat的shell脚本

    基本思路: 先检查待重启的tomcat的进程是否存在 存在则执行shutdown. 然后再次检查进程是否还存在,不存在则执行kill 然后删除工作空间及10天前的日志. 最后执行启动. #!/bin/ ...

  5. [dx11]利用SpriteFont绘制中文--本地化文本

    1.下载DirectX11 Tool Kit SDK,解压后编译,生成DirectXTK.lib库文件和MakeSpriteFont应用工具; 2.在Dx11环境基础上,用生成的库文件搭建XTK环境; ...

  6. win10系统安装web3js的正确方法

    在安装web3的时候 用npm  install web3 –save-dev 在win10系统下会一直安装不成功.后来换用了 cnpm install web3 –save-dev 安装时候报出:C ...

  7. 微软职位内部推荐-Senior Program Manager

    微软近期Open的职位: Title: Senior Program Manager – Bing Multimedia Relevance Group: Search Technology Cent ...

  8. Bootstrap 样式设计 栅格系统

    .col-xs- 超小屏幕 手机 (<768px) .col-sm- 小屏幕 平板 (≥768px) .col-md- 中等屏幕 桌面显示器 (≥992px) .col-lg- 大屏幕 大桌面显 ...

  9. PAT甲题题解-1049. Counting Ones-数学问题

    n位数,总共有0~10^n-1共计10^n个数那么所有数出现的总次数变为n*(10^n)个数1出现的次数便是十分之一,所以n位数中,1出现的次数为n*10^(n-1)知道这一个后,接下来就方便求了. ...

  10. 20135119_涂文斌 实验二 Java面向对象程序设计

    北京电子科技学院(BESTI) 实  验  报  告 课程: Java        班级:1351           姓名:涂文斌          学号:20135119 成绩:         ...