flume custom source, and place kafka channel, to achieve flume data transfer kafka

When using flume to collect data, sometimes we need to customize the source, while the official to the case, and sometimes can not meet our needs, the following case is modeled on the architecture of the source code written.
The following case is: Custom source, instead of using kafka channel, because our goal is, through the flume will collect data to kafka, so save the process from channel to sink, improve efficiency, and to prevent the custom source repeated transmission data.
The following code, interpreted code has Remarks:

package DataCollect;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.EventDrivenSource;
import org.apache.flume.channel.ChannelProcessor;
import org.apache.flume.conf.Configurable;
import org.apache.flume.event.EventBuilder;
import org.apache.flume.source.AbstractSource;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
*自定义source,用来保证不重复消费
* 原始日志文件
* 偏移量文件
* 编码格式
* 睡眠时间
* */

/*
* flume1.6及以前版本
* 如果想监控目录中的多个文件
* 文件的目录
* Filelisten类
* 拿出改名字的文件名
* 变化的文件
* 删除的文件
* 新增的文件
* */
//要继承抽象的AbstractSource类,然后要实现EventDrivenSource、Configurable接口
public class MySource extends AbstractSource implements EventDrivenSource, Configurable {
    //定义成员属性
    //定义原始文件路径
    private String filepath;
    //定义偏移量文件路径
    private String offsetpath;
    //定义编码集
    private String charset;
    //定义间隔时间
    private Long interval;

    //定义线程池
    private ExecutorService executor;

    //filerunner对象
    private fileRunner fileRunner;

    //加载采集方案(配置文件)
    //利用context取出配置文件中的各种定义信息
    public void configure(Context context) {
        //加载原始日志文件
        filepath = context.getString("filepath");
        //加载偏移量文件
        offsetpath = context.getString("offsetpath");
        //加载编码格式
        charset = context.getString("charset","UTF-8");
        //加载睡眠时间
        interval = context.getLong("interval",5000L);
    }
    //执行操作的
    @Override
    public synchronized void start() {
        //创建线程
        //固定线程的线程池
//        Executors.newFixedThreadPool()
        //可缓冲线程池
//        Executors.newCachedThreadPool()
        //单线程的线程池
        //因为还要用线程去监控文件,所以要把executor定义在公共属性中
        executor = Executors.newSingleThreadExecutor();
        //创建一个channel发送的类的对象
        ChannelProcessor channelProcessor = getChannelProcessor();
        //创建对象
        fileRunner = new fileRunner(filepath,offsetpath,charset,interval,channelProcessor);
        //线程要去执行命令
        executor.submit(fileRunner);
        super.start();
    }

    //定义一个类实现Runnable接口
    public class fileRunner implements Runnable{
        //定义成员属性
        private String filepath;
        private String offsetpath;
        private String charset;
        private Long interval;

        //定义原始日志文件的封装类
        private RandomAccessFile accessFile;

        //定义channel发送类,作用就是将封装的event反送给channel
        private ChannelProcessor channelProcessor;

        //定义偏移量
        private Long offset=0L;

        //定义偏移量文件类
        private File offsetFile;

        //定义一个标志
        private boolean flag;

        public void setFlag(boolean flag) {
            this.flag = flag;
        }

