spark优化性能结构化流式Kafka

时间:2020-04-13 06:39:50

标签: apache-spark apache-kafka kafka-consumer-api spark-structured-streaming

用于测试应用程序的两台服务器(4core + 16GRAM)

我的想法是从kafka获取数据,使用多线程处理,并保存在Elasticsearch

spark-submit --class com.yizhisec.bigdata.TrafficEs 
--master yarn 
--deploy-mode cluster 
--executor-memory 512M 
--executor-cores 2 
--conf spark.streaming.concurrentJobs=5 
--num-executors 5 
--supervise bigdata-1.0.jar

enter image description here 但执行者中的仅一项任务 enter image description here

代码

我已经使用numPartitions来获取数据

spark.readStream().format("kafka")
                .option("kafka.bootstrap.servers", prop.getProperty("kafka.broker.list"))
                .option("kafka.ssl.truststore.location", prop.getProperty("kafka.jks.path"))
                .option("kafka.ssl.truststore.password", prop.getProperty("kafka.jks.passwd"))
                .option("kafka.security.protocol", "SSL")
                .option("kafka.ssl.endpoint.identification.algorithm", "")
                .option("startingOffsets", "earliest")
                .option("numPartitions", prop.getProperty("kafka.partition"))
                .option("subscribe", topic)
                .load()
                .selectExpr("CAST(topic AS STRING)", "CAST(value AS STRING)");

流程代码

Dataset<Traffic> df = StreamSparkUtils.steamToTraffic(rawDf);
String[] appProtoFilter = properties.getProperty("appproto").split(",");

Dataset<TrafficNode> nodeDataset = df
        .filter(df.col("appproto").isin(appProtoFilter))
        .map(new MapFunction<Traffic, TrafficNode>() {
            @Override
            public TrafficNode call(Traffic traffic) throws Exception {
                TrafficNode n = new TrafficNode();
                n.setDestport(traffic.getDestport());
                n.setSrcip(traffic.getSrcip());
                n.setDestip(traffic.getDestip());
                n.setAppproto(traffic.getAppproto());
                n.setEndtime(traffic.getEnd_time());
                return n;
            }
        }, Encoders.bean(TrafficNode.class));




StreamingQuery query = null;
try {
    query = StreamSparkUtils.streamSinkEs(nodeDataset, "loh_traffic");
    query.awaitTermination();
} catch (IOException | StreamingQueryException e) {
    e.printStackTrace();
}

如何优化它。

保存到es

public static StreamingQuery streamSinkEs(Dataset<?> dataSet, String index) throws IOException {
        Properties properties = readProp();
        return dataSet.writeStream()
                .option("es.nodes", properties.getProperty("es.nodes"))
                .option("es.port", properties.getProperty("es.port"))
                .option("checkpointLocation", properties.getProperty("es.checkpoint"))
                .format("es")
                .start(index);
    }

streamtoTraffic

    public static Dataset<Traffic> steamToTraffic(Dataset<Row> df) {
        if (df == null) {
            return null;
        }

        StructType trafficSchema = new StructType()
                .add("guid", DataTypes.LongType)
                ...
                .add("downsize", DataTypes.LongType);

        Dataset<Row> ds = df.select(functions.from_json(df.col("value").cast(DataTypes.StringType), trafficSchema).as("data")).select("data.*");
        return ds.as(ExpressionEncoder.javaBean(Traffic.class));
    }

1 个答案:

答案 0 :(得分:0)

从Kafka读取的应用程序会按主题中分区的数量进行扩展。只要您将数据存储在一个分区中,您的应用程序就只能使用一个使用者来获取数据。

在您的情况下,Spark应用程序中有多少执行程序无关紧要,因为只有一个执行程序可以从一个分区读取。这是因为在Kafka中,我们具有消费者组的概念。如果要提高性能,则应在Kafka主题中增加分区数。

以下是Kafa documentation的摘录,描述了消费者组和分区之间的交互:

通过在主题内具有并行性(即分区)的概念,Kafka能够在用户进程池中提供顺序保证和负载均衡。这是通过将主题中的分区分配给消费者组中的消费者来实现的,以便每个分区都由组中的一个消费者完全消费。通过这样做,我们确保使用者是该分区的唯一读取器,并按顺序使用数据。由于存在许多分区,因此仍然可以平衡许多使用者实例上的负载。但是请注意,使用者组中的使用者实例不能超过分区。