厲害了!除了find命令,還有這麼多文件查找命令,高手必備!

大家好,我是良許。

在系統里查找文件,是所有工程師都必備的技能(不管你用的是 Windows 、Linux、還是 MacOS 系統)。對於 Linux 操作系統,單單一個 find 命令就可以完成非常多的搜索工作。

但是,文件搜索命令遠不止一個 find 命令,還有很多。本文就對 Linux 下文件搜索命令進行一個科普,讓你能夠在短時間內找到自己需要的文件。

1. find

find 命令應該是最經典的命令了,談到搜索工具第一個想到的肯定是 find 命令。但是,find 命令非常強大,想要把它的功能都介紹一遍,恐怕要寫好幾篇文章。

所以,這裏就偷個懶,介紹最基本的,根據文件名查找文件的方法。假如我們想搜索當前目錄(及其子目錄)下所有 .sh 文件,可以這樣搜索:

2. locate

locate 是另外一個根據文件名來搜索文件的命令。區別於 find 命令,locate 命令無需指定路徑,直接搜索即可。

這個命令不是直接去系統的各個角落搜索文件,而是在一個叫 mlocate.db 的數據庫下搜索。這個數據庫位於 /var/lib/mlocate/mlocate.db ,它包含了系統里所有文件的索引,並且會在每天早上的時候由 cron 工具自動更新一次。

正因為如此,locate 的搜索速度遠快於 find 命令,因為它直接在數據庫里檢索,速度自然更快。

locate 命令在找到文件之後,將直接显示該文件的絕對路徑,比如:

但是 locate 命令有個弊端,它無法搜索當天所創建的文件,因為它的數據庫一天只在早上更新一次。比如我現在創建一個新文件,locate 沒辦法搜索到:

為了解決這個問題,我們可以使用 updatedb 命令手動去更新它的數據庫:

$ sudo updadb

然後,我們就可以搜索到新文件了。

3. which

which 命令主要用來查找可執行文件的位置,它搜索的位置指定在 $PATH$MANPATH 環境變量下的值,默認情況下,which 命令將显示可執行文件的第一個存儲位置:

如果某個可執行文件存儲在多個位置,可以使用 -a 選項列出所有的位置。

如果你想一次性查找多個文件,可以直接跟在 which 命令後面即可。

4. whereis

whereis 命令會在系統默認安裝目錄(一般是有root權限時默認安裝的軟件)查找二進制文件、源碼、文檔中包含給定查詢關鍵詞的文件。(默認目錄有 /bin, /sbin, /usr/bin, /usr/lib, /usr/local/man等類似路徑)。

一般包含以下三部分內容:

  • 二進制文件的路徑
  • 二進制文件的源碼路徑
  • 對應 man 文件的路徑

比如我們現在搜索 ls 命令:

我們可以使用 -b 選項來只搜索可執行文件所在位置,使用 -B 選項指定搜索位置,使用 -f 選項列出文件的信息。

同樣地,我們可以使用 -s 限定只搜索源碼路徑,使用 -m 搜索 man page 路徑,使用 -s 指定搜索源代碼文件的路徑,使用 -M 指定搜索幫助文件的路徑。

公眾號:良許Linux

有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

對抗代碼遺忘的思考——提問回顧與個人總結

一、提問回顧

提問博客點這裏

關於第一章中的“軟件的非連續性”

經過這一學期的實踐,我對軟件的非連續性有了比較具體的認識。

在我們的項目中,後端涉及比較複雜的狀態機,而可能一些小的輸入變化就會觸髮狀態機的改變,進而影響系統的運行狀態。測試後端狀態機是整個項目最困難、最複雜的地方,在這部分,軟件的非連續性就體現得淋漓盡致。

而應對這種困難的方式也有很多,一方面,可以做充分的測試,盡量覆蓋狀態機的每一條狀態轉移路徑,另一方面,在設計時也要做好充分的考慮,盡量讓系統的狀態數較少,轉移數較少,從而使得系統的穩定性提高,易於維護和管理。

關於第二章中提到的“單元測試”

在學期初提出的問題是

是否有必要追求100%覆蓋率的單元測試

經過這一個學期的實踐,我覺得我找到了答案,測試是必需的,但100%覆蓋率的單元測試既不必要,不也現實。

在這個學期的項目中,我們的產品從前端到後端涉及到了瀏覽器、服務器、Docker、文件服務器、磁盤文件系統。其中,很多功能都是需要多個組件協同完成的,而單獨處於一個組件內部的單元測試常常無法兼顧整體,例如,在服務器系統內就很難監控到Docker的運行狀態,相應的單元測試也就很難開展。因為不存在一個組件可以訪問和控制到整個系統的每個組件,如果真的存在這樣的組件的話,那說明系統的設計也是不好的,沒有很好地實現系統層次之間的隔離,整個軟件的耦合度比較高。

但是,這是不是意味着我們可以放棄對單元測試的要求而為偷懶找借口了呢?不是的。

首先,單元測試依然起着十分重要的作用,拿建築樓房為例子,單元測試的作用就是保證每個磚塊、每根鋼筋的質量,雖然它很難保證整個樓房的藍圖設計是正確無誤的,但是作為第一層保障,它可以有效地保證我們的最低層組件工作正常。

以我們的項目為例,在後端服務器實現中,會用到很多的工具類,這些類之間的耦合度很低,各自完成相關的一系列功能,為上層提供服務。單元測試在這裏就可以發揮很大的作用,對每個工具類的每個方法進行全面的測試,可以為將來進一步的開發排除很多潛在的隱患。

而除此之外,我還認識到了測試不僅僅是單元測試這麼簡單,測試是一門學問,相應的也有諸多經典的測試方法,例如,在我們的項目開發後期,常常採用錄製腳本的方式進行測試,通過瀏覽器錄製一系列操作形成腳本,這個腳本可以重放,從而在軟件開發過程中可以隨時回歸測試,保證之前腳本中包含的功能運行正常。

所以,總結來說,測試十分重要,但100%的單元測試往往不切實際,但在單元測試無法覆蓋的地方,必須要有其他的測試手段進行彌補,要保證軟件的每個環節都有相應的測試作為保障,才能在接下來的開發中保持軟件的高質量。

關於第四章中提到的“結對編程”

在學期初提出的問題是

結對編程的開發方式開起來很美好,但是在實際團隊開發中真的有廣泛使用嗎

經過本學期的結對編程實踐,我依然對當初提出的問題持保留意見,也尚不能完全認同結對編程中的所有優點。

在書籍中介紹到結對編程有如下幾個優點

  • 可以起到教學作用,技術高的程序員可以幫助技術較弱的程序員進步
  • 可以提高代碼質量,因為代碼的每個部分的質量都取決於一對程序員中在該方面技術較高的一個
  • 可以提高團隊凝聚力,促進團隊交流,利於團隊管理
  • 可以有效應對團隊人員變動

針對這幾點,我也想談談我在實踐過接對編程后的理解和困惑。

  • 可以起到教學作用,技術高的程序員可以幫助技術較弱的程序員進步

關於這個優點我完全認同,在接對編程實踐中,我從搭檔身上學到了不少東西,不論是編程技巧還是設計藝術,同時,我有時也能夠為他提供一些幫助和想法,應該是完全起到了相互促進的作用。

但是,在這個優點背後有一個問題值得思考,也是我所困惑的。這樣的時間成本是否較高,一方學到東西的代價是另一方需要停下開發的腳步,而且,並非每個人都有成為老師的潛質,有的人技術高超但是表達能力不強,讓他們教授別人技術或許不如讓他人查閱相關資料來的效率高。同時,編程和思維都是有節奏和狀態的,打斷編程者的連續輸出其實對於編程人員來說是一件效率很低的事情,這樣看來,這個教學作用的成本是否較高。

  • 可以提高代碼質量,因為代碼的每個部分的質量都取決於一對程序員中在該方面技術較高的一個

這個優點我也完全認同,但是,在我實踐過程中的體會就是,達到這個最優往往會經歷一番周轉。

當雙方在某個設計點上意見不一致時,需要停下進行分析,這本身沒有問題,尤其是在雙方技術水平相差較大時,一方會主導話題,從而使得問題的討論和解決較快。而當雙方技術水平相當時,問題就可能出現,可能雙方各有不錯的設計,但這兩個設計相去甚遠,說服任意一方接受理解另一方的設計都是比較困難的事情。其本質就是默契問題,是否雙方有很高的默契度,編程和設計習慣上風格一致,這些都影響着結對編程的效果和實際性。倘若雙方無法保持高度默契,那麼有時時間就會被浪費在類似上面提到的場景上。

  • 可以提高團隊凝聚力,促進團隊交流,利於團隊管理

這一點我完全認同,也認為十分實際,當下,很多公司也有技術茶話會這樣的活動,目的也是為了可以一邊交流技術、提高團隊水平,一邊也可以提高團隊的凝聚力、促進團隊交流。

  • 可以有效應對團隊人員變動

確實有這方面的意義,但是我個人感覺這依然沒有解決本質問題,這樣的解決辦法下,風險依然是在人員本身上,只不過將一個人變成了兩個人而已。

而我認為更好的解決辦法可以是完善相關的內部文檔,每個人負責各自工作範圍內的技術文檔維護,這樣可以有效地規避掉人員變動的風險。

這一點在這學期的強制轉會中深有體會,據我了解,有的團隊就將項目開荒初期的學習和技術文檔維護了下來,從而使得轉會新來的成員可以快速上手項目,團隊的開發進度不會受到很大的影響。

關於第十六章中提到的“要成為領域的專家,才能創新”

在書中,作者的觀點是這樣的

這個想法看起來沒什麼錯,我們不就是為了成為某個領域的專家,才來上學,拿學位,希望拿到學位之後成為專家,然後再開始這個領域的創新?但是統計數據表明,70%的創新者說,他們最成功的創新,是在他們的拿手領域之外發現的。

之後作者舉出了HTTP的誕生阿里巴巴的誕生等例子,佐證上面的觀點。

經過這學期的實踐,我對這個觀點有了一定的理解,也有了一些自己的看法。

在這學期,我們是自選項目,也算是做了一些創新性的設計實現,但是這背後是經過了一定的調研和學習才得來的。雖然不能說經過調研我們就成為了領域的專家,但是至少對領域有了一定的了解和認知,知道哪些是實現了的,哪些是空白的,哪些東西因為沒人想到所以沒人做,哪些東西因為難度太大所以沒人做。

當然,作者的意思不是說對某個領域聞所未聞就企圖在其中有所創新,而是說創新的領域不一定是自己最拿手的領域。這一點,經過這學期的實踐,我有了全新的理解。

創新和實現不同,創新更關注的是要站在一個應用者和設計者的角度去思考,而不是實現者或者架構師的角度,它更多關注的是需要什麼而不是如何實現。當然,這兩者都十分重要,單純有想法但無法實現也無濟於事。但是,這背後正是作者想要表達的意思,不一定每個人都有實現某個東西的能力,但是每個人一定都有想到、想出某個東西的能力。

那我們的項目為例,我們發現在初學編程時,環境的配置很麻煩,IDE的安裝很麻煩,所以我們想解決這個問題,於是我們提出了自己的WEB版IDE,對這方面的需求提供了一定的支持。這個創意源自於我們的真實需求,同樣,那些偉大的創意,也往往來自於真實的需求。這正是作者想要表達和闡述的。

關於第十七章中提到的“磨合階段”

經過這學期的實踐,我發現沒有磨合階段的團隊和無法磨合的團隊都很少,以我們的團隊為例,大家在最開始雖然意見不統一,但是經過一段不長的時間的調整和交流,很快大家在認識上就可以達成一致。

我認為,除了一些極端人員難以融入團隊,可能會成為團隊的害群之馬以外,大部分時候,團隊都是可以磨合和愉快合作的。

而在學期開始時,我提出了如下問題

如何判斷一個團隊是處於“磨合階段”還是說這個團隊的人員配置本身真的存在問題呢

針對這一點,結合我的實踐體會,我認為,如果一個團隊能夠明確地完成分工,並且每個人都能夠清楚自己的職責和任務,那麼這個團隊就可以繼續合作下去。即使最開始存在團隊成員的進度不能很好地達標的情況,但也依然可以認為團隊的人員構成本身沒有問題,是可以合作的。

