Flume 如何自定义 Mysql Sink?

前言

本文隶属于专栏《1000个问题搞定大数据技术体系》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构和参考文献请见1000个问题搞定大数据技术体系

正文

场景描述

官方提供的sink类型已经很多,但是有时候并不能满足实际开发当中的需求,此时我们就需要根据实际需求自定义某些sink。

如:需要把接受到的数据按照规则进行过滤之后写入到某张mysql表中,所以此时需要我们自己实现MySQLSink。

自定义 Mysql Sink 步骤

  • 1、根据官方说明自定义 MysqlSink 需要继承 AbstractSink 类并实现 Configurable

  • 2、实现对应的方法

    • configure(Context context)
      • 初始化context
    • start()
      • 启动准备操作
    • process()
      • 从channel获取数据,然后解析之后,保存在mysql表中
    • stop()
      • 关闭相关资源

实践

  1. 创建 mysql 数据库以及 mysql 数据库表
--创建一个数据库
CREATE
DATABASE IF NOT EXISTS mysqlsource DEFAULT CHARACTER SET utf8 ;

--创建一个表,用户保存拉取目标表位置的信息
CREATE TABLE mysqlsource.flume2mysql
(
    id         int(11) NOT NULL AUTO_INCREMENT,
    createTime varchar(64)  NOT NULL,
    content    varchar(255) NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. 构建maven工程,添加依赖
    <properties>
        <flume.version>1.9.0</flume.version>
        <mysql.version>8.0.24</mysql.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.flume</groupId>
            <artifactId>flume-ng-core</artifactId>
            <version>${flume.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
    </dependencies>
  1. 定义 MysqlSink 类
package com.shockang.study.bigdata.flume;

import org.apache.flume.Channel;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.Transaction;
import org.apache.flume.conf.Configurable;
import org.apache.flume.sink.AbstractSink;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 自定义MysqlSink
 */
public class MysqlSink extends AbstractSink implements Configurable {
    private String mysqlurl = "";
    private String username = "";
    private String password = "";
    private String tableName = "";

    Connection con = null;

    @Override
    public Status process() {
        Status status = null;
        // Start transaction
        Channel ch = getChannel();
        Transaction txn = ch.getTransaction();
        txn.begin();
        try {
            Event event = ch.take();

            if (event != null) {
                //获取body中的数据
                String body = new String(event.getBody(), "UTF-8");

                //如果日志中有以下关键字的不需要保存,过滤掉
                if (body.contains("delete") || body.contains("drop") || body.contains("alert")) {
                    status = Status.BACKOFF;
                } else {

                    //存入Mysql
                    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String createtime = df.format(new Date());

                    PreparedStatement stmt = con.prepareStatement("insert into " + tableName + " (createtime, content) values (?, ?)");
                    stmt.setString(1, createtime);
                    stmt.setString(2, body);
                    stmt.execute();
                    stmt.close();
                    status = Status.READY;
                }
            } else {
                status = Status.BACKOFF;
            }

            txn.commit();
        } catch (Throwable t) {
            txn.rollback();
            t.getCause().printStackTrace();
            status = Status.BACKOFF;
        } finally {
            txn.close();
        }

        return status;
    }

    /**
     * 获取配置文件中指定的参数
     *
     * @param context
     */
    @Override
    public void configure(Context context) {
        mysqlurl = context.getString("mysqlurl");
        username = context.getString("username");
        password = context.getString("password");
        tableName = context.getString("tablename");
    }

    @Override
    public synchronized void start() {
        try {
            //初始化数据库连接
            con = DriverManager.getConnection(mysqlurl, username, password);
            super.start();
            System.out.println("finish start");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public synchronized void stop() {
        try {
            con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        super.stop();
    }

}
  1. 测试

① 程序打成jar包,上传jar包到flume的lib目录下

② 配置文件准备

vim mysqlsink.conf
a1.sources = r1
a1.sinks = k1
a1.channels = c1

#配置source
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /opt/bigdata/flumeData/data.log
a1.sources.r1.channels = c1

#配置channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100

#配置sink
a1.sinks.k1.channel = c1
a1.sinks.k1.type = com.shockang.study.bigdata.flume.MysqlSink
a1.sinks.k1.mysqlurl=jdbc:mysql://node1:3306/mysqlsource?useSSL=false
a1.sinks.k1.username=root
a1.sinks.k1.password=123456
a1.sinks.k1.tablename=flume2mysql

③ 启动flume配置

flume-ng agent -n a1 -c /opt/bigdata/flume/myconf -f /opt/bigdata/flume/myconf/mysqlsink.conf -Dflume.root.logger=info,console

④ 最后向文件中添加数据,观察mysql表中的数据

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 29
    评论
Flume是一个分布式的日志收集系统,它能够将数据从不同的数据源收集起来,并将其传输到目标系统。MySQL sinkFlume中的一种sink类型,用于将数据写入MySQL数据库。 下面是一个使用Java编写的MySQL sink的示例: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import org.apache.flume.Context; import org.apache.flume.Event; import org.apache.flume.conf.Configurable; import org.apache.flume.sink.AbstractSink; public class MySQLSink extends AbstractSink implements Configurable { private String driver; private String url; private String username; private String password; private String tableName; private Connection connection; private PreparedStatement statement; @Override public void configure(Context context) { driver = context.getString("driver"); url = context.getString("url"); username = context.getString("username"); password = context.getString("password"); tableName = context.getString("tableName"); } @Override public void start() { try { Class.forName(driver); connection = DriverManager.getConnection(url, username, password); statement = connection.prepareStatement("INSERT INTO " + tableName + " (message) VALUES (?)"); super.start(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } @Override public void stop() { try { if (statement != null) { statement.close(); } if (connection != null) { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } super.stop(); } @Override public Status process() throws EventDeliveryException { Status status = null; Event event = null; try { event = getChannel().take(); if (event != null) { String message = new String(event.getBody()); statement.setString(1, message); statement.executeUpdate(); status = Status.READY; } else { status = Status.BACKOFF; } } catch (Exception e) { e.printStackTrace(); status = Status.BACKOFF; } return status; } } ``` 在这个示例中,我们实现了Flume中的`AbstractSink`类,并实现了其中的`configure`、`start`、`stop`和`process`方法。在`configure`方法中,我们从Flume的配置文件中获取MySQL数据库相关的配置信息。在`start`方法中,我们建立了一个到MySQL数据库的连接,并准备好了一个PreparedStatement对象用于插入数据。在`stop`方法中,我们关闭了连接和PreparedStatement对象。在`process`方法中,我们从Flume的channel中获取一个event,将其转换为一个字符串,并执行插入到MySQL数据库的操作。如果处理成功,返回`Status.READY`表示可以继续处理,否则返回`Status.BACKOFF`表示需要停止处理一段时间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值