spark与codis、kafka 整合

1 篇文章 0 订阅

整理一下这几天的劳动成果,方便自己日后使用,可能存在目前没有发现的问题,若有博友发现有问题的请指明~谢谢


EngineIntime_Start.scala 类,接收kafka消息

package cn.com.bonc.engine

<pre name="code" class="plain">import java.io.FileNotFoundException
import java.util.ArrayList
import java.util.HashSet
import java.util.Properties
import scala.io.Source
import scala.util.control.Breaks._
import org.apache.commons.lang.StringUtils
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext._
import org.apache.spark.SparkContext
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD._
import org.apache.spark.rdd.RDD
import org.apache.spark.serializer.KryoRegistrator
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.Seconds
import org.apache.spark.streaming.StreamingContext
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.kafka.KafkaUtils
import kafka.javaapi.producer.Producer
import kafka.producer.KeyedMessage
import kafka.producer.ProducerConfig
import kafka.serializer.StringDecoder
import redis.clients.jedis.Jedis
import redis.clients.jedis.JedisPool
import com.wandoulabs.jodis.RoundRobinJedisPool
import org.slf4j.LoggerFactory
import cn.com.bonc.tools.InternalRedisClient<pre name="code" class="plain">/**
 * @author 123456
 */

object EngineIntime_Start  {
  val logger = LoggerFactory.getLogger(this.getClass)

  def main(args: Array[String]) {
  
    val messMap =  new java.util.HashMap[String,String]()
    try {
          //读kafka producer与broker配置参数,为了代码好维护,变量没有写死在代码里
        for(line<-Source.fromFile("conf/engine_intime.conf").getLines()){
          if (!line.contains("#")&&line.contains("=")) {
            val mks = StringUtils.splitPreserveAllTokens(line,"=")
            messMap.put(mks(0), mks(1))
            }
          }
   
      } catch {
        case e: FileNotFoundException => {
          logger.warn("====here catch a FileNotFoundException==== please check the file “conf/engine_intime.conf”!!");
        }
        case e:Exception =>  {
          logger.warn("====here catch a Exception==== "+e);
          }
      }
    
    val sourceTopics = messMap.get("sourceTopics")
    val group = messMap.get("group")
    val zkQuorum = messMap.get("zkQuorum")
    val brokerIP = messMap.get("brokerIP")
    val aimTopics = messMap.get("aimTopics")
    
    println("sourceTopics : "+sourceTopics)
    println("group : "+group)
    println("zkQuorum : "+zkQuorum)
    println("brokerIP : "+brokerIP)
    println("aimTopics :"+aimTopics)

    System.setProperty("spark.streaming.concurrentJobs", "30")//线程数量
    val conf = new SparkConf()
    conf.set("spark.speculation", "true")//去掉执行慢的节点,该配置没有看出效果,可能没有生效
    val executor_num = conf.get("spark.executor.instances")//获取executor cores个数
    //println("============================= : "+executor_num+" ===========================")
    val ssc = new StreamingContext(conf,Seconds(15))//秒
    val sc =  ssc.sparkContext
    val topicpMap = sourceTopics.split(",").map((_,sourceTopics.split(",").size*10)).toMap
  //设置kafka连接配置  smallest读旧数据,  默认是largest
    val kafkaParams:Map[String,String] = scala.collection.immutable.Map("zookeeper.connect" -> zkQuorum,
          "group.id" -> group,
          "auto.offset.reset" -> "largest",
          "zookeeper.session.timeout.ms" -> "6000",
          "zookeeper.sync.time.ms" -> "2000",
          "auto.commit.interval.ms" -> "1000")
              
     val ds_999 = KafkaUtils.createStream[String, String, StringDecoder,StringDecoder](ssc, kafkaParams, topicpMap,StorageLevel.MEMORY_AND_DISK_SER_2).map(_._2)
     //由于数据是从kafka里接收过来的,数据有可能会在一个数据块上,会造成只有一个节点在工作,其他节点闲着,如果数据量很大的话,会执行缓慢,没有发挥出分布式的优点。所以这里把数据分成executor cores个数乘与3倍的partition,尽可能使用foreachPartition、mapPartitions。
     //1、创建codis连接池,获取jedis连接,使用完连接一定要及时还回连接池。2、创建连接不能在foreachPartition外部创建,会报没有序列化即使连接池object已经序列化了。
     //3、一定不能在fforeachPartition里具体处理数据action的方法里创建连接池,即这里的EngineIntime_Producer.topicProduce()方法
     val linesDst = ds_999.repartition(executor_num.toInt*3).foreachRDD (rdd => {
         rdd. foreachPartition( partitionOfRecords => {
            logger.warn("=======================================start11=================================")     
            val maxTotal = -1
            val maxIdle = -1
            val minIdle = 1
            val db = "xl_redis"
            val redisHost = "10.162.x.x:2181,10.162.x.x:2181,10.162.x.x:2181"
            val pool = InternalRedisClient.makePool(redisHost, maxTotal, maxIdle, minIdle,db)
            logger.warn("=======================================start22=================================")  
            var jedis:Jedis = null
            try {
             logger.warn("=====================getResource start========================")
            jedis =pool.getResource
            logger.warn("=====================getResource complete========================")
           } catch {
             case t: Throwable => logger.warn(t+" "+t.printStackTrace())
             case e: Exception => logger.warn("here catch a exception :"+ e+"\n"+"========new pool fail!!!!")
             
           }
          EngineIntime_Producer.topicProduce(aimTopics,brokerIP,partitionOfRecords,jedis)
           jedis.close
         } )
         
       rdd.unpersist(true)
      })
     
    ssc.start()
    ssc.awaitTermination()
    //ssc.stop(true, true)
    
     //在确认所有receiver都关闭的情况下才终止程序
     sys.addShutdownHook({  
     ssc.stop(true,true)})
  }
}

 

