Linkedin Camus的使用

Preface

在实现Lambda架构的时候,我以Kafka作为系统的输入,同时需要将数据批量从Kafka导入到HDFS存储起来,以备Batch layer批处理计算。

而从Kafka到HDFS的数据传输,Linkedin已经有一个开源的项目,即Linkedin Camus。Camus是Kafka到HDFS的管道,它实际上是向Hadoop提交一个作业,并从Kafka获取指定topic的消息,存储到HDFS中。

实际上在使用Camus的时候,只有3件事是我们关注的。

  1. 确定MessageDecoder
  2. 确定RecordWriterProvider
  3. 确定Kafka话题

MessageDecoder

MessageDecoder 是对Kafka的消息进行解析的解析器,比如Camus自带了几个Decoder:KafkaAvroMessageDecoder,JsonStringMessageDecoder等。 
我们也可以自行设计符合自己业务需求的Decoder,而消息解析后的内容,会作为输出后期被存储到HDFS(当然还会加上压缩)

我在系统中实现了一个简单的字符串解析器,基本保持消息原文,也就是我只是要直接存储消息原文即可:

package com.linkedin.camus.etl.kafka.coders;

import com.linkedin.camus.coders.CamusWrapper;
import com.linkedin.camus.coders.MessageDecoder;
import org.apache.log4j.Logger;
import java.util.Properties;


/**
 * MessageDecoder class that will convert the payload into a String object,
 * System.currentTimeMillis() will be used to set CamusWrapper's
 * timestamp property

 * This MessageDecoder returns a CamusWrapper that works with Strings payloads,
 */
public class StringMessageDecoder extends MessageDecoder[], String> {
  private static final Logger log = Logger.getLogger(StringMessageDecoder.class);

  @Override
  public void init(Properties props, String topicName) {
    this.props = props;
    this.topicName = topicName;
  }

  @Override
  public CamusWrapper<String> decode(byte[] payload) {
    long timestamp = 0;
    String payloadString;

    payloadString = new String(payload);
    timestamp = System.currentTimeMillis();

    return new CamusWrapper<String>(payloadString, timestamp);
  }
}

简单说一下,该类继承于com.linkedin.camus.coders.MessageDecoder,输入的类型为byte[],解析后的类型为String。重点是实现decode方法,返回一个CamusWrapper对象,该对象包含了解析后的数据。

RecordWriterProvider

相比MessageDecoder在Camus的输入端发挥作用,RecordWriterProvider则在输出端发挥作用,确定Camus如何把数据写到HDFS中。

Camus同样有实现好的,比如AvroRecordWriterProvider,这也是Camus默认的,因为Camus默认是支持Avro格式,而我只需要纯文本即可,所以我使用的是同样Camus自带的com.linkedin.camus.etl.kafka.common.StringRecordWriterProvider,代码在Camus源码中可以找到。

既然是RecordWriter的Provider,其核心功能当然是提供一个RecordWriter,这是Hadoop的一个类,规定了Job如何输出到文件系统的,只要实现其中的write方法即可。

另外,关于输出还有一个压缩的问题,比如在StringRecordWriterProvider中根据配置文件确定是否压缩以及压缩算法,并确定输出时的行为,部分代码如下:

isCompressed = FileOutputFormat.getCompressOutput(context);

if (isCompressed) {
  Class codecClass = null;
  if ("snappy".equals(EtlMultiOutputFormat.getEtlOutputCodec(context))) {
    codecClass = SnappyCodec.class;
  } else if ("gzip".equals((EtlMultiOutputFormat.getEtlOutputCodec(context)))) {
    codecClass = GzipCodec.class;
  } else {
    codecClass = DefaultCodec.class;
  }
  codec = ReflectionUtils.newInstance(codecClass, conf);
  extension = codec.getDefaultExtension();
}

以及:

FileSystem fs = path.getFileSystem(context.getConfiguration());
if (!isCompressed) {
  FSDataOutputStream fileOut = fs.create(path, false);
  return new ByteRecordWriter(fileOut, recordDelimiter);
} else {
  FSDataOutputStream fileOut = fs.create(path, false);
  return new ByteRecordWriter(new DataOutputStream(codec.createOutputStream(fileOut)), recordDelimiter);
}

配置

以上说到的内容,都是通过配置文件在构建的时候整合到camus中。

camus.message.decoder.class 用来配置MessageDecoder

etl.record.writer.provider.class 用来配置RecordWriterProvider

关于压缩,可以参照下面配置:

mapred.output.compress=true     #告诉Hadoop需不需要压缩
etl.output.codec=deflate        #配置压缩算法为deflate
etl.deflate.level=6

另外,配置一些HDFS路径

etl.destination.path 是文件最终输出的文件夹 
etl.execution.base.path 和 etl.execution.history.path分别是Camus运行信息临时存放和已完成的Job的运行信息存放的地方

kafka.whitelist.topicskafka.blacklist.topics分别是Kafka话题的白名单和黑名单,将要订阅的topics以逗号分隔的形式赋值给whitelist即可。

你可能感兴趣的:(大数据)