大數據實戰項目(3)-離線處理及實時處理部分

大數據實戰項目(1)-項目簡介、開發技術、工具、架構等
大數據實戰項目(2)-數據採集、處理、分發流程所涉及到的框架及配置
這一部分主要是對數據進行離線處理和實時處理的總結。

離線數據處理

MySQL+Hive

MySQL一方面用來存儲Hive的元數據,另一方面存儲離線分析的結果。

1)MySQL的安裝

2)Hive的安裝

#hive-log4j.properties
#日誌目錄需要提前創建
property.hive.log.dir = /opt/modules/hive-2.1.0/logs
#修改hive-env.sh配置文件
#Set HADOOP_HOME to point to a specific hadoop install directory
  HADOOP_HOME=/opt/modules/hadoop-2.6.0
  HBASE_HOME=/opt/modules/hbase-1.0.0-cdh5.4.0
# Hive Configuration Directory can be controlled by:
 export HIVE_CONF_DIR=/opt/modules/hive-2.1.0/conf
# Folder containing extra ibraries required for hive compilation/execution can be controlled by:
 export HIVE_AUX_JARS_PATH=/opt/modules/hive-2.1.0/lib

3)Hive與MySQL集成

  • 創建hive-site.xml文件,配置mysql元數據庫metastore
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
  <property>
    <name>javax.jdo.option.ConnectionURL</name>
    <value>jdbc:mysql://bigdata-pro01.bigDAta.com/metastore?createDatabaseIfNotExist=true</value>
  </property>
  
  <property>
    <name>javax.jdo.option.ConnectionDriverName</name>
    <value>com.mysql.jdbc.Driver</value>
  </property>
  
 <property>
    <name>javax.jdo.option.ConnectionUserName</name>
    <value>root</value>
  </property>
  
  <property>
    <name>javax.jdo.option.ConnectionPassword</name>
    <value>123456</value>
  </property>
  <!--打印表頭-->
  <property>
    <name>hive.cli.print.header</name>
    <value>true</value>
  </property>
  <!--打印當前所在數據庫-->
  <property>
    <name>hive.cli.print.current.db</name>
    <value>true</value>
  </property>
  <!--該屬性在Hive與HBase集成時用到,Hive通過該屬性去連接HBase集羣-->
  <property>
    <name>hbase.zookeeper.quorum</name>   
	<value>bigdata-pro01.bigDAta.com,bigdata-pro02.bigDAta.com,bigdata-pro03.bigDAta.com</value>
  </property>


</configuration>
  • 在MySQL數據中設置用戶連接信息,可以無阻礙訪問mysql數據庫,其次要保證Hive所在節點能無密鑰登錄其他集羣內節點

4)Hive與MySQL集成測試

  • 啓動HDFS和YARN服務
  • 啓動Hive
  • 通過Hive服務創建表,並向這個表中加載數據,在Hive查看錶中內容
  • 在MySQL數據庫metastore中查看元數據

Hive+HBase集成

Hive是一個數據倉庫,主要是轉爲MapReduce完成對大量數據的離線分析和決策,之前完成了Flume集成HBase,此時HBase中能源源不斷地插入數據,那麼如何使Hive中也有數據呢?使用外部表進行Hive與HBase的關聯

 <!--在hive-site.xml中添加該屬性-->
<property>
    <name>hbase.zookeeper.quorum</name>   
	<value>bigdata-pro01.bigDAta.com,bigdata-pro02.bigDAta.com,bigdata-pro03.bigDAta.com</value>
  </property>

