zk维护kafkaoffset

由于未提供博客具体内容,暂无法生成包含关键信息的摘要。
package spark_guohang

import java.util.Properties

import kafka.common.TopicAndPartition
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.streaming.kafka010.OffsetRange
import org.apache.zookeeper.Watcher.Event.EventType
import org.apache.zookeeper.ZooDefs.Ids
import org.apache.zookeeper.{CreateMode, WatchedEvent, Watcher, ZooKeeper}
//摘选自国航demo中在zookeeper中维护kafka的offset,①-④连接zk(initZK递归自己) 监听节点(process) 设置配置信息(chgFlag已更改,如过zk没有开通则调用initzk再一次在设置一次)⑤-⑥为读取offset维护offset



                          //zk所在地        zk监听的目录节点          zk的通知机制,即继承watch类后实现process方法里面的参数为events即可获得所有改动过后的数据https://blog.youkuaiyun.com/zkp_java/article/details/82711810
class ZookeeperWatchDemo(zkQuorum:String,zkCfgNode:String) extends Watcher {

          var zk: ZooKeeper =_ //这种_的用法代表使用默认值,引用数据类型为null
          var timeout:Int=100000
          var conf:Properties=new Properties()



       /*④try:此时拿到的为节点的状态和数据 但是为二进制所以需要转为string类型,然后将此数据上的 " 变更为 ,然后按照|切割
        *利用map将里面的参数配置取出来并按=来切拿取0设置k和拿取1设置v,(此时需要设置conf定义为默认空值),此时我们将配置全部设置上了
        * 然后调用chgFlag方法设为true代表我们已经设置上配置信息了
        * catch:如果没有设置成功则睡三秒,如果zk的状态是关闭的或者没有连接的状态,先关闭一下zk 然后在初始化zk,最后在调用一次initzk(直到成功配置上信息)
        *getdata为二进制数组 第一个参数是监听的目录节点,第二参数为是否监听此节点,第三个参数为节点状态
        */
    def refreshConf: Unit ={
      /*
      zk.getDate获取到zkCfgNode的相关数据,是一个byte[].那么肯定是诸如"/zk/hadoop/.../.../"之类的玩意。让后将所有的"替换掉。
      DEFAULT_DELIMITER应该是个常量。。。应是个`,`。最后遍历split后的数组。
       */
      try {
        new String(zk.getData(zkCfgNode, false, null)).replaceAll("\"", "")
          .split("\\|").map(f => conf.setProperty(f.split("=")(0), f.split("=")(1)))
        this.chgFlag(true)      //  "/testzwd","offsetZknode=/kafkaoffsetd|zkTimeout=10000l"
      }catch
        {
          case e:Exception=>{
            Thread.sleep(3000)
            if(zk.getState==ZooKeeper.States.CLOSED || zk.getState==ZooKeeper.States.NOT_CONNECTED){

              zk.close()
              initZk()
              this.refreshConf
              }
            }
        }




    }
      /**①连接zk服务
        *try:刚开始zk肯定是没有开启状态 所以必然是null状态和没有存活状态 但是还是要先判断严谨!先锁一下当前线程
        * 先判断zk是否为空 如果为空则关闭zk(以防万一),重新建立连接(newzk),然后监听指定的目录变化
        * 只要zk的状态为非连接状态,则睡一下3000毫秒,再一次连接直到成功
        * catch:抛出zk连接异常后3秒继续初始化,关掉zk服务递归自己(initzk)
        */
      private def initZk(): Unit ={

        try {
          if (zk == null || !zk.getState.isAlive) this synchronized {
            if (zk == null) zk.close()
            zk = new ZooKeeper(zkQuorum, timeout, this)//zk所在地,zk会话时间,监听节点的事件
            zk.exists(zkCfgNode, true)

            while (zk.getState ne ZooKeeper.States.CONNECTED) {
              Thread.sleep(3000)
            }
          }
        }catch{
          case e:Exception=>{

            Thread.sleep(3000)
            if(zk.getState==ZooKeeper.States.CLOSED || zk.getState==ZooKeeper.States.NOT_CONNECTED){
              zk.close()
              initZk()
            }
          }
        }
      }


