Flume+Kafka+Spark搭建與案例實操

大數據開發文檔

本文檔主要講述了flume+kafka+spark的單機分佈式搭建,由淺入深,介紹了常見大數據流處理流程

單機版環境搭建及相關DEMO

Flume

Flume基本介紹與架構

Flume是Cloudera提供的一個高可用的,高可靠的,分佈式的海量日誌採集、聚合和傳輸的系統。Flume基於流式架構,靈活簡單。

Flume出生日記

有很多的服務和系統

  • network devices
  • operating system
  • web servers
  • Applications

這些系統都會產生很多的日誌,那麼把這些日誌拿出來,用來分析時非常有用的。

如何解決數據從其他的server上移動到Hadoop上?

shell cp hadoop集羣上的機器上, hadoop fs -put …/ 直接拷貝日誌,但是沒辦法監控,而cp的時效性也不好,容錯負載均衡也沒辦法做

======>

Flume誕生了

Flume架構

Flume組成架構如圖1-1,所示:

在這裏插入圖片描述
​ 圖1-1 Flume組成架構

Agent

Agent是一個JVM進程,它以事件的形式將數據從源頭送至目的,是Flume數據傳輸的基本單元。

Agent主要有3個部分組成,Source、Channel、Sink。

Source

Source是負責接收數據到Flume Agent的組件。Source組件可以處理各種類型、各種格式的日誌數據,包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、syslog、http、legacy。

Channel

Channel是位於Source和Sink之間的緩衝區。因此,Channel允許Source和Sink運作在不同的速率上。Channel是線程安全的,可以同時處理幾個Source的寫入操作和幾個Sink的讀取操作。

Flume自帶兩種Channel:Memory ChannelFile Channel

Memory Channel是內存中的隊列。Memory Channel在不需要關心數據丟失的情景下適用。如果需要關心數據丟失,那麼Memory Channel就不應該使用,因爲程序死亡、機器宕機或者重啓都會導致數據丟失。

File Channel將所有事件寫到磁盤。因此在程序關閉或機器宕機的情況下不會丟失數據。

Sink

Sink不斷地輪詢Channel中的事件且批量地移除它們,並將這些事件批量寫入到存儲或索引系統、或者被髮送到另一個Flume Agent。

Sink是完全事務性的。在從Channel批量刪除數據之前,每個Sink用Channel啓動一個事務。批量事件一旦成功寫出到存儲系統或下一個Flume Agent,Sink就利用Channel提交事務。事務一旦被提交,該Channel從自己的內部緩衝區刪除事件。

Sink組件目的地包括hdfs、logger、avro、thrift、ipc、file、null、HBase、solr、自定義。

Event

傳輸單元,Flume數據傳輸的基本單元,以事件的形式將數據從源頭送至目的地。

Flume拓撲結構

Flume的拓撲結構如圖1-3、1-4、1-5和1-6所示:

在這裏插入圖片描述

​ 圖1-3 Flume Agent連接

在這裏插入圖片描述

​ 圖1-4 單source,多channel、sink

在這裏插入圖片描述

​ 圖1-5 Flume負載均衡

在這裏插入圖片描述

​ 圖1-6 Flume Agent聚合

Flume安裝部署

Flume的安裝相對簡單,但是前提是要先下好Java環境JDK,1.8以上即可,JDK安裝可以查看Kafka安裝流程,這裏以Linux下的安裝爲例

Flume安裝地址

安裝部署

  1. 解壓apache-flume-1.7.0-bin.tar.gz到/usr/local/目錄下(安裝包詳見安裝包文件夾flume文件夾下的tar.gz壓縮包)
#把下載的包移動到目錄
$ sudo mv apache-flume-1.7.0-bin.tar.gz /usr/local
#解壓
$ sudo tar -zxvf apache-flume-1.7.0-bin.tar.gz  /usr/local/
  1. 修改apache-flume-1.7.0-bin的名稱爲flume
$ sudo mv apache-flume-1.7.0-bin flume
  1. 將flume/conf下的flume-env.sh.template文件修改爲flume-env.sh,並配置flume-env.sh文件
$ mv flume-env.sh.template flume-env.sh

$ vi flume-env.sh

export JAVA_HOME=/opt/module/jdk1.8.0_144(這裏路徑替換爲本機JDK安裝目錄)

案例實操

  • 監控端口數據

    • 案例需求:首先,Flume監控本機44444端口,然後通過telnet工具向本機44444端口發送消息,最後Flume將監聽的數據實時顯示在控制檯。

    • 需求分析

在這裏插入圖片描述

  • 實現步驟:

    • 安裝telnet工具

      在/usr/local目錄下創建flume-telnet文件夾。

      $ mkdir flume-telnet
      

      再將rpm軟件包(xinetd-2.3.14-40.el6.x86_64.rpm、telnet-0.17-48.el6.x86_64.rpm和telnet-server-0.17-48.el6.x86_64.rpm)拷入/usr/local/flume-telnet文件夾下面。執行RPM軟件包安裝命令:

      $ sudo rpm -ivh xinetd-2.3.14-40.el6.x86_64.rpm
      
      $ sudo rpm -ivh telnet-0.17-48.el6.x86_64.rpm
      
      $ sudo rpm -ivh telnet-server-0.17-48.el6.x86_64.rpm
      
      
  • 判斷44444端口是否被佔用

    判斷44444端口是否佔用,如果被佔用則kill掉或者更換端口

    $ sudo netstat -tunlp | grep 44444
    功能描述:netstat命令是一個監控TCP/IP網絡的非常有用的工具,它可以顯示路由表、實際的網絡連接以及每一個網絡接口設備的狀態信息。
    
    基本語法:netstat [選項]
    
    選項參數:
    
    -t或--tcp:顯示TCP傳輸協議的連線狀況; 
    
    -u或--udp:顯示UDP傳輸協議的連線狀況;
    
           -n或--numeric:直接使用ip地址,而不通過域名服務器; 
    
           -l或--listening:顯示監控中的服務器的Socket; 
    
           -p或--programs:顯示正在使用Socket的程序識別碼和程序名稱;
    
    
  • 創建Flume Agent配置文件flume-telnet-logger.conf

    在flume目錄下創建job文件夾並進入job文件夾

    $ mkdir job
    $ cd job/	
    
  • 在job文件夾下創建Flume Agent配置文件flume-telnet-logger.conf

    $ touch flume-telnet-logger.conf
    # 如果覺得vim上手難度太大,可以使用gedit來進行編輯
    $ vim flume-telnet-logger.conf
    # 在conf文件中加入以下內容
    
    # Name the components on this agent
    
    a1.sources = r1
    
    a1.sinks = k1
    
    a1.channels = c1
    
     
    
    # Describe/configure the source
    
    a1.sources.r1.type = netcat
    
    a1.sources.r1.bind = localhost
    
    a1.sources.r1.port = 44444
    
     
    
    # Describe the sink
    
    a1.sinks.k1.type = logger
    
     
    
    # Use a channel which buffers events in memory
    
    a1.channels.c1.type = memory
    
    a1.channels.c1.capacity = 1000
    
    a1.channels.c1.transactionCapacity = 100
    
     
    
    # Bind the source and sink to the channel
    
    a1.sources.r1.channels = c1
    
    a1.sinks.k1.channel = c1
    

注:配置文件來源於官方手冊

在這裏插入圖片描述

  • 先開啓flume監聽端口

    $ bin/flume-ng agent --conf conf/ --name a1 --conf-file job/flume-telnet-logger.conf -Dflume.root.logger=INFO,console
    
    參數說明:
    
           --conf conf/  :表示配置文件存儲在conf/目錄
    
           --name a1       :表示給agent起名爲a1
    
           --conf-file job/flume-telnet.conf :flume本次啓動讀取的配置文件是在job文件夾下的flume-telnet.conf文件。
    
           -Dflume.root.logger==INFO,console :-D表示flume運行時動態修改flume.root.logger參數屬性值,並將控制檯日誌打印級別設置爲INFO級別。日誌級別包括:log、info、warn、error。
    
  • 使用telnet工具向本機的44444端口發送內容

    $ telnet localhost 44444
    
  • 將A服務器上的日誌實時採集到B服務器

    一般跨節點都是使用avro sink

    技術選型有兩種方案:

    • exec source + memory channel + avro sink

      // Flume的關鍵就是寫配置文件,仍然是在conf文件夾下創建配置文件
      // avro-memory-sink.conf
      
      # Name the components on this agent
      exec-memory-avro.sources = exec-source
      exec-memory-avro.sinks = arvo-sink
      exec-memory-avro.channels = memory-channel
      
      # Describe/configure the source
      exec-memory-avro.sources.exec-source.type = exec
      exec-memory-avro.sources.exec-source.command = tail -F $FLUME_HOME/logs/flume.log
      exec-memory-avro.sources.exec-source.shell = /bin/sh -c
      
      # Describe the sink
      exec-memory-avro.sinks.arvo-sink.type = avro
      exec-memory-avro.sinks.arvo-sink.hostname = localhost
      exec-memory-avro.sinks.arvo-sink.port = 44444
      
      # Use a channel which buffers events in memory
      exec-memory-avro.channels.memory-channel.type = memory
      exec-memory-avro.channels.memory-channel.capacity = 1000
      exec-memory-avro.channels.memory-channel.transactionCapacity = 100
      
      # Bind the source and sink to the channel
      exec-memory-avro.sources.exec-source.channels = memory-channel
      exec-memory-avro.sinks.arvo-sink.channel = memory-channel
      
    • avro source + memory channel + logger sink

      // avro-logger-sink.conf
      # Name the components on this agent
      avro-memory-logger.sources = avro-source
      avro-memory-logger.sinks = logger-sink
      avro-memory-logger.channels = memory-channel
      
      # Describe/configure the source
      avro-memory-logger.sources.avro-source.type = avro
      avro-memory-logger.sources.avro-source.bind = localhost
      avro-memory-logger.sources.avro-source.port = 44444
      
      # Describe the sink
      avro-memory-logger.sinks.logger-sink.type = logger
      
      # Use a channel which buffers events in memory
      avro-memory-logger.channels.memory-channel.type = memory
      avro-memory-logger.channels.memory-channel.capacity = 1000
      avro-memory-logger.channels.memory-channel.transactionCapacity = 100
      
      # Bind the source and sink to the channel
      avro-memory-logger.sources.avro-source.channels = memory-channel
      avro-memory-logger.sinks.logger-sink.channel = memory-channel
      

    接下來啓動兩個配置

    先啓動avro-memory-logger
    
    flume-ng agent \
    
    --name avro-memory-logger \
    
    --conf $FLUME_HOME/conf \
    
    --conf-file $FLUME_HOME/conf/avro-memory-logger.conf \
    
    -Dflume.root.logger=INFO,console
    
    再啓動另外一個
    
    flume-ng agent --name exec-memory-avro 
    
    --conf $FLUME_HOME/conf \
    
    --conf-file $FLUME_HOME/conf/exec-memory-avro.conf \
    
    -Dflume.root.logger=INFO,console
    
    

