spark_note04_ip地址查询热力图源数据

ip地址查询

1. 需求分析

​ 在互联网中,我们经常会见到城市热点图这样的报表数据,例如在百度统计中,会统计今年的热门旅游城市、热门报考学校等,会将这样的信息显示在热点图中。

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

2. 技术调研

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

3. 架构设计

搭建spark集群

4. 开发流程

4.1. 数据准备

ip日志信息

在ip日志信息中,我们只需要关心ip这一个维度就可以了,其他的不做介绍

在这里插入图片描述

城市ip段信息

在这里插入图片描述

4.2. 代码开发

思路

1、 加载日志数据,获取ip信息

2、 加载ip段信息,获取ip起始和结束数字,城市信息

3、 将日志的ip分割出来,转换为数字,和ip段比较

4、 比较的时候采用二分法查找

5、 过滤出相应的值

6、存到mysql node03数据库

7、之后就可以从mysql中取数据 做热点图啦

实现

 <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.32</version>
</dependency>
package cn.itcast.rdd

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

import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 *需求:
 * 通过日志信息(运行商或者网站自己生成)和城市ip段信息来判断用户的ip段,统计热点经纬度。
 *
 *
 * 思路
 * 1、	加载日志数据,获取ip信息
 * 2、	加载ip段信息,获取ip起始和结束数字,城市信息
 * 3、	将日志的ip分割出来,转换为数字,和ip段比较
 * 4、	比较的时候采用二分法查找
 * 5、	过滤出相应的值
 *
 * 需添加一个mysql驱动依赖包
 * 
 *
 * 需先建好数据库表
 * create table `iplocation`( `longitude` varchar(20), `latitude` VARCHAR(20), `total_count` bigint )
 */

//todo:利用spark来实现IP地址查询

object IpLocation {

  //实现把String类型的ip转换成Long 192.168.200.100
  def ip2Long(ip:String):Long={
    val ips: Array[String] = ip.split("\\.")
    var ipNum:Long=0L //初始

    //遍历
    for(i <- ips){
      ipNum = i.toLong | ipNum << 8L  // 或异 左移8位 //没有很懂
    }
    ipNum

  }

  //二分查找法
  def binarySearch(ipNum:Long,broadcastValue:Array[(String,String,String,String)]):Int={
    //定义开始下标
    var start = 0 //可变
    //定义结束下标
    var end = broadcastValue.length - 1

    //遍历
    while(start<end){
      //获取中间下标
      val middle=(start+end)/2

      if(ipNum >= broadcastValue(middle)._1.toLong && ipNum <= broadcastValue(middle)._2.toLong){ //表示在这个区间里面
        return middle
      }

      if(ipNum < broadcastValue(middle)._1.toLong){
        end = middle
      }

      if(ipNum>broadcastValue(middle)._2.toLong){
        start=middle

      }
    }
    -1 //如果没有找到这个下标 直接返回-1

  }

  //定义一个方法 把结果数据写入到mysql数据库中
  def data2mysql(iter: Iterator[((String, String), Int)]) : Unit = {  //ctrl+N后还是改过的
    /**
     * 每次以分区为单位 建立mysal连接
     */
    //定义数据库连接
    var conn:Connection=null
    //定义PrepareStatement
    var ps:PreparedStatement=null

    //定义sql语句
    val sql = "insert into iplocation(longitude,latitude,total_count) values(?,?,?)"

    conn = DriverManager.getConnection("jdbc:mysql://192.168.239.133:3306/spark","root","123456")
    ps=conn.prepareStatement(sql)

    //遍历iter
    // ctrl+alt+t try-catch-finally
    try {
      iter.foreach(line => {
        //给每一个问号赋值
        ps.setString(1, line._1._1) //元组的第一位还是个元组 再取这个元组的第一位
        ps.setString(2, line._1._1)
        ps.setLong(3, line._2)
        //执行
        ps.execute()
      })
    } catch {
      //抛异常
      case  e:Exception => println(e)
    } finally {
      //关闭连接
      if(ps != null) {
        ps.close()
      }
      if(conn != null){
        conn.close()
      }
    }

  }

  def main(args: Array[String]): Unit = {
    //1.创建SparkConf
    val sparkConf: SparkConf = new SparkConf().setAppName("IpLocation").setMaster("local[2]")

    //2.创建SparkContent
    val sc = new SparkContext(sparkConf)
    sc.setLogLevel("warn")

    //3.读取城市的ip段信息 获取(ip开始数字、ip结束数字、经度、维度)
    val city_ip_rdd: RDD[(String, String, String, String)] = sc.textFile("F:\\AAAA_HM大数据\\00-课件\\18_spark\\spark\\day02\\资料\\服务器访问日志根据ip地址查找区域\\ip.txt").map(_.split("\\|")).map(x=>(x(2),x(3),x(x.length-2),x(x.length-1)))
    //利用广播变量把数据下发到每个worker节点
    val cityIpBroadcast: Broadcast[Array[(String, String, String, String)]] = sc.broadcast(city_ip_rdd.collect())

    //4.读取运营商日志数据,获取所有的ip地址
    val ips: RDD[String] = sc.textFile("F:\\AAAA_HM大数据\\00-课件\\18_spark\\spark\\day02\\资料\\服务器访问日志根据ip地址查找区域\\20090121000132.394251.http.format").map(_.split("\\|")(1))

    //5.遍历ips 获取每一个ip地址,然后把ip地址转换成Long数字,进行比较,获取这个ip对应的数字在数组中的下标
    val result: RDD[((String, String), Int)] = ips.mapPartitions(iter => {
      /**
       * 先写的中间这块 最后ctrl+alt+v 用result来接收
       */
      //获取广播变量值
      val boradcastValue: Array[(String, String, String, String)] = cityIpBroadcast.value

      //遍历迭代器 获取每一个ip地址
      iter.map(ip => {
        //将ip地址转换成数字Long类型
        val ipNum: Long = ip2Long(ip) //117.101.201.237

        //拿到ipNum去广播变量的值中匹配,获取对应的下标(二分查找法)
        val index: Int = binarySearch(ipNum, boradcastValue)

        //按照下标获取经度纬度
        val value: (String, String, String, String) = boradcastValue(index)

        //返回((经度,纬度),1)
        ((value._3, value._4), 1)
      })
    })

    //6.相同经度和纬度出现的1累加
    val finalResult: RDD[((String, String), Int)] = result.reduceByKey(_+_)

    //7.打印输出
    finalResult.foreach(println) //是个action

    //把结果数据写入到mysql中
    finalResult.foreachPartition(data2mysql)

    //8.关闭
    sc.stop()

  }

}

/*
打印输出
((经纬度区间),ip数)

((108.948024,34.263161),1824)
((107.7601,29.32548),85)
((106.504962,29.533155),400)
((107.39007,29.70292),47)
((106.56347,29.52311),3)
((116.405285,39.904989),1535)
((106.27633,29.97227),36)
((114.502461,38.045474),383)
((107.08166,29.85359),29)
((102.712251,25.040609),126)
((106.57434,29.60658),177)
((106.51107,29.50197),91)
 */
 

test

import cn.itcast.rdd.IpLocation

/**
 * 1.0.1.0|1.0.3.255|16777472|16778239|亚洲|中国|福建|福州||电信|350100|China|CN|119.306239|26.075302
 */
object IPTest {
  def main(args: Array[String]): Unit = {
    val ipNum: Long = IpLocation.ip2Long("1.0.3.255")
    println(ipNum)
  }
}

/*
16778239
 */

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值