因為如果每個成員都清楚自己的任務和職責,那麼這至少說明了大家在工作和認識上達成了一致,在這個一致達成的基礎上開展後續的合作都是有可能的。而倘若成員之間連分工和職責分配都無法明確,則要麼是團隊管理出了問題,PM沒有很好地協調成員,要麼就是團隊成員無法達成一致,整個團隊內對任務和目標沒有一個清晰的認識和把握。

所以,我的理解是,團隊成員之間能否對團隊任務和目標有一個統一的認識和把握是可以作為評判團隊人員配置是否合理的一個重要標準。

二、新的問題

實踐出真知,經過一個學期的實踐后,再回顧最初提出的問題,確實顯得有些稚嫩和缺乏實踐了。

不過在實踐過程中,我也產生了一些新的問題,下面提出來和大家共同探討。

如何對抗歷史代碼遺忘問題

這個項目總共歷時2個月,從最初的搭建,到後來項目功能越來越多,魯棒性越來越強,這其中都是需要進行代碼的修改和添加的。

然而,在項目後期常常會出現一個情況,就是幾周前的代碼在幾周后再次閱讀時不能很快地回憶出其中的實現細節。

這裏並不是想表達代碼的可讀性較差,即使是在充分利用面向對象編程和函數式編程的優勢的情況下,回顧以往的代碼依然要耗費一定的時間去閱讀。

特別是在系統的狀態較多較複雜的情況下,這種問題尤為明顯,需要花費比較多的時間理清楚系統的狀態遷移。

我認為,完備的設計文檔和代碼註釋會有幫助,同時,設計多個小型專用系統來替代單一的多功能大型系統也會有所幫助。

但是我並無法滿足於上述兩種可能的解決方案,同時,因為問題暴露較晚,所以我也沒有機會去實踐檢驗上述辦法是否真的有效。

所以在這裏提出這個問題,希望能夠得到幫助。

軟件功能和軟件安全性的矛盾

網絡安全是一個很大的領域,而WEB應用依賴於網絡,自然WEB安全也是一個很大的話題。

在項目初期,我們花費了不少時間在安全機制的設計上,甚至對於安全機制的考量一度影響到了我們正常功能與核心功能的開發進度。

我們每個人都承認,在軟件領域,軟件的安全性無論對於用戶還是開發者都是十分重要的,它關乎雙方的利益。但是,在能力有限,或是缺乏相應的專業技術人員的情況下,過多的安全設計考慮常常會影響到功能的開發。

所以,我很困惑應該將安全機制設計放在軟件開發的哪個階段。

倘若在最初軟件設計時就將大部分安全機制考慮到並設計在最初架構中,那這樣肯定會影響到軟件的開發進度,特別是在缺乏專業技術人員的情況下。

而若不在最初設計時將安全機制考慮在內,而寄希望於在後期逐漸添加安全機制,那麼很可能出現一種情況,就是為了添加安全機制,軟件架構需要一些變動,這有時甚至會破壞單元測試的可用性,因為更高的安全性背後往往是更低的便利性,可能會有一些原本可以正常工作的單元測試不能在新的安全機制下運行,這就又給回歸測試帶來的困難,進而迫使軟件開發進入了一種比較危險的狀態。

所以,我很想知道,在有限的條件下,應該如何平衡安全機制設計和功能開發。

三、實踐知識點回顧

需求階段

在需求階段,我學到的知識點是

需求調研不僅要保證提出的需求是真需求,同時也要保證我們的產品有優於同類產品的地方

我們的項目是WEB版的IDE,其實,這個領域的同類產品還是有不少的,但是我們在需求調研時提出了我們獨特的地方,即面向新手

我們提出的需求是,許多新手初學編程時會被諸如環境配置、IDE配置等一系列配置工作阻礙,而我們的目標就是為他們消除阻礙,能夠提供一個開箱即用的編程環境。

那麼,我們提出的需求是真需求嗎?是的。

據調查,很多大一新生在編程學習的前半個學期都會對開發環境有着或多或少的困惑和不解。然而理解這背後的一系列內容又本身超出了他們現有的能力範圍,那麼,我們產品的使用人群和使用場景也就應運而生了。

而我們的產品是否有優於同類產品的地方呢?是的。

根據調研,市面上大多數的WEB版IDE的功能都十分強大,基本涵蓋了非WEB版IDE的絕大部分功能。但同非WEB版的IDE類似,這些WEB版的IDE依然使用難度較高,需要一定的經驗,也需要一定的配置,並不能做的開箱即用,而這就是我們產品的優勢。

設計階段

在設計階段,我學到的知識點是

有效地分隔任務為若干個子任務會有利於設計的進展

在我們項目最初的設計階段,我們就將項目進行了分隔,粗略分為了前端、編輯器、後端這三個部分,而這三個部分又各自被分隔為若干個更小的部分,然後針對每個部分的功能需求去單獨設計。這樣,一方面有利於強化對於項目的整體把握,另一方面也有利於控制軟件的複雜度,可以使得每個小部分都得到較優的設計和實現。

實現階段

在實現階段,我學到的知識點是

團隊開發中的每日例會十分重要,有利於每個成員把握整體的開發進度

在alpha和beta階段都有14天的scrum階段,其中每天都要舉行例會,在實現階段的每日例會是十分重要的。

一方面,通過每日例會,每個成員都可以比較清晰地把握團隊整體的開發進展,進而便於規劃自己未來幾天的任務。

另一方面,也可以起到監督和督促的作用,在每日例會上,大家都會彙報自己的工作進展,這在無形中就起到了督促作用。

此外,每日例會也能夠起到活躍團隊氣氛、促進團隊團結的效果,每日例會不僅是彙報工作的地方,也是團隊成員交流的機會。

測試

在測試階段,我學到的知識點是

要常常回歸測試,不要等到最後統一測試,那樣往往費力不討好

在alpha階段,我們的測試基本上是在發布前統一進行的,在開發過程中的測試較少,所以最後發布前的測試工作十分緊張。

統一測試的壞處在於,一方面,統一測試的工作量很大,面對一個初有體積的項目,要進行覆蓋度較高的測試是十分耗時耗力的工作。另一方面,在最後統一測試的修復成本較高,在軟件開發階段發現並修正錯誤往往是比較容易的,但是當軟件完成了整合,再進行測試並修正錯誤,那樣的修復成本往往較高,因為在軟件整合完成之後發現的BUG有可能是整合導致的,也有可能是某個組件自身帶來的,這樣不僅問題定位困難,而且修復時常常會涉及到多個組件,牽一發而動全身。

發布

在發布階段,我學到的知識點是

推廣十分重要,優秀的推廣能夠助力讓產品最終擁有大量的用戶

在alpha階段的發布環節,我們團隊並沒有十分重視產品的推廣,因而導致在alpha階段最終用戶數量不多,相應的,收到的用戶反饋也就較少,為beta階段的展開帶來了一定的困難。

而在beta階段,我們及早進行了發布和推廣,有力地吸引了一批有效用戶,他們為我們的項目提出了寶貴的意見和建議,從長遠來看,這將十分有助於我們產品的進一步發展和更新。

維護

在維護階段,我學到的知識點是

要做好系統數據收集工作,在維護階段要密切關注系統數據記錄,及時發現系統可能存在的問題

在alpha階段,我們的後端系統並沒有設計太多的日誌系統,用戶的操作基本都沒有被有效記錄下來,導致維護時出現了問題很難定位。

在beta階段,我們針對後端系統的設計強化了系統日誌方面的實現,記錄了所有數據庫查詢操作、API訪問操作等諸如此類操作的記錄,便於在維護時定位錯誤和排查隱患。

四、理解與心得

無論是個人項目、結對項目還是團隊項目,都需要有一個思路清晰的領導者(管理者)來把握整體的前進方向。

在個人項目中,自己是那個管理者,在結對項目中,兩人都是管理者,而在團隊項目中,PM是主導的管理者,每個人也都參与其中。

為什麼說這個管理者十分重要,因為在多人完成一項任務時,思路統一是很重要的一件事情。每個人都有自己的主意,那樣是無法擰成一股繩來合作的,需要有人來管理和協調,讓大家的想法達成一致,才能使得團隊高效地合作。

同時,管理團隊和管理軟件開發周期也是一門學問。

管理團隊涉及到協調大家不同的意見,盡量不偏不坦,保證每個人的個性的同時又要保證團隊的統一。要能夠充分發揮每個人的特點,盡量滿足每個人的愛好和想法,這本身就是一項十分有挑戰的事情。

而管理軟件開發周期更甚,軟件開發涉及到多個環節,每個環節都有每個環節的任務和特點,在每個階段都指定明確的目標對於按期完成軟件開發而言有十分重要的意義。

同時,軟件開發不僅是技術的挑戰,也是設計的挑戰。

高超的技術可以幫助程序員實現功能,但是並不能幫助程序員設計出優秀的產品。軟件開發往往細節之處見真功夫,這種功夫不一定是實現難度有多高,往往是設計思想上的巧妙。優秀的設計可以讓產品更加易用,更加具有粘性,讓用戶更加依賴於產品。

這一點我在我們的項目中深有體會。我們的IDE主打易用,所以在很多地方的設計上都是精雕細琢,如功能的布局、UI的設計、項目的入口等等,都力求讓用戶能夠快速上手,長期使用。

此外,我也有一些想進行反思的地方。

首先是和大家的交流方式上,我覺得我一直存在一定的問題,同時,這也是團隊合作中十分重要的地方。團隊合作,大家不僅僅只是簡單的在一起做個東西,人際層面的往來對團隊的建設也十分重要,良好的交流方式有利於提高團隊的凝聚力,營造良好的團隊氛圍。在這一點上,我覺得我做的還不夠,有時會將個人情緒遷怒到他人,也是感謝大家對我的包容吧,能夠讓團隊的合作一直很愉快。

還有就是缺乏思考,尤其是設計上的思考。好的軟件開發人員會設計優秀的接口和服務,讓使用者一看就能覺出這是大家之作,方便易用的同時功能全面。這一點上我覺得我做的還不夠,很多時候都是功能有了就可以了,而不再進一步考慮如何優化接口,優化API設計,讓調用者更加方便地使用接口與服務,這一點在未來的軟件開發設計上也是需要提高的。

總結起來說,這一學期的軟工實踐體驗是很充實的。從單人項目到結對項目再到最終的團隊項目,實踐了不同模式下的開發過程,也最終取得了一個小有規模的產品,無論是過程還是結果都值得欣慰。同時,通過一系列的實踐,我也深刻體會到了團隊大型軟件開發的難度和痛點,也為將來的進一步學習和發展奠定了基礎、明確了方向,從這些角度上講,都是十分有益的。希望這個學期的實踐可以成為未來軟件開發生涯的一個好的開端。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

熱浪再襲歐 德比荷法熱到40度 法德核電廠被迫停機

摘錄自2019年7月24、26日自由時報、中央社報導

今年夏天的第二波熱浪,25日讓歐洲多國氣溫再創新高,不少城市都測到超過攝氏40度的高溫。荷蘭25日在東部城鎮代倫(Deelen)測得破紀錄的攝氏41.7度高溫。比利時25日在東北部的小布羅赫爾空軍基地測到40.6度,為該國自1833年以來的最高溫。德國25日在西部林根(Lingen)測到41.5度。

法國巴黎25日創下史上最高溫,該市蒙蘇里(Montsouris)區下午測到42.4度的高溫。

熱浪也迫使南法一座核電廠於23日起停機至月底。

依賴海水冷卻反應爐的核電廠受熱浪影響,法國電力公司(EDF)表示,全法國的核電廠25日發電量減少約5.2十億瓦(GW)或8%,南部塔恩-加倫省(Tarn-et-Garonne)的戈費契(Golfech)核電廠,因加倫河(Garonne River)水溫過高,2個反應爐分別於23日起停機,直到本月30日。另有6座核反應爐發電量縮減。

德國電力公用事業公司意昂(E.ON)旗下核電公司PreussenElektra 26日表示,由於威塞河(Weser River)水溫過高,發電量1430 MW的格隆德(Grohnde)核電廠預計26日中午起至28日暫停運轉。若氣候改變可能調整計畫。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

保育有成 長鬚鯨和山地大猩猩數量回穩

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

小師妹學JVM之:深入理解JIT和編譯優化-你看不懂系列

