使用Maxmind Geo Data的Spark UDF

时间:2017-12-11 10:24:27

标签: scala apache-spark maxmind snowplow

我尝试使用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

有什么想法? 感谢

1 个答案:

答案 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"))