SparkStreaming与Kafka010之05之04 广播变量的变化重置 与redis监控的结合

1.这个记录的实例就是说,接了上一篇的代码修改,加了广播变量的东西。
我在mysql存的某个规则,
我启动sparkStreaming程序的时候使用broadcast广播出去,注意这个就仅执行一次的
然后吧在redis有这么一个kv作为标志,比如说flag=true,每次sparkStreaming程序程序处理数据用到规则之前,都要先到redis看一下这个标志位变没变。
如果有其他程序或者人为修改了mysql数据中的规则,它会一同修改redis的标志位(这个不是本程序做的事)。
如果变了,则重新读取mysql规则;广播变量删除;重新广播;标志复位;
就这么事。
就是 广播变量的变化与redis的结合

package Kafka010
import Kafka010.Utils.{MyKafkaUtils, RedisUtilsDemo}
import cn.bigdata.antispider.common.util.jedis.JedisConnectionUtil
import database.QueryDB
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.TopicPartition
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka010._
import org.apache.spark.streaming.{Seconds, StreamingContext}
import scala.collection.mutable.ArrayBuffer

/**
 * Created by Shi shuai RollerQing on 2019/12/24 19:47
 *
 * kakfa的API 0-10版本
 *
 * 广播变量的变化重置 与redis的结合
 */
object Kafka010Demo04 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName(s"${this.getClass.getCanonicalName}")
    val ssc = new StreamingContext(conf, Seconds(5))
    //    ssc.addStreamingListener()

    val groupID = "SparkKafka010"
    val topics = List("topicB")
    val kafkaParams: Map[String, String] = MyKafkaUtils.getKafkaConsumerParams(groupID, "false")
    //从外部存储中获取offset
    val offsets: Map[TopicPartition, Long] = RedisUtilsDemo.getOffsetFromRedis("topicB", groupID)

    // 获取规则;仅执行一次
    val sql = "select value from nh_filter_rule"
    val rules: ArrayBuffer[String] = QueryDB.queryData(sql, "value")
    @volatile var bc = ssc.sparkContext.broadcast(rules)
    val jedis = JedisConnectionUtil.getJedisCluster

    val ds: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](
      ssc,
      LocationStrategies.PreferConsistent,
      ConsumerStrategies.Subscribe[String, String](topics, kafkaParams, offsets)//要的就是这样的类型
    )
    ds.foreachRDD(rdd => {
      val ranges: Array[OffsetRange] = rdd.asInstanceOf[HasOffsetRanges].offsetRanges

      //监控redis中的标志
      val flag = jedis.get("flag")

      // 标志变化。 重新读数;广播变量删除;重新广播;标志复位;
      if(flag != null && flag.toBoolean) {
        val rules: ArrayBuffer[String] = QueryDB.queryData(sql, "value") //变了就重新查一遍 更新一下数据
        bc.unpersist()
        bc = ssc.sparkContext.broadcast(rules)
        jedis.set("flag", "false")
      }

      //代表对数据进行处理 这里就是随便写了 真实的环境就是会用到广播的rules进行过滤等操作的
      if(! rdd.isEmpty())
        println(rdd.count())

      //代表对offset进行处理
      ranges.foreach(offset => {
        println(s"${offset.partition}, ${offset.fromOffset}, ${offset.untilOffset}")
      })
      RedisUtilsDemo.saveOffsetToRedis(ranges, groupID)
    })
    ssc.start()
    ssc.awaitTermination()
  }

运行没有问题。
SparkStreaming与Kafka010之05之04 广播变量的变化重置 与redis监控的结合_第1张图片
//unpersist用于删除磁盘、内存中的相关序列化对象。

//persist用于设置RDD的StorageLevel。
//不带参数的persist和cache函数,相当于persist(StorageLevel.MEMORY_ONLY)。
//getStorageLevel用于查询getStorageLevel信息。
//注意:RDD的StorageLevel只能设置一次。

//@volatile var bc = ssc.sparkContext.broadcast(rules)
//实际上这个注解或是关键字,大多用于被并发访问的共享变量
//对于volatile变量,同一变量的写操作总是先于读操作。
//volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,
// 比如:操作系统、硬件或者其他线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。


package database

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

import scala.collection.mutable.ArrayBuffer

/**
  * 工具类的作用:通过sql和value读取数据库的某一个字段
  *  
  */
object QueryDB {
  def queryData(sql: String, field: String): ArrayBuffer[String] = {
    //创建ab,用来封装数据
    val arr = new ArrayBuffer[String]()
    //获取连接
    val conn: Connection = c3p0Util.getConnection
    //执行sql语句
    val ps = conn.prepareStatement(sql)
    val rs: ResultSet = ps.executeQuery()
    //封装数据
    while (rs.next()){
      arr .+= (rs.getString(field)) //获取数据库中的某个字段的值 以数组形式返回
    }
    c3p0Util.close(conn, ps, rs)
    //返回结果
    arr
  }
}

package database;

import cn.bigdata.antispider.common.util.jedis.PropertiesUtil;
import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class c3p0Util {
    public static ComboPooledDataSource cpds = null;

    /**
     * 创建c3p0连接池
     */
    private static void createConnectionPool(){
        Properties prop = PropertiesUtil.getProperties("c3p0.properties");
        cpds = new ComboPooledDataSource(true);
        try {
            cpds.setJdbcUrl(prop.getProperty("jdbcUrl"));
            cpds.setDriverClass(prop.getProperty("driverClass"));
            cpds.setUser(prop.getProperty("username"));
            cpds.setPassword(prop.getProperty("password"));
            cpds.setMinPoolSize(new Integer(prop.getProperty("minPoolSize")));
            cpds.setMaxPoolSize(new Integer(prop.getProperty("maxPoolSize")));
            cpds.setInitialPoolSize(new Integer(prop.getProperty("initialPoolSize")));
            cpds.setMaxIdleTime(new Integer(prop.getProperty("maxIdleTime")));
            cpds.setAcquireIncrement(new Integer(prop.getProperty("acquireIncrement")));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static synchronized void connectionPoolInit(){
        if(cpds == null){
            createConnectionPool();
        }
    }
    /**
     * 获取连接池
     * @return 返回一个连接
     */
    public static Connection getConnection(){
        try{
            if(cpds == null){
                connectionPoolInit();
            }
            Connection conn = cpds.getConnection();
            return conn;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 关闭连接
     * @param conn Connection
     * @param pst PreparedStatement
     * @param rs ResultSet
     */
    public static void close(Connection conn,PreparedStatement pst,ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(pst!=null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
   }
    public static void close(Connection conn,PreparedStatement pst){
        if(pst!=null){
            try {
                pst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void close(Connection conn){
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

c3p0.properties

#c3p0数据库连接池配置
#mysql驱动
driverClass=com.mysql.jdbc.Driver
#mysql数据库连接地址
jdbcUrl=jdbc:mysql://hadoop01:3306/gciantispider?useUnicode=true&characterEncoding=utf8
#用户名
username=root
#密码
password=root
#初始化的连接数,取值应在minPoolSize与maxPoolSize之间,Default: 3
initialPoolSize=10
#连接池中保留的最小连接
minPoolSize=10
#连接池中保留的最大连接
maxPoolSize=100
#当连接池中的连接耗尽的时候c3p0一次同时获取的连接数,Default:3
acquireIncrement=3
#最大空闲时间
maxIdleTime=1000
#定义在从数据库获取新连接失败后重复尝试的次数,Default: 30
acquireRetryAttempts=30
#两次连接中间隔时间,单位毫秒,Default: 1000
acquireRetryDelay=1000

其他配置文件与代码去上一篇找,都在后面了 Kafka0-10版本之03

你可能感兴趣的:(spark,kafka)