目錄

  • 簡介
  • JIT編譯器
  • Tiered Compilation分層編譯
  • OSR(On-Stack Replacement)
  • Deoptimization
  • 常見的編譯優化舉例
    • Inlining內聯
    • Branch Prediction分支預測
    • Loop unswitching
    • Loop unrolling展開
    • Escape analysis逃逸分析
  • 總結

簡介

小師妹已經學完JVM的簡單部分了,接下來要進入的是JVM中比較晦澀難懂的概念,這些概念是那麼的枯燥乏味,甚至還有點惹人討厭,但是要想深入理解JVM,這些概念是必須的,我將會盡量嘗試用簡單的例子來解釋它們,但一定會有人看不懂,沒關係,這個系列本不是給所有人看的。

更多精彩內容且看:

  • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

JIT編譯器

小師妹:F師兄,我的基礎已經打牢了嗎?可以進入這麼複雜的內容環節了嗎?

小師妹不試試怎麼知道不行呢?了解點深入內容可以幫助你更好的理解之前的知識。現在我們開始吧。

上次我們在講java程序的處理流程的時候,還記得那通用的幾步吧。

小師妹:當然記得了,編寫源代碼,javac編譯成字節碼,加載到JVM中執行。

對,其實在JVM的執行引擎中,有三個部分:解釋器,JIT編譯器和垃圾回收器。

解釋器會將前面編譯生成的字節碼翻譯成機器語言,因為每次都要翻譯,相當於比直接編譯成機器碼要多了一步,所以java執行起來會比較慢。

為了解決這個問題,JVM引入了JIT(Just-in-Time)編譯器,將熱點代碼編譯成為機器碼。

Tiered Compilation分層編譯

小師妹你知道嗎?在JDK8之前,HotSpot VM又分為三種。分別是 client VM, server VM, 和 minimal VM,分別用在客戶端,服務器,和嵌入式系統。

但是隨着硬件技術的發展,這些硬件上面的限制都不是什麼大事了。所以從JDK8之後,已經不再區分這些VM了,現在統一使用VM的實現來替代他們。

小師妹,你覺得Client VM和Server VM的本質區別在哪一部分呢?

小師妹,編譯成字節碼應該都是使用javac,都是同樣的命令,字節碼上面肯定是一樣的。難點是在執行引擎上面的不同?

說的對,因為Client VM和Server VM的出現,所以在JIT中出現了兩種不同的編譯器,C1 for Client VM, C2 for Server VM。

因為javac的編譯只能做少量的優化,其實大量的動態優化是在JIT中做的。C2相對於C1,其優化的程度更深,更加激進。

為了更好的提升編譯效率,JVM在JDK7中引入了分層編譯Tiered compilation的概念。

對於JIT本身來說,動態編譯是需要佔用用戶內存空間的,有可能會造成較高的延遲。

對於Server服務器來說,因為代碼要服務很多個client,所以磨刀不誤砍柴工,短暫的延遲帶來永久的收益,聽起來是可以接受的。

Server端的JIT編譯也不是立馬進行的,它可能需要收集到足夠多的信息之後,才進行編譯。

而對於Client來說,延遲帶來的性能影響就需要進行考慮了。和Server相比,它只進行了簡單的機器碼的編譯。

為了滿足不同層次的編譯需求,於是引入了分層編譯的概念。

大概來說分層編譯可以分為三層:

  1. 第一層就是禁用C1和C2編譯器,這個時候沒有JIT進行。
  2. 第二層就是只開啟C1編譯器,因為C1編譯器只會進行一些簡單的JIT優化,所以這個可以應對常規情況。
  3. 第三層就是同時開啟C1和C2編譯器。

在JDK7中,你可以使用下面的命令來開啟分層編譯:

-XX:+TieredCompilation

而在JDK8之後,恭喜你,分層編譯已經是默認的選項了,不用再手動開啟。

OSR(On-Stack Replacement)

小師妹:F師兄,你剛剛講到Server的JIT不是立馬就進行編譯的,它會等待一定的時間來搜集所需的信息,那麼代碼不是要從字節碼轉換成機器碼?

對的,這個過程就叫做OSR(On-Stack Replacement)。為什麼叫OSR呢?我們知道JVM的底層實現是一個棧的虛擬機,所以這個替換實際上是一系列的Stack操作。

上圖所示,m1方法從最初的解釋frame變成了後面的compiled frame。

Deoptimization

這個世界是平衡的,有陰就有陽,有優化就有反優化。

小師妹:F師兄,為什麼優化了之後還要反優化呢?這樣對性能不是下降了嗎?

通常來說是這樣的,但是有些特殊的情況下面,確實是需要進行反優化的。

下面是比較常見的情況:

  1. 需要調試的情況

如果代碼正在進行單個步驟的調試,那麼之前被編譯成為機器碼的代碼需要反優化回來,從而能夠調試。

  1. 代碼廢棄的情況

當一個被編譯過的方法,因為種種原因不可用了,這個時候就需要將其反優化。

  1. 優化之前編譯的代碼

有可能出現之前優化過的代碼可能不夠完美,需要重新優化的情況,這種情況下同樣也需要進行反優化。

常見的編譯優化舉例

除了JIT編譯成機器碼之外,JIT還有一下常見的代碼優化方式,我們來一一介紹。

Inlining內聯

舉個例子:

int a = 1;
int b = 2;
int result = add(a, b);
...
public int add(int x, int y) { return x + y; }
int result = a + b; //內聯替換

上面的add方法可以簡單的被替換成為內聯表達式。

Branch Prediction分支預測

通常來說對於條件分支,因為需要有一個if的判斷條件,JVM需要在執行完畢判斷條件,得到返回結果之後,才能夠繼續準備後面的執行代碼,如果有了分支預測,那麼JVM可以提前準備相應的執行代碼,如果分支檢查成功就直接執行,省去了代碼準備的步驟。

比如下面的代碼:

// make an array of random doubles 0..1
double[] bigArray = makeBigArray();
for (int i = 0; i < bigArray.length; i++)
{
 double cur = bigArray[i];
 if (cur > 0.5) { doThis();} else { doThat();}
}

Loop unswitching

如果我們在循環語句裏面添加了if語句,為了提升併發的執行效率,可以將if語句從循環中提取出來:

  int i, w, x[1000], y[1000];
  for (i = 0; i < 1000; i++) {
    x[i] += y[i];
    if (w)
      y[i] = 0;
  }

可以改為下面的方式:

  int i, w, x[1000], y[1000];
  if (w) {
    for (i = 0; i < 1000; i++) {
      x[i] += y[i];
      y[i] = 0;
    }
  } else {
    for (i = 0; i < 1000; i++) {
      x[i] += y[i];
    }
  }

Loop unrolling展開

在循環語句中,因為要不斷的進行跳轉,所以限制了執行的速度,我們可以對循環語句中的邏輯進行適當的展開:

 int x;
 for (x = 0; x < 100; x++)
 {
     delete(x);
 }

轉變為:

 int x; 
 for (x = 0; x < 100; x += 5 )
 {
     delete(x);
     delete(x + 1);
     delete(x + 2);
     delete(x + 3);
     delete(x + 4);
 }

雖然循環體變長了,但是跳轉次數變少了,其實是可以提升執行速度的。

Escape analysis逃逸分析

什麼叫逃逸分析呢?簡單點講就是分析這個線程中的對象,有沒有可能會被其他對象或者線程所訪問,如果有的話,那麼這個對象應該在Heap中分配,這樣才能讓對其他的對象可見。

如果沒有其他的對象訪問,那麼完全可以在stack中分配這個對象,棧上分配肯定比堆上分配要快,因為不用考慮同步的問題。

我們舉個例子:

  public static void main(String[] args) {
    example();
  }
  public static void example() {
    Foo foo = new Foo(); //alloc
    Bar bar = new Bar(); //alloc
    bar.setFoo(foo);
  }
}

class Foo {}

class Bar {
  private Foo foo;
  public void setFoo(Foo foo) {
    this.foo = foo;
  }
}

上面的例子中,setFoo引用了foo對象,如果bar對象是在heap中分配的話,那麼引用的foo對象就逃逸了,也需要被分配在heap空間中。

但是因為bar和foo對象都只是在example方法中調用的,所以,JVM可以分析出來沒有其他的對象需要引用他們,那麼直接在example的方法棧中分配這兩個對象即可。

逃逸分析還有一個作用就是lock coarsening。

為了在多線程環境中保證資源的有序訪問,JVM引入了鎖的概念,雖然鎖可以保證多線程的有序執行,但是如果實在單線程環境中呢?是不是還需要一直使用鎖呢?

比如下面的例子:

public String getNames() {
     Vector<String> v = new Vector<>();
     v.add("Me");
     v.add("You");
     v.add("Her");
     return v.toString();
}

Vector是一個同步對象,如果是在單線程環境中,這個同步鎖是沒有意義的,因此在JDK6之後,鎖只在被需要的時候才會使用。

這樣就能提升程序的執行效率。

總結

本文介紹了JIT的原理和一些基本的優化方式。後面我們會繼續探索JIT和JVM的秘密,敬請期待。

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/jvm-jit-in-detail/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

新型液流電池可發電與供氫,電動車與氫燃料車都適用

電池電動車與氫燃料電池車都有望成為新一代交通主力,但這兩種車款各採用不同的「充電方式」,一種是電力、一種是補充氫氣,各國得針對兩種不同的車系分別打造管線與「加油站」,而近日英國格拉斯哥大學打造新型液流電池,不僅可讓汽車在幾秒內完成燃料補充,還可以釋放電力與氫氣、完美解決兩種電動車系統不相容的狀況。

液流電池(flow battery)由兩個電解質槽組成,充放電時電解質會被幫補到中間的發電室,而發電室也會以薄膜隔開兩種溶液、形成兩個電極,最後產生離子交換來發電,而由於兩種電解質是分開存放,不會有電解質相互滲漏與自身放電等安全性問題,是一種良好的儲能生力軍。

只是液流電池體積龐大,即使該電池具有安全性與穩定性高等優點,仍不適合用於 3C 產品與電動車,目前大多研究團隊都是想把液流電池用在再生能源儲能系統。

而格拉斯哥大學這次想將突破以往液流電池無法用在汽車的印象,並成功透過奈米粒子溶液打造新型液流電池,其中團隊所用的電解質是一種奈米懸浮液(suspension),每個奈米粒子都可以當成一顆小型電池,儲存能量更是一般液流電池的 10 倍,還能以電力或氫氣的形式釋放。

且由於液流電池主要以液態電解質驅動,團隊更指出,新型液流電池可在幾秒內完成移除舊液體、補充新電解質,因此該系統的「充電」時間可跟一般汽油車一樣,大大縮短電動車的充電速度。

格拉斯哥大學 Regius Chair of Chemistry 教授 Leroy(Lee)Cronin 表示,若再生能源想要有效運作,還需要儲存容量、靈活性高的儲能系統來幫忙解決間歇性能源與電力尖峰等問題,而團隊提出的新型化學充電方式,除了可用在儲能系統,還可應用在電動車中。

該研究將有助於未來液流電池的應用,有望縮短液流電池與大規模商業化的距離。團隊也指出,新型液流電池的能量密度相當高,可提升將來電動車的續航里程與儲能系統的儲存容量,目前研究已發表在《》。

(首圖來源:。文/DaisyChuang)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

中國推新能源車結合再生能源應用,拚減碳逾 20 萬噸

新華社報導,中國新能源汽車和再生能源綜合應用商業化推廣專案正式啟動,據中國汽車工程學會常務副理事長兼秘書長張進華表示,整個專案營運期間將直接減排二氧化碳超過 20 萬噸。

張進華指出,新能源汽車和再生能源綜合應用商業化推廣專案的實施,是落實中國汽車產業中長期發展規劃的重要工作之一。據瞭解,為促進中國新能源汽車與再生能源產業綜合應用,充分發揮新能源汽車全生命週期的節能減排效益,減少溫室氣體排放,中國工信部和聯合國工業發展組織共同向全球環境基金申報此項目並獲得批准,中國汽車工程學會將全面承擔專案實施與組織工作,實施週期為 3 年。

據瞭解,該專案具體內容包括組織制定新能源汽車和再生能源綜合應用的政策和標準法規,展開智慧充電系統、再生能源微電網、車網互動、移動充電系統、退役電池梯次利用等技術示範和驗證工作。目前,已確定將中國上海市、江蘇省鹽城市和如皋市作為示範城市,展開新能源汽車和再生能源綜合應用的技術示範和驗證。

