通过ip规则 匹配业务数据的地址(经纬度或省市 城市) 用到广播变量,并将规则写入到mysql中

通过ip规则 匹配业务数据的地址(经纬度或省市 城市) 用到广播变量,并将规则写入到mysql中

工具类:

1.将ip地址转为十进制

2.匹配ip规则时 用二分法查找

import scala.collection.mutable.ArrayBuffer

object IpUtils {

  /**
    * 将IP地址转成十进制
    *
    * @param ip
    * @return
    */
  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
  }

  /**
    * 二分法查找
    *
    * @param lines
    * @param ip
    * @return
    */
  def binarySearch(lines: ArrayBuffer[(Long, Long, String, String)], ip: Long): Int = {
    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 //没有找到
  }

  def binarySearch(lines: Array[(Long, Long, String, String)], ip: Long): Int = {
    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 //没有找到
  }
}

开始正菜

1.读取ip规则数据-->rdd-->切分数据,留下要用的数据-->将数据收集到driver端 然后广播变量到对应所有的executor端

2.读取业务数据-->rdd-->切分数据,留下能用的信息-->将数据通过工具类中的方法转为十进制,并进行二分查找,返回相应经纬度或城市,这里要的是城市.-->reduceByKey-->得到结果

3.将结果写入到mysql数据库内:

  1. res.foearchpartition(data2Mysql)
  2. 定义这个data2Mysql:
    1. 创建数据库连接,最好是每个分区创建一次,重复使用这个链接
    2. 批量写入到数据库中(具体见以下代码)

import java.sql.{Connection, Date, DriverManager, PreparedStatement}
import com.hjm.spark.utils.IpUtils
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object IpLocation {

  def main(args: Array[String]): Unit = {

    val isLocal = args(0).toBoolean

    val conf = new SparkConf().setAppName(this.getClass.getCanonicalName)

    if (isLocal) {
      conf.setMaster("local[*]")
    }
    val sc = new SparkContext(conf)

    //先读取iP规则数据
    val ipRuleLines: RDD[String] = sc.textFile(args(1))

    val ipRulesInDriver: Array[(Long, Long, String, String)] = ipRuleLines.map(line => {
      val fields = line.split("[|]")
      val startNum = fields(2).toLong
      val endNum = fields(3).toLong
      val province = fields(6)
      val city = fields(7)
      (startNum, endNum, province, city)
    }).collect //将ip规则收集到driver

    //将dirver端准备好的数据广播的Executor中
    //返回一个广播变量的引用(是在driver端),该方法是一个阻塞的方法
    val broadcastRef: Broadcast[Array[(Long, Long, String, String)]] = sc.broadcast(ipRulesInDriver)

    //读取访问日志数据
    val accessLog: RDD[String] = sc.textFile(args(2))

    //对访问日志进行整理
    val provinceAndOne: RDD[(String, Int)] = accessLog.map(line => {
      val fields = line.split("[|]")
      val ip = fields(1)
      val ipNum = IpUtils.ip2Long(ip)
      //在Executor中关联事先广播好的数据
      //通过广播变量的引用,可以获取到事先广播到Exector中的数据
      val ipRulesInExecutor: Array[(Long, Long, String, String)] = broadcastRef.value
      //使用二分法查找
      val index = IpUtils.binarySearch(ipRulesInExecutor, ipNum)
      var province = "未知"
      if (index != -1) {
        province = ipRulesInExecutor(index)._3
      }
      (province, 1)
    })
    val res = provinceAndOne.reduceByKey(_ + _)
    //打印输出
    // res.foreach(println)
    println(res.collect().toBuffer)
    //ArrayBuffer((陕西,1824), (河北,383), (云南,126), (未知,1), (重庆,868), (北京,1535))

    /**
     *
     * 将结果保存到mysql表中
     */
    res.foreachPartition(dataMysql)
    //关闭sc
    sc.stop()
  }
  val dataMysql = (it: Iterator[(String, Int)]) => {
    var conn: Connection = null
    var ps: PreparedStatement = null
    try {

      //需要把Class.forName("com.mysql.jdbc.Driver").newInstance()加上 否则在分布式提交的时候,数据没有插入到数据库中。
      Class.forName("com.mysql.jdbc.Driver").newInstance()
      //准备mysql数据 不加这个可能会报错 :&useSSL=false
      conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hjm?characterEncoding=utf-8&useSSL=false", "root", "123456")
      //写sql语句对象
      ps = conn.prepareStatement("insert into ip values (null,?,?)")
      //将数据set进数据表 //必须在数据库创建一个新表 用于接受数据
      it.foreach(t => {
        ps.setString(1, t._1)
        ps.setInt(2, t._2)
        //批量写入
        ps.addBatch()
      })
      //写入到数据库
      ps.executeBatch()
      //返回迭代器
      ()
    } catch {
      case e: Exception => {}
    } finally {
      //关闭资源
      if (conn != null) conn.close()
      if (ps != null) ps.close()
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值