Azure HDInsight 上的 Spark 羣集配合自定義的Python來分析網站日誌

1、前言:本文是個實踐博客,演示如何結合使用自定義庫和 HDInsight 上的 Spark 來分析日誌數據。 咱們使用的自定義庫是一個名爲 iislogparser.py的 Python 庫。

  1. 每步的輸入和對應的輸出
  2. 糾正了原文中一個因版本引發的小問題

前提:你先在Azure HDinsight上有一個Apache Spark集羣,(彷佛如今只能是2.*版本的spark了)

2、將原始數據另存爲 RDD

在本部分中,將使用與 HDInsight 中的 Apache Spark 羣集關聯的 Jupyter 筆記原本運行用於處理原始示例數據並將其保存爲 Hive 表的做業。 示例數據是全部羣集在默認狀況下均會提供的 .csv 文件 (hvac.csv)。 將數據保存爲 Hive 表後,下一節將使用 Power BI 和 Tableau 等 BI 工具鏈接 Hive 表。javascript

1. 在 Azure 門戶上的啓動板中,單擊 Spark 羣集的磁貼(若是已將它固定到啓動板)。 也能夠單擊「所有瀏覽」 > 「HDInsight 羣集」導航到羣集。

2. 在 Spark 羣集邊欄選項卡中單擊「羣集儀表板」,而後單擊「Jupyter Notebook」。 出現提示時,請輸入羣集的管理員憑據。

3. 建立新的筆記本。 單擊「新建」,而後單擊「PySpark」。

4. 隨即建立新筆記本,並以 Untitled.pynb 名稱打開。 單擊頂部的筆記本名稱,並輸入一個友好名稱。

5. 使用筆記本是使用 PySpark 內核建立的,所以不須要顯式建立任何上下文。 運行第一個代碼單元格時,系統自動建立 Spark 和 Hive 上下文。 首先,能夠導入此方案所需的類型。 將如下代碼段粘貼到空白單元格中,並按 SHIFT + ENTER

from pyspark.sql import Row
from pyspark.sql.types import *

6. 使用羣集上已可用的示例日誌數據建立 RDD。 能夠從 \HdiSamples\HdiSamples\WebsiteLogSampleData\SampleLog\909f2b.log 中訪問與羣集關聯的默認存儲賬戶中的數據。

logs = sc.textFile('wasb:///HdiSamples/HdiSamples/WebsiteLogSampleData/SampleLog/909f2b.log')

此處的日誌格式大體爲:java

#Software: Microsoft Internet Information Services 8.0
#Fields: date time s-sitename cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Cookie) cs(Referer) cs-host sc-status sc-substatus sc-win32-status sc-bytes cs-bytes time-taken
2014-01-01 02:01:09 SAMPLEWEBSITE GET /blogposts/mvc4/step2.png X-ARR-LOG-ID=2ec4b8ad-3cf0-4442-93ab-837317ece6a1 80 - 1.54.23.196 Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/31.0.1650.63+Safari/537.36 - http://weblogs.asp.net/sample/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx www.sample.com 200 0 0 53175 871 46
2014-01-01 02:01:09 SAMPLEWEBSITE GET /blogposts/mvc4/step3.png X-ARR-LOG-ID=9eace870-2f49-4efd-b204-0d170da46b4a 80 - 1.54.23.196 Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/31.0.1650.63+Safari/537.36 - http://weblogs.asp.net/sample/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx www.sample.com 200 0 0 51237 871 32
2014-01-01 02:21:19 SAMPLEWEBSITE GET /blogposts/mvcrouting/step11.jpg X-ARR-LOG-ID=117d64f4-5bf4-44e0-9ef5-669737d69adc 80 - 115.64.147.147 Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_9_1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/31.0.1650.63+Safari/537.36 ARRAffinity=909f2bd5d754dbde10a14dff095f3fbe3b22ca6b04cccdaf15bd58ecb51e1fe4;+WAWebSiteSID=7613e9e104a04a31a798554d8954622c http://weblogs.asp.net/sample/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx www.sample.com 200 0 0 30944 976 15

7. 檢索示例日誌集以驗證上一步是否成功完成。

logs.take(5)

應該會看到與下面相似的輸出:python