將 HBase中的部分jar包拷貝到Hive中,如果兩者都是CDH版本,就不需要進行拷貝;若hive安裝時自帶了以下jar包,將其刪除。使用軟連接的方式

  export HBASE_HOME=/opt/modules/hbase-1.0.0-cdh5.4.0
  export HIVE_HOME=/opt/modules/hive-2.1.0
  
  ln -s $HBASE_HOME/lib/hbase-server-1.0.0-cdh5.4.0.jar $HIVE_HOME/lib/hbase-server-1.0.0-cdh5.4.0.jar
  
  ln -s $HBASE_HOME/lib/hbase-client-1.0.0-cdh5.4.0.jar $HIVE_HOME/lib/hbase-client-1.0.0-cdh5.4.0.jar
  
  ln -s $HBASE_HOME/lib/hbase-protocol-1.0.0-cdh5.4.0.jar $HIVE_HOME/lib/hbase-protocol-1.0.0-cdh5.4.0.jar 
  
  ln -s $HBASE_HOME/lib/hbase-it-1.0.0-cdh5.4.0.jar $HIVE_HOME/lib/hbase-it-1.0.0-cdh5.4.0.jar 
  
  ln -s $HBASE_HOME/lib/htrace-core-3.0.4.jar $HIVE_HOME/lib/htrace-core-3.0.4.jar
  
  ln -s $HBASE_HOME/lib/hbase-hadoop2-compat-1.0.0-cdh5.4.0.jar $HIVE_HOME/lib/hbase-hadoop2-compat-1.0.0-cdh5.4.0.jar 
  
  ln -s $HBASE_HOME/lib/hbase-hadoop-compat-1.0.0-cdh5.4.0.jar $HIVE_HOME/lib/hbase-hadoop-compat-1.0.0-cdh5.4.0.jar
  
  ln -s $HBASE_HOME/lib/high-scale-lib-1.1.1.jar $HIVE_HOME/lib/high-scale-lib-1.1.1.jar 
  
  ln -s $HBASE_HOME/lib/hbase-common-1.0.0-cdh5.4.0.jar $HIVE_HOME/lib/hbase-common-1.0.0-cdh5.4.0.jar

在Hive中創建一個與HBase中的表建立關聯的外部表

create external table weblogs(
id string,
datatime string,
userid string,
searchname string,
retorder string,
cliorder string,
cliurl string
)  
STORED BY  'org.apache.hadoop.hive.hbase.HBaseStorageHandler' 
WITH SERDEPROPERTIES("hbase.columns.mapping" = ":key,info:datatime,info:userid,info:searchname,info:retorder,info:cliorder,info:cliurl") 
TBLPROPERTIES("hbase.table.name" = "weblogs");

可通過在Hive與HBase中輸入count ‘weblogs’,查看數據是否同步。

Cloudera Hue可視化分析

1)下載、安裝及編譯
詳細過程,記錄在Hadoop可視化神器-Hue安裝、編譯、運行
2)基本配置

1.配置desktop/conf/hue.ini
2.修改desktop.db文件權限

3)集成
具體內容參考:Hue集成HDFS、YARN、Hive、MySql、HBase的相關配置,此處僅是流程。

  • 與HDFS集成
    在這裏插入圖片描述

  • 與YARN集成
    在這裏插入圖片描述

  • 與Hive集成

在這裏插入圖片描述

  • 與MySQL集成

在這裏插入圖片描述

  • 與HBase集成

在這裏插入圖片描述

實時數據處理

Spark與Kafka集成

1)Spark下載 安裝與編譯

2)Structured Streaming 與Kafka集成

  • kafka_2.11-0.10.0.0.jar kafka-clients-0.10.0.0.jar spark-sql-kafka-0-10_2.11-2.2.0.jar spark-streaming-kafka-0-10_2.11-2.1.0.jar等包添加到spark下的jars目錄下

  • 在IDEA中編寫如下代碼,Structured Streaming從kafka中讀取數據,並進行計算

     val spark  = SparkSession.builder()
          .master("local[2]")
          .appName("streaming").getOrCreate()
    
      val df = spark
          .readStream
          .format("kafka")
          .option("kafka.bootstrap.servers", "bigdata-pro01.bigDAta.com:9092,bigdata-pro02.bigDAta.com:9092,bigdata-pro03.bigDAta.com:9092")
          .option("subscribe", "weblogs")
          .load()
     import spark.implicits._
        val lines = df.selectExpr("CAST(value AS STRING)").as[String]
        val weblog = lines.map(_.split(","))
          .map(x => Weblog(x(0), x(1), x(2),x(3),x(4),x(5)))
        val titleCount = weblog
          .groupBy("searchname").count().toDF("titleName","count")
    

Spark與MySQL集成

由於這裏僅僅需要對報表進行展示,前臺展示的字段並不多,MySQL完全可以支撐。在HBase中有幾百萬條數據( 一個瀏覽話題可能有十幾萬人搜索過,也就是說一個話題就有十幾萬條數據,這麼大量數據當然要存在Hbase中 ),而經過Spark的計算, 這十幾萬條數據在mysql中就變成了一條數據(titleName,count)。

如果需要實時查詢用戶各種信息(數據量很大,字段很多),那麼就需要實時的直接從Hbase裏查,而不會在Mysql中。

