最近在学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. 数据清洗

最新文章

  1. matlab 曲线拟合
  2. vue.js 请求数据
  3. JAVA正则表达式中如何匹配反斜杠 \
  4. Ueditor百度网页编辑器开发者版java utf-8的使用
  5. java 深入浅出工厂模式
  6. Identity标识列
  7. POJ 2406 Power Strings
  8. ScrollVIew 边界阴影效果
  9. bzoj 1185 旋转卡壳 最小矩形覆盖
  10. 搭建linux环境下jenkins可移植环境
  11. MVC中的UrlHelper
  12. 如何使用JAVA语言抓取某个网页中的邮箱地址
  13. IBM openblockchain学习(四)--crypto源代码分析
  14. Douglas Adams - 3 Rules That Describe Our Reactions To Technologies 科技影响生活的三个规律
  15. 网络协议 21 - RPC 协议(中)- 基于 JSON 的 RESTful 接口协议
  16. UIGestureRecognizer - BNR
  17. __x__(22)0907第四天__ 垂直外边距重叠
  18. 告诉你,Spring Boot 真是个牛逼货!
  19. PyCharmMarkdown插件的方法
  20. 【python原理解析】gc原理初步解析

热门文章

  1. Linux rhel7 无线网络配置
  2. 【第五课】LNMP环境的入门
  3. IIS充当反向代理转发请求到Kestrel
  4. python中类中属性和方法的具体定义方法和使用
  5. java 软件开发面试宝典
  6. LeetCode-37.解数独
  7. 微软职位内部推荐-Software Development Engineer II_Commerce
  8. OpenState之 Mac学习 实验
  9. VSTS写单元测试
  10. 用javaScript将页面滚动条到底部