[u'#Software: Microsoft Internet Information Services 8.0',
  u'#Fields: date time s-sitename cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Cookie) cs(Referer) cs-host sc-status sc-substatus sc-win32-status sc-bytes cs-bytes time-taken',
  u'2014-01-01 02:01:09 SAMPLEWEBSITE GET /blogposts/mvc4/step2.png X-ARR-LOG-ID=2ec4b8ad-3cf0-4442-93ab-837317ece6a1 80 - 1.54.23.196 Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/31.0.1650.63+Safari/537.36 - http://weblogs.asp.net/sample/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx www.sample.com 200 0 0 53175 871 46',
  u'2014-01-01 02:01:09 SAMPLEWEBSITE GET /blogposts/mvc4/step3.png X-ARR-LOG-ID=9eace870-2f49-4efd-b204-0d170da46b4a 80 - 1.54.23.196 Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/31.0.1650.63+Safari/537.36 - http://weblogs.asp.net/sample/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx www.sample.com 200 0 0 51237 871 32',
  u'2014-01-01 02:01:09 SAMPLEWEBSITE GET /blogposts/mvc4/step4.png X-ARR-LOG-ID=4bea5b3d-8ac9-46c9-9b8c-ec3e9500cbea 80 - 1.54.23.196 Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/31.0.1650.63+Safari/537.36 - http://weblogs.asp.net/sample/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx www.sample.com 200 0 0 72177 871 47']

3、使用自定義 Python 庫分析日誌數據

1. 在上面的輸出中,前幾行包括標頭信息,其他的每一行均與此標頭中描述的架構相匹配。 分析此類日誌可能很複雜。 所以,可以使用自定義 Python 庫 (iislogparser.py),它能使分析這類日誌變得容易得多。 默認狀況下,此庫包含在 /HdiSamples/HdiSamples/WebsiteLogSampleData/iislogparser.py處 HDInsight 上的 Spark 羣集中。可是,此庫不在 PYTHONPATH 中,所以不能經過 import iislogparser 等導入語句來使用它。 要使用此庫,必須將其分發給全部輔助角色節點。 運行如下代碼段。

sc.addPyFile('wasb:///HdiSamples/HdiSamples/WebsiteLogSampleData/iislogparser.py')

2. 若是日誌行是標題行,則 iislogparser 提供返回 None 的函數 parse_log_line,而且在遇到日誌行時返回 LogLine 類的實例。 使用 LogLine 類從 RDD 中僅提取日誌行:

def parse_line(l):
    import iislogparser
    return iislogparser.parse_log_line(l)
logLines = logs.map(parse_line).filter(lambda p: p is not None).cache()

3. 檢索一些提取的日誌行,以驗證該步驟是否成功完成。

logLines.take(2)

輸出應以下所示:ios