val url ="jdbc:mysql://bigdata-pro01.bigDAta.com:3306/test"
    val username="root"
    val password="123456"
    val writer = new JDBCSink(url,username,password)
    val query = titleCount.writeStream
      .foreach(writer)
      .outputMode("update")
      .trigger(ProcessingTime("5 seconds"))
      .start()
    query.awaitTermination()

其中的JDBCSink具體代碼如下所示:

import java.sql._

import org.apache.spark.sql.{ForeachWriter, Row}

class JDBCSink(url:String, username:String,password:String) extends ForeachWriter[Row]{
  //var是一個變量
  //val常量
  var statement : Statement =_
  var resultSet : ResultSet =_
  var connection : Connection=_
  override def open(partitionId: Long, version: Long): Boolean = {
    connection = new MySqlPool(url,username,password).getJdbcConn();
    statement = connection.createStatement()
    return true
  }
  //處理數據
  override def process(value: Row): Unit = {
    // 將titleName中的[[]]用空格代替。標記一箇中括號表達式的開始。要匹配 [,請使用 \[
    val titleName = value.getAs[String]("titleName").replaceAll("[\\[\\]]","")
    val count = value.getAs[Long]("count");

    val querySql = "select 1 from webCount " +
      "where titleName = '"+titleName+"'"

    val updateSql = "update webCount set " +
      "count = "+count+" where titleName = '"+titleName+"'"

    val insertSql = "insert into webCount(titleName,count)" +
      "values('"+titleName+"',"+count+")"

    try{
      var resultSet = statement.executeQuery(querySql)
      if(resultSet.next()){
        //如果有執行updateSql
        statement.executeUpdate(updateSql)
      }else{
        //沒有的話就執行insertSql
        statement.execute(insertSql)
      }
    }catch {
      case ex: SQLException => {
        println("SQLException")
      }
      case ex: Exception => {
        println("Exception")
      }
      case ex: RuntimeException => {
        println("RuntimeException")
      }
      case ex: Throwable => {
        println("Throwable")
      }
    }
  }

  override def close(errorOrNull: Throwable): Unit = {
    if(statement==null){
      statement.close()
    }
    if(connection==null){
      connection.close()
    }
  }

}

而在JDBCSink中用到的MySqlPool連接池的具體代碼如下所示

import java.sql.{Connection, DriverManager}
import java.util
class MySqlPool(url:String, user:String, pwd:String) extends Serializable{
  private val max = 3               //連接池連接總數
  private val connectionNum = 1    //每次產生連接數
  private var conNum = 0            //當前連接池已產生的連接數
  private val pool = new util.LinkedList[Connection]()    //連接池

  //獲取連接
  def getJdbcConn() : Connection = {
    //同步代碼塊
    AnyRef.synchronized({
      if(pool.isEmpty){
        //加載驅動
        preGetConn()
        for(i <- 1 to connectionNum){
          val conn = DriverManager.getConnection(url,user,pwd)
          pool.push(conn)
          conNum +=  1
        }
      }
      pool.poll()
    })
  }

  //釋放連接
  def releaseConn(conn:Connection): Unit ={
    pool.push(conn)
  }
  //加載驅動
  private def preGetConn() : Unit = {
    //控制加載
    if(conNum < max && !pool.isEmpty){
      println("Jdbc Pool has no connection now, please wait a moments!")
      Thread.sleep(2000)
      preGetConn()
    }else{
      Class.forName("com.mysql.jdbc.Driver");
    }
  }

}

WEB系統開發

  • 從MySQL中查詢數據 WeblogService:包括查詢20條 titleName,count,以及titleSum
  • 基於WebSocket協議的數據推送服務開發
  • 基於Echarts框架,編寫前端頁面的展示index.html

啓動各個服務,展示最終結果

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
經歷了各種版本問題,各種各樣的bug,閱讀了所使用的框架的官網網站(有些內容很難找到,尤其是英文網站,但是隻要找到位置後,閱讀還是比較容易理解的,記得大致瀏覽下該位置所在頁面上的其他內容,因爲有些注意事項會在下文寫出)查閱了大量博客文獻(感謝各位博主的分享),終於將此次大數據實戰項目搞定,至此該大數據實戰項目告一段落,但仍然需要對其中所涉及到的框架的原理進入深入理解,否則在出現bug的時候,很難快速解決問題。
加油!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章