在這裏插入圖片描述

在這裏插入圖片描述

一個可能因爲手誤出現的bug

log4j:WARN No appenders could be found for logger (org.apache.flume.lifecycle.LifecycleSupervisor).
log4j:WARN Please initialize the log4j system properly.

log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

出現這個錯誤是因爲路徑沒有寫對

往監聽的日誌中輸入一段字符串,可以看到我們的logger sink 已經成功接收到信息

在這裏插入圖片描述

上面Flume的基本流程圖如下

在這裏插入圖片描述


Kafka

Kafka是由Apache軟件基金會開發的一個開源流處理平臺,由ScalaJava編寫。該項目的目標是爲處理實時數據提供一個統一、高吞吐、低延遲的平臺。其持久化層本質上是一個“按照分佈式事務日誌架構的大規模發佈/訂閱消息隊列”,[3]這使它作爲企業級基礎設施來處理流式數據非常有價值。此外,Kafka可以通過Kafka Connect連接到外部系統(用於數據輸入/輸出),並提供了Kafka Streams——一個Java流式處理

具體的架構可以查看官網的intro部分

因爲在實際編程中使用kafka_2.11-0.11.00以上版本和使用以下版本的Java API 不一致,所以推薦直接參照官網的文檔進行編程。

環境搭建

單機單節點

搭建說明

需要有一定的Linux操作經驗,對於沒有權限之類的問題要懂得通過命令解決

Kafka的安裝相比Flume來說更加複雜,因爲Kafka依賴於Zookeeper

環境說明:

  • os:Ubuntu 18.04
  • zookeeper:zookeeper 3.4.9
  • kafka:kafka_2.11-0.11.0.0
  • jdk:jdk 8(kafka啓動需要使用到jdk)

詳細說明:

一、jdk安裝

jdk分爲以下幾種:jre、openjdk、 oracle jdk,這裏我們要安裝的是oracle jdk(推薦安裝)

add-apt-repository ppa:webupd8team/java
apt-get update
apt-get install oracle-java8-installer
apt-get install oracle-java8-set-default

測試安裝版本:

img

二、安裝配置zookeeper單機模式

下載zookeeper 3.4.5,開始安裝(軟件包詳見軟件包下的kafka中的壓縮包):

cd /usr/local
wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.5/zookeeper-3.4.5.tar.gz

img

等待安裝成功:

img

解壓:

tar -zxvf zookeeper-3.4.5.tar.gz

解壓後同目錄下便存在相同文件夾:

img

切換到conf目錄下:

cd zookeeper-3.4.5/conf/

img

複製zoo_sample.cfg到zoo.cfg:

cp zoo_sample.cfg zoo.cfg

然後編輯zoo.cfg如下(其它不用管,默認即可):

initLimit=10
syncLimit=5
dataDir=/home/young/zookeeper/data
clientPort=2181

img

別忘了新建dataDir目錄:

mkdir /home/young/zookeeper/data

爲zookeeper創建環境變量,打開/etc/profile文件,並在最末尾添加如下內容:

vi /etc/profile

添加內容如下:

export ZOOKEEPER_HOME=/home/young/zookeeper
export PATH=.:$ZOOKEEPER_HOME/bin:$JAVA_HOME/bin:$PATH

img

配置完成之後,切換到zookeeper/bin目錄下,啓動服務:

img

關閉服務:

img

這裏暫時先關閉zookeeper服務,防止下面使用kafka啓動時報端口占用錯誤。

三、安裝配置kafka單機模式

下載kafka(安裝包詳見軟件包kafka下的壓縮包):

cd /usr/local
wget https://www.apache.org/dyn/closer.cgi?path=/kafka/0.11.0.0/kafka_2.11-0.11.0.0.tgz

解壓:

tar -zxvf kafka_2.11-0.11.0.0.tgz

img

進入kafka/config目錄下:

img

以上文件是需要修改的文件,下面一個個修改配置:

配置server.properties:

以下爲修改的,其他爲默認即可:

#broker.id需改成正整數,單機爲1就好
broker.id=1
#指定端口號
port=9092
#localhost這一項還有其他要修改,詳細見下面說明
host.name=localhost
#指定kafka的日誌目錄
log.dirs=/usr/local/kafka_2.11-0.11.0.0/kafka-logs
#連接zookeeper配置項,這裏指定的是單機,所以只需要配置localhost,若是實際生產環境,需要在這裏添加其他ip地址和端口號
zookeeper.connect=localhost:2181

img

配置zookeeper.properties:

#數據目錄
dataDir=/usr/local/kafka_2.11-0.11.0.0/zookeeper/data
#客戶端端口
clientPort=2181
host.name=localhost

img

配置producer.properties:

zookeeper.connect=localhost:2181

img

配置consumer.properties:

zookeeper.connect=localhost:2181

img

最後還需要拷貝幾個jar文件到kafka的libs目錄,分別是zookeeper-xxxx.jar、log4j-xxxx.jar、slf4j-simple-xxxx.jar,最後如下:

img

四、kafka的使用

啓動zookeeper服務:

bin/zookeeper-server-start.sh config/zookeeper.properties

img

img

新開一個窗口啓動kafka服務:

bin/kafka-server-start.sh config/server.properties

img

img

至此單機服務搭建已經全部完成

單機多節點

對於單機單節點只需要使用一個配置文件來啓動即可,那麼對於單機多節點,只需要建立多個配置文件,並且啓動即可。比如我們需要有三個節點。

在這裏插入圖片描述

然後我們的每個server properies裏面的端口以及ID要不一致

server-1.properties

在這裏插入圖片描述

server-2.properties

在這裏插入圖片描述

server-3.properties

在這裏插入圖片描述

當然其對應的log對應目錄也要修改,這個就不多說了

然後在控制檯啓動

> bin/kafka-server-start.sh config/server-1.properties &
> bin/kafka-server-start.sh config/server-2.properties &
> bin/kafka-server-start.sh config/server-3.properties &

通過jps -m 能看到三個kafka即可(可能以普通用戶看不到相應的進程,只是因爲沒給到權限,可以給權限或者直接sudo su切換到超級用戶)

Kafka控制檯的一些命令操作

控制檯中我們可以通過命令建立topic,並且開啓一個消費者一個生產者來模擬通信,這些在官網的quickstart中都有詳盡的描述

[外鏈圖片轉存失敗(img-cCfCODtn-1569486879029)(../%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%9C%80%E7%BB%88%E7%89%88%E6%96%87%E6%A1%A3/kafka%E5%AD%A6%E4%B9%A0/producer.png)]

在這裏插入圖片描述

通過我們的一個叫topic的標籤,我們建立了一個生產者和一個消費者,可以明顯看到消費者接收到了生產者的消息。其他比較常用的命令,比如describe等可以自行探索。

Java API控制Kafka

接下來會說一個簡單的在Java中使用Kafka小例子

這裏都是基於2.11_0.11.0.0.0版本以及之後的編程來說明,更低版本相應的API有些許變化,低版本中很多函數已經被替代和廢除。

基本配置

  • 首先在Idea中建立一個新的Maven項目,這裏我們選擇一個achetype:scala-archetype-simple