(本文內容由 授權使用。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

【大廠面試08期】談一談你對HashMap的理解?

摘要

HashMap的原理也是大廠面試中經常會涉及的問題,同時也是工作中常用到的Java容器,本文主要通過對以下問題進行分析講解,來幫助大家理解HashMap的原理。

1.HashMap添加一個鍵值對的過程是怎麼樣的?

2.為什麼說HashMap不是線程安全的?

3.為什麼要一起重寫hashCode()和equal()方法?

HashMap添加一個鍵值對的過程是怎麼樣的?

這是網上找的一張流程圖,可以結合著步驟來看這個流程圖,了解添加鍵值對的過程。

1.初始化table

判斷table是否為空或為null,否則執行resize()方法(resize方法一般是擴容時調用,也可以調用來初始化table)。

2.計算hash值

根據鍵值key計算hash值。(因為hashCode是一個int類型的變量,是4字節,32位,所以這裡會將hashCode的低16位與高16位進行一個異或運算,來保留高位的特徵,以便於得到的hash值更加均勻分佈)

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

3.插入或更新節點

根據(n – 1) & hash計算得到插入的數組下標i,然後進行判斷

table[i]==null

那麼說明當前數組下標下,沒有hash衝突的元素,直接新建節點添加。

table[i].hash == hash &&(table[i]== key || (key != null && key.equals(table[i].key)))

判斷table[i]的首個元素是否和key一樣,如果相同直接更新value。

table[i] instanceof TreeNode

判斷table[i] 是否為treeNode,即table[i] 是否是紅黑樹,如果是紅黑樹,則直接在樹中插入鍵值對。

其他情況

上面的判斷條件都不滿足,說明table[i]存儲的是一個鏈表,那麼遍歷鏈表,判斷是否存在已有元素的key與插入鍵值對的key相等,如果是,那麼更新value,如果沒有,那麼在鏈表末尾插入一個新節點。插入之後判斷鏈表長度是否大於8,大於8的話把鏈錶轉換為紅黑樹。

4.擴容

插入成功后,判斷實際存在的鍵值對數量size是否超多了最大容量threshold(一般是數組長度*負載因子0.75),如果超過,進行擴容。

源代碼如下:

2.為什麼說HashMap不是線程安全的?

其實通過學習HashMap添加鍵值對的方法,我們可以看到整個方法內都沒有使用到鎖,所以一旦多線併發訪問,就有可能造成數據不一致的問題,

例如:

如果有兩個添加鍵值對的線程都執行到if ((tab = table) == null || (n = tab.length) == 0)這行語句,都對table變量進行數組初始化,就會造成已經初始化好的數組table被覆蓋,然後前面初始化的線程會將鍵值對添加到之前初始化的數組中去,造成鍵值對丟失。

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // tab為空則創建 
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    ...後面的代碼省略
}

3.為什麼要一起重寫hashCode()和equal()方法?

當我們的對象一旦作為HashMap中的key,或者是HashSet中的元素使用時,就必須同時重寫hashCode()和equal()方法

首先看看hashCode()和equal()方法的默認實現

可以看到Obejct類中的源碼如下,可以看到equals()方法的默認實現是判斷兩個對象的內存地址是否相同來決定返回結果。

    public native int hashCode();
	public boolean equals(Object obj) {
        return (this == obj);
    }

網上很多博客說hashCode的默認實現是返回內存地址,其實不對,以OpenJDK為例,hashCode的默認計算方法有5種,有返回隨機數的,有返回內存地址,具體採用哪一種計算方法取決於運行時庫和JVM的具體實現。

感興趣的朋友可以看看這篇博客
https://blog.csdn.net/xusiwei1236/article/details/45152201

然後看看hashCode()方法,equal()方法在HashMap中的應用

static final int hash(Object key) {
    int h;
    //因為hashCode是一個int類型的變量,是4字節,32位,所以這裡會將hashCode的低16位與高16位進行一個異或運算,來保留高位的特徵,以便於得到的hash值更加均勻分佈
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

為了將一組鍵值對均勻得存儲在一個數組中,HashMap對key的hashCode進行計算得到一個hash值,用hash對數組長度取模,得到數組下標,將鍵值對存儲在數組下標對應的鏈表下(假設鏈表長度小於8,沒有達到轉換為紅黑樹的閥值)。

下面是添加鍵值對的putVal()方法,當數組下標對應的是一個鏈表時執行的代碼

//遍歷鏈表
for (int binCount = 0; ; ++binCount) {
    if ((e = p.next) == null) {//已經遍歷到鏈表末尾,說明鏈表不存在這個key
        p.next = newNode(hash, key, value, null);//在末尾添加這個鍵值對
        if (binCount >= TREEIFY_THRESHOLD - 1) //超過鏈錶轉化為紅黑樹的閥值(也急速鏈表長度》=8)
            treeifyBin(tab, hash);
        break;
    }
    if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k))))
        break;
    p = e;
}

可以清楚地看到判斷添加的key與鏈表中已存在的key是否相等的方法主要是e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))),
也就是:
1.先判斷hash值是否相等,不相等直接結束判斷,因為hash值不相等,key肯定不相等。
2.判斷兩個key對象的內存地址是否相等(相等指向內存中同一個對象)。
3.key不為null,調用key的equal()方法判斷是否相等,因為有可能兩個key在內存中存儲的地址不一樣,但是是相等的。
就像是

String a = new String("test");
String b = new String("test");

System.out.println("a==b is "+a==b);//打印為false
System.out.println("a.equals(b) is "+a.equals(b));//打印為true

背景

假設我們有一個KeyObject類,假設我們認為兩個KeyObject的屬性a相等,那麼KeyObject就是相等的相等的,我們將KeyObject作為HashMap的key,以KeyObject是否相等作為去重標準,不能重複添加KeyObject相等,value不等的值到HashMap中去

public static class KeyObject {
    Integer a;
    public KeyObject(Integer a) {
        this.a = a;
    }
}

假設都hashCode()方法和equals()方法都不重寫(結果:HashMap無法保證去重)

執行以下代碼:

public static void main(String[] args) {
    KeyObject key1 = new KeyObject(1);
    KeyObject key2 = new KeyObject(1);
    System.out.println("key1的hashCode為"+ key1.hashCode());
    System.out.println("key2的hashCode為" + key2.hashCode());
    System.out.println("key1.equals(key2)的結果為"+(key1.equals(key2)));
    HashMap<KeyObject,String> hashMap = new HashMap<KeyObject,String>();
    hashMap.put(key1,"value1");
    hashMap.put(key2,"value2");
    //打印hashMap
    for(KeyObject key :hashMap.keySet()){
        System.out.println("KeyObject.a="+key.a+" : "+hashMap.get(key));
    }
}

如果KeyObject的hashCode()方法和equals()方法都不重寫,那麼即便KeyObject的屬性a都是1,key1和key2的hashCode都是不相同的,key1和key2調用equals()方法也不相等,這樣hashMap中就可以同時存在key1和key2了。

打印結果:

key1的hashCode為728890494
key2的hashCode為1558600329
key1.equals(key2)的結果為false
KeyObject.a=1 : value1
KeyObject.a=1 : value2

假如只重寫hashCode()方法(結果:無法正確地與鏈表元素進行相等判斷,從而無法保證去重)

執行以下代碼:

 public static class KeyObject {
    Integer a;
    public KeyObject(Integer a) {
        this.a = a;
    }

    @Override
    public int hashCode() {
        return a;
    }

    public static void main(String[] args) {
        KeyObject key1 = new KeyObject(1);
        KeyObject key2 = new KeyObject(1);
        System.out.println("key1的hashCode為"+ key1.hashCode());
        System.out.println("key2的hashCode為" + key2.hashCode());
        System.out.println("key1.equals(key2)的結果為"+(key1.equals(key2)));
        HashMap<KeyObject,String> hashMap = new HashMap<KeyObject,String>();
        hashMap.put(key1,"value1");
        hashMap.put(key2,"value2");
        for(KeyObject key :hashMap.keySet()){
            System.out.println("TestObject.a="+key.a+" : "+hashMap.get(key));
        }
    }
}

此時equal()方法的實現是默認實現,也就是當兩個對象的內存地址相等時,equal()方法才返回true,雖然key1和key2的a屬性是相同的,但是他們在內存中是不同的對象,所以key1==key2結果會是false,KeyObject的equals()方法默認實現是判斷兩個對象的內存地址,所以 key1.equals(key2)也會是false,所以這兩個鍵值對可以重複地添加到hashMap中去。

輸出結果:

key1的hashCode為1
key2的hashCode為1
key1.equals(key2)的結果為false
TestObject.a=1 : value1
TestObject.a=1 : value2

假如只重寫equals()方法(結果:映射到HashMap中不同數組下標,無法保證去重)

public static class KeyObject {
    Integer a;
    public KeyObject(Integer a) {
        this.a = a;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        KeyObject keyObject = (KeyObject) o;
        return Objects.equals(a, keyObject.a);
    }

    public static void main(String[] args) {
        KeyObject key1 = new KeyObject(1);
        KeyObject key2 = new KeyObject(1);
        System.out.println("key1的hashCode為"+ key1.hashCode());
        System.out.println("key2的hashCode為" + key2.hashCode());
        System.out.println("key1.equals(key2)的結果為"+(key1.equals(key2)));
        HashMap<KeyObject,String> hashMap = new HashMap<KeyObject,String>();
        hashMap.put(key1,"value1");
        hashMap.put(key2,"value2");
        for(KeyObject key :hashMap.keySet()){
            System.out.println("TestObject.a="+key.a+" : "+hashMap.get(key));
        }
    }
}

假設只equals()方法,hashCode方法會是默認實現,具體的計算方法取決於JVM,(測試時發現是內存地址不同但是相等的對象,它們的hashCode不相同),所以計算得到的數組下標不相同,會存儲到hashMap中不同數組下標下的鏈表中,也會導致HashMap中存在重複元素。

輸出結果如下:

key1的hashCode為1289479439
key2的hashCode為6738746
key1.equals(key2)的結果為true
TestObject.a=1 : value1
TestObject.a=1 : value2

總結

所以當我們的對象一旦作為HashMap中的key,或者是HashSet中的元素使用時,就必須同時重寫hashCode()和equal()方法,因為hashCode會影響key存儲的數組下標及與鏈表元素的初步判斷,equal()是作為判斷key與鏈表中的key是否相等的最後標準。

  • 所以只重寫hashCode()方法,會導致無法正確地與鏈表元素進行相等判斷,從而無法保證去重)
  • 只重寫equals()方法導致鍵值對映射到HashMap中不同數組下標,無法保證去重

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

從0到1學會logstash的玩法(ELK)

本篇文章採用的採用的是logstash7.7.0版本,主要從如下幾個方面介紹

1、logstash是什麼,可以用來幹啥

2、logstash的基本原理是什麼

3、怎麼去玩這個elk的組件logstash

一、logstash是什麼,有哪些作用

1.1、概念

官方概念:Logstash是免費且開放的服務器端數據處理管道,能夠從多個來源採集數據,轉換數據,然後將數據發送到您最喜歡的“存儲庫”中。

1.2、功能

Logstash能夠動態地採集、轉換和傳輸數據,不受格式或複雜度的影響。利用Grok從非結構化數據中派生出結構,從IP地址解碼出地理坐標,匿名化或排除敏感字段,並簡化整體處理過程。

採集數據:

  能夠採集各種樣式、大小和來源的數據往往以各種各樣的形式,比如log日誌,收集redis、kafka等熱門分佈式技術的數據,並且還可以收集實現了java的JMS規範的消息中心的數據,或分散或集中地存在於很多系統中。Logstash支持各種輸入選擇,可以同時從眾多常用來源捕捉事件。能夠以連續的流式傳輸方式,輕鬆地從日誌、指標、Web應用、數據存儲以及各種AWS服務採集數據

 

 

 

解析數據和轉換:

  數據從源傳輸到存儲庫的過程中,Logstash過濾器能夠解析各個事件,識別已命名的字段以構建結構,並將它們轉換成通用格式,以便進行更強大的分析和實現商業價值。
