在Flink文档中,提供connector读取源数据和把处理结果存储到外部系统中。但是没有提供数据库的connector,如果要读写数据库,官网给出了异步IO(Asynchronous I/O)专门用于访问外部数据,详细可看:
https://ci.apache.org/projects/flink/flink-docs-release-1.6/dev/stream/operators/asyncio.html
还有一种方法是继承RichSourceFunction,重写里面的方法,所有的数据库flink都可以通过这两种方式进行数据的读写,这里以hbase为例进行说明。
读取hbase,自定义source即可,自定义source参考上篇博客:https://blog.csdn.net/aA518189/article/details/88726467
写入HBase提供两种方式:
由于flink是一条一条的处理数据,所以我们在插入hbase的时候不能来一条flush一下,不然会给hbase造成很大的压力,而且会产生很多线程导致集群崩溃,本身亲自测试过,所以线上任务必须控制flush的频率。
做法:我们可以在open方法中定义一个变量,然后在写入hbase时比如500条flush一次,或则加入一个list,判断list的大小满足某个阈值flush一下
实现方式:
我们需要自己自定义一个hbase的操作类实现OutputFormat接口,重写里面的抽象方法,也就是下面的抽象方法
public interface OutputFormat extends Serializable {
void configure(Configuration parameters);
void open(int taskNumber, int numTasks) throws IOException;
void writeRecord(IT record) throws IOException;
void close() throws IOException;
}
configure方法主要用于:配置输出格式。由于输出格式是通用的,因此是无参数的,这个方法是输出格式根据配置值设置基本字段的地方,此方法总是在实例化输出格式上首先调用,但是我们不会这个方法做实际测操作。
用于打开输出格式的并行实例,以存储其并行实例的结果,调用此方法时,将确保配置该方法的输出格式。所以在open方法中我们会进行hbase的连接,配置,建表等操作。
用于将数据写入数据源,所以我们会在这个方法中调用写入hbase的API
这个不用说了就是关闭数据源的连接
org.apache.hbase
hbase-client
1.2.4
org.apache.hbase
hbase-server
1.2.4
public class HBaseOutputFormat implements OutputFormat> {
private org.apache.hadoop.conf.Configuration conf = null;
private Connection conn = null;
private Table table = null;
@Override
public void configure(Configuration parameters) {
}
@Override
public void open(int taskNumber, int numTasks) throws IOException {
HbaseUtil.setConf("ip1,ip2,ip3", "2181");
conn = HbaseUtil.connection;
HbaseUtil.createTable("flink_test2","info");
}
@Override
public void writeRecord(Tuple5 record) throws IOException {
Put put = new Put(Bytes.toBytes(record.f0+record.f4));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("uerid"), Bytes.toBytes(record.f0));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("behavior"), Bytes.toBytes(record.f3));
ArrayList putList = new ArrayList<>();
putList.add(put);
//设置缓存1m,当达到1m时数据会自动刷到hbase
BufferedMutatorParams params = new BufferedMutatorParams(TableName.valueOf("flink_test2"));
params.writeBufferSize(1024 * 1024); //设置缓存的大小
BufferedMutator mutator = conn.getBufferedMutator(params);
mutator.mutate(putList);
if(putList.size()>=500){
mutator.flush();
putList.clear();
}
}
public void close() throws IOException {
if (table != null) {
table.close();
}
if (conn != null) {
conn.close();
}
}
}
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
env.enableCheckpointing(5000)
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.getCheckpointConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE)
val dataStream = env.addSource()
...........
dataStream.writeUsingOutputFormat(new HBaseOutputFormat());//写入Hbase
---------------------
使用方式和第一种方式一样,不再叙述,直接上代码:
public class HbaseSinkUtil extends RichSinkFunction {
private Connection conn = null;
private Table table = null;
private HbaseUtil hbaseUtil;
private BufferedMutatorParams params;
private String Column_family="info";
private String tName="event_etl";
private BufferedMutator mutator;
private ArrayList putList;
private int count;
SimpleDateFormat df;
@Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
hbaseUtil=new HbaseUtil();
hbaseUtil.setConf("ip", "2181");
conn = hbaseUtil.connection;
params = new BufferedMutatorParams(TableName.valueOf(tName));
mutator= conn.getBufferedMutator(params);
putList= new ArrayList<>();
//设置缓存1m,当达到5m时数据会自动刷到hbase
params.writeBufferSize(1024 * 1024*5); //设置缓存的大小
count=0;
}
@Override
public void invoke(String value, Context context) throws Exception {
JSONObject jsonObject = JSON.parseObject(value);
String user_id = jsonObject.get("user_id").toString();
String platform = jsonObject.get("platform").toString();
// 一个put操作一行数据,并设置rowkey名称
Put put = new Put(Bytes.toBytes(StringUtil.reverse(String.valueOf(time_local)) +anchor_id));
put.addColumn(Bytes.toBytes(Column_family), Bytes.toBytes("user_id"), Bytes.toBytes(user_id));
put.addColumn(Bytes.toBytes(Column_family), Bytes.toBytes("platform"), Bytes.toBytes(platform));
mutator.mutate(put);
//每满2000条刷新一下数据
if(count>=2000){
mutator.flush();
count=0;
}
count++;
}
@Override
public void close() throws Exception {
super.close();
if (table != null) {
table.close();
}
if (conn != null) {
conn.close();
}
putList.clear();
}
}
上述代码涉及的hbaseUtil类的实现参考博客:https://blog.csdn.net/aA518189/article/details/85298889
如果flink程序写入hbase的时候报如下错误:
Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.EvictionConfig
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at org.apache.flink.runtime.execution.librarycache.FlinkUserCodeClassLoaders$ChildFirstClassLoader.loadClass(FlinkUserCodeClassLoaders.java:129)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 4 more
解决方案:在pom.xml文件中添加如下依赖即可:
org.apache.commons
commons-pool2
2.0
扫一扫加入大数据技术交流群,了解更多大数据技术,还有免费资料等你哦
扫一扫加入大数据技术交流群,了解更多大数据技术,还有免费资料等你哦
扫一扫加入大数据技术交流群,了解更多大数据技术,还有免费资料等你哦