一个Scala版的连接池,并在使用Spark Streaming进行Word Count时,把每批数据都存到mySql中
这里判断的两个空不能去掉,可能有人以为在调用getDriver方法时已经判断过pool.isEmpty了,在进行判断是没有意义的,而且当 连接数已经大于等于max时,会死循环,但是要考虑到运行过成中,在spark中是多线程运行的,在调用,getConnection方法时,可能当时pool中是空的,但是在调用后,可能其他线程的数据运行完了,会还连接, 那么此时再进行判断时pool就不是空了,两个调教都不成立,才能跳出循环,此时的情况是,获取的连接数已经大于等最大(max)的值,并且 已经有人把连接还了, 就可以直接取连接了,不用再创建,也不能再创建(连接数不能大于设置的最大连接数的)
import java.sql.{Connection, DriverManager}
import java.util
object JDBCConnectePools02 {
private val max = 10 //设置连接最大数
private val ConnectionNum = 10 //设置 每次可以获取几个Connection
private var conNum = 0//连接数
private val pool = new util.LinkedList[Connection]() //连接池
def getDriver() : Unit = { //加载Driver
//加载
//这里判断的两个空不能去掉
//可能有人以为在调用getDriver方法时已经判断过pool.isEmpty了,
//在进行判断是没有意义的,而且当 连接数已经大于等于max时,会死循环
//但是要考虑到运行过成中,在spark中是多线程运行的,在调用
//getConnection方法时,可能当时pool中是空的,但是在调用后,
//可能其他线程的数据运行完了,会还连接,
//那么此时再进行判断时pool就不是空了,两个调教都不成立,
//才能跳出循环,此时的情况是,获取的连接数已经大于等最大(max)的值
//并且 已经有人把连接换了, 就可以直接取连接了,不用再创建
//,也不能再创建
if(conNum < max && pool.isEmpty){ //
Class.forName("com.mysql.jdbc.Driver")
}else if(conNum>=max && pool.isEmpty){
print("当前暂无可用Connection")
Thread.sleep(2000)
getDriver()
}
}
def getConn(): Connection ={
if(pool.isEmpty){
getDriver()
for(i <- 1 to ConnectionNum){ //创建10个连接
val conn = DriverManager.getConnection("jdbc:mysql://hadoop01:3306/updatewordcount","root","root")
pool.push(conn) // 把连接放到连接池中,push是LinkedList中的方法
conNum += 1
}
}
val conn: Connection = pool.pop()//从线程池所在LinkedList中弹出一个Connection,pop 是LinkedList的方法
conn //返回一个Connection
}
def returnConn( conn :Connection): Unit ={ //还连接
pool.push(conn)
}
}
一个简单的使用,使用sparkStreaming,尽心wordCount,每次把结果放到MySql 中
这个代码里面一共有3foreach,分别是foreachRDD、froeachPartition
和foreach ,对于这三个foreach我的理解是,第一个foreachRDD是
DStream的方法,所以是不会触发job的,第二个foreachPartition
是rdd调用的,会触发job,
第三个foreach,是在foreachPartition中的,foreachPartition
中的函数的参数是指 每个分区中的数据对应的迭代器
所以这个foreach相当于Scala语言中的foreach,也不会提交job,可以用下面注释的for来代替
import java.sql.{Connection, PreparedStatement}
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Duration, StreamingContext}
object JDBCWordCont02 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("wc").setMaster("local[2]")
//新建一个StreamingContext,每个5s是一个批次
val ssc = new StreamingContext(conf,new Duration(5000))
//接受hadoop01主机的 8888端口的数据
val data: ReceiverInputDStream[String] = ssc.socketTextStream("hadoop01",8888)
//进行切分压平
val split: DStream[String] = data.flatMap(_.split(" "))
//单词和1组合
val wordAndOne: DStream[(String, Int)] = split.map((_,1))
//对rdd进行遍历,想要使用foreachPartition 需要foreachRDD ,
//对里面的rdd进行操作,DStream中没有foreachpartition方法,
//如果直接使用foreach方法不好,会大量的去连接,还连接,对性能有影响
wordAndOne.foreachRDD(rdd=>{
//对RDD中的数据进行聚合
val reduced: RDD[(String, Int)] = rdd.reduceByKey(_+_)
reduced.foreachPartition(item =>{
//获取连接
val conn: Connection = JDBCConnectePools02.getConn()
item.foreach(one=>{
val pstm: PreparedStatement = conn.prepareStatement("insert into wordcount(word,count) values(?,?)")
pstm.setString(1,one._1)
pstm.setInt(2,one._2)
pstm.executeUpdate()
})
/*上代码里面一共有3foreach,分别是foreachRDD、froeachPartition
和foreach ,对于这三个foreach我的理解是,第一个foreachRDD是
DStream的方法,所以是不会触发job的,第二个foreachPartition
是rdd调用的,会触发job,
第三个foreach,是在foreachPartition中的,foreachPartition
中的函数的参数是指 每个分区中的数据对应的迭代器
所以这个foreach相当于Scala语言中的foreach,也不会提交job,可以用下面注释的for来代替
*/
/*
for(one <- item){ //把聚合后的数据存到mysql 中
val pstm: PreparedStatement = conn.prepareStatement("insert into wordcount(word,count) values(?,?)")
pstm.setString(1,one._1)
pstm.setInt(2,one._2)
pstm.executeUpdate()
}
*/
//还连接
JDBCConnectePools02.returnConn(conn)
})
})
ssc.start()
ssc.awaitTermination()
}
}