Logstash能夠動態地轉換和解析數據,不受格式或複雜度的影響:

  • 利用Grok從非結構化數據中解析出結構數據
  • 從IP地址破譯出地理坐標
  • 將PII數據匿名化,完全排除敏感字段
  • 簡化整體處理,不受數據源、格式或架構的影響

  數據輸入端從各種數據源收集到的數據可能會有很多不是我們想要的,這時我們可以給Logstash定義過濾器,過濾器可以定義多個,它們依次執行,最終把我們想要的數據過濾出來,然後把這些數據解析成目標數據庫,如elasticsearch等能支持的數據格式存儲數據。

 

數據轉存:

  選擇好存儲庫,導出數據到存儲庫進行存儲,儘管Elasticsearch是我們的首選輸出方向,能夠為我們的搜索和分析帶來無限可能,但它並非唯一選擇。Logstash提供眾多輸出選擇,可以將數據發送到您要指定的地方,比如redis、kafka等

 

 

 

 

二、logstash的基本原理

  logstash分為三個步驟:inputs(必須的)→ filters(可選的)→ outputs(必須的),inputs生成時間,filters對其事件進行過濾和處理,outputs輸出到輸出端或者決定其存儲在哪些組件里。inputs和outputs支持編碼和解碼。

 

執行模型:

Logstash是協調inputs、filters和outputs執行事件處理的管道。

  Logstash管道中的每個input階段都在自己的線程中運行。將寫事件輸入到內存(默認)或磁盤上的中心隊列。每個管道工作線程從該隊列中取出一批事件,通過配置的filter處理該批事件,然後通過output輸出到指定的組件存儲。管道處理數據量的大小和管道工作線程的數量是可配置的

  默認情況下,Logstash使用管道階段(input→filter和filter→output)之間的內存限制隊列來緩衝事件。如果Logstash不安全地終止,存儲在內存中的所有事件都將丟失。為了幫助防止數據丟失,可以啟用Logstash將飛行中的事件持久化到磁盤。有關詳細信息,請參閱持久隊列https://www.elastic.co/guide/en/logstash/current/persistent-queues.html

如下是目前logstash7.7.0支持的inputs、outputs、filters

inputs:

azure_event_hubs,beats,cloudwatch,couchdb_changes,dead_letter_queue,elasticsearch,exec,file,ganglia,gelf,generator,github,google_cloud_storage,google_pubsub,graphite,heartbeat,http,http_poller,imap,irc,java_generator,java_stdin,jdbc,jms,jmx,kafka,kinesis,log4j,lumberjack,meetup,pipe,puppet_facter,rabbitmq,redis,relp,rss,s3,s3-sns-sqs,salesforce,snmp,snmptrap,sqlite,sqs,stdin,stomp,syslog,tcp,twitter,udp,unix,varnishlog,websocket,wmi,xmpp

outputs:

boundary, circonus, cloudwatch, csv, datadog, datadog_metrics, elastic_app_search, elasticsearch, email, exec, file, ganglia, gelf, google_bigquery, google_cloud_storage, google_pubsub, graphite, graphtastic, http, influxdb, irc, sink, java_stdout, juggernaut, kafka, librato, loggly, lumberjack, metriccatcher, mongodb, nagios, nagios_nsca, opentsdb, pagerduty, pipe, rabbitmq, redis, redmine, riak, riemann, s3, sns, solr_http, sqs, statsd, stdout, stomp, syslog, tcp, timber, udp, webhdfs, websocket, xmpp, zabbix

filters:

aggregate, alter, bytes, cidr, cipher, clone, csv, date, de_dot, dissect, dns, drop, elapsed, elasticsearch, environment, extractnumbers, fingerprint, geoip, grok, http, i18n, java_uuid, jdbc_static, jdbc_streaming, json, json_encode, kv, memcached, metricize, metrics, mutate, prune, range, ruby, sleep, split, syslog_pri, threats_classifier, throttle, tld, translate, truncate, urldecode, useragent, uuid, xml

三、玩一玩logstash

3.1、壓縮包方式安裝

下載地址1:https://www.elastic.co/cn/downloads/logstash   

下載地址2:https://elasticsearch.cn/download/

這裏需要安裝jdk,我使用的是elasticsearch7.7.0自帶的jdk:

解壓即安裝:

tar -zxvf logstash-7.7.0.tar.gz

來個logstash版本的HelloWorld:

./bin/logstash -e 'input { stdin { } } output { stdout {} }'

 

 

 

3.2、logstash配置文件

 logstash.yml:包含Logstash配置標誌。您可以在此文件中設置標誌,而不是在命令行中傳遞標誌。在命令行上設置的任何標誌都會覆蓋logstash中的相應設置

 pipelines.yml:包含在單個Logstash實例中運行多個管道的框架和指令。

 jvm.options:包含JVM配置標誌。使用此文件設置總堆空間的初始值和最大值。您還可以使用此文件為Logsta設置語言環境

 log4j2.properties:包含log4j 2庫的默認設置

 start.options (Linux):用於配置啟動服務腳本

logstash.yml文件詳解:

node.name  #默認主機名,該節點的描述名字
path.data  #LOGSTASH_HOME/data ,Logstash及其插件用於任何持久需求的目錄
pipeline.id #默認main,pipeline的id
pipeline.java_execution #默認true,使用java執行引擎
pipeline.workers #默認為主機cpu的個數,表示并行執行管道的過濾和輸出階段的worker的數量
pipeline.batch.size #默認125 表示單個工作線程在嘗試執行過濾器和輸出之前從輸入中收集的最大事件數
pipeline.batch.delay #默認50 在創建管道事件時,在將一個小批分派給管道工作者之前,每個事件需要等待多長時間(毫秒)
pipeline.unsafe_shutdown  #默認false,當設置為true時,即使內存中仍有運行的事件,強制Logstash在關閉期間將會退出。默認情況下,Logstash將拒絕退出,直到所有接收到的事件都被推入輸出。啟用此選項可能導致關機期間數據丟失
pipeline.ordered #默認auto,設置管道事件順序。true將強制對管道進行排序,如果有多個worker,則阻止logstash啟動。如果為false,將禁用維持秩序所需的處理。訂單順序不會得到保證,但可以節省維護訂單的處理成本
path.config #默認LOGSTASH_HOME/config  管道的Logstash配置的路徑
config.test_and_exit #默認false,設置為true時,檢查配置是否有效,然後退出。請注意,使用此設置不會檢查grok模式的正確性
config.reload.automatic #默認false,當設置為true時,定期檢查配置是否已更改,並在更改時重新加載配置。這也可以通過SIGHUP信號手動觸發
config.reload.interval  #默認3s ,檢查配置文件頻率
config.debug #默認false 當設置為true時,將完全編譯的配置显示為調試日誌消息
queue.type #默認memory ,用於事件緩衝的內部排隊模型。為基於內存中的遺留隊列指定內存,或為基於磁盤的脫機隊列(持久隊列)指定持久內存
path.queue #默認path.data/queue  ,在啟用持久隊列時存儲數據文件的目錄路徑
queue.page_capacity #默認64mb ,啟用持久隊列時(隊列),使用的頁面數據文件的大小。隊列數據由分隔為頁面的僅追加數據文件組成
queue.max_events #默認0,表示無限。啟用持久隊列時,隊列中未讀事件的最大數量
queue.max_bytes  #默認1024mb,隊列的總容量,以字節為單位。確保磁盤驅動器的容量大於這裏指定的值
queue.checkpoint.acks #默認1024,當啟用持久隊列(隊列)時,在強制執行檢查點之前被隔離的事件的最大數量
queue.checkpoint.writes #默認1024,當啟用持久隊列(隊列)時,強制執行檢查點之前的最大寫入事件數
queue.checkpoint.retry #默認false,啟用后,對於任何失敗的檢查點寫,Logstash將對每個嘗試的檢查點寫重試一次。任何後續錯誤都不會重試。並且不推薦使用,除非是在那些特定的環境中
queue.drain #默認false,啟用后,Logstash將等待,直到持久隊列耗盡,然後關閉
path.dead_letter_queue#默認path.data/dead_letter_queue,存儲dead-letter隊列的目錄
http.host #默認"127.0.0.1" 表示endpoint REST端點的綁定地址。
http.port #默認9600 表示endpoint REST端點的綁定端口。
log.level #默認info,日誌級別fatal,error,warn,info,debug,trace,
log.format #默認plain 日誌格式
path.logs  #默認LOGSTASH_HOME/logs 日誌目錄

3.3、keystore

keystore可以保護一些敏感的信息,使用變量的方式替代,比如使用ES_PWD代替elasticsearch的密碼,可以通過${ES_PWD}來獲取elasticsearch的密碼,這樣就是的密碼不再是明文密碼。

./bin/logstash-keystore create   #創建一個keyword
 ./bin/logstash-keystore add ES_PWD #創建一個elastic的passwd,然後通過${ES_PWD}使用該密碼
 ./bin/logstash-keystore list  #查看已經設置好的鍵值對
 ./bin/logstash-keystore remove ES_PWD #刪除在keyword中的key

例如:

 

 

 3.4、logstash命令

注意:參數和logstash.yml配置文件對應(這裏不詳解,請查看3.2節)

-n, --node.name NAME          
-f, --path.config CONFIG_PATH 
-e, --config.string CONFIG_STRING
--field-reference-parser MODE 
--modules MODULES             
-M, --modules.variable MODULES_VARIABLE
--setup                      
--cloud.id CLOUD_ID                                            
--cloud.auth CLOUD_AUTH       
--pipeline.id ID              
-w, --pipeline.workers COUNT 
--pipeline.ordered ORDERED   
--java-execution                                               
--plugin-classloaders         
-b, --pipeline.batch.size SIZE 
-u, --pipeline.batch.delay DELAY_IN_MS 
--pipeline.unsafe_shutdown    
--path.data PATH              
-p, --path.plugins PATH       
-l, --path.logs PATH         
--log.level LEVEL             
--config.debug                
-i, --interactive SHELL      
-V, --version                 
-t, --config.test_and_exit    
-r, --config.reload.automatic 
--config.reload.interval 
--http.host HTTP_HOST         
--http.port HTTP_PORT         
--log.format FORMAT           
--path.settings SETTINGS_DIR

3.5、logstash配置文件的格式和value type

1、logstash配置文件的格式如下:

輸入,解析過濾,輸出,其中filter不是必須的,其他兩個是必須的。

input {
  ...
}

filter {
  ...
}

output {
  ...
}

2、value types(logstash支持的數據類型)

array:數組可以是單個或者多個字符串值。

users => [ {id => 1, name => bob}, {id => 2, name => jane} ]

Lists:集合

path => [ "/var/log/messages", "/var/log/*.log" ]
uris => [ "http://elastic.co", "http://example.net" ]

Boolean:true 或者false

ssl_enable => true

Bytes:字節類型

my_bytes => "1113"   # 1113 bytes
my_bytes => "10MiB"  # 10485760 bytes
my_bytes => "100kib" # 102400 bytes
my_bytes => "180 mb" # 180000000 bytes

Codec:編碼類型

codec => "json"

Hash:哈希(散列)

match => {
  "field1" => "value1"
  "field2" => "value2"
  ...
}
# or as a single line. No commas between entries:
match => { "field1" => "value1" "field2" => "value2" }

Number:数字類型

port => 33

Password:密碼類型

my_password => "password"

URI:uri類型

my_uri => "http://foo:bar@example.net"

Path: 路徑類型

my_path => "/tmp/logstash"

String:字符串類型,字符串必須是單個字符序列。注意,字符串值被括在雙引號或單引號中

3.6、logstash的權限配置

配置賬號,一個種是role,一種是user,配置方式有兩種,一種是通過elasticsearch的API配置,一種是通過kibana配置:

第一種方式:通過elasticsearch的API配置:

#添加一個logstash_writer的角色
POST _xpack/security/role/logstash_writer
{
  "cluster": ["manage_index_templates", "monitor", "manage_ilm"], 
  "indices": [
    {
      "names": [ "logstash-*" ],  #索引的模式匹配
      "privileges": ["write","create","delete","create_index","manage","manage_ilm"]   #權限內容
    }
  ]
}

#添加一個有logstash_writer角色權限的用戶:logstash_internal
POST _xpack/security/user/logstash_internal
{
  "password" : "x-pack-test-password",
  "roles" : [ "logstash_writer"],  #分配角色
  "full_name" : "Internal Logstash User"
}

#添加一個logstash_reader角色,只有read權限
POST _xpack/security/role/logstash_reader
{
  "indices": [
    {
      "names": [ "logstash-*" ], 
      "privileges": ["read","view_index_metadata"]
    }
  ]
}

