我尝试使用Maxmind snowplow library在数据框中的每个IP上提取地理数据。
我们正在使用Spark SQL(spark版本2.1.0),我在下面的类中创建了一个UDF:
class UdfDefinitions @Inject() extends Serializable with StrictLogging {
sparkSession.sparkContext.addFile("s3n://s3-maxmind-db/latest/GeoIPCity.dat")
val s3Config = configuration.databases.dataWarehouse.s3
val lruCacheConst = 20000
val ipLookups = IpLookups(geoFile = Some(SparkFiles.get(s3Config.geoIPFileName) ),
ispFile = None, orgFile = None, domainFile = None, memCache = false, lruCache = lruCacheConst)
def lookupIP(ip: String): LookupIPResult = {
val loc: Option[IpLocation] = ipLookups.getFile.performLookups(ip)._1
loc match {
case None => LookupIPResult("", "", "")
case Some(x) => LookupIPResult(Option(x.countryName).getOrElse(""),
x.city.getOrElse(""), x.regionName.getOrElse(""))
}
}
val lookupIPUDF: UserDefinedFunction = udf(lookupIP _)
}
目的是在UDF外部创建指向文件(ipLookups)的指针并在其中使用,因此不要在每行上打开文件。这会得到一个没有序列化的任务错误,当我们在UDF中使用addFiles时,我们得到太多的文件打开错误(当使用大型数据集时,在小数据集上它确实有效)。
该线程展示了如何使用RDD解决问题,但我们想使用Spark SQL。 using maxmind geoip in spark serialized
有什么想法? 感谢
答案 0 :(得分:0)
这里的问题是IpLookups不是Serializable。然而它从静态文件(我收集的内容)中进行查找,因此您应该能够解决这个问题。我建议您克隆repo并使IpLookups Serializable。然后,为了使它与spark SQL一起工作,将所有内容包装在类中。在主要的spark工作中,你可以写下如下内容:
val IPResolver = new MySerializableIpResolver()
val resolveIP = udf((ip : String) => IPResolver.resolve(ip))
data.withColumn("Result", resolveIP($"IP"))
如果您没有那么多不同的IP地址,还有另一种解决方案:您可以在驱动程序中执行所有操作。
val ipMap = data.select("IP").distinct.collect
.map(/* calls to the non serializable IpLookups but that's ok, we are in the driver*/)
.toMap
val resolveIP = udf((ip : String) => ipMap(ip))
data.withColumn("Result", resolveIP($"IP"))