**spark连接Redis的时候报错: **
Caused by: java.io.NotSerializableException: io.jimdb.JimdbNewCluster$$Enhanced
Serialization stack:
刚开始还以为是spark连接redis过程中无法序列化的问题,尝试了很多种方法。
也借鉴过这位老哥写的博客:
https://blog.csdn.net/qq_28743951/article/details/86599918
但是好像都没有用!!!
代码如下:
featureData.rdd.foreach{
s =>
redisDataCount += 1L
val jda = s.getAs[String]("did")
val redisKey = redisKeys + jda
val redisValue = s.getAs[String]("feature")
redisCli.set(redisKey, redisValue) //连接redis
redisCli.expire(redisKey, 4, TimeUnit.HOURS) //设置超时时间
}
发现问题:
foreachRDD是基于rdd的一个操作,行为表现与我们在计算操作的部分基本是一致的,这也意味着一个很经典的情况:rdd是分布式的运算。它需要在在不同的机器上运行相同的程序。假设我们给这个函数传递的函数里直接创建redis连接的话,会出现一个序列化错误,因为它需要把这个连接对象序列化之后发送给计算程序,这点无法实现。
解决办法:
那这里,发现问题也就比较好解决了,我们把数据拉回到driver上执行就可以了。
featureData.rdd.collect().foreach{
s =>
redisDataCount += 1L
val jda = s.getAs[String]("did")
val redisKey = redisKeys + jda
val redisValue = s.getAs[String]("feature")
redisCli.set(redisKey, redisValue) //连接redis
redisCli.expire(redisKey, 4, TimeUnit.HOURS) //设置超时时间
但是如果数据量太大的话,放在driver也是不行的,这里我参考了一位大佬的方法:
利用rdd的分布式特性,rdd使用的也是经典的分区计算模型,每个节点计算的部分数据处于分区中,一般来讲,对于这一个分区的数据,我们建立一个连接对象已经是可以接受的了。
代码如下:
featureData.rdd.foreachPartition{
partitionOfRecords =>
val redis = new GetClientJimKV(newJimUrl, host)
val redisCli = redis.getJimClient
partitionOfRecords.take(10)
partitionOfRecords.foreach{
s =>
val jda = s.getAs[String]("did")
val redisKey = redisKeys + jda
val redisValue = s.getAs[String]("feature")
redisCli.set(redisKey, redisValue)
redisCli.expire(redisKey, 4, TimeUnit.HOURS)
}
}
参考:
https://juejin.cn/post/7031197529500991518