Spark实现ip地址查询


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y1pFyMj5-1604026846059)(22EB9C75784A49A78B59A903A199BE53)]

需求分析

在互联网中,我们经常会见到城市热点图这样的报表数据,例如在百度统计中,会统计今年的热门旅游城市、热门报考学校等,会将这样的信息显示在热点图中。我们根据每个用户的IP地址,与我们的IP地址段进行比较,确认每个IP落在哪一个IP端内,获取经纬度,然后绘制热力图

在这里插入图片描述

因此,我们需要通过日志信息(运行商或者网站自己生成)和城市ip段信息来判断用户的ip段,统计热点经纬度。

技术调研

因为我们的需求是完成一张报表信息,所以对程序的实时性没有要求,所以可以选择内存计算spark来实现上述功能。

创建mysql数据库表

创建mysql数据库表用于存储经纬度以及出现的次数

USE `spark`;

DROP TABLE IF EXISTS `iplocation`;

CREATE TABLE `iplocation` (
  `longitude` varchar(32) DEFAULT NULL,
  `latitude` varchar(32) DEFAULT NULL,
  `total_count` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

思路

1、加载城市ip段信息,获取ip起始数字和结束数字,经度,维度

2、加载日志数据,获取ip信息,然后转换为数字,和ip段比较

3、比较的时候采用二分法查找,找到对应的经度和维度

4、然后对经度和维度做单词计数

代码开发实现

package com.IP

import java.sql.{Connection, DriverManager, PreparedStatement}

import org.apache.spark.{SparkConf, SparkContext}

object IpLocation {
  //将IP地址转换为long类型的数据,便于后期二分查找
  def ipToLong(ip: String): Long = {
    val split: Array[String] = ip.split("\\.")
    var returnNum: Long = 0
    for (num <- split) {
      returnNum = num.toLong | returnNum << 8L
    }
    returnNum
  }

  //通过二分查找法,将ip转换为long的数字,通过二分查找法,查找ip落在哪一个经纬度范围内
  def binarySearch(longIp: Long, value: Array[(String, String, String, String)]): Int = {
    //定义二分查找的初始下标
    var start = 0;
    //定义二分查找的结束下标
    var end = value.length - 1
    //如果初始下标等于结束下标,查找结束,不等于则一直查找
    while (start <= end) {
      //定义中间下标,通过比较,确定初始与结束值是否与中间下标相等
      var middle = (start + end) / 2
      //如果ip大于初始值,小于结束值,直接返回,注意这里需要用到return
      if (longIp >= value(middle)._1.toLong && longIp <= value(middle)._2.toLong) {
        return middle
      }
      //如果ip小于中间值,ip位于左边
      if (longIp < value(middle)._1.toLong) {
        end = middle - 1
      }
      //如果ip大于中间值,ip位于右边
      if (longIp > value(middle)._2.toLong) {
        start = middle + 1
      }
    }
    //为了防止报错,直接返回一个数字
    -1
  }


  //将数据插入mysql表当中
  val data2Mysql = (result: Iterator[((String, String), Int)]) => {
    //创建连接,这里写法不规范
    val connection: Connection = DriverManager.getConnection("jdbc:mysql://192.168.72.130:3306/spark", "root", "My12345!")
    //获取PreparedStatement
    val prepareStatement: PreparedStatement = connection.prepareStatement("insert into iplocation(longitude,latitude,total_count) values (?,?,?)")
    // 迭代器通过foreach得到每一条数据
    result.foreach(x => {
      //通过prepareStatement将每一条数据插入数据库
      prepareStatement.setString(1, x._1._1)
      prepareStatement.setString(2, x._1._2)
      prepareStatement.setInt(3, x._2)
      prepareStatement.execute()
    })
    prepareStatement.close()
    connection.close()

  }

  def main(args: Array[String]): Unit = {
    val context = new SparkContext(new SparkConf().setMaster("local[2]").setAppName("countIpHeat"))
    context.setLogLevel("WARN")
    //获取ip字典数据
    val file = context.textFile("file:///G:\\A_实训前置1\\08spark\\城市热点假数据\\ip.txt")
    //进行切分,取出字典中四个字段,起始ip,结束ip以及经度,纬度
    val map = file.map(x => x.split("\\|")).map(x => (x(2), x(3), x(x.length - 2), x(x.length - 1)))
    //通过广播变量,将字典数据进行全局缓存
    val broadcast = context.broadcast(map.collect())
    //读取用户上网数据,获取ip
    val file2 = context.textFile("file:///G:\\A_实训前置1\\08spark\\城市热点假数据\\20090121000132.394251.http.format")
    val ipRdd = file2.map(x => x.split("\\|")(1))
    //ip数据调用mapPartitions 对每个分区的数据进行操作
    val partitions = ipRdd.mapPartitions(iter => {
      //获取广播变量的值
      val value: Array[(String, String, String, String)] = broadcast.value
      //迭代器调用map,取出每一个ip,然后获取ip对应的经度,纬度,封装成元组返回
      iter.map(ip => {
        val longIp: Long = ipToLong(ip)
        val resultIndex = binarySearch(longIp, value)
        ((value(resultIndex)._3, value(resultIndex)._4), 1)
      })
    })
    //统计每个经纬度里面出现的人数值
    val key = partitions.reduceByKey(_ + _)
    //将数据保存mysql数据库
    key.foreachPartition(data2Mysql)
    context.stop()

  }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WGS.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值