1. 程式人生 > >大資料視覺化之Nginx伺服器日誌分析及視覺化展示(Nginx+flume+HDFS+Spark+Highcharts)

大資料視覺化之Nginx伺服器日誌分析及視覺化展示(Nginx+flume+HDFS+Spark+Highcharts)

專案說明:

本專案為一個web程式,能夠利用大資料技術,對nginx伺服器的訪問日誌進行圖形化展示。當訪問此程式時,可以看到nginx日誌中統計出來的全天分時網站點選量等資訊的圖表展示。每條Nginx日誌的平均大小為250位元組左右,假設每天產生1億條訪問日誌,日誌檔案的總大小約為230GB,本程式適合對此類海量日誌資料進行分析和統計工作,並快速得出統計結果。

程式執行環境:

  1. 採用三臺虛擬機器來模擬真實環境下的Hadoop高可用叢集服務。主機名和ip地址分別為master1-192.168.49.128、master2-192.168.49.129、slave1-192.168.49.130。
  2. 在master1和master2上分別安裝flume資料匯入工具、Nginx伺服器以及Spark2。

專案架構圖:

專案架構圖

實現過程:

第一步:搭建分散式Nginx伺服器環境,在兩臺虛擬機器上分別配置Nginx伺服器以及flume,將兩臺Nginx伺服器的日誌檔案統一匯入到HDFS上的同一個目錄下(此專案為HDFS上的/flume/collector1目錄)。
master1的flume配置檔案如下:

tail1.sources=src1
tail1.channels=ch1
tail1.sinks=sink1
tail1.sources.src1.type=exec
tail1.sources
.src1.command=tail -F /var/log/nginx/access.log tail1.sources.src1.channels=ch1 tail1.channels.ch1.type=memory tail1.channels.ch1.capacity=500 tail1.sinks.sink1.type=avro tail1.sinks.sink1.hostname=192.168.49.128 tail1.sinks.sink1.port=6000 tail1.sinks.sink1.batch-size=1 tail1.sinks.sink1.channel=ch1 collector1.sources
=src1 collector1.channels=ch1 collector1.sinks=sink1 collector1.sources.src1.type=avro collector1.sources.src1.bind=192.168.49.128 collector1.sources.src1.port=6000 collector1.sources.src1.channels=ch1 collector1.channels.ch1.type=memory collector1.channels.ch1.capacity=500 collector1.sinks.sink1.type=hdfs collector1.sinks.sink1.hdfs.path=/flume/collector1 collector1.sinks.sink1.hdfs.filePrefix=access_log collector1.sinks.sink1.hdfs.writeFormat=Text collector1.sinks.sink1.channel=ch1 collector1.sinks.sink1.hdfs.fileType=DataStream

master2的flume配置檔案如下:

tail1.sources=src1
tail1.channels=ch1
tail1.sinks=sink1

tail1.sources.src1.type=exec
tail1.sources.src1.command=tail -F /var/log/nginx/access.log
tail1.sources.src1.channels=ch1

tail1.channels.ch1.type=memory
tail1.channels.ch1.capacity=500

tail1.sinks.sink1.type=hdfs
tail1.sinks.sink1.hdfs.path=/flume/collector1
tail1.sinks.sink1.hdfs.filePrefix=access_log
tail1.sinks.sink1.hdfs.writeFormat=Text
tail1.sinks.sink1.channel=ch1
tail1.sinks.sink1.hdfs.fileType=DataStream

日誌檔案匯入後的結果如下圖:
這裡寫圖片描述
第二步:使用Spark提取相關的日誌資料項並分析。(本文中以Nginx伺服器中的按時間統計瀏覽量為例進行說明)
Nginx日誌格式樣例如下:

192.168.49.123 - - [25/Nov/2017:17:05:11 +0800] "GET /ip/part-00000 HTTP/1.1" 200 364 "http://192.168.49.128/project/index.html" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0" "-"
192.168.49.1 - - [22/Nov/2017:09:34:03 +0800] "GET /nginx-logo.png HTTP/1.1" 200 368 "http://192.168.49.129/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0" "-"
192.168.49.1 - - [22/Nov/2017:09:35:56 +0800] "GET /poweredby.png HTTP/1.1" 200 2811 "http://192.168.49.129/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36" "-"
192.168.49.1 - - [22/Nov/2017:09:45:56 +0800] "GET /nginx-logo.png HTTP/1.1" 200 368 "http://192.168.49.129/" "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2" "-"