      /**③
        * @param ischange   这里的property是传给哪里了??
        */
      def chgFlag(ischange:Boolean): Unit ={
        conf.setProperty("ischange",ischange.toString)

      }



  /**②监听事件数据变动并及时刷新
    * @param watchedEvent 为监听的事件数据变动
    *  这个类为实现监听节点的变动
    *   watchedEvent.getType: 当前的状态
    *   EventType.NodeDataChanged 事件的类型为 节点信息变动
    *   zk.exists 返回监听此路径上节点信息的变动 true为监听 false为不监听
    *
    */
     override def process(watchedEvent: WatchedEvent): Unit = {
            if(watchedEvent.getType==EventType.NodeDataChanged){
              zk.exists(zkCfgNode,true)

                this.refreshConf

            }
     }


      /**⑤读取topic中的offset-->readOffsets方法是获得offset的字符串,然后取offsetRangesStrOpt里面匹配我们想要的结果
        *先获取到ofsetzknode的地址(在zk的所在位置) 然后拼接字符串将地址与主题组在一起(offsetTopic),
        * 用来获取zk中kafka目录的数据(offset)然后返回给offsetRangesStrOpt变量
        * offsetRangesStrOpt变量匹配 只要是有值就匹配到offsetsRangesStr 没有值就会匹配none,然后offsetsRangesStr其实就是offset的数据
        * 按照,切割在按照:切割,匹配一个array的数组Array(partitionStr, offsetStr),将其变成一个map类型的输出给offsets,
        * 到了这里我们就读取到top的offset的所有信息了
        *
        * @param topic
        * @return
        */
    def readOffsets(topic:String):Option[Map[TopicAndPartition,Long]]={

        val offsetParent=conf.getProperty("offsetZknode")
        val offsetTopic=offsetParent+"/"+topic

        val offsetRangesStrOpt={

            val offset=zk.getData(offsetTopic,false,null)
            if(offset==null)None//如果为空 返回空
            else Some(new String(offset))//如果有值返回值 推测可能是byte

        }

      offsetRangesStrOpt match { //offsetsRangesStrOpt的模式匹配,其实就是offset内的数据
        case Some(offsetsRangesStr) => //如果匹配到Some。Some是scala中的一个类,该类代表有东西返回...... 这里的offsetsRangesStr其实就是offset                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             a中的一个类,该类代表有东西返回。。。。

          //  "/testzwd","offsetZknode=/kafkaoffsetd|zkTimeout=10000l"
          val offsets = offsetsRangesStr.split(",") //关于这个逻辑,你应该去自己试试看。自己写个zk的类,然后看看kafka在zk中的数据格式是什么样子的,会更加好理解。
            .map(s => s.split(":"))
            .map { case Array(partitionStr, offsetStr) => (TopicAndPartition(topic, partitionStr.toInt) -> offsetStr.toLong) }
            .toMap //看逻辑,会获取到([topic,partitionid],offset)

             Some(offsets) //返回offsets
        case None =>  //什么都没有匹配到的话就返回None
          None
      }
    }


            /**
              * ⑥这个逻辑是将streaming消费者的offset更新到zk中
              * 将offsetrange为我已经使用过得偏移量offset,取出分区和offset的起始,如果此节点为null说明此分区没有offset需要创建一个
              * 创建的参数为 offset的地址,数据,List<ACL>,是否为临时节点
              * 如果此分区中有则设置此分区.
              * @param topic
              * @param offsetsRanges
              */
      def saveOffsets(topic:String,offsetsRanges:Array[OffsetRange]): Unit ={

           val offsetParent= conf.getProperty("offsetZknode")
           val offsetToptic=offsetParent+"/"+topic

           val offsetsRangesStr = offsetsRanges.map(offsetRange=>s"${offsetRange.partition}:${offsetRange.fromOffset}").mkString(",")

           if(null == zk.exists(offsetsRangesStr,false)){
             zk.create(offsetToptic,null,Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT)

           }

            zk.setData(conf.getProperty("offsetZknode"),Bytes.toBytes(offsetsRangesStr),zk.exists(conf.getProperty("offsetZknode"),false).getVersion)
      }
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值