在這裏插入圖片描述

  • 接下來我們把Maven文件配置好,並且auto import dependencies,這裏如果沒有選擇auto import,我們可以在Pom.xml右鍵找到maven選項裏面有一個reload

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.test.spark</groupId>
      <artifactId>spark streaming</artifactId>
      <version>1.0</version>
      <inceptionYear>2008</inceptionYear>
      <properties>
        <scala.version>2.7.0</scala.version>
        <kafka.version>0.11.0.0</kafka.version>
      </properties>
    
    
      <dependencies>
        <dependency>
          <groupId>org.scala-lang</groupId>
          <artifactId>scala-library</artifactId>
          <version>${scala.version}</version>
        </dependency>
        <dependency>
          <groupId>org.apache.kafka</groupId>
          <artifactId>kafka_2.11</artifactId>
          <version>${kafka.version}</version>
        </dependency>
      </dependencies>
    
      <build>
        <sourceDirectory>src/main/scala</sourceDirectory>
        <testSourceDirectory>src/test/scala</testSourceDirectory>
        <plugins>
          <plugin>
            <groupId>org.scala-tools</groupId>
            <artifactId>maven-scala-plugin</artifactId>
            <executions>
              <execution>
                <goals>
                  <goal>compile</goal>
                  <goal>testCompile</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <scalaVersion>${scala.version}</scalaVersion>
              <args>
                <arg>-target:jvm-1.5</arg>
              </args>
            </configuration>
          </plugin>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-eclipse-plugin</artifactId>
            <configuration>
              <downloadSources>true</downloadSources>
              <buildcommands>
                <buildcommand>ch.epfl.lamp.sdt.core.scalabuilder</buildcommand>
              </buildcommands>
              <additionalProjectnatures>
                <projectnature>ch.epfl.lamp.sdt.core.scalanature</projectnature>
              </additionalProjectnatures>
              <classpathContainers>
                <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
                <classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
              </classpathContainers>
            </configuration>
          </plugin>
        </plugins>
      </build>
      <reporting>
        <plugins>
          <plugin>
            <groupId>org.scala-tools</groupId>
            <artifactId>maven-scala-plugin</artifactId>
            <configuration>
              <scalaVersion>${scala.version}</scalaVersion>
            </configuration>
          </plugin>
        </plugins>
      </reporting>
    </project>
    
    
    • 因爲我們使用Java編程,所以我們在main下面建立一個java文件夾,並且把整個文件夾設爲source,如下圖

      在這裏插入圖片描述

  • 然後我們在這個例子會涉及到幾個Class,包括啓動的Class,消費者,生產者,配置

在這裏插入圖片描述

代碼分析

//KafkaProperties.java

package com.test.spark.kafka;

/**
 * Kafka常用配置文件
 */
public class KafkaProperties {

    public static final String ZK= "211.83.96.204:2181";
    public static final String TOPIC= "test";
    public static final String BROKER_LIST = "211.83.96.204:9092";
    public static final String GROUP_ID = "test_group1";

}

首先看一下配置文件,爲了配置能更加全局化好修改,我們直接建立一個配置文件,把可能需要的一些全局參數放進來,方便後續開發。其中有zookeeper的IPTopic名稱服務器列表以及group_id

// KafkaProducerClient.java

package com.test.spark.kafka;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;


/**
 * Kafka 生產者
 */
public class KafkaProducerClient extends Thread{

    private  String topic;
    private  Producer<String, String> producer;
    public KafkaProducerClient(String topic) {
        this.topic = topic;

        Properties properties = new Properties();
        properties.put("bootstrap.servers","localhost:9092");
//        properties.put("serializer.class","kafka.serializer.StringEncoder");
        properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
        properties.put("request.required.acks","1");
        producer = new KafkaProducer<String, String>(properties);
    }