InternalRedisClient.scala类,创建连接池

package cn.com.bonc.tools

import com.wandoulabs.jodis.JedisResourcePool
import redis.clients.jedis.JedisPoolConfig
import com.wandoulabs.jodis.RoundRobinJedisPool
import org.slf4j.LoggerFactory
/**
 * @author 123456
 */
object InternalRedisClient extends Serializable {
  val logger = LoggerFactory.getLogger(this.getClass)
           @transient private var pool: JedisResourcePool = null
            def makePool(redisHost: String, maxTotal: Int, maxIdle: Int, minIdle: Int,database: String): JedisResourcePool = {
             
              if(pool == null) {
                   val jedisPoolConfig:JedisPoolConfig = new JedisPoolConfig();
                   jedisPoolConfig.setMaxTotal(maxTotal);//没有限制
                    jedisPoolConfig.setMaxIdle(maxIdle);
                    jedisPoolConfig.setMaxWaitMillis(10000);
                    jedisPoolConfig.setTestOnBorrow(true);
                    jedisPoolConfig.setTestOnReturn(true);
                    pool = new RoundRobinJedisPool(redisHost,10000, "/zk/codis/db_"+database+"/proxy",jedisPoolConfig, 10000); 
                   logger.warn("==================new pool===================")
                val hook = new Thread{
                     override def run = pool.close()
                   }
                   sys.addShutdownHook(hook.run)
              }
              
              pool
            }
           
            /*def getPool: JedisResourcePool = {
              assert(pool != null)
              pool
            }*/
          }



EngineIntime_Producer.scala类,结果数据写到新的kafka里
package cn.com.bonc.engine

<pre name="code" class="plain">import org.apache.commons.lang.StringUtils
import org.apache.hadoop.hbase.util.Bytes
import org.apache.commons.io.output.FileWriterWithEncoding
import kafka.javaapi.producer.Producer
import kafka.producer.KeyedMessage
import kafka.producer.ProducerConfig
import java.io.IOException
import redis.clients.jedis.Jedis
import scala.util.control.Breaks._
import org.slf4j.LoggerFactory<pre name="code" class="plain">/**
 * @author 123456
 */
