【问题标题】:Storm Kafkaspout KryoSerialization issue for java bean from kafka topic来自kafka主题的java bean的Storm Kafkaspout KryoSerialization问题
【发布时间】:2016-11-16 05:30:55
【问题描述】:

您好,我是 Storm 和 Kafka 的新手。 我正在使用storm 1.0.1和kafka 0.10.0 我们有一个可以从 kafka 主题接收 java bean 的 kafkaspout。 我花了几个小时挖掘找到正确的方法。 找到了几篇有用的文章,但到目前为止没有一种方法对我有用。

以下是我的代码:

风暴拓扑:

public class StormTopology {

public static void main(String[] args) throws Exception {
    //Topo test /zkroot test
    if (args.length == 4) {
        System.out.println("started");
        BrokerHosts hosts = new ZkHosts("localhost:2181");

        SpoutConfig kafkaConf1 = new SpoutConfig(hosts, args[1], args[2],
                args[3]);

        kafkaConf1.zkRoot = args[2];
        kafkaConf1.useStartOffsetTimeIfOffsetOutOfRange = true;
        kafkaConf1.startOffsetTime = kafka.api.OffsetRequest.LatestTime();
        kafkaConf1.scheme = new SchemeAsMultiScheme(new KryoScheme());
        KafkaSpout kafkaSpout1 = new KafkaSpout(kafkaConf1);

        System.out.println("started");

        ShuffleBolt shuffleBolt = new ShuffleBolt(args[1]);
        AnalysisBolt analysisBolt = new AnalysisBolt(args[1]);
        TopologyBuilder topologyBuilder = new TopologyBuilder();
        topologyBuilder.setSpout("kafkaspout", kafkaSpout1, 1);
        //builder.setBolt("counterbolt2", countbolt2, 3).shuffleGrouping("kafkaspout");
        //This is for field grouping in bolt we need two bolt for field grouping or it wont work
        topologyBuilder.setBolt("shuffleBolt", shuffleBolt, 3).shuffleGrouping("kafkaspout");
        topologyBuilder.setBolt("analysisBolt", analysisBolt, 5).fieldsGrouping("shuffleBolt", new Fields("trip"));
        Config config = new Config();
        config.registerSerialization(VehicleTrip.class, VehicleTripKyroSerializer.class);
        config.setDebug(true);
        config.setNumWorkers(1);

        LocalCluster cluster = new LocalCluster();
        cluster.submitTopology(args[0], config, topologyBuilder.createTopology());

        // StormSubmitter.submitTopology(args[0], config,
        // builder.createTopology());

    } else {
        System.out
                .println("Insufficent Arguements - topologyName kafkaTopic ZKRoot ID");
    }
}

}

我正在使用 kryo 序列化 kafka 中的数据

Kafka制片人:

public class StreamKafkaProducer {

private static Producer producer;
private final Properties props = new Properties();
private static final StreamKafkaProducer KAFKA_PRODUCER = new StreamKafkaProducer();

private StreamKafkaProducer(){
    props.put("bootstrap.servers", "localhost:9092");
    props.put("acks", "all");
    props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
    props.put("value.serializer", "com.abc.serializer.MySerializer");
    producer = new org.apache.kafka.clients.producer.KafkaProducer(props);
}

public static StreamKafkaProducer getStreamKafkaProducer(){
    return KAFKA_PRODUCER;
}

public void produce(String topic, VehicleTrip vehicleTrip){
    ProducerRecord<String,VehicleTrip> producerRecord = new ProducerRecord<>(topic,vehicleTrip);
    producer.send(producerRecord);
    //producer.close();
}

public static void closeProducer(){
    producer.close();
}

}

Kyro 序列化器:

public class DataKyroSerializer extends Serializer<Data> implements Serializable {
@Override
public void write(Kryo kryo, Output output, VehicleTrip vehicleTrip) {
    output.writeLong(data.getStartedOn().getTime());
    output.writeLong(data.getEndedOn().getTime());
}

@Override
public Data read(Kryo kryo, Input input, Class<VehicleTrip> aClass) {
    Data data = new Data();
    data.setStartedOn(new Date(input.readLong()));
    data.setEndedOn(new Date(input.readLong()));
    return data;
}

我需要将数据返回到 Data bean。

根据几篇文章,我需要提供自定义方案并使其成为拓扑的一部分,但到目前为止我还没有运气

螺栓和方案代码

方案:

public class KryoScheme implements Scheme {

    private ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
            kryo.addDefaultSerializer(Data.class, new DataKyroSerializer());
            return kryo;
        };
    };

    @Override
    public List<Object> deserialize(ByteBuffer ser) {
        return Utils.tuple(kryos.get().readObject(new ByteBufferInput(ser.array()), Data.class));
    }

    @Override
    public Fields getOutputFields( ) {
        return new Fields( "data" );
    }

}

和螺栓:

public class AnalysisBolt implements IBasicBolt {
/**
 *
 */
private static final long serialVersionUID = 1L;
private String topicname = null;

public AnalysisBolt(String topicname) {
    this.topicname = topicname;
}

public void prepare(Map stormConf, TopologyContext topologyContext) {
    System.out.println("prepare");
}

public void execute(Tuple input, BasicOutputCollector collector) {
    System.out.println("execute");

    Fields fields = input.getFields();
    try {   

        JSONObject eventJson = (JSONObject) JSONSerializer.toJSON((String) input
                .getValueByField(fields.get(1)));
        String StartTime = (String) eventJson.get("startedOn");
        String EndTime = (String) eventJson.get("endedOn");
        String Oid = (String) eventJson.get("_id");
        int V_id =  (Integer) eventJson.get("vehicleId");
        //call method getEventForVehicleWithinTime(Long vehicleId, Date startTime, Date endTime)

        System.out.println("==========="+Oid+"| "+V_id+"| "+StartTime+"| "+EndTime);

} catch (Exception e) {
    e.printStackTrace();

}

}

但如果我提交风暴拓扑我会收到错误:

java.lang.IllegalStateException: Spout 'kafkaspout' contains a
non-serializable field of type com.abc.topology.KryoScheme$1, which
was instantiated prior to topology creation.
com.minda.iconnect.topology.KryoScheme$1 should be instantiated within
the prepare method of 'kafkaspout at the earliest.

感谢帮助调试问题并指导正确路径。

谢谢

【问题讨论】:

    标签: serialization apache-kafka apache-storm kryo


    【解决方案1】:

    您的 ThreadLocal 不可序列化。更可取的解决方案是使您的序列化程序既可序列化又可线程安全。如果这是不可能的,那么我会看到 2 个替代方案,因为没有准备方法,因为你会得到一个螺栓。

    1. 将其声明为静态的,它本质上是瞬态的。
    2. 将其声明为瞬态并通过私有 get 方法访问它。然后您可以在首次访问时初始化该变量。

    【讨论】:

      【解决方案2】:

      在 Storm 生命周期内,拓扑被实例化,然后序列化为字节格式以存储在 ZooKeeper 中,然后再执行拓扑。在此步骤中,如果拓扑中的 spout 或 bolt 具有已初始化的不可序列化属性,则序列化将失败。

      如果需要不可序列化的字段,请在 bolt 或 spout 的 prepare 方法中对其进行初始化,该方法在拓扑传递给 worker 后运行。

      来源: Best Practices for implementing Apache Storm

      【讨论】:

        猜你喜欢
        • 2021-03-20
        • 1970-01-01
        • 2017-10-25
        • 2017-10-19
        • 1970-01-01
        • 2016-11-23
        • 2016-06-29
        • 2016-04-04
        • 1970-01-01
        相关资源
        最近更新 更多