#添加一個有logstash_reader角色權限的用戶:logstash_user
POST _xpack/security/user/logstash_user
{
  "password" : "x-pack-test-password",
  "roles" : [ "logstash_reader", "logstash_admin"], 
  "full_name" : "Kibana User for Logstash"
}

第二種:通過kibana的界面配置

Management > Roles
Management > Users

 

 

 權限選擇見elasticsearch官網:

https://www.elastic.co/guide/en/elasticsearch/reference/current/authorization.html

https://www.elastic.co/guide/en/elasticsearch/reference/current/security-privileges.html

 3.7、多管道配置

  如果需要在同一個進程中運行多個管道,Logstash提供了一種通過名為pipelines.yml的配置文件來實現此目的的方法。

例如:

- pipeline.id: my-pipeline_1
  path.config: "/etc/path/to/p1.config"
  pipeline.workers: 3
- pipeline.id: my-other-pipeline
  path.config: "/etc/different/path/p2.cfg"
  queue.type: persisted

  該文件在YAML文件格式中,並包含一個字典列表,其中每個字典描述一個管道,每個鍵/值對指定該管道的設置。該示例展示了通過id和配置路徑描述的兩個不同管道。對於第一個管道,為pipeline.workers的值設置為3,而在另一个中,持久隊列特性被啟用。未在pipelines.yml顯式設置的值。yml文件將使用到logstash中指定的默認值。
  當啟動Logstash不帶參數時,它將讀取管道pipelines.yml。yml文件並實例化文件中指定的所有管道。另一方面,當使用-e或-f時,Logstash會忽略管道。

注意:

  • 如果當前配置的事件流不共享相同的輸入/過濾器和輸出,並且使用標記和條件將它們彼此分離,這顯得多個管道尤其重要
  • 在單個實例中擁有多個管道還允許這些事件流具有不同的性能和持久性參數(例如,pipeline.workers和persistent queues的不同設置)。這種分離意味着一條管道中的阻塞輸出不會對另一條管道產生反壓力。
  • 考慮管道之間的資源競爭是很重要的,因為默認值是針對單個管道調優的。因此,例如,考慮減少每個管道使用pipeline.worker的數量,因為每個管道在默認情況下每個CPU核使用一個worker。
  • 每個管道都隔離持久隊列和死信隊列,它們的位置命名空間為pipeline.id的值

各管道之間的通信原理:https://www.elastic.co/guide/en/logstash/current/pipeline-to-pipeline.html,有興趣的可以了解下。

3.8、配置的重新加載

在我們運行logstash的過程,不想停掉logstash進程,但是又想修改配置,就可以使用到配置的重新加載了,有兩種方式。

第一種是在啟動的時候指定參數:

bin/logstash -f apache.config --config.reload.automatic

Logstash每3秒檢查一次配置更改。要更改此間隔,請使用–config.reload.interval <interval>選項,其中interval指定Logstash檢查配置文件更改的頻率(以秒為單位),請注意,必須使用單位限定符(s)

第二種就是強制加載配置文件

kill -SIGHUP pid  #pid為logstash的pid

自動配置重新加載配置注意點:

  • 當Logstash檢測到配置文件中的更改時,它將通過停止所有輸入來停止當前管道,並嘗試創建使用更新后的配置的新管道。驗證新配置的語法后,Logstash驗證所有輸入和輸出都可以初始化(例如,所有必需的端口都已打開)。如果檢查成功,則Logstash會將現有管道與新管道交換。如果檢查失敗,舊管道將繼續運行,並且錯誤將傳播到控制台。
  • 在自動重新加載配置期間,不會重新啟動JVM。管道的創建和交換都在同一過程中進行。
  • 對grok模式文件的更改也將重新加載,但僅在配置文件中的更改觸發重新加載(或重新啟動管道)時。

3.9、常用filter介紹

1、grok

注:grok插件是一個十分耗費資源的插件

官網:https://www.elastic.co/guide/en/logstash/current/plugins-filters-grok.html

Grok是將非結構化日誌數據解析為結構化和可查詢內容的好方法,非常適合syslog日誌,apache和其他Web服務器日誌,mysql日誌等等

首先官方提供了120中的匹配模式(但是我一直都沒打開這個網址):https://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns

還有一個用來驗證自定義的解析是否正確的一個網址:http://grokdebug.herokuapp.com/

既然我沒能打開官方提供的120個的模式匹配,從一片博客中找到了一部分的匹配模式(如下):https://blog.csdn.net/cui929434/article/details/94390617

USERNAME [a-zA-Z0-9._-]+
USER %{USERNAME}
INT (?:[+-]?(?:[0-9]+))
BASE10NUM (?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+)))
NUMBER (?:%{BASE10NUM})
BASE16NUM (?<![0-9A-Fa-f])(?:[+-]?(?:0x)?(?:[0-9A-Fa-f]+))
BASE16FLOAT \b(?<![0-9A-Fa-f.])(?:[+-]?(?:0x)?(?:(?:[0-9A-Fa-f]+(?:\.[0-9A-Fa-f]*)?)|(?:\.[0-9A-Fa-f]+)))\b

POSINT \b(?:[1-9][0-9]*)\b
NONNEGINT \b(?:[0-9]+)\b
WORD \b\w+\b
NOTSPACE \S+
SPACE \s*
DATA .*?
GREEDYDATA .*
QUOTEDSTRING (?>(?<!\\)(?>"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))
UUID [A-Fa-f0-9]{8}-(?:[A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}

# Networking
MAC (?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})
CISCOMAC (?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})
WINDOWSMAC (?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})
COMMONMAC (?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})
IPV6 ((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?
IPV4 (?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])
IP (?:%{IPV6}|%{IPV4})
HOSTNAME \b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\.?|\b)
HOST %{HOSTNAME}
IPORHOST (?:%{HOSTNAME}|%{IP})
HOSTPORT %{IPORHOST}:%{POSINT}

# paths
PATH (?:%{UNIXPATH}|%{WINPATH})
UNIXPATH (?>/(?>[\w_%!$@:.,-]+|\\.)*)+
TTY (?:/dev/(pts|tty([pq])?)(\w+)?/?(?:[0-9]+))
WINPATH (?>[A-Za-z]+:|\\)(?:\\[^\\?*]*)+
URIPROTO [A-Za-z]+(\+[A-Za-z+]+)?
URIHOST %{IPORHOST}(?::%{POSINT:port})?
# uripath comes loosely from RFC1738, but mostly from what Firefox
# doesn't turn into %XX
URIPATH (?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\-]*)+
#URIPARAM \?(?:[A-Za-z0-9]+(?:=(?:[^&]*))?(?:&(?:[A-Za-z0-9]+(?:=(?:[^&]*))?)?)*)?
URIPARAM \?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?\-\[\]]*
URIPATHPARAM %{URIPATH}(?:%{URIPARAM})?
URI %{URIPROTO}://(?:%{USER}(?::[^@]*)?@)?(?:%{URIHOST})?(?:%{URIPATHPARAM})?

# Months: January, Feb, 3, 03, 12, December
MONTH \b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b
MONTHNUM (?:0?[1-9]|1[0-2])
MONTHNUM2 (?:0[1-9]|1[0-2])
MONTHDAY (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])

# Days: Monday, Tue, Thu, etc...
DAY (?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)

# Years?
YEAR (?>\d\d){1,2}
HOUR (?:2[0123]|[01]?[0-9])
MINUTE (?:[0-5][0-9])
# '60' is a leap second in most time standards and thus is valid.
SECOND (?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)
TIME (?!<[0-9])%{HOUR}:%{MINUTE}(?::%{SECOND})(?![0-9])
# datestamp is YYYY/MM/DD-HH:MM:SS.UUUU (or something like it)
DATE_US %{MONTHNUM}[/-]%{MONTHDAY}[/-]%{YEAR}
DATE_EU %{MONTHDAY}[./-]%{MONTHNUM}[./-]%{YEAR}
ISO8601_TIMEZONE (?:Z|[+-]%{HOUR}(?::?%{MINUTE}))
ISO8601_SECOND (?:%{SECOND}|60)
TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
DATE %{DATE_US}|%{DATE_EU}
DATESTAMP %{DATE}[- ]%{TIME}
TZ (?:[PMCE][SD]T|UTC)
DATESTAMP_RFC822 %{DAY} %{MONTH} %{MONTHDAY} %{YEAR} %{TIME} %{TZ}
DATESTAMP_RFC2822 %{DAY}, %{MONTHDAY} %{MONTH} %{YEAR} %{TIME} %{ISO8601_TIMEZONE}
DATESTAMP_OTHER %{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{TZ} %{YEAR}
DATESTAMP_EVENTLOG %{YEAR}%{MONTHNUM2}%{MONTHDAY}%{HOUR}%{MINUTE}%{SECOND}

# Syslog Dates: Month Day HH:MM:SS
SYSLOGTIMESTAMP %{MONTH} +%{MONTHDAY} %{TIME}
PROG (?:[\w._/%-]+)
SYSLOGPROG %{PROG:program}(?:\[%{POSINT:pid}\])?
SYSLOGHOST %{IPORHOST}
SYSLOGFACILITY <%{NONNEGINT:facility}.%{NONNEGINT:priority}>
HTTPDATE %{MONTHDAY}/%{MONTH}/%{YEAR}:%{TIME} %{INT}

# Shortcuts
QS %{QUOTEDSTRING}

# Log formats
SYSLOGBASE %{SYSLOGTIMESTAMP:timestamp} (?:%{SYSLOGFACILITY} )?%{SYSLOGHOST:logsource} %{SYSLOGPROG}:
COMMONAPACHELOG %{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes}|-)
COMBINEDAPACHELOG %{COMMONAPACHELOG} %{QS:referrer} %{QS:agent}

# Log Levels
LOGLEVEL ([Aa]lert|ALERT|[Tt]race|TRACE|[Dd]ebug|DEBUG|[Nn]otice|NOTICE|[Ii]nfo|INFO|[Ww]arn?(?:ing)?|WARN?(?:ING)?|[Ee]rr?(?:or)?|ERR?(?:OR)?|[Cc]rit?(?:ical)?|CRIT?(?:ICAL)?|[Ff]atal|FATAL|[Ss]evere|SEVERE|EMERG(?:ENCY)?|[Ee]merg(?:ency)?)

grok內置的一些匹配模式

基礎語法,一種是自帶的模式,一種是自定義的模式:

自帶的模式語法:  %{SYNTAX:SEMANTIC}

SYNTAX是將匹配文本模式的名稱,grok自帶的那些匹配模式名
SEMANTIC是你給一段文字的標識相匹配該匹配模式匹配到的內容,相當於一個字段名

例如:

%{NUMBER:duration} %{IP:client}

比如要解析如下的日誌:

55.3.244.1 GET /index.html 15824 0.043

就可以使用該匹配模式去匹配:

%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}

得出的結果如下:

client: 55.3.244.1
method: GET
request: /index.html
bytes: 15824
duration: 0.043

 

自定義的模式語法:(?<field_name>the pattern here)

例如:

(?<queue_id>[0-9A-F]{10,11})  #表示10-11個字符的16進制

我們可以創建一個目錄pattters,把我自定義的模式添加進去,在使用的時候就可以使用grok自帶的匹配模式的語法,例如:

我們在./patterns/postfix文件中添加如下內容
POSTFIX_QUEUEID [0-9A-F]{10,11}

如上我們就定義了一個匹配模式了,可以使用如下的方式使用。假如我們有如下的日誌格式:

Jan  1 06:25:43 mailserver14 postfix/cleanup[21403]: BEF25A72965: message-id=<20130101142543.5828399CCAF@mailserver14.example.com>

然後我們對其就行解析:

filter {
      grok {
        patterns_dir => ["./patterns"]  #指定自定義的匹配模式路徑
        match => { "message" => "%{SYSLOGBASE} %{POSTFIX_QUEUEID:queue_id}: %{GREEDYDATA:syslog_message}" }
      }
    }

解析出的結果如下:

timestamp: Jan  1 06:25:43
logsource: mailserver14
program: postfix/cleanup
pid: 21403
queue_id: BEF25A72965
syslog_message: message-id=<20130101142543.5828399CCAF@mailserver14.example.com>

 grok的配置選項