    @Override
    public void run() {

        int messageNo = 1;

        while(true) {
            String message = "message_" + messageNo;
            producer.send(new ProducerRecord<String, String>(topic, message));
            System.out.println("Sent: " + message);

            messageNo ++;

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

消費者中我們使用多線程的方式,循環發送消息

// KafkaConsumerClient.java

package com.test.spark.kafka;

import kafka.consumer.ConsumerConnector$class;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;

import java.util.Arrays;
import java.util.List;
import java.util.Properties;

/**
 * Kafka消費者
 */
public class KafkaConsumerClient {
    private String topic;

    public KafkaConsumerClient(String topic) {
        this.topic = topic;


    }

    public void start() {
        Properties props = new Properties();

        props.put("bootstrap.servers", "localhost:9092");
        props.put("group.id", KafkaProperties.GROUP_ID);//不同ID 可以同時訂閱消息
        props.put("enable.auto.commit", "false");//自動commit
        props.put("auto.commit.interval.ms", "1000");//定時commit的週期
        props.put("session.timeout.ms", "30000");//consumer活性超時時間
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
        consumer.subscribe(Arrays.asList(this.topic));//訂閱TOPIC
        try {
            while(true) {//輪詢
                ConsumerRecords<String, String> records =consumer.poll(Long.MAX_VALUE);//超時等待時間
                for (TopicPartition partition : records.partitions()) {
                    List<ConsumerRecord<String, String>> partitionRecords = records.records(partition);
                    for (ConsumerRecord<String, String> record : partitionRecords) {
                        System.out.println("receive" + ": " + record.value());
                    }
                    consumer.commitSync();//同步
                }
            }
        } finally

        {
            consumer.close();
        }
    }
}

在消費中我們會輪詢消息

在這裏插入圖片描述

Flume+Kafka配合

把logger sink ===> kafka sink

sink kafka: producer

所以啓動一個kafka的consumer,直接對接到kafka sink消費掉即可

//avro-memory-kafka.conf

# Name the components on this agent
avro-memory-kafka.sources = avro-source
avro-memory-kafka.sinks = kafka-sink
avro-memory-kafka.channels = memory-channel

# Describe/configure the source
avro-memory-kafka.sources.avro-source.type = avro
avro-memory-kafka.sources.avro-source.bind = localhost
avro-memory-kafka.sources.avro-source.port = 44444

# Describe the sink
avro-memory-kafka.sinks.kafka-sink.type = org.apache.flume.sink.kafka.KafkaSink
avro-memory-kafka.sinks.kafka-sink.brokerList = localhost:9092
avro-memory-kafka.sinks.kafka-sink.topic = test
avro-memory-kafka.sinks.kafka-sink.batchSize = 5
avro-memory-kafka.sinks.kafka-sink.requiredAcks = 1

# Use a channel which buffers events in memory
avro-memory-kafka.channels.memory-channel.type = memory
avro-memory-kafka.channels.memory-channel.capacity = 1000
avro-memory-kafka.channels.memory-channel.transactionCapacity = 100

# Bind the source and sink to the channel
avro-memory-kafka.sources.avro-source.channels = memory-channel
avro-memory-kafka.sinks.kafka-sink.channel = memory-channel

[外鏈圖片轉存失敗(img-o7uizDQ2-1569486879031)(…/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%9C%80%E7%BB%88%E7%89%88%E6%96%87%E6%A1%A3/kafka%E5%AD%A6%E4%B9%A0/flume/connect_flume_kafka.png)]

注意這個batchSize,在數據量沒有到達設定的閾值時,他會有一個timeout,這之後纔會有數據發過來


Spark

Spark 簡介

  1. 什麼是Spark?Spark作爲Apache頂級的開源項目,是一個快速、通用的大規模數據處理引擎,和Hadoop的MapReduce計算框架類似,但是相對於MapReduce,Spark憑藉其可伸縮、基於內存計算等特點,以及可以直接讀寫Hadoop上任何格式數據的優勢,進行批處理時更加高效,並有更低的延遲。相對於“one stack to rule them all”的目標,實際上,Spark已經成爲輕量級大數據快速處理的統一平臺,各種不同的應用,如實時流處理、機器學習、交互式查詢等,都可以通過Spark建立在不同的存儲和運行系統上。
  2. Spark是基於內存計算的大數據並行計算框架。Spark基於內存計算,提高了在大數據環境下數據處理的實時性,同時保證了高容錯性和高可伸縮性,允許用戶將Spark部署在大量廉價硬件之上,形成集羣。
  3. Spark於2009年誕生於加州大學伯克利分校AMPLab。目前,已經成爲Apache軟件基金會旗下的頂級開源項目。相對於MapReduce上的批量計算、迭代型計算以及基於Hive的SQL查詢,Spark可以帶來上百倍的性能提升。目前Spark的生態系統日趨完善,Spark SQL的發佈、Hive on Spark項目的啓動以及大量大數據公司對Spark全棧的支持,讓Spark的數據分析範式更加豐富。

Spark環境搭建

Hadoop安裝(Spark依賴於Hadoop安裝)

參考鏈接

Hadoop可以通過HadoopDownloadOne 或者HadoopDownloadTwo 下載,一般選擇下載最新的穩定版本,即下載 “stable” 下的hadoop-2.x.y.tar.gz 這個格式的文件(詳見安裝文件夾中的hadoop-2.7.7)

$ sudo tar -zxf  hadoop-2.7.7.tar.gz  -C /usr/local    # 解壓到/usr/local中
$ cd /usr/local/
$ sudo mv ./hadoop-2.6.0/ ./hadoop            # 將文件夾名改爲hadoop
$ sudo chown -R hadoop ./hadoop       # 修改文件權限

Hadoop 解壓後即可使用。輸入如下命令來檢查 Hadoop 是否可用,成功則會顯示 Hadoop 版本信息:

$ cd /usr/local/hadoop
$ ./bin/hadoop version

Hadoop單機配置及運行測試

Hadoop 默認模式爲非分佈式模式(本地模式),無需進行其他配置即可運行。非分佈式即單 Java 進程,方便進行調試。

現在我們可以執行例子來感受下 Hadoop 的運行。Hadoop 附帶了豐富的例子(運行 ./bin/hadoop jar ./share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.7.jar 可以看到所有例子),包括 wordcount、terasort、join、grep 等。

$ cd /usr/local/hadoop
$ mkdir ./input
$ cp ./etc/hadoop/*.xml ./input   # 將配置文件作爲輸入文件
$ ./bin/Hadoop jar ./share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar grep ./input ./output 'dfs[a-z.]+'
$ cat ./output/*          # 查看運行結果

注意,Hadoop 默認不會覆蓋結果文件,因此再次運行上面實例會提示出錯,需要先將 ./output 刪除。

如果中間提示 Error: JAVA_HOME is not set and could not be found. 的錯誤,則說明之前設置 JAVA_HOME 環境變量那邊就沒設置好,請按教程先設置好 JAVA_HOME 變量,否則後面的過程都是進行不下去的。如果已經按照前面教程在.bashrc文件中設置了JAVA_HOME,還是出現 Error: JAVA_HOME is not set and could not be found. 的錯誤,那麼,請到hadoop的安裝目錄修改配置文件“/usr/local/hadoop/etc/hadoop/hadoop-env.sh”,在裏面找到“export JAVA_HOME=${JAVA_HOME}”這行,然後,把它修改成JAVA安裝路徑的具體地址,比如,“export JAVA_HOME=/usr/lib/jvm/default-java”,然後,再次啓動Hadoop。

Spark安裝

此處採用Spark和Hadoop一起安裝使用,這樣,就可以讓Spark使用HDFS存取數據。需要說明的是,當安裝好Spark以後,裏面就自帶了scala環境,不需要額外安裝scala。在安裝spark之前,需要先安裝Java和Hadoop。

需要的具體運行環境如下:

Ø Ubuntu16.04以上

Ø Hadoop 2.7.1以上

Ø Java JDK 1.8以上

Ø Spark 2.1.0 以上

Ø Python 3.4以上

(此次系統環境使用的Ubuntu16.04,自帶Python,不需額外安裝)

Spark官網下載

由於已經安裝了Hadoop,所以在Choose a package type後面需要選擇Pre-build with user-provided Hadoop,然後點擊Download Spark後面的spark-2.1.0-bin-without-hadoop.tgz下載即可。需要說明的是,Pre-build with user-provided Hadoop:屬於“Hadoop free”版,這樣下載到的Spark,可應用到任意Hadoop版本。

Spark部署模式主要有四種:Local模式(單機模式)、Standalone模式(使用Spark自帶的簡單集羣管理器)、YARN模式(使用YARN作爲集羣管理器)和Mesos模式(使用Mesos作爲集羣管理器)。

這裏介紹Local模式(單機模式)的 Spark安裝。我們選擇Spark 2.4.3版本,並且假設當前使用用戶名hadoop登錄了Linux操作系統。

$ sudo tar -zxf ~/下載/spark-2.4.3-bin-without-hadoop.tgz -C/usr/local/
$ sudo mv ./spark-2.4.3-bin-without-hadoop/ ./spark
$ sudo chown -R hadoop:hadoop ./spark       # 此處的 hadoop 爲你的用戶名

安裝後,還需要修改Spark的配置文件spark-env.sh

$ cd /usr/local/spark
$ sudo cp conf/spark-env.sh.template conf/spark-env.sh
$ sudo vim conf/spark-env.sh
#添加下面的環境變量信息
export SPARK_DIST_CLASSPATH=$(/usr/local/hadoop/bin/hadoop:classpath)

有了上面的配置信息以後,Spark就可以把數據存儲到Hadoop分佈式文件系統HDFS中,也可以從HDFS中讀取數據。如果沒有配置上面信息,Spark就只能讀寫本地數據,無法讀寫HDFS數據。

配置完成後就可以直接使用,不需要像Hadoop運行啓動命令。通過運行Spark自帶的示例,驗證Spark是否安裝成功。

$ cd /usr/local/spark
$ bin/run-example SparkPi

過濾後的運行結果如下圖示,可以得到π 的 5 位小數近似值:

在這裏插入圖片描述

Spark不依賴Hadoop安裝

Spark同樣也可以不依賴hadoop進行安裝,但是仍然需要JDK環境,同樣是在Spark官網上,選擇spark-2.4.3-bin-hadoop2.7.tgz。我們直接將其解壓出來,下面我們開始配置環境變量。我們進入編輯/etc/profile,在最後加上如下代碼。

#Spark
export SPARK_HOME=/opt/spark-2.4.3
export PATH=$PATH:$SPARK_HOME/bin

然後進入/spark-2.3.1/bin目錄下即可直接運行spark-shell。

下面配置本地集羣環境,首先我們進入剛剛解壓的Spark目錄,進入/spark-2.2.1/conf/,拷貝一份spark-env.sh。

$ cp spark-env.sh.template spark-env.sh

然後我們編輯這個文件,添加如下環境設置(按自身環境修改)

#export SCALA_HOME=/opt/scala-2.13.0
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-3.b13.el7_5.x86_64  #這裏是你jdk的安裝路徑
export SPARK_HOME=/opt/spark-2.4.3
export SPARK_MASTER_IP=XXX.XX.XX.XXX  #將這裏的xxx改爲自己的Linux的ip地址
#export SPARK_EXECUTOR_MEMORY=512M
#export SPARK_WORKER_MEMORY=1G
#export master=spark://XXX.XX.XX.XXX:7070

再回到conf目錄下,拷貝一份slaves。

$ cp slaves.template slaves

在slaves最後加上localhost,保存即可。最後想要啓動spark,進入安裝目錄下的sbin文件夾下,運行start-all.sh輸入登錄密碼,master和worker進程就能按照配置文件啓動。

在Spark Shell 中運行代碼

這裏介紹Spark Shell的基本使用。Spark shell提供了簡單的方式來學習API,並且提供了交互的方式來分析數據。它屬於REPL(Read-Eval-Print Loop,交互式解釋器),提供了交互式執行環境,表達式計算完成就會輸出結果,而不必等到整個程序運行完畢,因此可即時查看中間結果,並對程序進行修改,這樣可以在很大程度上提升開發效率。

Spark Shell支持Scala和Python,本文使用 Scala 來進行介紹。前面已經安裝了Hadoop和Spark,如果Spark不使用HDFS和YARN,那麼就不用啓動Hadoop也可以正常使用Spark。如果在使用Spark的過程中需要用到 HDFS,就要首先啓動 Hadoop。

這裏假設不需要用到HDFS,因此,就沒有啓動Hadoop。現在直接開始使用Spark。Spark-shell命令及其常用的參數如下:

$ ./bin/spark-shell —master

Spark的運行模式取決於傳遞給SparkContext的Master URL的值。Master URL可以是以下任一種形式:

  • local 使用一個Worker線程本地化運行SPARK(完全不併行)

  • local[*] 使用邏輯CPU個數數量的線程來本地化運行Spark

  • local[K] 使用K個Worker線程本地化運行Spark(理想情況下,K應該根據運行機器的CPU核數設定)

  • spark://HOST:PORT 連接到指定的Spark standalone master。默認端口是7077.

  • yarn-client 以客戶端模式連接YARN集羣。集羣的位置可以在HADOOP_CONF_DIR 環境變量中找到。

  • yarn-cluster 以集羣模式連接YARN集羣。集羣的位置可以在HADOOP_CONF_DIR 環境變量中找到。

  • mesos://HOST:PORT 連接到指定的Mesos集羣。默認接口是5050。

需要強調的是,本文采用“本地模式”(local)運行Spark,關於如何在集羣模式下運行Spark,之後的文章會着重介紹。

在Spark中採用本地模式啓動Spark Shell的命令主要包含以下參數:

–master:這個參數表示當前的Spark Shell要連接到哪個master,如果是local[*],就是使用本地模式啓動spark-shell,其中,中括號內的星號表示需要使用幾個CPU核心(core);

–jars: 這個參數用於把相關的JAR包添加到CLASSPATH中;如果有多個jar包,可以使用逗號分隔符連接它們;

比如,要採用本地模式,在4個CPU核心上運行spark-shell:

$ cd /usr/local/spark
$ /bin/spark-shell —master local[4]

或者,可以在CLASSPATH中添加code.jar,命令如下:

$ cd /usr/local/spark
$ ./bin/spark-shell -master local[4] --jars code.jar

可以執行spark-shell –help命令,獲取完整的選項列表,具體如下:

$ cd /usr/local/spark
$ ./bin/spark-shell —help

[外鏈圖片轉存失敗(img-36hzRWo9-1569486879033)(spark-shell.png)]

上面是命令使用方法介紹,下面正式使用命令進入spark-shell環境,可以通過下面命令啓動spark-shell環境:

scala> 8*2+5
res0: Int = 21

最後,可以使用命令“:quit”退出Spark Shell,如下所示:

scala>:quit

或者,也可以直接使用“Ctrl+D”組合鍵,退出Spark Shell

Scala編寫wordCount

任務需求

學會了上文基本的安裝和執行後,現在練習一個任務:編寫一個Spark應用程序,對某個文件中的單詞進行詞頻統計。

準備工作:進入Linux系統,打開“終端”,進入Shell命令提示符狀態,然後,執行如下命令新建目錄:

$ cd /usr/local/spark
$ mkdir mycode
$ cd mycode
$ mkdir wordcount
$ cd wordcount

然後,在/usr/local/spark/mycode/wordcount目錄下新建一個包含了一些語句的文本文件word.txt,命令如下:

$  vim word.txt

首先可以在文本文件中隨意輸入一些單詞,用空格隔開,編寫Spark程序對該文件進行單詞詞頻統計。然後,按鍵盤Esc鍵退出vim編輯狀態,輸入“:wq”保存文件並退出vim編輯器。

在Spark-Shell中執行詞頻統計

  • 啓動Spark-Shell

    首先,登錄Linux系統(要注意記住登錄採用的用戶名,本教程統一採用hadoop用戶名進行登錄),打開“終端”(可以在Linux系統中使用Ctrl+Alt+T組合鍵開啓終端),進入shell命令提示符狀態,然後執行以下命令進入spark-shell:

$ cd /usr/local/spark
$ ./bin_spark-shell
$ …這裏省略啓動過程顯示的一大堆信息
$ scala>

​ 啓動進入spark-shell需要一點時間,在進入spark-shell後,我們可能還需要到Linux文件系統中對相關目錄下的文件進行編輯和操作(比如要查看spark程序執行過程生成的文件),這個無法在park-shell中完成,因此,這裏再打開第二個終端,用來在Linux系統的Shell命令提示符下操作。

  • 加載本地文件

    在開始具體詞頻統計代碼之前,需要考慮如何加載文件,文件可能位於本地文件系統中,也有可能存放在分佈式文件系統HDFS中,下面先介紹介紹如何加載本地文件,以及如何加載HDFS中的文件。首先,請在第二個終端窗口下操作,用下面命令到達/usr/local/spark/mycode/wordcount目錄,查看一下上面已經建好的word.txt的內容:

    $ cd /usr/local/spark/mycode/wordcount
    $ cat word.txt
    
    

    Cat命令會把word.txt文件的內容全部顯示到屏幕上。

    現在切換回spark-shell,然後輸入下面命令:

    scala> val textFile = sc.textFile(“file:///usr/local/spark/mycode/wordcount/word.txt”)
    

    上面代碼中,val後面的是變量textFile,而sc.textFile()中的這個textFile是sc的一個方法名稱,這個方法用來加載文件數據。這兩個textFile不是一個東西,不要混淆。實際上,val後面的是變量textFile,你完全可以換個變量名稱,比如,val lines = sc.textFile(“file:///usr/local/spark/mycode/wordcount/word.txt”)。這裏使用相同名稱,就是有意強調二者的區別。

    注意要加載本地文件,必須採用“file:///”開頭的這種格式。執行上上面這條命令以後,並不會馬上顯示結果,因爲Spark採用惰性機制,只有遇到“行動”類型的操作,纔會從頭到尾執行所有操作。所以,下面我們執行一條“行動”類型的語句,就可以看到結果:

    scala>textFile.first()
    

    first()是一個“行動”(Action)類型的操作,會啓動真正的計算過程,從文件中加載數據到變量textFile中,並取出第一行文本。屏幕上會顯示很多反饋信息,這裏不再給出,你可以從這些結果信息中,找到word.txt文件中的第一行的內容。

    正因爲Spark採用了惰性機制,在執行轉換操作的時候,即使我們輸入了錯誤的語句,spark-shell也不會馬上報錯,而是等到執行“行動”類型的語句時啓動真正的計算,那個時候“轉換”操作語句中的錯誤就會顯示出來,比如:

     val textFile = sc.textFile(“file:///usr/local/spark/mycode/wordcount/word123.txt”)
    

    上面我們使用了一個根本就不存在的word123.txt,執行上面語句時,spark-shell根本不會報錯,因爲,沒有遇到“行動”類型的first()操作之前,這個加載操作時不會真正執行的。然後,我們執行一個“行動”類型的操作first(),如下:

     scala> textFile.first()
    

    執行上面語句後,會返回錯誤信息“拒絕連接”,因爲這個word123.txt文件根本就不存在。現在我們可以練習一下如何把textFile變量中的內容再次寫回到另外一個文本文件wordback.txt中:

    val textFile = sc.textFile(“file:///usr/local/spark/mycode/wordcount/word.txt”)
    textFile.saveAsTextFile(“file:///usr/local/spark/mycode/wordcount/writeback”)
    
    

    上面的saveAsTextFile()括號裏面的參數是保存文件的路徑,不是文件名。saveAsTextFile()是一個“行動”(Action)類型的操作,所以馬上會執行真正的計算過程,從word.txt中加載數據到變量textFile中,然後,又把textFile中的數據寫回到本地文件目錄“_usr_local_spark_mycode_wordcount_writeback/”下面,現在讓我們切換到Linux Shell命令提示符窗口中,執行下面命令:

    $ cd /usr/local/spark/mycode/wordcount/writeback/
    $ ls
    

    執行結果會顯示,有兩個文件part-00000和_SUCCESS,我們可以使用cat命令查看一下part-00000文件,會發現結果是和上面word.txt中的內容一樣的。

    詞頻統計

    有了前面的鋪墊性介紹,下面我們開始第一個Spark應用程序:WordCount。請切換到spark-shell窗口,輸入如下命令:

    scala> val textFile = sc.textFile(“file:///usr/local/spark/mycode/wordcount/word.txt”)
    scala> val wordCount = textFile.flatMap(line => line.split(“ “)).map(word => (word, 1)).reduceByKey((a, b) => a + b)
    scala> wordCount.collect()
    

    上面只給出了代碼,省略了執行過程中返回的結果信息,因爲返回信息很多。下面簡單解釋一下上面的語句。

    • textFile包含了多行文本內容,textFile.flatMap(line => line.split(” “))會遍歷textFile中的每行文本內容,當遍歷到其中一行文本內容時,會把文本內容賦值給變量line,並執行Lamda表達式line => line.split(” “)。line => line.split(” “)是一個Lamda表達式,左邊表示輸入參數,右邊表示函數裏面執行的處理邏輯,這裏執行line.split(” “),也就是針對line中的一行文本內容,採用空格作爲分隔符進行單詞切分,從一行文本切分得到很多個單詞構成的單詞集合。這樣,對於textFile中的每行文本,都會使用Lamda表達式得到一個單詞集合,最終,多行文本,就得到多個單詞集合。textFile.flatMap()操作就把這多個單詞集合“拍扁”得到一個大的單詞集合。

    • 然後,針對這個大的單詞集合,執行map()操作,也就是map(word => (word, 1)),這個map操作會遍歷這個集合中的每個單詞,當遍歷到其中一個單詞時,就把當前這個單詞賦值給變量word,並執行Lamda表達式word => (word, 1),這個Lamda表達式的含義是,word作爲函數的輸入參數,然後,執行函數處理邏輯,這裏會執行(word, 1),也就是針對輸入的word,構建得到一個tuple,形式爲(word,1),key是word,value是1(表示該單詞出現1次)。

    • 程序執行到這裏,已經得到一個RDD,這個RDD的每個元素是(key,value)形式的tuple。最後,針對這個RDD,執行reduceByKey((a, b) => a + b)操作,這個操作會把所有RDD元素按照key進行分組,然後使用給定的函數(這裏就是Lamda表達式:(a, b) => a + b),對具有相同的key的多個value進行reduce操作,返回reduce後的(key,value),比如(“hadoop”,1)和(“hadoop”,1),具有相同的key,進行reduce以後就得到(“hadoop”,2),這樣就計算得到了這個單詞的詞頻。

編寫獨立應用程序執行詞頻統計

在上面spark-shell編寫wordcount後,下面我們編寫一個Scala應用程序來實現詞頻統計。首先登錄Linux系統,進入Shell命令提示符狀態,然後執行下面命令:

$ cd /usr/local/spark/mycode/wordcount/
$ mkdir -p src/main/scala  這裏加入-p選項,可以一起創建src目錄及其子目錄

然後在“/usr/local/spark/mycode/wordcount/src/main/scala”目錄下新建一個test.scala文件,裏面包含如下代碼:

import org.apache.spark.SparkContext
import org.apache.spark.SparkContext./
import org.apache.spark.SparkConf

object WordCount {
def main(args: Array[String]) {
val inputFile =  “file:///usr/local/spark/mycode/wordcount/word.txt”
val conf = new SparkConf().setAppName(“WordCount”).setMaster(“local[2]”)
val sc = new SparkContext(conf)
val textFile = sc.textFile(inputFile)
val wordCount = textFile.flatMap(line => line.split(“ “)).map(word => (word, 1)).reduceByKey((a, b) => a + b)
wordCount.foreach(println)
}
}

注意,SparkConf().setAppName(“WordCount”).setMaster(“local[2]”)這句語句,也可以刪除.setMaster(“local[2]”),只保留 val conf = new SparkConf().setAppName(“WordCount”)

如果test.scala沒有調用SparkAPI,則只要使用scalac命令編譯後執行即可。此處test.scala程序依賴 Spark API,因此需要通過 sbt 進行編譯打包。首先執行如下命令:

$ cd /usr/local/spark/mycode/wordcount/
$ vim simple.sbt

通過上面代碼,新建一個simple.sbt文件,請在該文件中輸入下面代碼:

name := “Simple Project”
version := “1.0”
scalaVersion := “2.11.8”
libraryDependencies += “org.apache.spark” %% “spark-core” % “2.1.0”

下面我們使用sbt打包Scala程序。爲保證sbt能正常運行,先執行如下命令檢查整個應用程序的文件結構,應該是類似下面的文件結構:

$ ./src
$ ./src/main
$ ./src/main/scala
$ ./src/main/scala/test.scala
$ ./simple.sbt
$ ./word.txt

接着,我們就可以通過如下代碼將整個應用程序打包成 JAR(首次運行同樣需要下載依賴包 ):

$ cd /usr/local/spark/mycode/wordcount/  請一定把這目錄設置爲當前目錄
$ /usr/local/sbt/sbt package

上面執行過程需要消耗幾分鐘時間,屏幕上會返回一下信息:

hadoop@dblab-VirtualBox:_usr_local_spark_mycode_wordcount$ /usr_local_sbt_sbt package
OpenJDK 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0
[info] Set current project to Simple Project (in build file:/usr_local_spark_mycode_wordcount/)
[info] Updating {file:/usr_local_spark_mycode_wordcount/}wordcount…
[info] Resolving jline#jline;2.12.1 …
[info] Done updating.
[info] Packaging _usr_local_spark_mycode_wordcount_target_scala-2.11_simple-project_2.11-1.0.jar …
[info] Done packaging.
[success] Total time: 34 s, completed 2017-2-20 10:13:13

若屏幕上返回上述信息表明打包成功,生成的 jar 包的位置爲/usr/local/spark/mycode/wordcount/target/scala-2.11_simple-project_2.11-1.0.jar

最後通過spark-submit 運行程序。我們就可以將生成的jar包通過spark-submit提交到Spark中運行了,命令如下:

$ /usr/local/spark/bin/spark-submit —class “WordCount”  /usr/local/spark/mycode/wordcount/target/scala-2.11_simple-project_2.11-1.0.jar

最終得到的詞頻統計結果類似如下:

(Spark,1)
(is,1)
(than,1)
(fast,1)
(love,2)
(i,1)
(I,1)
(hadoop,2)

Flume_Kafka_SparkStreaming實現詞頻統計

準備工作

在做這個project之前,需要預先準備好的環境如下:

安裝kafka(參考第一節)、安裝flume(參考第二節)、安裝Spark(參考第三節) 。

做完上面三個工作之後,我們開始進入正式的詞頻統計Demo。

Spark準備工作

要通過Kafka連接Spark來進行Spark Streaming操作,Kafka和Flume等高級輸入源,需要依賴獨立的庫(jar文件)。也就是說Spark需要jar包讓Kafka和Spark streaming相連。按照我們前面安裝好的Spark版本,這些jar包都不在裏面,爲了證明這一點,我們現在可以測試一下。請打開一個新的終端,輸入以下命令啓動spark-shell:

$ cd /usr/local/spark
$ ./bin/spark-shell

啓動成功後,在spark-shell中執行下面import語句:

import org.apache.spark.streaming.kafka._

程序報錯,因爲找不到相關jar包。根據Spark官網的說明,對於Spark版本,如果要使用Kafka,則需要下載spark-streaming-kafka相關jar包。Jar包下載地址(注意版本對應關係)。

在這裏插入圖片描述

接下來需要把這個文件複製到Spark目錄的jars目錄下,輸入以下命令:

$ cd /usr/local/spark/jars
$ mkdir kafka
$ cp ./spark-streaming-kafka-0-8_2.11-2.1.0.jar /usr/local/spark/jars/kafka

下面把Kafka安裝目錄的libs目錄下的所有jar文件複製到/usr/local/spark/jars/kafka目錄下輸入以下命令:至此,所有環境準備工作已全部完成,下面開始編寫代碼。

Project 過程

  • 編寫Flume配置文件flume_to_kafka.conf

    輸入命令:

    $ cd /usr/local/kafka/libs
    $ ls
    $ cp ./* /usr/local/spark/jars/kafka
    
    

    內容如下:

    a1.sources=r1
    a1.channels=c1
    a1.sinks=k1
    #Describe/configure the source 
    a1.sources.r1.type=netcat
    a1.sources.r1.bind=localhost
    a1.sources.r1.port=33333
    #Describe the sink
    a1.sinks.k1.type=org.apache.flume.sink.kafka.KafkaSink  
    a1.sinks.k1.kafka.topic=test  
    a1.sinks.k1.kafka.bootstrap.servers=localhost:9092  
    a1.sinks.k1.kafka.producer.acks=1  
    a1.sinks.k1.flumeBatchSize=20  
    #Use a channel which buffers events in memory  
    a1.channels.c1.type=memory
    a1.channels.c1.capacity=1000000
    a1.channels.c1.transactionCapacity=1000000
    #Bind the source and sink to the channel
    a1.sources.r1.channels=c1
    a1.sinks.k1.channel=c1
    
  • 編寫Spark Streaming程序(進行詞頻統計的程序)

    首先創建scala代碼的目錄結構,輸入命令:

    $ cd /usr/local/spark/mycode
    $ mkdir flume_to_kafka
    $ cd flume_to_kafka
    $ mkdir -p src/main/scala
    $ cd src/main/scala
    $ vim KafkaWordCounter.scala
    
    

    KafkaWordCounter.scala是用於單詞詞頻統計,它會把從kafka發送過來的單詞進行詞頻統計,代碼內容如下:

    reduceByKeyAndWindow函數作用解釋如下:

    package org.apache.spark.examples.streaming
    import org.apache.spark._
    import org.apache.spark.SparkConf
    import org.apache.spark.streaming._
    import org.apache.spark.streaming.kafka._
    import org.apache.spark.streaming.StreamingContext._
    import org.apache.spark.streaming.kafka.KafkaUtils
    
    object KafkaWordCounter{
    def main(args:Array[String]){
    StreamingExamples.setStreamingLogLevels()
    val sc=new SparkConf().setAppName("KafkaWordCounter").setMaster("local[2]")
    val ssc=new StreamingContext(sc,Seconds(10))
    ssc.checkpoint("file:///usr/local/spark/mycode/flume_to_kafka/checkpoint") //設置檢查點
    val zkQuorum="localhost:2181" //Zookeeper服務器地址
    val group="1"  //topic所在的group,可以設置爲自己想要的名稱,比如不用1,而是val group = "test-consumer-group" 
    val topics="test" //topics的名稱          
    val numThreads=1 //每個topic的分區數
    val topicMap=topics.split(",").map((_,numThreads.toInt)).toMap
    val lineMap=KafkaUtils.createStream(ssc,zkQuorum,group,topicMap)
    val lines=lineMap.map(_._2)
    val words=lines.flatMap(_.split(" "))
    val pair=words.map(x => (x,1))
    val wordCounts=pair.reduceByKeyAndWindow(_ + _,_ - _,Minutes(2),Seconds(10),2) 
    wordCounts.print
    ssc.start
    ssc.awaitTermination
    }
    }
    
    

    reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]) 更加高效的reduceByKeyAndWindow,每個窗口的reduce值,是基於先前窗口的reduce值進行增量計算得到的;它會對進入滑動窗口的新數據進行reduce操作,並對離開窗口的老數據進行“逆向reduce”操作。但是,只能用於“可逆reduce函數”,即那些reduce函數都有一個對應的“逆向reduce函數”(以InvFunc參數傳入);

    此代碼中就是一個窗口轉換操作reduceByKeyAndWindow,其中,Minutes(2)是滑動窗口長度,Seconds(10)是滑動窗口時間間隔(每隔多長時間滑動一次窗口)。reduceByKeyAndWindow中就使用了加法和減法這兩個reduce函數,加法和減法這兩種reduce函數都是“可逆的reduce函數”,也就是說,當滑動窗口到達一個新的位置時,原來之前被窗口框住的部分數據離開了窗口,又有新的數據被窗口框住,但是,這時計算窗口內單詞的詞頻時,不需要對當前窗口內的所有單詞全部重新執行統計,而是隻要把窗口內新增進來的元素,增量加入到統計結果中,把離開窗口的元素從統計結果中減去,這樣,就大大提高了統計的效率。尤其對於窗口長度較大時,這種“逆函數”帶來的效率的提高是很明顯的。

  • 創建StreamingExamples.scala

    繼續在當前目錄(/usr/local/spark/mycode/flume_to_kafka/src/main/scala)下創建StreamingExamples.scala代碼文件,用於設置log4j,輸入命令:

    vim StreamingExamples.scala

    package org.apache.spark.examples.streaming
    import org.apache.spark.internal.Logging
    import org.apache.log4j.{Level, Logger}
    //Utility functions for Spark Streaming examples. 
    object StreamingExamples extends Logging {
    //Set reasonable logging levels for streaming if the user has not configured log4j. 
      def setStreamingLogLevels() {
        val log4jInitialized = Logger.getRootLogger.getAllAppenders.hasMoreElements
        if (!log4jInitialized) {
          // We first log something to initialize Spark's default logging, then we override the
          // logging level.
          logInfo("Setting log level to [WARN] for streaming example." +" To override add a custom log4j.properties to the classpath.")
          Logger.getRootLogger.setLevel(Level.WARN)
        }
      }
    }
    
    
  • 創建StreamingExamples.scala

    繼續在當前目錄(/usr/local/spark/mycode/flume_to_kafka/src/main/scala)下創建StreamingExamples.scala代碼文件,用於設置log4j,輸入命令:

    vim StreamingExamples.scala

    package org.apache.spark.examples.streaming
    import org.apache.spark.internal.Logging
    import org.apache.log4j.{Level, Logger}
    //Utility functions for Spark Streaming examples. 
    object StreamingExamples extends Logging {
    //Set reasonable logging levels for streaming if the user has not configured log4j. 
      def setStreamingLogLevels() {
        val log4jInitialized = Logger.getRootLogger.getAllAppenders.hasMoreElements
        if (!log4jInitialized) {
          // We first log something to initialize Spark's default logging, then we override the
          // logging level.
          logInfo("Setting log level to [WARN] for streaming example." +" To override add a custom log4j.properties to the classpath.")
          Logger.getRootLogger.setLevel(Level.WARN)
        }
      }
    }
    
    
  • 打包文件simple.sbt

    輸入命令:

    $ cd /usr/local/spark/mycode/flume_to_kafka
    $ vim simple.sbt
    

    內容如下:

    name := "Simple Project"
    version := "1.0"
    scalaVersion := "2.11.8"
    libraryDependencies += "org.apache.spark" %% "spark-core" % "2.1.0"
    libraryDependencies += "org.apache.spark" % "spark-streaming_2.11" % "2.1.0"
    libraryDependencies += "org.apache.spark" % "spark-streaming-kafka-0-8_2.11" % "2.1.0"
    

    要注意版本號一定要設置正確,在/usr/local/spark/mycode/flume_to_kafka目錄下輸入命令:

    $ cd /usr/local/spark/mycode/flume_to_kafka
    $ find .
    

    打包之前,這條命令用來查看代碼結構,目錄結構如下所示:

在這裏插入圖片描述

  • 打包編譯

一定要在/usr/local/spark/mycode/flume_to_kafka目錄下運行打包命令。

輸入命令:

$ cd /usr/local/spark/mycode/flume_to_kafka
$ /usr/local/sbt/sbt package

第一次打包的過程可能會很慢,請耐心等待幾分鐘。打包成功後,會看到SUCCESS的提示。

  • 啓動zookeeper和kafka
#啓動zookeeper:
$ cd /usr/local/kafka
$ ./bin/zookeeper-server-start.sh config/zookeeper.properties

# 新開一個終端,啓動Kafka:
$ cd /usr/local/kafka
$ bin/kafka-server-start.sh config/server.properties

  • 運行程序KafkaWordCounter

打開一個新的終端,我們已經創建過topic,名爲test(這是之前在flume_to_kafka.conf中設置的topic名字),端口號2181。在終端運行KafkaWordCounter程序,進行詞頻統計,由於現在沒有啓動輸入,所以只有提示信息,沒有結果。

輸入命令:

$ cd /usr/local/spark
$/usr/local/spark/bin/spark-submit --driver-class-path /usr/local/spark/jars/*:/usr/local/spark/jars/kafka/* --class "org.apache.spark.examples.streaming.KafkaWordCounter" /usr/local/spark/mycode/flume_to_kafka/target/scala-2.11/simple-project_2.11-1.0.jar

其中”/usr/local/spark/jars/“和”/usr/local/spark/jars/kafka/”用來指明引用的jar包,“org.apache.spark.examples.streaming.KafkaWordCounter”代表包名和類名,這是編寫KafkaWordCounter.scala裏面的包名和類名,最後一個參數用來說明打包文件的位置。

執行該命令後,屏幕上會顯示程序運行的相關信息,並會每隔10秒鐘刷新一次信息,用來輸出詞頻統計的結果,此時還只有提示信息,如下所示:

[外鏈圖片轉存失敗(img-ZnMkMxIN-1569486879035)(result_one.png)]

在啓動Flume之前,Zookeeper和Kafka要先啓動成功,不然啓動Flume會報連不上Kafka的錯誤。

  • 啓動flume agent

打開第四個終端,在這個新的終端中啓動Flume Agent

輸入命令:

$ cd /usr/local/flume
$ bin/flume-ng agent --conf ./conf --conf-file ./conf/flume_to_kafka.conf --name a1 -Dflume.root.logger=INFO,console

啓動agent以後,該agent就會一直監聽localhost的33333端口,這樣,我們下面就可以通過“telnet localhost 33333”命令向Flume Source發送消息。這個終端也不要關閉,讓它一直處於監聽狀態。

  • 發送消息

打開第五個終端,發送消息。輸入命令:

$ telnet localhost 33333

這個端口33333是在flume conf文件中設置的source

在這個窗口裏面隨便敲入若干個字符和若干個回車,這些消息都會被Flume監聽到,Flume把消息採集到以後彙集到Sink,然後由Sink發送給Kafka的topic(test)。因爲spark Streaming程序不斷地在監控topic,在輸入終端和前面運行詞頻統計程序那個終端窗口內看到統計結果。


分佈式環境搭建及相關DEMO

Flume

Flume在分佈式環境下跟單機下一致,只需要在一臺機器上搭建即可。

Kafka

搭建高吞吐量Kafka分佈式發佈訂閱消息集羣

  • Zookeeper集羣: 121.48.163.195:2181 , 113.54.154.68:2181,113.54.159.232:2181

  • kafka 集羣: 121.48.163.195 , 113.54.154.68,113.54.159.232

搭建 kafka 集羣

kafka 集羣: 121.48.163.195 , 113.54.154.68,113.54.159.232

  1. 下載kafka和zookeeper

    步驟和前面單機版一致

  2. 修改配置

    $ vim /usr/local/kafka_2.12-0.11.0.0/config/server.properties 
    
    設置broker.id
    第一臺爲broker.id = 0
    第二臺爲broker.id = 1
    第三臺爲broker.id = 2
    注意這個broker.id每臺服務器不能重複
    
    然後設置zookeeper的集羣地址
    zookeeper.connect=121.48.163.195:2181 , 113.54.154.68:2181,113.54.159.232:2181
    
  3. 修改zookeeper配置文件

    $ vim /usr/local/zookeeper-3.4.5/conf/zoo.cfg
    #添加server.1 server.2 server.3
    
    server.1=121.48.163.195:2888:3888
    server.2=113.54.154.68:2888:3888
    server.3=113.54.159.232:2888:3888
    
    #添加id
    $ sudo echo "1" > /usr/local/zookeeper-3.4.5/data/myid(每臺機器的id可以和brokerid保持一致)
    
  4. 啓動服務

    # 每臺機器運行命令,但是在實際大型集羣中可以使用腳本的方式一鍵啓動
    $ bin/kafka-server-start.sh config/server.properties &
    
  5. 創建主題

    $ /usr/local/kafka_2.12-0.11.0.0/bin/kafka-topics.sh --create --zookeeper 121.48.163.195:2181 , 113.54.154.68:2181,113.54.159.232:2181 --replication-factor 2 --partitions 1 --topic ymq 
    
    --replication-factor 2 #複製兩份
    
    --partitions 1 #創建1個分區
    
    --topic #主題爲ymq
    
    # 運行list topic命令,可以看到該主題:
    
    $ /usr/local/kafka_2.12-0.11.0.0/bin/kafka-topics.sh --list --zookeeper 121.48.163.195:2181 , 113.54.154.68:2181,113.54.159.232:2181
    
    
  6. 其它操作

    其它操作基本語法差不多一致,不再贅述,詳情可以參考官網

  7. Kafka Manager

    Yahoo開源Kafka集羣管理器Kafka Manager

Spark

  • 選取三臺服務器

    • 121.48.163.195 主節點
    • 113.54.154.68 從節點
    • 113.54.159.232 從節點

    設置三臺服務器root用戶,之後操作都用root用戶進行,便於管理

  • 修改hosts文件

    $ sudo vim /etc/hosts
    # 在上面加上服務器ip
    121.48.163.195 Master
    113.54.154.68  Slave1
    113.54.159.232 Slave2
    

    修改完之後source /etc/hosts

  • SSH無密碼驗證配置

    • 安裝和啓動ssh協議

      我們需要兩個服務:ssh和rsync。可以通過下面命令查看是否已經安裝:

       rpm -qa|grep openssh
      
           rpm -qa|grep rsync
      
        如果沒有安裝ssh和rsync,可以通過下面命令進行安裝:
      
           sudo apt  install ssh (安裝ssh協議)
      
           sudo apt  install rsync (rsync是一個遠程數據同步工具,可通過LAN/WAN快速同步多臺主機間的文件)
      
           service sshd restart (啓動服務)
      
    • 配置Master無密碼登錄所有Slave

      配置Master節點,以下是在Master節點的配置操作。

      • 在Master節點上生成密碼對,在Master節點上執行以下命令:

      ssh-keygen -t rsa -P ‘’

      生成的密鑰對:id_rsa和id_rsa.pub,默認存儲在"/root/.ssh"目錄下。

      • 接着在Master節點上做如下配置,把id_rsa.pub追加到授權的key裏面去。

      cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

      • 修改ssh配置文件"/etc/ssh/sshd_config"的下列內容,將以下內容的註釋去掉,在三臺機器上均進行修改:

         RSAAuthentication yes # 啓用 RSA 認證
        
             PubkeyAuthentication yes # 啓用公鑰私鑰配對認證方式
        
             AuthorizedKeysFile .ssh/authorized_keys # 公鑰文件路徑(和上面生成的文件同)
        
        
      • 重啓ssh服務,才能使剛纔設置有效。

        service sshd restart

      • 驗證無密碼登錄本機是否成功

        ssh localhost

      • 接下來的就是把公鑰複製到所有的Slave機器上。使用下面的命令進行復制公鑰:

        $ scp /root/.ssh/id_rsa.pub root@Slave1:/root/
        
        $ scp /root/.ssh/id_rsa.pub root@Slave2:/root/
            
        

      接着配置Slave節點,以下是在Slave1節點的配置操作。

      1>在"/root/“下創建”.ssh"文件夾,如果已經存在就不需要創建了。

      mkdir /root/.ssh

      2)將Master的公鑰追加到Slave1的授權文件"authorized_keys"中去。

      cat /root/id_rsa.pub >> /root/.ssh/authorized_keys

      3)修改"/etc/ssh/sshd_config",具體步驟參考前面Master設置的第3步和第4步。

      4)用Master使用ssh無密碼登錄Slave1

      ssh 114.55.246.77

      5)把"/root/"目錄下的"id_rsa.pub"文件刪除掉。

      rm –r /root/id_rsa.pub

      重複上面的5個步驟把Slave2服務器進行相同的配置。

    • 配置Slave無密碼登錄Master

      以下是在Slave1節點的配置操作。

      1)創建"Slave1"自己的公鑰和私鑰,並把自己的公鑰追加到"authorized_keys"文件中,執行下面命令:

      ssh-keygen -t rsa -P ‘’

      cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys

      2)將Slave1節點的公鑰"id_rsa.pub"複製到Master節點的"/root/"目錄下。

      scp /root/.ssh/id_rsa.pub root@Master:/root/

      以下是在Master節點的配置操作。

      1)將Slave1的公鑰追加到Master的授權文件"authorized_keys"中去。

      cat ~/id_rsa.pub >> ~/.ssh/authorized_keys

      2)刪除Slave1複製過來的"id_rsa.pub"文件。

      rm –r /root/id_rsa.pub

      配置完成後測試從Slave1到Master無密碼登錄。

      ssh 114.55.246.88

      按照上面的步驟把Slave2和Master之間建立起無密碼登錄。這樣,Master能無密碼驗證登錄每個Slave,每個Slave也能無密碼驗證登錄到Master。

  • 安裝基礎環境(JAVA和SCALA環境)

    這裏不再贅述

  • Hadoop2.7.3完全分佈式搭建

    以下是在Master節點操作:

    • 下載二進制包hadoop-2.7.7.tar.gz

    • 解壓並移動到相應目錄,我習慣將軟件放到/opt目錄下,命令如下:

      $ tar -zxvf hadoop-2.7.3.tar.gz
      
      $ mv hadoop-2.7.7 /opt
      
      
    • 修改對應的配置文件,修改/etc/profile,增加如下內容:

       export HADOOP_HOME=/opt/hadoop-2.7.3/
       export PATH=$PATH:$HADOOP_HOME/bin
       export PATH=$PATH:$HADOOP_HOME/sbin
       export HADOOP_MAPRED_HOME=$HADOOP_HOME
       export HADOOP_COMMON_HOME=$HADOOP_HOME
       export HADOOP_HDFS_HOME=$HADOOP_HOME
       export YARN_HOME=$HADOOP_HOME
       export HADOOP_ROOT_LOGGER=INFO,console
       export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
       export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib"
      
    • 修改完成後執行$ source /etc/profile

    • 修改$HADOOP_HOME/etc/hadoop/hadoop-env.sh,修改JAVA_HOME 如下:

      export JAVA_HOME=/usr/local/jdk1.8.0_121

    • 修改$HADOOP_HOME/etc/hadoop/slaves,將原來的localhost刪除,改成如下內容:

      • Slave1

      • Slave2

    • 修改$HADOOP_HOME/etc/hadoop/core-site.xml

      <configuration>
            <property>
                <name>fs.defaultFS</name>
                <value>hdfs://Master:9000</value>
            </property>
            <property>
               <name>io.file.buffer.size</name>
      		 <value>131072</value>
           </property>
           <property>
                <name>hadoop.tmp.dir</name>
                <value>/opt/hadoop-2.7.7/tmp</value>
           </property>
      </configuration>
      
      
    • 修改$HADOOP_HOME/etc/hadoop/hdfs-site.xml

      <configuration>
          <property>
            <name>dfs.namenode.secondary.http-address</name>
            <value>Master:50090</value>
          </property>
          <property>
            <name>dfs.replication</name>
            <value>2</value>
          </property>
          <property>
            <name>dfs.namenode.name.dir</name>
            <value>file:/opt/hadoop-2.7.7/hdfs/name</value>
          </property>
          <property>
            <name>dfs.datanode.data.dir</name>
            <value>file:/opt/hadoop-2.7.7/hdfs/data</value>
          </property>
      </configuration>
      
      
    • cp mapred-site.xml.template mapred-site.xml,並修改$HADOOP_HOME/etc/hadoop/mapred-site.xml

      <configuration>
       <property>
          <name>mapreduce.framework.name</name>
          <value>yarn</value>
        </property>
        <property>
                <name>mapreduce.jobhistory.address</name>
                <value>Master:10020</value>
        </property>
        <property>
                <name>mapreduce.jobhistory.address</name>
                <value>Master:19888</value>
        </property>
      </configuration>
      
      
    • 修改$HADOOP_HOME/etc/hadoop/yarn-site.xml

      <configuration>
           <property>
               <name>yarn.nodemanager.aux-services</name>
               <value>mapreduce_shuffle</value>
           </property>
           <property>
               <name>yarn.resourcemanager.address</name>
               <value>Master:8032</value>
           </property>
           <property>
               <name>yarn.resourcemanager.scheduler.address</name>
               <value>Master:8030</value>
           </property>
           <property>
               <name>yarn.resourcemanager.resource-tracker.address</name>
               <value>Master:8031</value>
           </property>
           <property>
               <name>yarn.resourcemanager.admin.address</name>
               <value>Master:8033</value>
           </property>
           <property>
               <name>yarn.resourcemanager.webapp.address</name>
               <value>Master:8088</value>
           </property>
      </configuration>
      
    • 複製Master節點的hadoop文件夾到Slave1和Slave2上

    $ scp -r /opt/hadoop-2.7.7 root@Slave1:/opt
    $ scp -r /opt/hadoop-2.7.7 root@Slave2:/opt
    
    • 在Slave1和Slave2上分別修改/etc/profile,過程同Master一樣

    • 在Master節點啓動集羣,啓動之前格式化一下namenode:

      • Hadoop namenode -format

      • 啓動:/opt/hadoop-2.7.7/sbin/start-all.sh

      • 至此hadoop的完全分佈式搭建完畢

    • 查看集羣是否啓動成功:

      $ jps -m 
    
      Master顯示:
    
         SecondaryNameNode
    
         ResourceManager
    
         NameNode
    
      Slave顯示:
    
         NodeManager
    
         DataNode
    
    
  • Spark完全分佈式環境搭建

    以下操作都在Master節點進行。

    • 下載二進制包spark-2.4.3-bin-hadoop2.7.tgz

    • 解壓並移動到相應目錄,命令如下:

      $ tar -zxvf spark-2.4.3-bin-hadoop2.7.tgz
      
      $ mv hadoop-2.7.3 /opt
      
      
    • 修改相應的配置文件,修改/etc/profie,增加如下內容:

      export SPARK_HOME=/opt/spark-2.4.3-bin-hadoop2.7/
      export PATH=$PATH:$SPARK_HOME/bin
      
    • 複製spark-env.sh.template成spark-env.sh

      $ cp spark-env.sh.template spark-env.sh
      
    • 修改$SPARK_HOME/conf/spark-env.sh,添加如下內容:

      export   JAVA_HOME=/usr/local/jdk1.8.0_121   
      export   SCALA_HOME=/usr/share/scala   
      export   HADOOP_HOME=/opt/hadoop-2.7.3   
      export   HADOOP_CONF_DIR=/opt/hadoop-2.7.3/etc/hadoop   
      export   SPARK_MASTER_IP=114.55.246.88   
      export   SPARK_MASTER_HOST=114.55.246.88   
      export   SPARK_LOCAL_IP=114.55.246.88   
      export   SPARK_WORKER_MEMORY=1g   
      export   SPARK_WORKER_CORES=2   
      export   SPARK_HOME=/opt/spark-2.4.3-bin-hadoop2.7   
      export   SPARK_DIST_CLASSPATH=$(/opt/hadoop-2.7.3/bin/hadoop classpath)   
      
    • 複製slaves.template成slaves

      $ cp slaves.template slaves
      
    • 修改$SPARK_HOME/conf/slaves,添加如下內容:

      Master
      Slave1
      Slave2
      
    • 將配置好的spark文件複製到Slave1和Slave2節點

      $ scp /opt/spark-2.4.3-bin-hadoop2.7 root@Slave1:/opt
      $ scp /opt/spark-2.4.3-bin-hadoop2.7 root@Slave2:/opt
      
    • 修改Slave1和Slave2配置

      在Slave1和Slave2上分別修改/etc/profile,增加Spark的配置,過程同Master一樣。

      在Slave1和Slave2修改$SPARK_HOME/conf/spark-env.sh,將export SPARK_LOCAL_IP=114.55.246.88改成Slave1和Slave2對應節點的IP。

    • 在Master節點啓動集羣

      /opt/spark-2.4.3-bin-hadoop2.7/sbin/start-all.sh

    • 查看集羣是否啓動成功

      $ jps -m
      Master在Hadoop的基礎上新增了:
      
           Master
      
        Slave在Hadoop的基礎上新增了:
      
           Worker
      
      

在我的博客查看更多
作者:槐洛文

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