//业务逻辑处理,结果发到kafka里
object EngineIntime_Producer {
  val logger = LoggerFactory.getLogger(this.getClass)
  def topicProduce(topicName:String,brokerIP:String,iter:scala.collection.Iterator[String],jedis:Jedis){
     import java.util._
     import java.io._
     import org.apache.hadoop.hbase.util.Bytes
     val producer = new Producer[String, String](kafkaConn(brokerIP))
     var kmgs:List[KeyedMessage[String, String]] = new ArrayList[KeyedMessage[String, String]]()     
     var re_value = ""
     if (iter!=null) {
       iter.foreach ( str => {
        breakable { 
          if (str.==("")) break;
        var sb = new StringBuilder();
        val colvalues = StringUtils.splitPreserveAllTokens(str,"\\|");
        
        if (colvalues.length!=0 && colvalues(0).length()==11 && isNumeric(colvalues(0))) {
          val device_number_byte = Bytes.toBytes(colvalues(0).toLong);
          val device_number_hash = Bytes.toBytes((colvalues(0).hashCode()& 0x7fff).toShort);
          val rowkey = Bytes.add(device_number_hash, device_number_byte);
          var value:Array[Byte] = null
          try {
             value = jedis.get(rowkey);  
          }catch {
            case e: Exception => {logger.warn("======"+e+"=======")}
            Thread.sleep(6000)
            try {
              value = jedis.get(rowkey);
            } catch {
              case e: Exception => {logger.warn("======"+e+"=======")}
            }
          }
        sb.append(colvalues(0))
        sb.append("|")
        sb.append(colvalues(1))
        sb.append("|")
        sb.append(colvalues(2))
        sb.append("|")
        sb.append(colvalues(4))
        sb.append("|")
        sb.append(colvalues(5))
        sb.append("|")
        sb.append(colvalues(6))
        sb.append("|")
        sb.append(colvalues(7))
        sb.append("|")
        sb.append(colvalues(12))
        sb.append("|")
        sb.append(colvalues(13))
        sb.append("|")
        sb.append(colvalues(14))
        
        if(value!=null ){
            val re_values = StringUtils.splitPreserveAllTokens(new String(value),"\\|")
            re_values.length match {
              case 15 => {
                sb.append("|")
                sb.append(re_values(1))
                sb.append("|")
                sb.append(re_values(2))
                sb.append("|")
                sb.append(re_values(3))
                sb.append("|")
                sb.append(re_values(4))
                sb.append("|")
                sb.append(re_values(5))
                sb.append("|")
                sb.append(re_values(6))
                sb.append("|")
                sb.append(re_values(7))
                sb.append("|")
                sb.append(re_values(8))
                sb.append("|")
                sb.append(re_values(9))
                sb.append("|")
                sb.append(re_values(10))
                sb.append("|")
                sb.append(re_values(11))
                sb.append("|")
                sb.append(re_values(12))
                sb.append("|")
                sb.append(re_values(13))
                sb.append("|")
                sb.append(re_values(14))}
              case 7 => {
                sb.append("|")
                sb.append(re_values(1))
                sb.append("|")
                sb.append(re_values(2))
                sb.append("|")
                sb.append(re_values(3))
                sb.append("|")
                sb.append(re_values(4))
                sb.append("|")
                sb.append(re_values(5))
                sb.append("|")
                sb.append(re_values(6))
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                }
              case _ => {
                sb.append("|")
                sb.append(re_values(1))
                sb.append("|")
                sb.append(re_values(2))
                sb.append("|")
                sb.append(re_values(3))
                sb.append("|")
                sb.append(re_values(4))
                sb.append("|")
                sb.append(re_values(5))
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                }
            }
         }else {
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
                sb.append("|")
                sb.append("")
           
         }
        var kmg = new KeyedMessage[String, String](topicName,sb.toString())
        kmgs.add(kmg)
        if (kmgs.size()==1000) {
          producer.send(kmgs)
          kmgs.clear()
          //Thread.sleep(3000)
        }
        }}})
     producer.send(kmgs)
     kmgs.clear()
     }
     producer.close
  }
      
  def kafkaConn(brokerIP:String):ProducerConfig={
      import java.util._
      import java.io._
      val properties = new Properties();   
      properties.put("metadata.broker.list", brokerIP);
      properties.put("serializer.class", "kafka.serializer.StringEncoder");
      properties.put("request.required.acks", "-1")
      val config = new ProducerConfig(properties);
      return config
  }
    
        /**
   * 判断字符串是数字
   * @param str
   * @return 是数字返回true,否则返回false
   */
      def isNumeric(str:String): Boolean =  {
        if ("".equals(str)) {
          return false;
        }else {
      for ( i <- 0 to str.length()-1){   
       if (!Character.isDigit(str.charAt(i))){
        return false;
       }
      }
      return true;
     }
     } 
}

 
 


        个人经验总结:刚开始我把创建codis连接池写在action方法里,连接池还是单例模式。大约5分钟有6000万数据量,程序跑一个小时,数据就开始处理不过来了,全部都卡在从codis里取数据的环节上,从codis页面查看峰值只有23万op/s,有大量连接codis超时警告,查看codis后台日志有很多代理连接超时,连接比默认值60高,后来把codis代理连接放开,程序跑了2个小时左右,codis代理全部挂掉,后来又把codis连接改为500,大约3个小时,codis代理又全部挂掉。后来查看codis各个节点代理、zk的连接情况,发现所有的连接都在第一台机器上,后9台机器代理没有任何连接。经过高人指点,花了两天时间,主要时间花在研究新的scalacodis客户端,改了一下午加一上午,终究还有有问题,第二天下午果断使用之前熟悉的javacodis客户端,花了一中午时间改了改,终于把问题解决了。

       这边这次出的问题,正好证明了codis一个代理极限是23万op/s,10个节点就是撑死能达到230万op/s。以后评估机器也有了证据~

       另外,尽可能不要使用+拼接字符串,效率慢,具体数值没有测试。字符串拼接较多字符时尽量选用StringBuilder()。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值