break_on_match
值類型是布爾值
默認是true
描述:match可以一次設定多組,預設會依照順序設定處理,如果日誌滿足設定條件,則會終止向下處理。但有的時候我們會希望讓Logstash跑完所有的設定,這時可以將break_on_match設為false。

keep_empty_captures
值類型是布爾值
默認值是 false
描述:如果為true,捕獲失敗的字段將設置為空值

match
值類型是數組
默認值是 {}
描述:字段值的模式匹配
例如:
filter {
  grok { match => { "message" => "Duration: %{NUMBER:duration}" } }
}
#如果你需要針對單個字段匹配多個模式,則該值可以是一組,例如:
filter {
  grok { match => { "message" => [ "Duration: %{NUMBER:duration}", "Speed: %{NUMBER:speed}" ] } }
}

named_captures_only
值類型是布爾值
默認值是 true
描述:如果設置為true,則僅存儲來自grok的命名捕獲

overwrite
值類型是 array
默認是[]
描述:覆蓋已經存在的字段內容
例如:

filter {
  grok {
    match => { "message" => "%{SYSLOGBASE} %{DATA:message}" }
    overwrite => [ "message" ]
  }
}
如果日誌是May 29 16:37:11 sadness logger: hello world經過match屬性match => { “message” => “%{SYSLOGBASE} %{DATA:message}” }處理后,message的值變成了hello world。這時如果使用了overwrite => [ “message” ]屬性,那麼原來的message的值將被覆蓋成新值。

pattern_definitions
值類型是 數組
默認值是 {}
描述:模式名稱和模式正則表達式,也是用於定義當前過濾器要使用的自定義模式。匹配現有名稱的模式將覆蓋預先存在的定義。可以將此視為僅適用於grok定義的內聯模式,patterns_dir是將模式寫在外部。
例如:
filter {
    grok {
        patterns_dir => "/usr/local/elk/logstash/patterns"
        pattern_definitions => {"MYSELFTIMESTAMP" => "20%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{HOUR}:?%{MINUTE}(?::?%{SECOND})"}
        match => {"message" => ["%{MYSELFTIMESTAMP:timestamp} %{JAVACLASS:message}","%{MYSELF:content}"]}
    }
}

patterns_dir
值類型是數組
默認值是 []
描述:一些複雜的正則表達式,不適合直接寫到filter中,可以指定一個文件夾,用來專門保存正則表達式的文件,需要注意的是該文件夾中的所有文件中的正則表達式都會被依次加載,包括備份文件。

patterns_dir => ["/opt/logstash/patterns", "/opt/logstash/extra_patterns"]
正則文件以文本格式描述:

patterns_file_glob
屬性值的類型:string
默認值:“*”
描述:針對patterns_dir屬性中指定的文件夾里哪些正則文件,可以在這個filter中生效,需要本屬性來指定。默認值“*”是指所有正則文件都生效。

tag_on_failure
值類型是數組
默認值是 [“_grokparsefailure”]
描述:沒有成功匹配時,將值附加到字段到tags

tag_on_timeout
值類型是字符串
默認值是 “_groktimeout”
描述:如果Grok正則表達式超時,則應用標記。

timeout_millis
值類型是数字
默認值是 30000
描述: 嘗試在這段時間后終止正則表達式。如果應用了多個模式,則這適用於每個模式。這將永遠不會提前超時,但超時可能需要一些時間。實際的超時時間是基於250ms量化的近似值。設置為0以禁用超時。

各組件的公共配置選項

break_on_match
值類型是布爾值
默認是true
描述:match可以一次設定多組,預設會依照順序設定處理,如果日誌滿足設定條件,則會終止向下處理。但有的時候我們會希望讓Logstash跑完所有的設定,這時可以將break_on_match設為false。

keep_empty_captures
值類型是布爾值
默認值是 false
描述:如果為true,捕獲失敗的字段將設置為空值

match
值類型是數組
默認值是 {}
描述:字段值的模式匹配
例如:

filter {
  grok { match => { "message" => "Duration: %{NUMBER:duration}" } }
}

#如果你需要針對單個字段匹配多個模式,則該值可以是一組,例如:
filter {
  grok { match => { "message" => [ "Duration: %{NUMBER:duration}", "Speed: %{NUMBER:speed}" ] } }
}

named_captures_only
值類型是布爾值
默認值是 true
描述:如果設置為true,則僅存儲來自grok的命名捕獲

overwrite
值類型是 array
默認是[]
描述:覆蓋已經存在的字段內容
例如:

filter {
  grok {
    match => { "message" => "%{SYSLOGBASE} %{DATA:message}" }
    overwrite => [ "message" ]
  }
}

如果日誌是May 29 16:37:11 sadness logger: hello world經過match屬性match => { “message” => “%{SYSLOGBASE} %{DATA:message}” }處理后,message的值變成了hello world。這時如果使用了overwrite => [ “message” ]屬性,那麼原來的message的值將被覆蓋成新值。

pattern_definitions
值類型是 數組
默認值是 {}
描述:模式名稱和模式正則表達式,也是用於定義當前過濾器要使用的自定義模式。匹配現有名稱的模式將覆蓋預先存在的定義。可以將此視為僅適用於grok定義的內聯模式,patterns_dir是將模式寫在外部。
例如:

filter {
    grok {
        patterns_dir => "/usr/local/elk/logstash/patterns"
        pattern_definitions => {"MYSELFTIMESTAMP" => "20%{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{HOUR}:?%{MINUTE}(?::?%{SECOND})"}
        match => {"message" => ["%{MYSELFTIMESTAMP:timestamp} %{JAVACLASS:message}","%{MYSELF:content}"]}
    }
}

patterns_dir
值類型是數組
默認值是 []
描述:一些複雜的正則表達式,不適合直接寫到filter中,可以指定一個文件夾,用來專門保存正則表達式的文件,需要注意的是該文件夾中的所有文件中的正則表達式都會被依次加載,包括備份文件。

patterns_dir => ["/opt/logstash/patterns", "/opt/logstash/extra_patterns"]
正則文件以文本格式描述:


patterns_file_glob
屬性值的類型:string
默認值:“*”
描述:針對patterns_dir屬性中指定的文件夾里哪些正則文件,可以在這個filter中生效,需要本屬性來指定。默認值“*”是指所有正則文件都生效。

tag_on_failure
值類型是數組
默認值是 [“_grokparsefailure”]
描述:沒有成功匹配時,將值附加到字段到tags

tag_on_timeout
值類型是字符串
默認值是 “_groktimeout”
描述:如果Grok正則表達式超時,則應用標記。

timeout_millis
值類型是数字
默認值是 30000
描述: 嘗試在這段時間后終止正則表達式。如果應用了多個模式,則這適用於每個模式。這將永遠不會提前超時,但超時可能需要一些時間。實際的超時時間是基於250ms量化的近似值。設置為0以禁用超時。

常用選項
所有過濾器插件都支持以下配置選項:


dd_field
值類型是散列
默認值是 {}
描述:如果匹配成功,向此事件添加任意字段。字段名可以是動態的,並使用%{Field}包含事件的一部分
filter {
      grok {
        add_field => { "foo_%{somefield}" => "Hello world, from %{host}" }
      }
    }

# 你也可以一次添加多個字段
filter {
  grok {
    add_field => {
      "foo_%{somefield}" => "Hello world, from %{host}"
      "new_field" => "new_static_value"
    }
  }
}

add_tag
值類型是數組
默認值是 []
描述:如果此過濾器成功,請向該事件添加任意標籤。標籤可以是動態的,並使用%{field} 語法包含事件的一部分。
例如:

filter {
  grok {
    add_tag => [ "foo_%{somefield}" ]
  }
}

# 你也可以一次添加多個標籤
filter {
  grok {
    add_tag => [ "foo_%{somefield}", "taggedy_tag"]
  }
}

enable_metric
值類型是布爾值
默認值是 true
描述:禁用或啟用度量標準

id
值類型是字符串
此值沒有默認值。
描述:向插件實例添加唯一ID,此ID用於跟蹤插件特定配置的信息。
例如:

filter {
  grok {
    id => "ABC"
  }
}

periodic_flush
值類型是布爾值
默認值是 false
描述:如果設置為ture,會定時的調用filter的更新函數(flush method)

remove_field
值的類型:array
默認值:[]
描述:刪除當前文檔中的指定filted

filter {
  grok {
    remove_field => [ "foo_%{somefield}" ]
  }
}
# 你也可以一次移除多個字段:
filter {
  grok {
    remove_field => [ "foo_%{somefield}", "my_extraneous_field" ]
  }
}

remove_tag
值類型是數組
默認值是 []
描述:如果此過濾器成功,請從該事件中移除任意標籤。標籤可以是動態的,並使用%{field} 語法包括事件的一部分。
例如:

filter {
  grok {
    remove_tag => [ "foo_%{somefield}" ]
  }
}
# 你也可以一次刪除多個標籤
filter {
  grok {
    remove_tag => [ "foo_%{somefield}", "sad_unwanted_tag"]
  }
}

2、mutate

官網網址:https://www.elastic.co/guide/en/logstash/current/plugins-filters-mutate.html

mutate允許對字段執行常規改變。可以重命名、刪除、替換和修改事件中的字段。

二話不說,來個例子先:

filter {
    mutate {
        split => ["hostname", "."]  #切分
        add_field => { "shortHostname" => "%{hostname[0]}" } #獲取切分后的第一個字段作為添加字段
    }
    mutate {
        rename => ["shortHostname", "hostname" ] #重命名
    }
}

mutate的配置選項:

常用的一些操作:
convert:轉換數據類型,數據類型hash
可以裝換的數據類型有:integer,string,integer_eu(和integer相同,显示格式為1.000),float,float_eu,boolean
實例:
filter {
      mutate {
        convert => {
          "fieldname" => "integer"
          "booleanfield" => "boolean"
        }
      }
    }

copy:類型hash,將現有字段複製到另一個字段。現有的目標域將被覆蓋
實例:
 filter {
      mutate {
         copy => { "source_field" => "dest_field" }
      }
    }
    
gsub:類型array,根據字段值匹配正則表達式,並用替換字符串替換所有匹配項。只支持字符串或字符串數組的字段。對於其他類型的字段,將不採取任何操作。
實例:
filter {
      mutate {
        gsub => [
          # replace all forward slashes with underscore
          "fieldname", "/", "_",
          # replace backslashes, question marks, hashes, and minuses
          # with a dot "."
          "fieldname2", "[\\?#-]", "."
        ]
      }
    }
    
join:類型hash,用分隔符連接數組。對非數組字段不執行任何操作
實例:
filter {
     mutate {
       join => { "fieldname" => "," }
     }
   }
  
lowercase:類型array,轉為小寫
實例:
filter {
      mutate {
        lowercase => [ "fieldname" ]
      }
    }
    
merge:類型hash,合併數組或散列的兩個字段。字符串字段將自動轉換為數組
實例:
filter {
     mutate {
        merge => { "dest_field" => "added_field" }
     }
   }
   
coerce:類型hash,設置存在但為空的字段的默認值
實例:
filter {
      mutate {
        # Sets the default value of the 'field1' field to 'default_value'
        coerce => { "field1" => "default_value" }
      }
    }
    
rename:類型hash,重命名
實例:
filter {
      mutate {
        # Renames the 'HOSTORIP' field to 'client_ip'
        rename => { "HOSTORIP" => "client_ip" }
      }
    }
    
replace:類型hash,用新值替換字段的值
實例:
filter {
      mutate {
        replace => { "message" => "%{source_host}: My new message" }
      }
    }
    
split:類型hash,使用分隔符將字段分割為數組。只對字符串字段有效
實例:
filter {
      mutate {
         split => { "fieldname" => "," }
      }
    }
    
strip:類型array,字段中刪除空白。注意:這隻對前導和后導空格有效。
實例:
filter {
      mutate {
         strip => ["field1", "field2"]
      }
    }
    
update:類型hash,使用新值更新現有字段。如果該字段不存在,則不採取任何操作。
實例:
filter {
      mutate {
        update => { "sample" => "My new message" }
      }
    }
    
uppercase:類型array,轉為大寫
實例:
filter {
      mutate {
        uppercase => [ "fieldname" ]
      }
    }
    
capitalize:類型array,將字符串轉換為等效的大寫字母。
實例:
filter {
      mutate {
        capitalize => [ "fieldname" ]
      }
    }

tag_on_failure:類型string,如果在應用此變異篩選器期間發生故障,則終止其餘操作,默認值:_mutate_error