python[2014-01-01 02:01:09 SAMPLEWEBSITE GET /blogposts/mvc4/step2.png X-ARR-LOG-ID=2ec4b8ad-3cf0-4442-93ab-837317ece6a1 80 - 1.54.23.196 Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/31.0.1650.63+Safari/537.36 - http://weblogs.asp.net/sample/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx www.sample.com 200 0 0 53175 871 46, 2014-01-01 02:01:09 SAMPLEWEBSITE GET /blogposts/mvc4/step3.png X-ARR-LOG-ID=9eace870-2f49-4efd-b204-0d170da46b4a 80 - 1.54.23.196 Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/31.0.1650.63+Safari/537.36 - http://weblogs.asp.net/sample/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx www.sample.com 200 0 0 51237 871 32]web

4. 反過來,LogLine 類具備一些有用的方法,如 is_error(),可返回日誌條目是否具備錯誤代碼。 使用此類計算提取日誌行中的錯誤數,而後將全部錯誤記錄到另外一個文件中。

errors = logLines.filter(lambda p: p.is_error())
numLines = logLines.count()
numErrors = errors.count()
print 'There are', numErrors, 'errors and', numLines, 'log entries'
errors.map(lambda p: str(p)).saveAsTextFile('wasb:///HdiSamples/HdiSamples/WebsiteLogSampleData/SampleLog/909f2b-2.log')

應該看到以下輸出:sql

There are 30 errors and 646 log entries

5. 還可以使用 Matplotlib 構造數據的可視化效果。 例如,若是要找出請求長時間運行的緣由,可能須要查找平均執行時間最長的文件。 下面的代碼片斷檢索執行請求花費時間最長的前 25 個資源。

def avgTimeTakenByKey(rdd):
    return rdd.combineByKey(lambda line: (line.time_taken, 1),
                            lambda x, line: (x[0] + line.time_taken, x[1] + 1),
                            lambda x, y: (x[0] + y[0], x[1] + y[1]))\
              .map(lambda x: (x[0], float(x[1][0]) / float(x[1][1])))

avgTimeTakenByKey(logLines.map(lambda p: (p.cs_uri_stem, p))).top(25, lambda x: x[1])

應該看到以下輸出:apache

[(u'/blogposts/mvc4/step13.png', 197.5),
 (u'/blogposts/mvc2/step10.jpg', 179.5),
 (u'/blogposts/extractusercontrol/step5.png', 170.0),
 (u'/blogposts/mvc4/step8.png', 159.0),
 (u'/blogposts/mvcrouting/step22.jpg', 155.0),
 (u'/blogposts/mvcrouting/step3.jpg', 152.0),
 (u'/blogposts/linqsproc1/step16.jpg', 138.75),
 (u'/blogposts/linqsproc1/step26.jpg', 137.33333333333334),
 (u'/blogposts/vs2008javascript/step10.jpg', 127.0),
 (u'/blogposts/nested/step2.jpg', 126.0),
 (u'/blogposts/adminpack/step1.png', 124.0),
 (u'/BlogPosts/datalistpaging/step2.png', 118.0),
 (u'/blogposts/mvc4/step35.png', 117.0),
 (u'/blogposts/mvcrouting/step2.jpg', 116.5),
 (u'/blogposts/aboutme/basketball.jpg', 109.0),
 (u'/blogposts/anonymoustypes/step11.jpg', 109.0),
 (u'/blogposts/mvc4/step12.png', 106.0),
 (u'/blogposts/linq8/step0.jpg', 105.5),
 (u'/blogposts/mvc2/step18.jpg', 104.0),
 (u'/blogposts/mvc2/step11.jpg', 104.0),
 (u'/blogposts/mvcrouting/step1.jpg', 104.0),
 (u'/blogposts/extractusercontrol/step1.png', 103.0),
 (u'/blogposts/sqlvideos/sqlvideos.jpg', 102.0),
 (u'/blogposts/mvcrouting/step21.jpg', 101.0),
 (u'/blogposts/mvc4/step1.png', 98.0)]

6. 還能夠圖繪形式顯示此信息。 建立繪圖的第一步是建立一個臨時表 AverageTime。 該表按照時間對日誌進行分組,以查看在任何特定時間是否存在任何異常延遲峯值。

avgTimeTakenByMinute = avgTimeTakenByKey(logLines.map(lambda p: (p.datetime.minute, p))).sortByKey()
schema = StructType([StructField('Minutes', IntegerType(), True),
                     StructField('Time', FloatType(), True)])

avgTimeTakenByMinuteDF = spark.createDataFrame(avgTimeTakenByMinute, schema)
avgTimeTakenByMinuteDF.registerTempTable('AverageTime')

Warning:原文doc中的sqlContext.createDataFrame 須要改成spark.createDataFrame,否則會遇到**'StructField' object has no attribute '_get_object_id'**的報錯服務器

7. 接下來能夠運行如下 SQL 查詢以獲取 AverageTime 表中的全部記錄。

%%sql -o averagetime
SELECT * FROM AverageTime

後接 -o averagetime 的 %%sql magic 可確保查詢輸出本地保存在 Jupyter 服務器上(一般在羣集的頭結點)。 輸出做爲 Pandas 數據幀進行保存,指定名稱爲 averagetime架構

8. 現可以使用 Matplotlib(用於構造數據效果可視化的庫)建立繪圖。 由於必須從本地保存的 averagetime 數據幀中建立繪圖,因此代碼片斷必須以 %%local magic 開頭。 這可確保代碼在 Jupyter 服務器上本地運行。

%%local
%matplotlib inline
import matplotlib.pyplot as plt

plt.plot(averagetime['Minutes'], averagetime['Time'], marker='o', linestyle='--')
plt.xlabel('Time (min)')
plt.ylabel('Average time taken for request (ms)')
plt.grid(True)

9. 完成運行應用程序以後,應該要關閉筆記本以釋放資源。 爲此,請在筆記本的「文件」菜單中,單擊「關閉並中止」。 這將會關閉 notebook。

相關文章
相關標籤/搜索