        //定义一个构造方法,参数需要上面的属性
        //这个构造方法的作用是,读取偏移量文件内容,获取到偏移量,然后根据获取到的偏移量的值,
        //将原始文件的位置搞到需要的偏移量的位置,相当于初始化,将一切准备开始读取原始文件之前的动作做完
        public fileRunner(String filepath, String offsetpath, String charset, Long interval, ChannelProcessor channelProcessor) {
            this.filepath = filepath;
            this.offsetpath = offsetpath;
            this.charset = charset;
            this.interval = interval;
            this.channelProcessor=channelProcessor;

            //判断偏移量文件是否存在,若不存在,则创建一个偏移量文件,因为这个偏移量文件在run方法中也要用,所以定义到成员属性中
            //封装成File类
            offsetFile = new File(this.offsetpath);
            //判断,如果不存在,则创建
            if(!offsetFile.exists()){
                try {
                    offsetFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //读取偏移量。这里要读取文件中的内容了,但是以往都是使用IO流进行读取文件内容,这里并没有使用IO流
            //我们可以使用文件的工具类,FileUtils,这个工具类中封装了对文件的各种操作
            try {
                //读取出偏移量
                String offsetStr = FileUtils.readFileToString(offsetFile);//这个方法的参数需要传入一个文件对象
                //判断,如果读取出的偏移量不是null或者“”
                if(null != offsetStr && !"".equals(offsetStr)){
                    //因为在run方法中会使用到该偏移量,所以定义到成员属性中
                    offset = Long.parseLong(offsetStr);//将字符串转成long型
                }
                //将原始文件搞到我们需要开始读取的位置(根据偏移量)
                //这里使用了RandomAccessFile类,这个类中有个seek方法,可以直接定义到我们需要的文件位置,类似于C++中的指针的含义
                //还有个readLine的方法,可以读取一行文件内容
                //在run方法中需要使用它去一行一行的读取原始文件,所以定义到成员属性中
                //将文件封装成RandomAccessFile
                accessFile = new RandomAccessFile(filepath, "r");//第一个参数是指定文件对象,第二个参数代表“只读”
                //调用seek方法,传入刚才获取到的偏移量,代表着我们已经将文件的开始读取位置,搞到了偏移量的位置
                accessFile.seek(offset);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //run方法中,开始读取原始文件,封装event,发送给channel,并更新偏移量。如此一直循环下去
        public void run() {
            while (flag){
                //得读取数据
                try {
                    //读取原始文件数据
                    String line = accessFile.readLine();
                    //判断读取到的一行内容是否为空,若为空代表暂时读取了文件所有的内容了,休眠一会
                    if(StringUtils.isNotEmpty(line)){
                    //将line封装成event
                    //方法的第二个参数需要的是Charset类的对象,我们定义的charset是字符串,所以要经过下面所写那样,转换成对象
                    Event event = EventBuilder.withBody(line, Charset.forName(charset));
                    //发送给channel
                    channelProcessor.processEvent(event);
                    //获取读完数据的原始日志的偏移量
                    offset = accessFile.getFilePointer();
                    //将偏移量更新到偏移量文件
                    //第一个参数需要一个文件对象,第二个参数是要写到文件中的数据(String型)
                    FileUtils.writeStringToFile(offsetFile,offset+"");
                    }else{
                        //没有读取到原始文件内容,休眠一会,休眠时间是一个间隔时间
                        try {
                            Thread.sleep(interval);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //关闭资源的
    @Override
    public synchronized void stop() {
        if(!Thread.currentThread().isInterrupted()){
            try {
                Thread.currentThread().wait(5000);
                //关闭上面的循环,使用FileRunner类的对象调用set方法,将flag的值改为false
                fileRunner.setFlag(false);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        super.stop();
    }

}

Code labeled jar first package, and then uploaded to the flume lib file folder
my original data files: /opt/module/flume/data/gaotie.txt
I stored in the offset file: / opt / module / flume / data / postfile
my conf configuration file named: Flume-file-kafka2.conf
conf configuration file content is written:

a1.sources=r1
a1.channels=c1

a1.sources.r1.type=DataCollect.MySource
a1.sources.r1.filepath=/opt/module/flume/data/gaotie.txt
a1.sources.r1.offsetpath=/opt/module/flume/data/postfile
a1.sources.r1.charset=utf-8
a1.sources.r1.interval=5000

a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = myhadoop101:9092,myhadoop102:9092,myhadoop103:9092
a1.channels.c1.kafka.topic = gaotie
a1.channels.c1.kafka.consumer.group.id = gaotie-consumer

a1.sources.r1.channels = c1

Then first start zookeeper

bin/zkServer.sh start

Restart kafka

bin/kafka-server-start.sh config/server.properties &

Finally, open flume supervisory command

bin/flume-ng agent --conf conf/ --name a1 --conf-file job/flume-file-kafka2.conf -Dflume.root.logger=INFO,console

Consumers can open kafka command

bin/kafka-console-consumer.sh --zookeeper myhadoop101:2181 --topic gaotie --from-beginning

To spend a specified topic of the content, to detect whether the success of the data transmitted kafka can also be found directly under kafka logs file folder specified topic, enter the topic folder to view files ending in .log whether there data

He published 189 original articles · won praise 13 · views 10000 +

Guess you like

Origin blog.csdn.net/NewBeeMu/article/details/103059967