公共配置見:grok公共配置

3、date

date filter用於從字段解析日期,然後使用該日期或時間戳作為事件的logstash時間戳

date常用的配置選項:

match:類型array,字段名在前,格式模式在後的數組[ field,formats... ],表示該字段能夠匹配到的時間模式,時間模式可以有多種
實例:
filter {
      date {
        match => [ "logdate", "MMM dd yyyy HH:mm:ss" ]
      }
    }
    
tag_on_failure:類型array,默認值["_dateparsefailure"],當沒有成功匹配時,將值追加到tags字段

target:類型String,默認值"@timestamp",將匹配的時間戳存儲到給定的目標字段中。如果沒有提供,默認更新事件到@timestamp字段。

timezone:類型String,表示時區,可以在該網址查看:http://joda-time.sourceforge.net/timezones.html

公共配置見:grok公共配置

這裏只對如上三種filter說明,具體其他的filter請見官網:https://www.elastic.co/guide/en/logstash/current/filter-plugins.html

3.10、案例一、apache日誌解析

這個例子屬官網的一個例子:https://www.elastic.co/guide/en/logstash/current/advanced-pipeline.html

但是我這裏不弄這麼負責,我們不使用filebeat,直接使用logstash,apache日誌的數據集下載:https://download.elastic.co/demos/logstash/gettingstarted/logstash-tutorial.log.gz

我這裏不打算安裝apach,所以直接使用官方提供的數據集。

下載數據集,然後解壓文件,就可以得到我們的一個日誌文件:logstash-tutorial.log

首先我們看一下apache日誌的格式:

[elk@lgh ~]$ tail -3 logstash-tutorial.log 
86.1.76.62 - - [04/Jan/2015:05:30:37 +0000] "GET /projects/xdotool/ HTTP/1.1" 200 12292 "http://www.haskell.org/haskellwiki/Xmonad/Frequently_asked_questions" "Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20140205 Firefox/24.0 Iceweasel/24.3.0"
86.1.76.62 - - [04/Jan/2015:05:30:37 +0000] "GET /reset.css HTTP/1.1" 200 1015 "http://www.semicomplete.com/projects/xdotool/" "Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20140205 Firefox/24.0 Iceweasel/24.3.0"
86.1.76.62 - - [04/Jan/2015:05:30:37 +0000] "GET /style2.css HTTP/1.1" 200 4877 "http://www.semicomplete.com/projects/xdotool/" "Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20140205 Firefox/24.0 Iceweasel/24.3.0"

然後開始配置:

cd logstash-7.7.0/ && mkdir conf.d
cd conf.d/ 
vim  apache.conf
#############apache.conf的內容如下###################
input {
        file {
            path => "/home/elk/logstash-tutorial.log"
            type => "log"
            start_position => "beginning"
            }
    }
filter {
    grok {
        match => { "message" => "%{COMBINEDAPACHELOG}"}
    }
}

output {
    stdout { codec => rubydebug }
}

然後啟動命令(可以選擇用nohup後台啟動):

 cd logstash-7.7.0/ && ./bin/logstash -f conf.d/apache.conf

執行結果如下(部分結果):

{
           "verb" => "GET",
          "bytes" => "8948",
           "type" => "log",
           "host" => "gxt_126_233",
    "httpversion" => "1.0",
        "message" => "67.214.178.190 - - [04/Jan/2015:05:20:59 +0000] \"GET /blog/geekery/installing-windows-8-consumer-preview.html HTTP/1.0\" 200 8948 \"http://www.semicomplete.com/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:21.0) Gecko/20100101 Firefox/21.0\"",
      "timestamp" => "04/Jan/2015:05:20:59 +0000",
       "referrer" => "\"http://www.semicomplete.com/\"",
     "@timestamp" => 2020-06-17T01:23:47.817Z,
           "path" => "/data/hd05/elk/logstash-tutorial.log",
          "ident" => "-",
       "response" => "200",
       "@version" => "1",
        "request" => "/blog/geekery/installing-windows-8-consumer-preview.html",
       "clientip" => "67.214.178.190",
          "agent" => "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:21.0) Gecko/20100101 Firefox/21.0\"",
           "auth" => "-"
}
{
           "verb" => "GET",
          "bytes" => "1015",
           "type" => "log",
           "host" => "gxt_126_233",
    "httpversion" => "1.1",
        "message" => "66.249.73.185 - - [04/Jan/2015:05:18:48 +0000] \"GET /reset.css HTTP/1.1\" 200 1015 \"-\" \"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)\"",
      "timestamp" => "04/Jan/2015:05:18:48 +0000",
       "referrer" => "\"-\"",
     "@timestamp" => 2020-06-17T01:23:47.815Z,
           "path" => "/data/hd05/elk/logstash-tutorial.log",
          "ident" => "-",
       "response" => "200",
       "@version" => "1",
        "request" => "/reset.css",
       "clientip" => "66.249.73.185",
          "agent" => "\"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)\"",
           "auth" => "-"
}
{
           "verb" => "GET",
          "bytes" => "28370",
           "type" => "log",
           "host" => "gxt_126_233",
    "httpversion" => "1.0",
        "message" => "207.241.237.220 - - [04/Jan/2015:05:21:16 +0000] \"GET /blog/tags/projects HTTP/1.0\" 200 28370 \"http://www.semicomplete.com/blog/tags/C\" \"Mozilla/5.0 (compatible; archive.org_bot +http://www.archive.org/details/archive.org_bot)\"",
      "timestamp" => "04/Jan/2015:05:21:16 +0000",
       "referrer" => "\"http://www.semicomplete.com/blog/tags/C\"",
     "@timestamp" => 2020-06-17T01:23:47.817Z,
           "path" => "/data/hd05/elk/logstash-tutorial.log",
          "ident" => "-",
       "response" => "200",
       "@version" => "1",
        "request" => "/blog/tags/projects",
       "clientip" => "207.241.237.220",
          "agent" => "\"Mozilla/5.0 (compatible; archive.org_bot +http://www.archive.org/details/archive.org_bot)\"",
           "auth" => "-"
}

執行結果

2.11、案例二、nginx日誌解析

首先安裝nginx:nginx功能介紹和基本安裝

這裏我們也不使用filebeat,因為這篇文章只是介紹logstash

配置:

cd conf.d/ 
vim  nginx.conf
############nginx.conf配置如下#####################
input {
        file {
            path => "/usr/local/nginx/logs/access.log"
            type => "log"
            start_position => "beginning"
            }
    }
   
filter {
grok {
        match => { "message" => ["(?<RemoteIP>(\d*.\d*.\d*.\d*)) - %{DATA:[nginx][access][user_name]} \[%{HTTPDATE:[nginx][access][time]}\] \"%{WORD:[nginx][access][method]} %{DATA:[nginx][access][url]} HTTP/%{NUMBER:[nginx][access][http_version]}\" %{NUMBER:[nginx][access][response_code]} %{NUMBER:[nginx][access][body_sent][bytes]} \"%{DATA:[nginx][access][referrer]}\" \"%{DATA:[nginx][access][agent]}\""] }
        add_field => {
                "Device" => "Charles Desktop"
        }
        remove_field => "message"
        remove_field => "beat.version"
        remove_field => "beat.name"
    }
}
    output {    
             elasticsearch {
                    hosts => ["192.168.110.130:9200"]
                    index => "nginx-log-%{+YYYY.MM.dd}"
                }
    }

如上的配置中輸出到elasticsearch中,這裏沒有設置密碼,所以不需要用戶和密碼,還有就是這裏使用的默認模板,如果想要修改的話可以使用,可以添加如下配置:

user => "elastic"  #用戶
password => "${ES_PWD}" #通過keystore存儲的密碼
manage_template => false #關閉默認的模板
template_name => "elastic-slowquery" #指定自定義的模板

執行命令啟動logstash

cd logstash-7.7.0/ && ./bin/logstash -f conf.d/nginx.conf

執行的結果(查看elasticsearch集群):

 

 

 從上面的兩個圖看,一個創建了我們在nginx.conf中指定的一個索引,然後索引的內容都是解析出來的一些字段內容。

3.12、案例三、elasticsearch慢日誌解析

這是實例我們採用filebeat+logstash+elasticsearch,還有權限驗證進行試驗:

這裏主要是對elasticsearch的慢日誌查詢做解析,雖然我在一篇文章搞懂filebeat(ELK)中篇文章中直接通過filebeat的elasticsearch(beat版本)的模塊對其做過解析,但是解析的還是不夠特別完善,這裏引入logstash對其解析,過濾。

首先配置filebeat文件(這裏只配置了一個輸入和一個輸出,沒有做多餘的處理,只是用來收集日誌):

#=========================== Filebeat inputs =============================
filebeat.inputs:

# Each - is an input. Most options can be set at the input level, so
# you can use different inputs for various configurations.
# Below are the input specific configurations.

- type: log

  # Change to true to enable this input configuration.
  enabled: true

  # Paths that should be crawled and fetched. Glob based paths.
  paths:
    - /var/logs/es_aaa_index_search_slowlog.log
    - /var/logs/es_bbb_index_search_slowlog.log
    #- c:\programdata\elasticsearch\logs\*

#================================ Outputs =====================================

# Configure what output to use when sending the data collected by the beat.

#----------------------------- Logstash output --------------------------------
output.logstash:
  # The Logstash hosts
  hosts: ["192.168.110.130:5044","192.168.110.131:5044","192.168.110.132:5044"]
  loadbalance: true   #這裏採用負載均衡機制,

  # Optional SSL. By default is off.
  # List of root certificates for HTTPS server verifications
  #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]

  # Certificate for SSL client authentication
  #ssl.certificate: "/etc/pki/client/cert.pem"

  # Client Certificate Key
  #ssl.key: "/etc/pki/client/cert.key"

然後啟動filebeat:

cd filebeat-7.7.0-linux-x86_64 && ./filebeat -e

然後配置logstash的配置文件:

cd conf.d/ 
vim  es.conf
############es.conf配置如下##############
input {
        beats{
                port => 5044
        }
    }
   
filter {
grok {
        match => {"message" => "\[%{TIMESTAMP_ISO8601:query_time},%{NUMBER:number1}\]\s*\[%{DATA:log_type}\]\s*\[%{DATA:index_query_type}\]\s*\[%{DATA:es_node}\]\s*\[%{DATA:index_name}\]\s*\[%{NUMBER:share_id}\]\s*took\[%{DATA:times_s}\],\s*took_millis\[%{NUMBER:query_times_ms}\],\s*types\[%{DATA:types}\],\s*stats\[%{DATA:status}\],\s*search_type\[%{DATA:search_type}\],\s*total_shards\[%{NUMBER:total_shards}\],\s*source\[%{DATA:json_query}\],\s*extra_source"}
        remove_field => ["message","@version","status","times_s","@timestamp","number1"]
    }
  mutate{
        convert => {
        "query_times_ms" => "integer"
}
}
}
    output {    
             elasticsearch {
                    hosts => ["192.168.110.130:9200","192.168.110.131:9200","192.168.110.132:9200"]
                    index => "elastic-slowquery-222"
                    user => "elastic"
                    password => "${ES_PWD}"
                    manage_template => false
                    template_name => "elastic-slowquery"
                }
    }

如上配置使用了用戶的權限驗證,以及elasticsearch的自定義模板

啟動logstash

cd logstash-7.7.0/ && ./bin/logstash -f conf.d/es.conf

登錄es查看結果:

 

logstash就介紹到這裏了,如果有疑問多看官網比較好

 

參考:

logstash官網:https://www.elastic.co/guide/en/logstash/current/index.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

澳洲政府修能源政策 溫室氣體減排以後再談

摘錄自2018年8月21日中央廣播電台報導

路透社報導,在執政聯盟內部的反對下,澳洲總理滕博爾(Malcolm Turnbull)今天(20日)取消能源政策中要求減少排放溫室氣體的部分。但他表示,澳洲仍信守巴黎氣候協議(Paris Agreement)承諾。

滕博爾政府的能源政策「國家能源保障」(National Energy Guarantee),先前要求發電產業的溫室氣體排放量,到2030年以前,必須比2005年減少26%。

墨爾本大學(University of Melbourne)政治學教授艾克斯利(Robyn Eckersley)則說:「這完全是對澳洲自由黨(Liberal Party)
右翼成員投降,他們希望永久保留澳洲的煤礦經濟。」

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準