Flink 中定时加载外部数据
社区中有好几个同学问过这样的场景:
flink 任务中,source 进来的数据,需要连接数据库里面的字段,再做后面的处理
这里假设一个 ETL 的场景,输入数据包含两个字段 “type, userid....” ,需要根据 type,连接一张 mysql 的配置表,关联 type 对应的具体内容。相对于输入数据的数量,type 的值是很少的(这里默认只有10种), 所以对应配置表就只有10条数据,配置是会定时修改的(比如跑批补充数据),配置的修改必须在一定时间内生效。
实时 ETL,需要用里面的一个字段去关联数据库,补充其他数据,进来的数据中关联字段是很单一的(就10个),对应数据库的数据也很少,如果用 异步 IO,感觉会比较傻(浪费资源、性能还不好)。同时数据库的数据是会不定时修改的,所以不能在启动的时候一次性加载。
Flink 现在对应这种场景可以使用 Boradcase state 做,如:基于Broadcast 状态的Flink Etl Demo
这里想说的是另一种更简单的方法: 使用定时器,定时加载数据库的数据 (就是简单的Java定时器)
先说一下代码流程:
1、自定义的 source,输入逗号分隔的两个字段
2、使用 RichMapFunction 转换数据,在 open 中定义定时器,定时触发查询 mysql 的任务,并将结果放到一个 map 中
3、输入数据关联 map 的数据,然后输出
先看下数据库中的数据:
mysql> select * from timer;
+------+------+
| id | name |
+------+------+
| 0 | 0zOq |
| 1 | 1hKC |
| 2 | 2ibM |
| 3 | 3fCe |
| 4 | 4TaM |
| 5 | 5URU |
| 6 | 6WhP |
| 7 | 7zjn |
| 8 | 8Szl |
| 9 | 9blS |
+------+------+
10 rows in set (0.01 sec)
总共10条数据,id 就是对应的关联字段,需要填充的数据是 name
下面是主要的代码:// 自定义的source,输出 x,xxx 格式随机字符
val input = env.addSource(new TwoStringSource)
val stream = input.map(new RichMapFunction[String, String] { val jdbcUrl = "jdbc:mysql://venn:3306?useSSL=false&allowPublicKeyRetrieval=true"
val username = "root"
val password = "123456"
val driverName = "com.mysql.jdbc.Driver"
var conn: Connection = null
var ps: PreparedStatement = null
val map = new util.HashMap[String, String]() override def open(parameters: Configuration): Unit = {
logger.info("init....")
query()
// new Timer
val timer = new Timer(true)
// schedule is 10 second 定义了一个10秒的定时器,定时执行查询数据库的方法
timer.schedule(new TimerTask {
override def run(): Unit = {
query()
}
}, 10000, 10000)
} override def map(value: String): String = {
// concat input and mysql data,简单关联输出
value + "-" + map.get(value.split(",")(0))
} /**
* query mysql for get new config data
*/
def query() = {
logger.info("query mysql")
try {
Class.forName(driverName)
conn = DriverManager.getConnection(jdbcUrl, username, password)
ps = conn.prepareStatement("select id,name from venn.timer")
val rs = ps.executeQuery while (!rs.isClosed && rs.next) {
val id = rs.getString(1)
val name = rs.getString(2)
// 将结果放到 map 中
map.put(id, name)
}
logger.info("get config from db size : {}", map.size()) } catch {
case e@(_: ClassNotFoundException | _: SQLException) =>
e.printStackTrace()
} finally {
ps.close()
conn.close()
}
}
})
// .print() val sink = new FlinkKafkaProducer[String]("timer_out"
, new MyKafkaSerializationSchema[String]()
, Common.getProp
, FlinkKafkaProducer.Semantic.EXACTLY_ONCE)
stream.addSink(sink)
简单的Java定时器:
val timer = new Timer(true)
// schedule is 10 second, 5 second between successive task executions
timer.schedule(new TimerTask {
override def run(): Unit = {
query()
}
}, 10000, )
------------------20200327 改---------------------
之前 博客写的有问题,public void schedule(TimerTask task, long delay, long period) 的第三个参数才是重复执行的时间间隔,0 是不执行,我之前写的时候放上去的案例,调用的 Timer 的构造方法是: public void schedule(TimerTask task, long delay) 只会在 delay 时间后调用一次,并不会重复执行,不需要 调用 : public void schedule(TimerTask task, long delay, long period) 这样的构造方法,才能真正的定时执行。
使用之前的方法执行的,会看到query 方法执行了两次,是 open 中主动调用了一次和 之后调度了一次,定时器就结束了。
感谢社区大佬指出
同时社区还有大佬指出 : ScheduledExecutorService 会比 timer 更好;理由: Timer里边的逻辑失败的话不会抛出任何异常,直接结束,建议用ScheduledExecutorService替换Timer并且捕获下异常看看
------------------------------------
看下输出的数据:
7,N-7zjn
7,C-7zjn
7,U-7zjn
4,T-4TaM
7,J-7zjn
9,R-9blS
4,C-4TaM
9,T-9blS
4,A-4TaM
6,I-6WhP
9,U-9blS
注:“-” 之前是原始数据,后面是关联后的数据
部署到服务器上定时器的调度:
2019-09-28 18:28:13,476 INFO com.venn.stream.api.timer.CustomerTimerDemo$ - query mysql
2019-09-28 18:28:13,480 INFO com.venn.stream.api.timer.CustomerTimerDemo$ - get config from db size : 10
2019-09-28 18:28:18,553 INFO org.apache.flink.streaming.api.functions.sink.TwoPhaseCommitSinkFunction - FlinkKafkaProducer 0/1 - checkpoint 17 complete, committing transaction TransactionHolder{handle=KafkaTransactionState [transactionalId=null, producerId=-1, epoch=-1], transactionStartTime=1569666488499} from checkpoint 17
2019-09-28 18:28:23,476 INFO com.venn.stream.api.timer.CustomerTimerDemo$ - query mysql
2019-09-28 18:28:23,481 INFO com.venn.stream.api.timer.CustomerTimerDemo$ - get config from db size : 10
2019-09-28 18:28:28,549 INFO org.apache.flink.streaming.api.functions.sink.TwoPhaseCommitSinkFunction - FlinkKafkaProducer 0/1 - checkpoint 18 complete, committing transaction TransactionHolder{handle=KafkaTransactionState [transactionalId=null, producerId=-1, epoch=-1], transactionStartTime=1569666498505} from checkpoint 18
2019-09-28 18:28:33,477 INFO com.venn.stream.api.timer.CustomerTimerDemo$ - query mysql
2019-09-28 18:28:33,484 INFO com.venn.stream.api.timer.CustomerTimerDemo$ - get config from db size : 10
十秒调度一次
欢迎关注Flink菜鸟公众号,会不定期更新Flink(开发技术)相关的推文
Flink 中定时加载外部数据的更多相关文章
- iOS中 UIWebView加载网络数据 技术分享
直奔核心: #import "TechnologyDetailViewController.h" #define kScreenWidth [UIScreen mainScreen ...
- Vue.js中滚动条加载更多数据
本文章参考:http://www.cnblogs.com/ssrsblogs/p/6108423.html 分析:1.需要判断滚动条是否到底部: 需要用到DOM的三个属性值,即scrollTop.cl ...
- Skyline TerraExplorer -二次开发- 加载外部数据的各种连接串
Skyline 可以连接外部的数据源,包括SQL Server,Oracle ,excel,mySQL,SQlite,WFS....... 连接字符串如下:例如连接shp文件,为“FileName=C ...
- WinForm中异步加载数据并使用进度条
在WinForm程序中,有时会因为加载大量数据导致UI界面假死,这种情况对于用户来说是非常不友好的.因此,在加载大量数据的情况下,首先应该将数据加载放在另一线程中进行,这样保证了UI界面的响应:其次可 ...
- Ajax在jQuery中的应用(加载异步数据、请求服务器数据)
加载异步数据 jQuery中的load()方法 load(url,[data],[callback]) url:被加载的页面地址 [data]:可选项表示发送到服务器的数据,其格式为 key/valu ...
- Ajax在jQuery中的应用---加载异步数据
Ajax是Asynchronous JavaScript and XML的缩写,其核心是通过XMLHttpRequest对象,以一种异步的方式,向服务器发送数据请求,并通过该对象接收请求返回的数据,从 ...
- Silverlight实用窍门系列:2.Silverlight动态加载外部XML指定地址的WebService---(动态加载外部XML文件中指定的WebService地址)【附带实例源码】
接上节所讲的,Silverlight可以加载外部的XML文件里面的内容,那么我们可不可以在外部XML里面配置一个WebService地址,并且以此加载这个地址来动态加载WebService呢?这样子就 ...
- html中的数据岛:利用DSO和javascript在html中动态加载和浏览xml数据
1.DSO也叫做数据源对象,IE 4.0引入了DSO,在IE 5.0对DSO技术进行很大的扩展.以往如果数据是通过SQL语言对数据库进行查询得到的结果,那么就把它们存放在ADO(ActiveX Dat ...
- 关于Vue中页面(父组件)下拉,页面中的子组件加载更多数据的实现方法
一个项目中存在很多这种情况:父组件(页面)中的子组件需要做下拉加载更多的需求,但是这个下拉到底部的动作只能通过监控页面(父组件)来完成 这就需要父子组件之间的通信,代码如下: 1. 建立一个用于父子组 ...
随机推荐
- Oracle-分析函数之取上下行数据lag()和lead()
这两个函数是偏移量函数,可以查出一个字段的上一个值或者下一个值,配合over来使用. lead函数,这个函数是向上偏移. lag函数是向下偏移一位. 语法 [语法] lag(EXPR,<OFFS ...
- Mac OpenSSL 生成支付宝 2048位密钥
安装OpenSSL: brew install openssl 然后: OpenSSL> genrsa -out rsa_private_key.pem 2048 #生成私钥 OpenSSL&g ...
- 【C语言基础】编码规范
from:程序员互动联盟 2016-12-28 1. 基本要求 1.1 程序结构清析,简单易懂,单个函数的程序行数不得超过100行. 1.2 打算干什么,要简单,直接了当,代码精简,避免垃圾程序. ...
- set的完整用法
#include<bits/stdc++.h> using namespace std; set<int>s; int main () { //begin()--返回指向第一个 ...
- 学习Spring-Data-Jpa(六)---spring-data-commons中的repository
1.spring-data-commons项目 spring-data-commons项目是所有spring-data项目的核心,我们来看一下该项目下的repository包中的接口和注解. 2.Re ...
- 002_基础电路_AD快捷键
AD快捷键设置 陆小果哥哥制作 1. F2----------------------------------------放置走线 a) b) 需设置,点中走线按住 ...
- RookeyFrame 删除 线下添加的model
环境:在model层已经添加了Crm_Cm_ContactInfo2 这个类,这个类现在已经添加到了数据库的,使用之前的方法(在前面的文章有提到该类) 删除步骤: 1.Sys_Module表 的字段 ...
- 猴猴吃香蕉 背包DP
猴猴吃香蕉 背包DP \(D\)次询问,第\(i\)次询问,每次有\(n_i\)个带权香蕉,问有多少方案使香蕉之积为\(k_i\),对结果取模\(1000000007\) \(n\le 10^3,k\ ...
- 高效率编辑器 Vim——操作篇,非常适合 Vim 新手
原文地址:https://linuxtoy.org/archives/efficient-editing-with-vim.html vim教程:http://vimcasts.org/ https: ...
- 利用JDK方式和GuavaAPI方式实现观察者模式
1.JDK方法实现案例需求: 去餐厅吃饭有时候需要排队,进行排队叫号.假如所有等待的人都是观察者,叫号系统就是一个被监听的主题对象.当叫号系统发出叫号通知后,所有等待的人,都会收到通知,然后检查自己的 ...