origen personalizado canal, y el lugar del canal kafka, kafka para lograr canal de transferencia de datos

Cuando se utiliza canal a recopilar datos, a veces tenemos que personalizar la fuente, mientras que el oficial al caso, y en ocasiones puede no satisfacer nuestras necesidades, el siguiente caso se inspira en la arquitectura del código fuente escrito.
El siguiente caso es: fuente personalizada, en lugar de utilizar el canal kafka, porque nuestro objetivo es, a través del canal recogerá datos a kafka, así salvar el proceso de canal en el fregadero, mejorar la eficiencia, y para evitar que la fuente personalizada datos de transmisión repetida.
El siguiente código, código interpretado tiene Observaciones:

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();
    }

}

Código etiquetado frasco primer paquete, y luego se cargan en el archivo lib canal carpeta de
mis archivos de datos originales: /opt/module/flume/data/gaotie.txt
que se almacena en el archivo de offset: / opt / módulo / canal / datos / POSTFILE
mi archivo de configuración llamado conf: Flume-file-kafka2.conf
conf contenido del archivo de configuración se escribe:

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

A continuación, la primera puesta empleado del zoológico

bin/zkServer.sh start

reinicio kafka

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

Por último, instrucción de supervisión canal abierto

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

Los consumidores pueden abrir comando kafka

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

Para pasar de un tema específico de los contenidos, para detectar si el éxito de los datos transmitidos kafka también se puede encontrar directamente bajo kafka troncos tema de carpeta especificada archivo, introduzca la carpeta tema para ver los archivos que terminan en .log si existe datos

Ha publicado 189 artículos originales · ganado elogios 13 · vistas 10000 +

Supongo que te gusta

Origin blog.csdn.net/NewBeeMu/article/details/103059967
Recomendado
Clasificación