Spark Streaming和ElasticSearch - 无法写入所有条目

时间:2015-04-24 09:35:12

标签: elasticsearch apache-kafka spark-streaming

我目前正在编写一个由制作人和消费者组成的Scala应用程序。制作人从外部源获取一些数据并在Kafka中写入em。消费者从Kafka读取并写信给Elasticsearch。

消费者基于Spark Streaming,每隔5秒从Kafka获取新消息并将其写入ElasticSearch。问题是我无法写入ES,因为我收到了很多错误,如下所示:

  

ERROR] [2015-04-24 11:21:14,734] [org.apache.spark.TaskContextImpl]:   TaskCompletionListener中的错误   org.elasticsearch.hadoop.EsHadoopException:无法全部写入   条目[3/26560](也许ES超载?)。拯救......在   org.elasticsearch.hadoop.rest.RestRepository.flush(RestRepository.java:225)   〜[elasticsearch-spark_2.10-2.1.0.Beta3.jar:2.1.0.Beta3] at   org.elasticsearch.hadoop.rest.RestRepository.close(RestRepository.java:236)   〜[elasticsearch-spark_2.10-2.1.0.Beta3.jar:2.1.0.Beta3] at   org.elasticsearch.hadoop.rest.RestService $ PartitionWriter.close(RestService.java:125)   〜[elasticsearch-spark_2.10-2.1.0.Beta3.jar:2.1.0.Beta3] at   org.elasticsearch.spark.rdd.EsRDDWriter $$ anonfun $写$ 1.适用$ MCV $ SP(EsRDDWriter.scala:33)   〜[elasticsearch-spark_2.10-2.1.0.Beta3.jar:2.1.0.Beta3] at   org.apache.spark.TaskContextImpl $$匿名$ 2.onTaskCompletion(TaskContextImpl.scala:57)   〜[spark-core_2.10-1.2.1.jar:1.2.1] at   org.apache.spark.TaskContextImpl $$ anonfun $ markTaskCompleted $ 1.适用(TaskContextImpl.scala:68)   [spark-core_2.10-1.2.1.jar:1.2.1] at   org.apache.spark.TaskContextImpl $$ anonfun $ markTaskCompleted $ 1.适用(TaskContextImpl.scala:66)   [spark-core_2.10-1.2.1.jar:1.2.1] at   scala.collection.mutable.ResizableArray $ class.foreach(ResizableArray.scala:59)   [na:na] at   scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:47)   [na:na] at   org.apache.spark.TaskContextImpl.markTaskCompleted(TaskContextImpl.scala:66)   [spark-core_2.10-1.2.1.jar:1.2.1] at   org.apache.spark.scheduler.Task.run(Task.scala:58)   [spark-core_2.10-1.2.1.jar:1.2.1] at   org.apache.spark.executor.Executor $ TaskRunner.run(Executor.scala:200)   [spark-core_2.10-1.2.1.jar:1.2.1] at   java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)   [na:1.7.0_65] at   java.util.concurrent.ThreadPoolExecutor中的$ Worker.run(ThreadPoolExecutor.java:615)   [na:1.7.0_65] at java.lang.Thread.run(Thread.java:745)[na:1.7.0_65]

考虑到制作人每15秒写一次6条消息,所以我真的不明白这是怎么过多的#34;可能会发生(我甚至清理了主题并刷新了所有旧消息,我认为它与偏移问题有关)。 Spark Streaming每5秒执行一次的任务可以通过以下代码汇总:

  val result = KafkaUtils.createStream[String, Array[Byte], StringDecoder, DefaultDecoder](ssc, kafkaParams, Map("wasp.raw" -> 1), StorageLevel.MEMORY_ONLY_SER_2)
  val convertedResult = result.map(k => (k._1 ,AvroToJsonUtil.avroToJson(k._2)))



  //TO-DO : Remove resource (yahoo/yahoo) hardcoded parameter
  log.info(s"*** EXECUTING SPARK STREAMING TASK  + ${java.lang.System.currentTimeMillis()}***")


  convertedResult.foreachRDD(rdd => {
      rdd.map(data => data._2).saveToEs("yahoo/yahoo", Map("es.input.json" -> "true"))

  })

如果我尝试打印消息而不是发送到ES,一切都很好,我实际上只看到6条消息。为什么我不能写ES?

为了完整起见,我使用此库写入ES:elasticsearch-spark_2.10以及最新的测试版。

6 个答案:

答案 0 :(得分:3)

经过多次重试后,我发现了一种写入ElasticSearch而不会出现任何错误的方法。基本上将参数"es.batch.size.entries" -> "1"传递给saveToES方法解决了这个问题。我不明白为什么使用默认或任何其他批量大小会导致上述错误,因为如果我尝试编写的内容超过允许的最大批量大小而不是更少,我会发出错误消息。

此外,我注意到实际上我写的是ES而不是我的所有消息,每批丢失1到3条消息。

答案 1 :(得分:2)

当我在Spark上将数据帧推送到ES时,我收到了相同的错误消息。即使使用"es.batch.size.entries" -> "1"配置,我也有同样的错误。 一旦我增加了ES中的线程池,我就可以解决这个问题。

例如,

批量池

threadpool.bulk.type: fixed
threadpool.bulk.size: 600
threadpool.bulk.queue_size: 30000

答案 2 :(得分:2)

就像这里已经提到的那样,这是文档写入冲突

您的convertedResult数据流包含多个具有相同ID的记录。当作为同一批次的一部分写入Elastic时,会产生上述错误。

可能的解决方案:

  1. 为每个记录生成唯一的ID。根据您的用例,可以通过几种不同的方式来完成。例如,一种常见的解决方案是通过组合 id lastModifiedDate 字段来创建一个新字段,并在写入Elastic时将该字段用作ID。
  2. 根据ID对记录执行重复数据删除-仅选择一个具有特定ID的记录,并丢弃其他重复数据。根据您的用例,这可能是最新记录(基于时间戳字段),最完整(大多数字段包含数据)等。

第一解决方案将存储您在流中收到的所有记录

第二种解决方案将根据重复数据删除逻辑仅存储特定标识的唯一记录。此结果与设置"es.batch.size.entries" -> "1"相同,除了不会通过一次写入一条记录来限制性能。

答案 3 :(得分:1)

其中一种可能性是群集/分片状态为RED。请解决此问题,这可能是由于未分配的副本。状态变为绿色后,API调用就成功了。

答案 4 :(得分:0)

这是一份文件写冲突

例如:
多个文档指定要使用的Elasticsearch的相同_id 这些文档位于不同的分区 Spark将多个分区写入ES 同时

结果是 Elasticsearch一次接收单个文档的多个更新 - 来自多个来源/通过多个节点/包含不同的数据

  

"我每批失去1到3条消息。"

  • 批量大小时的失败次数> 1
  • 批量写入大小" 1"

答案 5 :(得分:0)

只需添加此错误的另一个可能原因,希望对您有所帮助。 如果您的Elasticsearch索引包含子文档,则:

  1. 如果您使用的是自定义路由字段(不是_id),则根据 文档不能保证文档的唯一性。 从spark更新时,这可能会导致问题。
  2. 如果使用标准_id,则将保留唯一性,但是从Spark写入Elasticsearch时,需要确保提供了以下选项:
    • es.mapping.join
    • es.mapping.routing