文章目录
1. pom
(1). 依赖
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-core -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch-spark-20 -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch-spark-20_2.11</artifactId>
<version>7.11.1</version>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
<scope>compile</scope>
</dependency>
(2). 仓库
<repositories>
<repository>
<id>sonatype-oss</id>
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
2. 问题一 mapping 映射问题
(1). 异常详情
使用spark读取es7 时因为es 中mapping映射的数据类型是Date导致的bug
org.elasticsearch.hadoop.rest.EsHadoopParsingException: Cannot parse value [2021/03/06 18:47:27] for field [time]
Caused by: java.lang.IllegalArgumentException: Invalid format: "2021/03/06 18:47:27" is malformed at "/03/06 18:47:27"
Caused by: org.elasticsearch.hadoop.EsHadoopIllegalArgumentException: Cannot invoke method public org.joda.time.DateTime org.joda.time.format.DateTimeFormatter.parseDateTime(java.lang.String)
(2). 解决方法
//需在代码中添加如下配置
"es.mapping.date.rich" -> "false"
(3). 代码示例
//通过以下方式设置或者map形式都可
val conf: SparkConf = new SparkConf()
.set("es.mapping.date.rich","false")
3. 问题二 使用dataframe读取es失败
(1). 异常详情
使用sparksql 读取ES 数据时出现的异常
错误代码如下:
//设置spark的一些参数
val conf: SparkConf = new SparkConf()
.setAppName("KafkaToES")
.setMaster("local[*]")
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
.set("spark.executor.processTreeMetrics.enabled", "false")
//创建sparkseesion 对象
val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
//es的一些配置
val esMapConf: Map[String, String] = Map(
//配置节点信息
"es.nodes" -> "ip",
"es.port" -> "9200",
//配置es的索引
"es.resource" -> "es索引",
"es.mapping.date.rich" -> "false",
"es.read.metadata.field" -> "_id",
"es.nodes.wan.only" -> "false"
//配置用户名和密码
//"es.net.http.auth.user" -> "用户名",
//"es.net.http.auth.pass" -> "密码"
)
//读取数据
val esData: DataFrame = EsSparkSQL.esDF(spark, esMapConf)
//输出一下
esData.show(5)
Caused by: org.elasticsearch.hadoop.EsHadoopIllegalStateException: Position for '字段名(x.x的格式)' not found in row; typically this is caused by a mapping inconsistency
(2). 解决方法
改用EsSpark 去读取而非EsSparkSQL
//以map的形式
val value: RDD[(String, collection.Map[String, AnyRef])] = EsSpark.esRDD(sc, esMapConf)
//以json的格式读取
val value1: RDD[(String, String)] = EsSpark.esJsonRDD(sc, esMapConf)
(3). 代码示例
//设置spark的一些参数
val conf: SparkConf = new SparkConf()
.setAppName("KafkaToES")
.setMaster("local[*]")
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
.set("spark.executor.processTreeMetrics.enabled", "false")
//创建sparksession 对象
val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
//创建sparkcontext 对象
val sc: SparkContext = spark.sparkContext
//es的一些配置
val esMapConf: Map[String, String] = Map(
//配置节点信息
"es.nodes" -> "ip",
"es.port" -> "9200",
//配置es的索引
"es.resource" -> "es索引",
"es.mapping.date.rich" -> "false",
"es.read.metadata.field" -> "_id",
"es.nodes.wan.only" -> "false"
//配置用户名和密码
//"es.net.http.auth.user" -> "用户名",
//"es.net.http.auth.pass" -> "密码"
)
//以map的形式读取
val esData: RDD[(String, collection.Map[String, AnyRef])] = EsSpark.esRDD(sc, esMapConf)
//输入10条
esData.take(10).foreach(println(_))
//以json的格式读取
val esData2: RDD[(String, String)] = EsSpark.esJsonRDD(sc, esMapConf)
//输入10条
esData2.take(10).foreach(println(_))
4. 问题三 使用EsSpark的esRDD 方法读取后值的获取
(1). 异常详情
使用 EsSpark 的 esRDD 读取数据后返回的是嵌套map
因此获取具体值时需要不断的转类型, 并且不能用get 获取值,否则会爆异常,只能通过 getOrElse 或者直接 map(索引)的方式进行获取。
(2). 解决
def main(args: Array[String]): Unit = {
val map: Map[String, AnyRef] = Map(
"aaa" -> Map("bbb" -> "ccc"),
"ddd" -> Map("eee" -> Map("fff" -> "ggg")),
"name" -> "zhang"
)
//方式一
val map1: Map[String, AnyRef] = map("ddd").asInstanceOf[Map[String, AnyRef]]
val map2: Map[String, AnyRef] = map1("eee").asInstanceOf[Map[String, AnyRef]]
println(map2)
//方式二
val map3: Map[String, AnyRef] = map.getOrElse("ddd",null).asInstanceOf[Map[String, AnyRef]]
println(map3)
//方式三报错
//val map4: Map[String, AnyRef] = map.get("ddd").asInstanceOf[Map[String, AnyRef]]
//println(map4)
}
5. 问题四 该错误是基于问题三出现的类型转换异常
(1). 错误详情及解决方式
使用 EsSpark 的 esRDD 读取map数据后, 返回的是
可变
map 类型。
因此在使用asInstanceOf 进行类型强转的时候需要注意强转后也必须是可变
Map类型 。
6. 问题五 关于ES数据写入问题
以下几点 flink 类似
- ES 只支持 json 和 map 两种方式进行数据的写入
- 在进行ES 数据写入时, 对日期数据最好先进行mapping映射如过需要自动创建索引建议在kibana 的 index management 中设置index Templates.
- ES 写入数据如果想要实现 es 的ecs 命名规范, 必须使用嵌套map 或者嵌套json 的方式, 例如: 在kibana中显示的字段名为
user.name
, 字段值为张三
, 那么你写入的数据格式必须是Map("user" -> Map("name" -> "张三"))
, json 类似。
7. 最后
- 暂时想到的只有这么多, 后续想起来再补充。
- 如果有遇到相关的问题, 欢迎在评论区留言探讨。