採用Spark將日誌檔案通過冒號進行分割並提取第二部分,從而提取到小時數,統計之後將最終結果以TXT文字檔案的形式寫入Nginx伺服器下的time目錄中。關鍵程式碼如下:

log.map(lambda line:line.split(':')[1]).map(lambda line:(line,1)).reduceByKey(lambda a1,a2:a1+a2).sortByKey(lambda a1,a2:a1+a2).map(lambda (key,value):(int(key),value)).repartition(1).saveAsTextFile("file:///usr/share/nginx/html/time")

分析後生成的TXT文字檔案的內容如下,檔名為part-00000:

(9, 419)
(10, 237)
(11, 1614)
(12, 438)
(14, 15)
(15, 114)
(16, 318)

第三步:在Highcharts中呼叫統計和分析生成的TXT檔案中的統計結果,進行圖表的繪製與展示。採用ajax來讀取資料。
先附上JavaScript程式碼:

<script language="JavaScript">
$(document).ready(function() {
    var title = {
        style: {
            fontSize: '37px'
        },
        text: 'Ngnix伺服器天訪問量分佈'
    };
    var subtitle = {
        text: '來源:隨機生成'
    };
    var xAxis = {
        title: {
            text: '訪問時間(小時)'
        },
        categories: count2()
    };
    var yAxis = {
        title: {
            text: '訪問量(次)'
        },
        plotLines: [{
            value: 0,
            width: 1,
            color: '#808080'
        }]
    };

    var tooltip = {
        valueSuffix: '次'
    }

    var legend = {
        layout: 'vertical',
        align: 'right',
        verticalAlign: 'middle',
        borderWidth: 0
    };

    var series = [{
        name: 'Nginx',
        data: count()
    }];

    var json = {};

    json.title = title;
    json.subtitle = subtitle;
    json.xAxis = xAxis;
    json.yAxis = yAxis;
    json.tooltip = tooltip;
    json.legend = legend;
    json.series = series;

    $('#container2').highcharts(json);
});
</script>

X軸xAxis的資料來源採用count2函式來從檔案中讀取。count2函式程式碼如下:

function count2() {
    var rst;
    $.ajax({
        type: "get",
        async: false,
        url: "/time/part-00000",
        success: function(data) {
            var a1 = data.split(')');
            var key = "";
            for(i = 0; i < a1.length - 1; i++) {
                if(i == 0) {
                    key = key + (a1[i].split(',')[0]).substr(1) + ",";
                } else {
                    key = key + (a1[i].split(',')[0]).substr(2);
                    if(i < a1.length - 2) {
                        key = key + ",";
                    }
                }
            }
            rst = eval('[' + key + ']');
        }
    });
    return rst;
}

Y軸中每一項的值series採用count函式從檔案中讀取,count函式程式碼如下:

function count() {
    var rst;
    $.ajax({
        type: "get",
        async: false,
        url: "/time/part-00000",
        success: function(data) {
            var a1 = data.split(')');
            var b1 = "";
            var key = "";
            for(i = 0; i < a1.length - 1; i++) {
                b1 = b1.concat(a1[i].split(',')[1]);
                if(i < a1.length - 2) {
                    b1 = b1 + ",";
                }
            }
            for(i = 0; i < a1.length - 1; i++) {
                if(i == 0) {
                    key = key + (a1[i].split(',')[0]).substr(1) + ",";
                } else {
                    key = key + (a1[i].split(',')[0]).substr(2);
                    if(i < a1.length - 2) {
                        key = key + ",";
                    }
                }
            }
            rst = eval('[' + b1 + ']');
        }
    });
    return rst;
}

第四步:編寫spark指令碼,在每次執行程式之前刪掉原來產生的結果檔案,參考程式碼如下:

import shutil
import os
path="/usr/share/nginx/html/time"
if os.path.exists(path):
    shutil.rmtree(path)

第五步:設定Linux的計劃任務,通過crontab -e命令,在一定的間隔時間內,重新執行Spark程式,更新統計結果,實現資料的實時統計與展示。
最終效果如下圖所示: