目录
提示:所有需要的文件我全部放在资源里面了,可以自行下载
概述
需求:根据访问日志的ip地址计算出访问者的归属地,并且按照省份,计算出访问次数,然后将计算好的结果写入到MySQL
解决方案流程:
1.整理数据,切分出ip字段,然后将ip地址转换成十进制
2.加载规则,整理规则,取出有用的字段,然后将数据缓存到内存中(Executor中的内存中)
3.将访问log与ip规则进行匹配(二分法查找)
4.取出对应的省份名称,然后将其和一组合在一起
5.按省份名进行聚合
6.将聚合后的数据写入到MySQL中
代码实现
单级模式
1.ip地址转换成十进制
def ip2Long(ip: String): Long = {
val fragments = ip.split("[.]")
var ipNum = 0L
for (i <- 0 until fragments.length){
ipNum = fragments(i).toLong | ipNum << 8L
}
ipNum
}
2.加载规则
def readRules(path: String): Array[(Long, Long, String)] = { //传一个路径,返回起始ip地址对应的十进制,结束ip地址对应的十进制,省份
val bf: BufferedSource = Source.fromFile(path) //读取ip规则
val lines: Iterator[String] = bf.getLines() //拿到规则的每一条
val rules: Array[(Long, Long, String)] = lines.map(line => { //对ip规则进行整理,并放入到内存
val fileds = line.split("[|]")
val startNum = fileds(2).toLong //返回起始ip地址对应的十进制
val endNum = fileds(3).toLong //结束ip地址对应的十进制
val province = fileds(6) // 省份
(startNum, endNum, province)
}).toArray
rules
}
3.二分法查找(折半查找)
def binarySearch(lines: Array[(Long, Long, String)], ip: Long) : Int = { //传入数组和ip地址对应的十进制,如果查到返回对应角标,没查到返回-1
var low = 0
var high = lines.length - 1
while (low <= high) {
val middle = (low + high) / 2
if ((ip >= lines(middle)._1) && (ip <= lines(middle)._2))
return middle
if (ip < lines(middle)._1)
high = middle - 1
else {
low = middle + 1
}
}
-1
}
拿到角标后可以在rules中查找对应的数据省份:province = rules(index)._3
分布式模式
方案一:
上面那种是单机模式,
我们需要把所有的方法的提取出来作为工具类,做成分布式面临的问题是规则的放置问题,因为我们所有的Executor端都需要拿到完全相同的规则,但此时规则在Driver端,我们的解决方案是用广播变量把规则广播到Executor端
object IpLoaction1 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("IpLo