環境資訊中心綜合外電;姜唯 編譯;林大利 審校
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※教你寫出一流的銷售文案?
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※回頭車貨運收費標準
※別再煩惱如何寫文案,掌握八大原則!
※超省錢租車方案
※產品缺大量曝光嗎?你需要的是一流包裝設計!
摘錄自2019年10月26日三立新聞網俄羅斯報導
俄羅斯一知名馬戲團日前一名馴獸師與一隻300公斤中大棕熊進行表演,沒想到過程中大棕熊卻突然襲擊馴獸師,當下觀眾還以為是表驗內容,直到另一名馴獸師狠踹大棕熊,才引起觀眾一陣恐慌,紛紛往門口逃竄。事後馬戲團發出聲明,疑似是觀眾不斷拍照開閃光燈拍惹的禍,但所幸好馴獸師和大棕熊都沒有受到嚴重傷害。
綜合外媒報導,來自沃羅涅日州(Voronezh)的俄羅斯安史拉格馬戲團(Anshlag traveling circus)近來進行全國巡迴表演,不料日前23日卻發一起恐怖事件,馬戲團內一隻遭訓養重達660磅(近300公斤)的大棕熊,卻在表演中突然攻擊馴獸師。
影片中可見,一名馴獸師引導大棕熊推獨輪車之後,並給予大棕熊獎勵,但不知是大棕熊感覺獎勵不夠,還是怎麼了;大棕熊突然咬住馴獸師的手臂,之後將馴獸師撲倒在地,而現場觀眾看到該幕,還以為是表演內容,直到另一名訓練師上前踹了大棕熊3腳,才驚慌紛紛往門口逃竄。
報導指出,安史拉格巡迴表演馬戲團舞台上完全沒有設置柵欄,現場觀眾與舞台上的動物距離也只有數公尺,不過此事件沒有觀眾受到傷,而大棕熊事後也被電擊槍等制伏、甚至遭攻擊的馴獸師也沒有受到太重的傷。
對此,馬戲團經理明絲尼克(Lyudmila Misnik)表示,疑似是觀眾拍照時忘記關閉閃光燈,導致大棕熊受到驚嚇攻擊馴獸師,不過所幸訓練師和大棕熊均無大礙;並保證接下來的表演,包括狗、浣熊、大蟒蛇、猴子、鸚鵡,還有經典的空中飛人、魔術表演、鋼鐵人和小丑等將會如期舉行,但他也再次呼籲到場觀看表演民眾,拍照時務必關掉閃光燈,避免類似事件再次發生。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※超省錢租車方案
※別再煩惱如何寫文案,掌握八大原則!
※回頭車貨運收費標準
※教你寫出一流的銷售文案?
※FB行銷專家,教你從零開始的技巧
![]() |
結合智慧能源與智慧交通的新創科技領導品牌睿能創意股份有限公司Gogoro 於18 日宣布將與策略夥伴德國博世集團(BOSCH)子公司Coup 拓展電動機車共享服務的全球版圖,預計在今夏進駐巴黎,首輪將於巴黎街頭部署600 台智慧雙輪。Coup 於2016 年8 月即與Gogoro 合作,於柏林推出200 台電動機車共享服務,於今年3 月並擴充服務車隊,陸續引進1,000 台智慧雙輪。
Gogoro 創辦人暨執行長陸學森表示:「Gogoro 和Coup 的合作專注於創新科技與永續交通之經營,希望鼓勵消費者利用Gogoro 智慧雙輪做為都會通勤的最佳選擇。Coup 電動機車共享服務在柏林推出後獲得熱烈迴響,我們非常期待今年夏天將此商業模式引進巴黎時,也能成功帶動巴黎電動機車共享服務的熱潮。」
巴黎是歐洲交通最壅塞的城市之一,都會通勤族一直飽受交通問題所擾。巴黎同時也是全球機車最多的城市之一,在繁忙的市區空間,6 秒鐘即時更換電池自然成為Coup 在巴黎推廣電動機車共享服務的最佳解決方案。陸學森接續指出:「Gogoro 是一家結合創新與永續能源的科技公司,我們專注於創造全新商業模式、產品與服務,希望能點燃都會居民與政府的熱情,一起擁抱永續能源以及航向未來的新燃料。」
「柏林的電動機車共享計畫自2016 年8 月推出以來佳評如潮。騎著時尚的Gogoro 智慧雙輪快速且輕鬆靈活地穿梭於城市之中,成功地在柏林引領一股電動機車的通勤熱潮。柏林的成功經驗也使得我們更有信心將這個永續的都會通勤共享服務介紹到其他歐洲城市,因此我們決定將在今年夏天,於巴黎推出這項服務。Gogoro 與Coup 合作至今,持續以領先的科技不斷改善Coup 創新的共享服務,提高使用者操作的便利性,我們相信巴黎市民一定會愛上這項方便的服務以及整體的使用者經驗。」
Coup Mobility GmbH 執行長Mat Schubert 表示。Coup 服務將先於巴黎市中心的指定區域推出,顧客可將智慧雙輪停放在Coup 服務範圍內的指定停車區。任何達法定年齡並持有小型車駕駛執照(Class B)或國際駕照者,均可使用Coup 服務。顧客可輕鬆透過Coup 應用程式找到並預約離自己最近的智慧雙輪,該應用程式將在7 月於法國開放使用,有iOS 和Android 兩種版本,巴黎的定價模型將比照柏林,採取簡單而且便於使用者計算的方式,以30 分鐘為基本單位進型計價。
(合作媒體:。圖片出處:Gogoro)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※教你寫出一流的銷售文案?
![]() |
特斯拉(Tesla)懷著超級工廠大夢,準備在電池領域一統江湖,歐洲強國也不甘示弱,德國總理梅克爾(Angela Merkel)參加德國戴姆勒汽車(Daimler)斥資5 億歐元興建的鋰電池工廠的動土典禮,這項歐洲大陸的超級電池工廠計畫,將挑戰Tesla 在綠色電力領域的領導地位。
彭博報導,德國電池工廠位於柏林南部130 公里處,突顯主要汽車製造商和電力公司進入儲能的行動,這項技術對推動下一代綠色車輛,以及在需要時儲存風力與太陽能電力上至關重要。報導認為,隨著汽車製造商與電力公司同步發展,電池成本可能會迅速下滑。
彭博分析師認為,電池成本下降與能源密度增加,預計2030 年就可以看到電動車比燃料汽車更便宜的情況。根據彭博新能源財經(BNEF)數據,全球電池製造產能將在2021 年翻倍,達到2.78 億度,現在約1.03 億度,屆時歐洲市場佔比將從現在的2.5% 提高一倍。
瑞典、匈牙利和波蘭計劃中的大型工廠,以及戴姆勒在德國的電池組裝廠,預計會為福斯與雷諾等汽車製造商提供需求,屆時鋰離子電池成本將降低43%,使電動汽車成為主流。
對於公用事業而言,便宜的電池可以降低儲存單元的成本,儲存單元可將變動型再生能源如風能與太陽能的電力傳送至電網。義大利國家電力公司Enel SpA 將電池與風力發電場配套使用,電網管理人員對電力輸出的預測準度因此提高30%。
1990 年代初期消費類電子產品如電腦和手機使用的鋰離子電池,已經跨界應用於運輸和電力行業,但礙於成本,鋰電池在電網和汽車上的應用才剛剛開始,電池的興起對於電動汽車的消費者來說最為明顯,大多數主要汽車製造商將充電式電動車訂為未來10 年的中期計劃。
目前電池業務仍由亞洲電子製造商所主導。根據BNEF,韓國LG 和三星SDI 是頂級供應商,亞洲有望繼續保持領先地位,中國另有8 個工廠正在興建。
汽車製造商採取行動確保電池供應源,戴姆勒的工廠將是歐洲最大的工廠,供應旗下汽車與梅賽德斯賓士,並與屋頂太陽能安裝商Vivint Solar 合資生產家庭能源儲存系統。
2017 年1 月Tesla 的工廠完成三分之一,完工後每年產能可達35 千兆瓦,足以支持每年生產50 萬輛電動車,這將使Tesla 成為繼LG 化學之後的第2 大供應商,特斯拉也在計畫興建更多超級工廠。
戴姆勒的投資規模較小,且尚未披露產能目標。福斯正在與電池廠商討論投資項目,並計劃在德國興建原型組裝廠以開發自己的技術。位於斯德哥爾摩的創業公司NorthVolt AB 宣布計劃在2023 年在瑞典設立一家40 億歐元的電池廠。鋰電池產量大增可望降低所有應用的電池成本,讓家庭和電網的儲存更加實惠。
未來10 年電動汽車價格可望能與汽油或柴油車競爭,由於電池組是充電電動車中最昂貴的部分,佔總成本的三分之一,預計到2021 年,鋰離子電池將便宜43%,從目前的每千瓦273 美元降至156 美元。
鋰電池普及是可以預見的事,但問題是儲存對消費者或大型公用事業而言是否有利可圖仍然是一個懸而未決的問題。即使如此,電動汽車廠商也正在尋找未來,根據BNEF,2030 年前充電式電動車可能佔新車銷售的五分之一,即2,100 萬台。梅克爾訪問戴姆勒工廠展現了德國政府計劃2030 年讓600 萬輛電動汽車上路的決心。
(合作媒體:。圖片出處:Tesla)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
互聯網協議 IP 是 Internet Protocol 的縮寫,中文縮寫為“網協”。IP 協議是位於 OSI 模型中第三層的協議,其主要目的就是使得網絡間能夠互聯通信。前面介紹了 ARP 協議, 該協議用在第二層處理單一網絡中的通信。與其類似,第三層則負責跨網絡通信的地址。在 這層上工作的不止一個協議,但是最普遍的就是互聯網協議(IP)
1. IP協議介紹
互聯網協議地址(Internet Protocol Address,又譯為網際協議地址),縮寫為 IP 地址(IP Address)。在上一章介紹了 ARP 協議,通過分析包可以發現它是依靠 MAC 地址發送數據 的。但是,這樣做有一個重大的缺點。當 ARP 以廣播方式發送數據包時,需要確保所有設 備都要接收到該數據包。這樣,不僅傳輸效率低,而且局限在發送者所在的子網絡。也就是 說,如果兩台計算機不在同一個子網絡,廣播是傳不過去的。這種設計是合理的,否則互聯 網上每一台計算機都會受到所有包,將會導致網絡受到危害。 互聯網是無數子網共同組成的一個巨型網絡。
圖中就是一個簡單的互聯網環境,這裏列出了兩個子網絡。如果想要所有電腦都在同 一個子網絡內,這幾乎是不可能的。所以,需要找一種方法來區分那些 MAC 地址屬於同一 個子網絡,那些不是。如果是同一個子網絡,就採用廣播方式發送。否則就採用“路由”發 送。這也是在 OSI 七層模型中“網絡層”產生的原因。
它的作用就是引進一套新的地址,使得用戶能夠區分不同的計算機是否屬於同一個子網 絡。這套地址就叫做“網絡地址”,簡稱“網址”。但是,人們一般叫做是 IP 地址。這樣 每台計算機就有了兩種地址,一種是是 MAC 地址,另一種是網絡地址(IP 地址)。但是, 這兩種地址之間沒有任何聯繫,MAC 地址是綁定在網卡上的,網絡地址是管理員分配的, 它們只是隨機組合在一起。
2. IP地址
IP 地址是 IP 協議提供的一種統一的地址格式。它為互聯網上的每一個網絡和每一台主 機分配一個邏輯地址,以此來屏蔽物理地址的差異。IP 地址分為 IPv4(IP 協議的第四版) 和 IPv6(IP 協議第六版)兩大類。目前,最廣泛使用的是 IPv4。在該版本中規定,該地址 是由 32 個二進制位組成,用來標識連接到網絡的設備。由於讓用戶記住一串 32 位長的 01 字符確實比較困難,所以 IP 地址採用點分四組的表示法。
在點分四組表示法中,以 A、B、C、D 的形式構成 IP 地址的四組 1 和 0。它們分別轉 換為十進制 0 到 255 之間的數,如圖 3.2 所示。
圖 3.2 显示了 IPv4 地址 11000000.10101000.00000000.00000001,進行了點分四組的表 示法。從圖 3.2 中,可以看到這樣一串 32 位長的数字很不容易記住或者表示。但是採用點 分四組的表示法,就可以將以上一個很長的字符串表示為 192.168.0.1。這樣,用戶就比較容 易記住。
3. IP地址的構成
IP 地址之所以會被分成四個單獨的部分,是因為每個 IP 地址都包含兩個部分,分別是 網絡地址和主機地址。網絡地址用來標識設備所連接到的局域網,而主機地址則標識這個網 絡中的設備本身。例如,IP 地址 172.16.254.1 是一個 32 位的地址。假設它的網絡部分是前 24 位(192.168.254),那麼主機部分就是后 8 位(1)。處於同一個子網絡的計算機,它們 IP 地址的網絡部分必定是相同的。也就是說 172.16.254.2 應該與 172.16.254.1 處在同一個子 網絡。
但是,只查看 IP 地址是無法判斷網絡部分的。這時候就需要使用另一個參數“子網掩 碼”來判斷。所謂的“子網掩碼”就是表示子網絡特徵的一個參數。它在形式上等同於 IP 地址,也是一個 32 位二進制数字。它的網絡部分全部為 1,主機部分全部為 0。
下面以IP地址10.10.1.22為例,其二進制形式為00001010.00001010.00000001.00010110。 為了能夠區分出 IP 地址的每一個部分,將使用子網掩碼來表示。在本例中,10.10.1.22 的子 網掩碼是 11111111.11111111.00000000.00000000。這就意味着 IP 地址的前一半(10.10 或者 00001010.00001010)是網絡地址,而後一半(1.22 或者 00000001.00010110)表示是該網絡 上的主機,如圖 3.3 所示。
在 該 圖 中 的 子 網 掩 碼 也 可 以 寫 成 點 分 四 組 的 形 式 。 比 如 子 網 掩 碼11111111.11111111.0000000.000000,可以被寫成 255.255.0.0。
IP 地址和子網掩碼為簡便起見,通常會被些成無類型域間選路(Classless Inter Domain Routing,CIDR)的形式。在這種形式下,一個完整的 IP 地址後面會有一個左斜杠(/), 以及一個用來表示 IP 地址中網絡部分位數的数字。例如,IP 地址 10.10.1.22 和網絡掩碼 255.255.0.0,在 CIDR 表示法下就會被寫成 10.10.1.22/16 的形式。
4. 捕獲IP數據包
1)什麼是 IP 數據報
TCP/IP 協議定義了一個在因特網上傳輸的包,稱為 IP 數據報(IP Datagram)。IP 數據 報是一個與硬件無關的虛擬包,由首部(header)和數據兩部分組成。首部部分主要包括版 本、長度、IP 地址等信息。數據部分一般用來傳送其它的協議,如 TCP、UDP、ICMP 等。
IP 數據報的“首部”部分的長度為 20 到 60 個字節,整個數據報的總長度最大為 65535 字節。因此,理論上一個數據報的“數據”部分,最長為 65515 字節。由於以太網數據報的 “數據”部分,最長只有 1500 字節。因此如果 IP 數據報超過了 1500 字節,就需要分割成 幾個以太網數據報分開發送了。
2)TTL
捕獲 IP 協議包和其它包有點區別,因為在 IP 協議中涉及到一個 TTL(time-to-live,生 存時間)值問題。TTL 值指定數據包被路由器丟棄之前允許通過的網段數量。當數據包每 經過一個路由器,其 TTL 值將會減一。關於 TTL 的詳細信息,在後面進行介紹。下面將介 紹捕獲 IP 協議包,Wireshark 的位置。
為了證明 TTL 值的變化,本例中選擇使用三個路由器來捕獲數據包。捕獲 IP 協議數據 包的實驗環境,如圖 3.4 所示。
從圖中,可以看到使用兩個路由器,將三台主機分割成兩個網段。這三台主機的 IP 地址,在圖 3.4 中已經標出。在本例中,Wireshark 可以在 PC1 和 PC2 任意一台主機上運行。 但是,不可以在 PC3 上運行。因為,在後面將會分別分析同網段和不同網段中 IP 協議包。 如果在 PC3 上捕獲數據包,只能捕獲同網段的 IP 數據包。
3) 捕獲數據包
① 訪問一個網頁
打開瀏覽器,訪問 http://www.baidu.com 網站,將捕獲到如圖所示的界面。
從該界面的 Protocol 列,可以看到捕獲到有 DNS、TCP、HTTP 等協議的包。在這些包 中,都包含由 IP 頭部的詳細信息。但是,這樣可能會影響對 IP 協議包的分析。
② 執行 ping 命令
為了不受很多協議的影響,這裏通過執行 ping 命令僅捕獲 ICMP 協議的數據包。此時 在主機 PC1 上執行 ping 命令,分別 pingPC2 和 PC3。執行命令如下所示:
|
C:\Users\Administrator>ping 192.168.5.4 C:\Users\Administrator>ping 192.168.6.103 |
執行以上命令后,捕獲到的數據包如圖所示。
圖 捕獲到的 IP 協議包
從該界面的 Protocol 列,可以看到都是 ICMP 協議的包,而且每個包的顏色也都是相同 的。雖然從該界面看到捕獲到的數據包很多,但是只需要分析其中兩個包,就可以很清楚的 理解 IP 協議包格式。此時,用戶還可以使用 IP 的显示過濾器對數據包進行過濾。如過濾僅 显示主機 PC3(192.168.6.103)的數據包,輸入過濾器 ip.addr==192.168.6.103,显示界面如圖所示。
從該界面可以看到,以上數據包都是發送/來自 192.168.6.103 的數據包。
4) 捕獲 IP 分片數據包
在上面提到說,如果一個數據包超過 1500 個字節時,就需要將該包進行分片發送。通 常情況下,是不會出現這種情況的。但是為了幫助用戶更清晰的理解 IP 協議,下面通過使 用 ICMP 包,來產生 IP 分片數據包。本節將介紹如何捕獲到 IP 分片數據包。
使用 ICMP 包進行測試時,如果不指定包的大小可能無法查看到被分片的數據包。由於 IP 首部佔用 20 個字節,ICMP 首部占 8 個字節,所以捕獲到 ICMP 包大小最大為 1472 字節。 但是一般情況下,ping 命令默認的大小都不會超過 1472 個字節。這樣,發送的 ICMP 報文 就可以順利通過,不需要經過分片后再傳輸。如果想要捕獲到 IP 分片包,需要指定發送的 ICMP 包必須大於 1472 字節。
捕獲 IP 分片的數據包。具體操作步驟如下所示:
(1)啟動 Wireshark 捕獲工具。
(2)在 Wireshark 主界面的菜單欄中依次選擇 Capture|Options,或者單擊工具欄中的 (显示捕獲選項)圖標打開 Wireshark 捕獲選項窗口,如圖所示。
圖 捕獲選項界面
(3)在該界面設置捕獲接口、捕獲過濾器及捕獲文件的位置。這裏將捕獲的數據保存 到 ip-fragment.pcapng 捕獲文件中,如圖 3.10 所示。以上信息配置完后,單擊 Start 按鈕開始 捕獲數據包,如圖 所示。
圖 開始捕獲數據包
此時在主機 PC1 上執行 ping 命令,以產生 ICMP 數據包。執行命令如下所示:
|
C:\Users\lyw>ping 192.168.5.4 -l 3000 |
在該命令中,使用-l 選項指定捕獲包的大小為 3000 字節。執行以上命令后,將显示如 下所示的信息:
|
正在 Ping 192.168.5.4 具有 3000 字節的數據: 來自 192.168.5.4 的回復: 字節=3000 時間=5ms TTL=64 來自 192.168.5.4 的回復: 字節=3000 時間=5ms TTL=64 來自 192.168.5.4 的回復: 字節=3000 時間=5ms TTL=64 來自 192.168.5.4 的回復: 字節=3000 時間=5ms TTL=64 |
從以上輸出信息中,可以看到捕獲到每個包的大小都為 3000 字節。這時候,返回到 Wireshark 界面停止捕獲數據,將显示如圖所示的界面。
圖 IP 分片數據包
從該界面可以很清楚的看到,和前面捕獲到的數據包不同。在該界面 Protocol 列,显示 了 IPv4 協議的包。這是因為發送的數據包過大,所以經過了分片后發送的。
5、IP數據報首部格式
源 IP 地址和目的 IP 地址都是 IPv4 數據報首部最重要的組成部分。但是,在首部固定 部分的後面還有一些可選字段,並且其長度是可變的。下面將詳細介紹 IP 數據報首部格式, 如表 3-1 所示。
表 3-1 IP數據報首部格式
|
IP協議 |
|||||
|
偏移位 |
0~3 |
4~7 |
8~15 |
16~18 |
19~31 |
|
0 |
版本 |
首部長度 |
服務類型 |
總長度 |
|
|
32 |
標識符 |
標記 |
分段偏移 |
||
|
64 |
存活時間 |
1 |
首部校驗和 |
|
|
|
96 |
源IP地址 |
||||
|
128 |
目的IP地址 |
||||
|
160 |
選項 |
||||
|
160或192+ |
數據 |
||||
在表 3-1 中,每個字段代表的含義如下所示:
· 版本號:指 IP 協議所使用的版本。通信雙方使用的 IP 協議版本必須一致。目前廣 泛使用的 IP 協議版本號為 4,即 IPv4。
· 首部長度:IP 的首部長度,可表示的最大十進制數值是 15。注意,該字段所表示 的單位是 32 位字長(4 個字節)。因此,當 IP 首部長度為 1111(即十進制的 15) 時,首部長度就達到 60 字節。當 IP 分組的首部長度不是 4 字節的整數倍時,必須利用最後的填充字段加以填充。
· 服務類型:優先級標誌位和服務類型標誌位,被路由器用來進行流量的優先排序。
· 總長度:指 IP 首部和數據報中數據之後的長度,單位為字節。總長度字段為 16 位, 因此數據報的最大長度為 216-1=65535 字節。
· 標識符:一個唯一的標識数字,用來識別一個數據報或者被分片數據包的次序。
· 標識:用來標識一個數據報是否是一組分片數據報的一部分。標誌字段中的最低位 記為 MF(More Fragment)。MF=1 即表示後面“還有分片”的數據報。MF=0 表 示這已是若干數據包分片中的最後一個。標誌字段中間的一位記為 DF(Don’t Fragment),意思是“不能分片”。只有當 DF=0 時,才允許分片。
· 分片偏移:一個數據報是一個分片,這個域中的值就會被用來將數據報以正確的順 序重新組裝。
· 存活時間:用來定義數據報的生存周期,以經過路由器的條數/秒數進行描述。
· 協議:用來識別在數據包序列中上層協議數據報的類型。如,ICMP則協議值為1,TCP協議值為6,UDP協議值為17;更多的請自行百度
· 首部校驗和:一個錯誤檢測機制,用來確認 IP 首部的內容有沒有被損壞或者篡改。
· 源 IP 地址:發出數據報的主機的 IP 地址。
· 目的 IP 地址:數據報目的地的 IP 地址。
· 選項:保留作額外的 IP 選項。它包含着源站選路和時間戳的一些選項。
· 數據:使用 IP 傳遞的實際數據
1)存活時間 TTL
存活時間(TTL)值定義了在該數據報被丟棄之前,所能經歷的時間,或者能夠經過的 最大路由數目。TTL 在數據報被創建時就會被定義,而且通常在每次被發往一個路由器的 時候減 1。
例如,如果一個數據報的存活時間是 2,那麼當它到達第一個路由器的時候,其 TTL 會被減為 1,並會被發向第二個路由。這個路由接着會將 TTL 減為 0。這時,如果這個數據 報的最終目的地不在這個網絡中,那麼這個數據報就會被丟棄,如圖 3.13 所示。
圖 3.13就是數據報經過路由器后,TTL 值的變化。由於 TTL 的值在技術上還是基於時間的,一個非常繁忙的路由器可能會將 TTL 的值減去不止 1。但是通常情況下,還是可以 認為一個路由器設備在多數情況下只會將 TTL 的值減去 1。
了解 TTL 值的變化是非常重要的。一般用戶通常所關心的一個數據報的生存周期,只 是其從源前往目的地所花去的時間。但是考慮到一個數據報想要通過互聯網發往一台主機需 要經過數十個路由器。在這個數據報的路徑上,它可能會碰到被錯誤配置的路由器,而失去 其到達最終目的地的路徑。在這種情況下,這個路由器可能會做很多事情,其中一件就是將 數據報發向一個網絡,而產生一個死循環。如果出現死循環這種情況,可能導致一個程序或 者整個操作系統崩潰。同樣的,如果數據報在網絡上傳輸時,數據報可能會在路由器直接持 續循環,隨着循環數據報的增多,網絡中可用的帶寬將會減少,直至拒絕服務(DoS)的情 況出現。IP 首部中的 TTL 域,就是為了防止出現這種潛在的問題。
2)IP分片
數據報分片是將一個數據流分為更小的片段,是 IP 用於解決跨越不同類型網絡時可靠 傳輸的一個特性。一個數據報的分片主要是基於第二層數據鏈路層所使用的最大傳輸單元 (Maximum Transmission Unit,MTU)的大小,以及使用這些二層協議的設備配置情況。 在多數情況下,第二層所使用的數據鏈路協議是以太網,以太網的 MTU 是 1500。也就是說, 以太網的網絡上能傳輸的最大數據報大小是 1500 字節(不包括 14 字節的以太網頭本身)。
當一個設備準備傳輸一個 IP 數據報時,它將會比較這個數據報的大小,以及將要把這 個數據報傳送出去的網絡接口 MTU,用於決定是否需要將這個數據報分片。如果數據報的 大小大於 MTU,那麼這個數據報就會被分片。將一個數據報分片包括下列幾個步驟,如下 所示:
(1)設備將數據分為若干個可成功進行傳輸的數據報。
(2)每個 IP 首部的總長度域會被設置為每個分片的片段長度。
(3)更多分片標誌將會在數據流的所有數據報中設置為 1,除了最後一個數據報。
(4)IP 頭中分片部分的分片偏移將會被設置。
(5)數據報被發送出去
6、分析 IP 數據包
通過前面對 IP 協議的詳細介紹及數據包的捕獲,現在就可以來分析 IP 數據包了。
1)分析IP首部
這裏以捕獲文件的第一幀為例,介紹 IP 數據包首部,如圖 3.14 所示。
在該圖中從 Packet Details 面板中,可以看到有 IPv4 協議的包。這裏就詳細介紹在該包 中的詳細信息,如下所示:
|
Frame 1: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface 0 |
以上信息表示是第一幀信息,其大小為 74 個字節。
|
Ethernet II, Src: Elitegro_3f:c3:e5 (00:19:21:3f:c3:e5), Dst: Giga-Byt_eb:46:8d (50:e5:49:eb:46:8d) |
以上信息表示是以太網幀頭部信息。其中,源 MAC 地址為 00:19:21:3f:c3:e5,目標 MAC 地址為 50:e5:49:eb:46:8d。
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.5.4 (192.168.5.4) |
以上信息表示IPv4包頭部信息。其中源IP地址為192.168.5.2,目標IP地址為192.168.5.4。 在該包首部中還有很多其它字段的信息,下面將介紹該包中展開的所有信息。如下所示:
以上信息包括 IP 包首部的所有字段,對應到包首部格式中,如表 3-2 所示。
表 3-2 IP包首部格式
|
IP協議 |
|||||
|
偏移位 |
0~3 |
4~7 |
8~15 |
16~18 |
19~31 |
|
0 |
4 |
20 |
0x00 |
60 |
|
|
32 |
0x050e |
0x00 |
0 |
||
|
64 |
64 |
ICMP(1) |
0xea5c |
||
|
96 |
192.168.5.2 |
||||
|
128 |
192.168.5.4 |
||||
|
160 |
|
||||
|
160或192+ |
|
||||
在該包中最後一行信息如下所示:
|
Internet Control Message Protocol |
以上信息表示 ICMP 協議包信息。關於該協議的分析,在後面進行介紹。
2)分析IP數據包中TTL的變化
前面介紹過 TTL 值是經過路由器后才發送變化。也就是說如果在同一網段中傳輸數據 包時,TTL 值是不變的。只有與非同網段的主機進行通信時,該數據包的 TTL 值才會發生 變化。下面通過分析捕獲文件,來確定 TTL 值是否是這樣變化的。
① 分析同網段中數據包的 TTL 值
這裏同樣以捕獲文件為例,分析同網段 TTL 值的變化。在 ip.pcapng 捕獲文 件中,1~8 幀都是主機 PC1(192.168.5.2)和 PC2(192.168.5.4)之間的通信。這八幀可以 說是 4 個完整的數據包,也就是通過 ICMP 協議的發送和響應包。這裏以 ip.pcapng 捕獲文 件中的 3、4 幀為例,分析這兩個包中的 TTL 值。其中,3、4 幀的信息如圖 3.15 所示。
從該界面的 Packet List 面板中,Info 列可以看到 3、 4 幀包信息分別是 Echo(ping)request (請求)和 Echo(ping)reply(響應)。也就是說 192.168.5.2(PC1)發給 192.168.5.4 的 包是一個請求包,192.168.5.4(PC2)的包是一個響應包。其中,這兩台主機是在同一個網 絡中,所以這兩個包的 TTL 值應該相同。下面分別來看這兩個包中 IP 包首部的相信信息。
第三幀的 IP 包首部信息如下所示:
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.5.4 (192.168.5.4) Version: 4 #IP 協議版本號 Header length: 20 bytes #首部長度 Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport)) #服務標識符 Total Length: 60 #總長度 Identification: 0x050f (1295) #標識符 Flags: 0x00 #標誌 0… …. = Reserved bit: Not set #保留位 .0.. …. = Don’t fragment: Not set #不進行分片 ..0. …. = More fragments: Not set #更多分片 Fragment offset: 0 #分片偏移 Time to live: 64 #生存期 Protocol: ICMP (1) #協議 Header checksum: 0xea5b [validation disabled] #首部校驗和 Source: 192.168.5.2 (192.168.5.2) #源 IP 地址 Destination: 192.168.5.4 (192.168.5.4) #目標 IP 地址 [Source GeoIP: Unknown] #源 IP 地理位置 [Destination GeoIP: Unknown] #目標 IP 地理位置 |
以上信息是第三針中 IPv4 首部的詳細信息。從中可以看到,該包中的 TTL 值是 64。 第四幀的 IP 包首部信息如下所示
|
Internet Protocol Version 4, Src: 192.168.5.4 (192.168.5.4), Dst: 192.168.5.2 (192.168.5.2) Version: 4 #IP 協議版本號 Header length: 20 bytes #首部長度 Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport)) #服務標識符 Total Length: 60 #總長度 Identification: 0xc71d (50973) #標識符 Flags: 0x00 #標誌 0… …. = Reserved bit: Not set #保留位 .0.. …. = Don’t fragment: Not set #不進行分片 ..0. …. = More fragments: Not set #更多分片 Fragment offset: 0 #分片偏移 Time to live: 64 #生存期 Protocol: ICMP (1) #協議 Header checksum: 0x284d [validation disabled] #首部校驗和 [Good: False] [Bad: False] Source: 192.168.5.4 (192.168.5.4) #源 IP 地址 Destination: 192.168.5.2 (192.168.5.2) #目標 IP 地址 [Source GeoIP: Unknown] #源 IP 地理位置 [Destination GeoIP: Unknown] #目標 IP 地理位置 |
從以上信息中,可以看到每個字段的信息都和第三幀 IP 包首部的信息都相同。這兩個 包中的生存期(TTL),沒有發生變化。這是因為,主機 PC1 和 PC2 在同一個網段內,它 們之間傳輸的數據不需要經過路由器。
② 分析不同網段中數據包的 TTL 值
下面以捕獲文件為例,分析不同網段 TTL 值的變化。在 ip.pcapng 捕獲文件 中,9-16 幀是兩台(PC1 和 PC3)不同網段主機之間通信的數據包,如圖 3.16 所示。
在該界面显示的包同樣是四個完整的 ICMP 包,一個是請求包,一個是響應包。這裏分 析 9、10 幀中 IPv4 首部的詳細信息,如下所示。
第 9 幀 IPv4 首部信息,如下所示:
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.6.103 (192.168.6.103) Version: 4 #IP 協議版本號 Header length: 20 bytes #首部長度 Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport)) #服務標識符 Total Length: 60 #總長度 Identification: 0x0512 (1298) #標識符 Flags: 0x00 #標誌 0… …. = Reserved bit: Not set #保留位 .0.. …. = Don’t fragment: Not set #不進行分片 ..0. …. = More fragments: Not set #更多分片 Fragment offset: 0 #分片偏移 Time to live: 64 #生存期 Protocol: ICMP (1) #協議 Header checksum: 0xe8f5 [validation disabled] #首部校驗和 [Good: False] [Bad: False] Source: 192.168.5.2 (192.168.5.2) #源 IP 地址 Destination: 192.168.6.103 (192.168.6.103) #目標 IP 地址 [Source GeoIP: Unknown] #源 IP 地理位置 [Destination GeoIP: Unknown] #目標 IP 地理位置 |
以上包信息,是主機 PC1 發送給 PC3 的 IP 包首部信息。其中,TTL 值為 64。
第 10 幀 IPv4 首部信息,如下所示:
|
Internet Protocol Version 4, Src: 192.168.6.103 (192.168.6.103), Dst: 192.168.5.2 (192.168.5.2) Version: 4 #IP 協議版本號 Header length: 20 bytes #首部長度 Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport)) #服務標識符 Total Length: 60 #總長度 Identification: 0xa206 (41478) #標識符 Flags: 0x00 #標誌 0… …. = Reserved bit: Not set #保留位 .0.. …. = Don’t fragment: Not set #不進行分片 ..0. …. = More fragments: Not set #更多分片 Fragment offset: 0 #分片偏移 Time to live: 63 #生存期 Protocol: ICMP (1) #協議 Header checksum: 0x4d01 [validation disabled] #首部校驗和 [Good: False] [Bad: False] Source: 192.168.6.103 (192.168.6.103) #源 IP 地址 Destination: 192.168.5.2 (192.168.5.2) #目標 IP 地址 [Source GeoIP: Unknown] #源 IP 地理位置 [Destination GeoIP: Unknown] #目標 IP 地理位置 |
以上包信息,是主機 PC3 發送給 PC1 的 IP 包首部信息。從以上信息中,可以看到該 IPv4 首部中 TTL 值為 63。由此可以說明,PC3 發送回 PC1 的數據包經過了一個路由器。
③ IP 分片數據包分析
下面以捕獲文件為例,詳細分析 IP 分片。打開 ip-fragment.pcapng 捕獲文件,显示界面如圖 3.17 所示。
在該捕獲文件中,也是捕獲了四個 ping 包。1~6 幀是一個完整的 ping 包,其中 1~3 幀 是 ping 請求包,4~6 幀是 ping 響應包。也就是說,將第一個 ping 請求包,分為了 1~3 個數 據包。下面將詳細分析 1~3 幀的詳細信息。
⑴ 第 1 幀數據包
第 1 幀數據包詳細信息如圖 3.18 所示。
從該界面的 Packet Details 面板中,可以看到有四行信息。分別如下所示:
|
Frame 1: 1514 bytes on wire (12112 bits), 1514 bytes captured (12112 bits) on interface 0 |
以上信息表示第 1 幀數據包的信息,其大小為 1514 字節。
|
Ethernet II, Src: Elitegro_3f:c3:e5 (00:19:21:3f:c3:e5), Dst: Giga-Byt_eb:46:8d (50:e5:49:eb:46:8d) |
以上信息表示以太網幀頭部信息。其中源 MAC 地址為 00:19:21:3f:c3:e5,目標 MAC 地 址為 50:e5:49:eb:46:8d。
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.5.4 (192.168.5.4) |
以上信息表示 IPv4 頭部信息。在該頭部包括了具體的詳細信息。展開該行信息,內容 如下所示:
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.5.4 (192.168.5.4) Version: 4 #IP 協議版本 Header length: 20 bytes #首部長度 Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport)) #服務標識符 0000 00.. = Differentiated Services Codepoint: Default (0x00) …. ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00) Total Length: 1500 #總長度 Identification: 0x05a3 (1443) #標識符 Flags: 0x01 (More Fragments) #標誌 0… …. = Reserved bit: Not set #保留位 .0.. …. = Don’t fragment: Not set #不能分片。這裏的值為 0,表示可以進行 分片 ..1. …. = More fragments: Set #更多分片。這裏的值為 1,表示還有分片 的數據包 Fragment offset: 0 #分片偏移 Time to live: 64 #生存期 Protocol: ICMP (1) #協議 Header checksum: 0xc427 [validation disabled] #首部校驗和 [Good: False] [Bad: False] Source: 192.168.5.2 (192.168.5.2) #源 IP 地址 Destination: 192.168.5.4 (192.168.5.4) #目標 IP 地址 [Source GeoIP: Unknown] #源 IP 地理位置 [Destination GeoIP: Unknown] #目標 IP 地理位置 Reassembled IPv4 in frame: 3 #重組 IPv4 包 Data (1480 bytes) #數據 Data: 0800cfd0000100016162636465666768696a6b6c6d6e6f70… [Length: 1480] #長度為 1480 字節 |
以上信息是第 1 幀 IPv4 首部的詳細信息。從以上更多分片和分片偏移域部分,可以判定該數據包是分片數據包的一部分。這是后被分片的數據包,就會有一個大於 0 的分片偏移 或者就是設定了更多標誌為。從以上信息,可以看到更多分片標誌位被設定,也就是接收設 備應該等待接收序列中的另一個數據包。分片偏移為 0,表示這個數據包是這一系列分片中 的第一個包。所以,後面至少還有一個包。接下來看第二幀包信息。以上信息對應的 IPv4 首部格式中,显示結果如表 3-3 所示
⑵ 第 2 幀數據包
第 2 幀數據包詳細信息如圖 3.19 所示。
從 Wireshark 的 Packet Details 面板中,可以看到有四行詳細信息。而且包大小,和第一 個數據包的大小相同。下面將分析該包的詳細信息,如下所示。
|
Frame 2: 1514 bytes on wire (12112 bits), 1514 bytes captured (12112 bits) on interface 0 |
以上信息表示,這是第 2 幀的詳細信息。其中,該包的大小為 1514 個字節。
|
Ethernet II, Src: Elitegro_3f:c3:e5 (00:19:21:3f:c3:e5), Dst: Giga-Byt_eb:46:8d (50:e5:49:eb:46:8d) |
以上信息表示以太網幀頭部信息。其中,源 MAC 地址為 00:19:21:3f:c3:e5,目標 MAC 地址為 50:e5:49:eb:46:8d。
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.5.4 (192.168.5.4) |
以上信息表示 IPv4 首部的詳細信息。下面將詳細分析該包中每個字段的值,如下所示:
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.5.4 (192.168.5.4) Version: 4 #IP 協議版本 Header length: 20 bytes #首部長度 Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport)) #服務標識符 0000 00.. = Differentiated Services Codepoint: Default (0x00) …. ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00) Total Length: 1500 #總長度 Identification: 0x05a3 (1443) #標識符 Flags: 0x01 (More Fragments) #標誌 0… …. = Reserved bit: Not set #保留位 .0.. …. = Don’t fragment: Not set #不能分片。這裏的值為 0,表示可以進行 分片 ..1. …. = More fragments: Set #更多分片。這裏的值為 1,表示還有分片 的數據包 Fragment offset: 1480 #分片偏移 Time to live: 64 #生存期 Protocol: ICMP (1) #協議 Header checksum: 0xc36e [validation disabled] #首部校驗和 [Good: False] [Bad: False] Source: 192.168.5.2 (192.168.5.2) #源 IP 地址 Destination: 192.168.5.4 (192.168.5.4) #目標 IP 地址 [Source GeoIP: Unknown] #源 IP 地理位置 [Destination GeoIP: Unknown] #目標 IP 地理位置 Reassembled IPv4 in frame: 3 #重組 IPv4 包 Data (1480 bytes) #數據 Data: 6162636465666768696a6b6c6d6e6f707172737475767761… [Length: 1480] #長度為 1480 字節 |
根據以上信息介紹,可以看到在該包的 IPv4 首部也設定了更多分片的標誌為。而且可 以看到,這裏的分片偏移值為 1480。該值是由最大傳輸單元(MTU)1500,減去 IP 首部的 20 個字節得到的。以上信息對應到 IPv4 首部格式中,显示信息如表 3-4 所示。
|
IP協議 |
|||||
|
偏移位 |
0~3 |
4~7 |
8~15 |
16~18 |
19~31 |
|
0 |
4 |
20 |
0x00 |
1500 |
|
|
32 |
0x05a3 |
0x01 |
1480 |
||
|
64 |
64 |
ICMP(1) |
0xc36e |
||
|
96 |
192.168.5.2 |
||||
|
128 |
192.168.5.4 |
||||
|
160 |
|
||||
|
160或192+ |
1480 |
||||
⑶ 第 3 幀數據包
第 3 幀數據包詳細信息如圖 3.20 所示。
從 Wireshark 的 Packet Details 界面可以看到,該包中显示了四行信息,並且該包的協議 為 ICMP。下面將詳細分析該包中的信息。
|
Frame 3: 82 bytes on wire (656 bits), 82 bytes captured (656 bits) on interface 0 |
以上信息表示這是第 3 幀的詳細信息,其中包大小為 82 個字節。
|
Ethernet II, Src: Elitegro_3f:c3:e5 (00:19:21:3f:c3:e5), Dst: Giga-Byt_eb:46:8d (50:e5:49:eb:46:8d) |
以上信息表示以太網幀頭部的詳細信息。其中,源 MAC 地址為 00:19:21:3f:c3:e5,目 標 MAC 地址為 50:e5:49:eb:46:8d。
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.5.4 (192.168.5.4) |
以上信息表示 IPv4 首部信息,這裏着重分析該部分的詳細信息。如下所示:
|
Internet Protocol Version 4, Src: 192.168.5.2 (192.168.5.2), Dst: 192.168.5.4 (192.168.5.4) Version: 4 #IP 協議版本 Header length: 20 bytes #首部長度 Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport)) #服務標識符 0000 00.. = Differentiated Services Codepoint: Default (0x00) …. ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00) Total Length: 68 #總長度 Identification: 0x05a3 (1443) #標識符 Flags: 0x01 (More Fragments) #標誌 0… …. = Reserved bit: Not set #保留位 .0.. …. = Don’t fragment: Not set #不能分片。 ..0. …. = More fragments: Not set #更多分片。 Fragment offset: 2960 #分片偏移 Time to live: 64 #生存期 Protocol: ICMP (1) #協議 Header checksum: 0xe84d [validation disabled] #首部校驗和 [Good: False] [Bad: False] Source: 192.168.5.2 (192.168.5.2) #源 IP 地址 Destination: 192.168.5.4 (192.168.5.4) #目標 IP 地址 [Source GeoIP: Unknown] #源 IP 地理位置 [Destination GeoIP: Unknown] #目標 IP 地理位置 [3 IPv4 Fragments (3008 bytes): #1(1480), #2(1480), #3(48)] #三個 IPv4 分片,共 3000 個字 節 [Frame: 1, payload: 0-1479 (1480 bytes)] #第 1 幀加載了 1480 個字節 [Frame: 2, payload: 1480-2959 (1480 bytes)] #第 2 幀加載了 1480 個字節 [Frame: 3, payload: 2960-3007 (48 bytes)] #第 3 幀加載了 48 個字節 [Fragment count: 3] #分片數為 3 [Reassembled IPv4 length: 3008] #重組 IPv4 長度為 3008 [Reassembled IPv4 data: 0800cfd0000100016162636465666768696a6b6c6d6e6f70…] #重組 IPv4 數據 |
根據以上信息的描述,可以看到該數據包沒有設定更多分片標誌位,也就表示該數據包 是整個數據流中的最後一個分片。並且其分片偏移設定為 2960,是由 1480+(1500-20)得出 的結果。這些分片可以被認為是同一個數據序列的一部分,因為它們 IP 首部中的標誌位於 擁有相同的值。以上信息對應到 IP 首部格式,如表 3-5 所示
|
IP協議 |
|||||
|
偏移位 |
0~3 |
4~7 |
8~15 |
16~18 |
19~31 |
|
0 |
4 |
20 |
0x00 |
68 |
|
|
32 |
0x05a3 |
0x00 |
2960 |
||
|
64 |
64 |
ICMP(1) |
0xe84d |
||
|
96 |
192.168.5.2 |
||||
|
128 |
192.168.5.4 |
||||
|
160 |
|
||||
|
160或192+ |
|
||||
在該包中最後一行信息如下所示:
|
Internet Control Message Protocol |
以上信息表示 ICMP 協議包信息。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※教你寫出一流的銷售文案?
前文我聊到了docker machine的簡單使用和基本原理的說明,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/13160915.html;今天我們來聊一聊docker集群管理工具docker swarm;docker swarm是docker 官方的集群管理工具,它可以讓跨主機節點來創建,管理docker 集群;它的主要作用就是可以把多個節點主機的docker環境整合成一個大的docker資源池;docker swarm面向的就是這個大的docker 資源池在上面管理容器;在前面我們都只是在單台主機上的創建,管理容器,但是在生產環境中通常一台物理機上的容器實在是不能夠滿足當前業務的需求,所以docker swarm提供了一種集群解決方案,方便在多個節點上創建,管理容器;接下來我們來看看docker swarm集群的搭建過程吧;
docker swarm 在我們安裝好docker時就已經安裝好了,我們可以使用docker info來查看
[root@node1 ~]# docker info Client: Debug Mode: false Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 19.03.11 Storage Driver: overlay2 Backing Filesystem: xfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog Swarm: inactive Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429 runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd init version: fec3683 Security Options: seccomp Profile: default Kernel Version: 3.10.0-693.el7.x86_64 Operating System: CentOS Linux 7 (Core) OSType: linux Architecture: x86_64 CPUs: 4 Total Memory: 3.686GiB Name: docker-node01 ID: 4HXP:YJ5W:4SM5:NAPM:NXPZ:QFIU:ARVJ:BYDG:KVWU:5AAJ:77GC:X7GQ Docker Root Dir: /var/lib/docker Debug Mode: false Registry: https://index.docker.io/v1/ Labels: provider=generic Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false [root@node1 ~]#
提示:從上面的信息可以看到,swarm是處於非活躍狀態,這是因為我們還沒有初始化集群,所以對應的swarm選項的值是處於inactive狀態;
初始化集群
[root@docker-node01 ~]# docker swarm init --advertise-addr 192.168.0.41
Swarm initialized: current node (ynz304mbltxx10v3i15ldkmj1) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-2m9x12n102ca4qlyjpseobzik 192.168.0.41:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
[root@docker-node01 ~]#
提示:從上面反饋的信息可以看到,集群初始化成功,並且告訴我們當前節點為管理節點,如果想要其他節點加入到該集群,可以在對應節點上運行docker swarm join –token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-2m9x12n102ca4qlyjpseobzik 192.168.0.41:2377 這個命令,就把對應節點當作work節點加入到該集群,如果想要以管理節點身份加入到集群,我們需要在當前終端運行docker swarm join-token manager命令
[root@docker-node01 ~]# docker swarm join-token manager
To add a manager to this swarm, run the following command:
docker swarm join --token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-dqjeh8hp6cp99bksjc03b8yu3 192.168.0.41:2377
[root@docker-node01 ~]#
提示:我們執行docker swarm join-token manager命令,它返回了一個命令,並告訴我們添加一個管理節點,在對應節點上執行docker swarm join –token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-dqjeh8hp6cp99bksjc03b8yu3 192.168.0.41:2377命令即可;
到此docker swarm集群就初始化完畢,接下來我們把其他節點加入到該集群
把docker-node02以work節點身份加入集群
[root@node2 ~]# docker swarm join --token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-2m9x12n102ca4qlyjpseobzik 192.168.0.41:2377 This node joined a swarm as a worker. [root@node2 ~]#
提示:沒有報錯就表示加入集群成功;我們可以使用docker info來查看當前的docker 環境詳細信息
提示:從上面的信息可以看到,在docker-node02這台主機上docker swarm 已經激活,並且可以看到管理節點的地址;除了以上方式可以確定docker-node02以及加入到集群;我們還可以在管理節點上運行docker node ls 查看集群節點信息;
查看集群節點信息
提示:在管理節點上運行docker node ls 就可以列出當前集群里有多少節點已經成功加入進來;
把docker-node03以管理節點身份加入到集群
提示:可以看到docker-node03已經是集群的管理節點,所以可以在docker-node03這個節點執行docker node ls 命令;到此docker swarm集群就搭建好了;接下來我們來說一說docker swarm集群的常用管理
有關節點相關管理命令
docker node ls :列出當前集群上的所有節點
[root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active 19.03.11 aeo8j7zit9qkoeeft3j0q1h0z docker-node03 Ready Active Reachable 19.03.11 [root@docker-node01 ~]#
提示:該命令只能在管理節點上執行;
docker node inspect :查看指定節點的詳細信息;
[root@docker-node01 ~]# docker node inspect docker-node01
[
{
"ID": "ynz304mbltxx10v3i15ldkmj1",
"Version": {
"Index": 9
},
"CreatedAt": "2020-06-20T05:57:17.57684293Z",
"UpdatedAt": "2020-06-20T05:57:18.18575648Z",
"Spec": {
"Labels": {},
"Role": "manager",
"Availability": "active"
},
"Description": {
"Hostname": "docker-node01",
"Platform": {
"Architecture": "x86_64",
"OS": "linux"
},
"Resources": {
"NanoCPUs": 4000000000,
"MemoryBytes": 3958075392
},
"Engine": {
"EngineVersion": "19.03.11",
"Labels": {
"provider": "generic"
},
"Plugins": [
{
"Type": "Log",
"Name": "awslogs"
},
{
"Type": "Log",
"Name": "fluentd"
},
{
"Type": "Log",
"Name": "gcplogs"
},
{
"Type": "Log",
"Name": "gelf"
},
{
"Type": "Log",
"Name": "journald"
},
{
"Type": "Log",
"Name": "json-file"
},
{
"Type": "Log",
"Name": "local"
},
{
"Type": "Log",
"Name": "logentries"
},
{
"Type": "Log",
"Name": "splunk"
},
{
"Type": "Log",
"Name": "syslog"
},
{
"Type": "Network",
"Name": "bridge"
},
{
"Type": "Network",
"Name": "host"
},
{
"Type": "Network",
"Name": "ipvlan"
},
{
"Type": "Network",
"Name": "macvlan"
},
{
"Type": "Network",
"Name": "null"
},
{
"Type": "Network",
"Name": "overlay"
},
{
"Type": "Volume",
"Name": "local"
}
]
},
"TLSInfo": {
"TrustRoot": "-----BEGIN CERTIFICATE-----\nMIIBaTCCARCgAwIBAgIUeBd/eSZ7WaiyLby9o1yWpjps3gwwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMjAwNjIwMDU1MjAwWhcNNDAwNjE1MDU1\nMjAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABMsYxnGoPbM4gqb23E1TvOeQcLcY56XysLuF8tYKm56GuKpeD/SqXrUCYqKZ\nHV+WSqcM0fD1g+mgZwlUwFzNxhajQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBTV64kbvS83eRHyI6hdJeEIv3GmrTAKBggqhkjO\nPQQDAgNHADBEAiBBB4hLn0ijybJWH5j5rtMdAoj8l/6M3PXERnRSlhbcawIgLoby\newMHCnm8IIrUGe7s4CZ07iHG477punuPMKDgqJ0=\n-----END CERTIFICATE-----\n",
"CertIssuerSubject": "MBMxETAPBgNVBAMTCHN3YXJtLWNh",
"CertIssuerPublicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyxjGcag9sziCpvbcTVO855BwtxjnpfKwu4Xy1gqbnoa4ql4P9KpetQJiopkdX5ZKpwzR8PWD6aBnCVTAXM3GFg=="
}
},
"Status": {
"State": "ready",
"Addr": "192.168.0.41"
},
"ManagerStatus": {
"Leader": true,
"Reachability": "reachable",
"Addr": "192.168.0.41:2377"
}
}
]
[root@docker-node01 ~]#
docker node ps :列出指定節點上運行容器的清單
[root@docker-node01 ~]# docker node ps ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS [root@docker-node01 ~]# docker node ps docker-node01 ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS [root@docker-node01 ~]#
提示:類似docker ps 命令,我上面沒有運行容器,所以看不到對應信息;默認不指定節點名稱表示查看當前節點上的運行容器清單;
docker node rm :刪除指定節點
[root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active 19.03.11 aeo8j7zit9qkoeeft3j0q1h0z docker-node03 Ready Active Reachable 19.03.11 [root@docker-node01 ~]# docker node rm docker-node03 Error response from daemon: rpc error: code = FailedPrecondition desc = node aeo8j7zit9qkoeeft3j0q1h0z is a cluster manager and is a member of the raft cluster. It must be demoted to worker before removal [root@docker-node01 ~]# docker node rm docker-node02 Error response from daemon: rpc error: code = FailedPrecondition desc = node tzkm0ymzjdmc1r8d54snievf1 is not down and can't be removed [root@docker-node01 ~]#
提示:刪除節點前必須滿足,被刪除的節點不是管理節點,其次就是要刪除的節點必須是down狀態;
docker swarm leave:離開當前集群
[root@docker-node03 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e7958ffa16cd nginx "/docker-entrypoint.…" 28 seconds ago Up 26 seconds 80/tcp n1 [root@docker-node03 ~]# docker swarm leave Error response from daemon: You are attempting to leave the swarm on a node that is participating as a manager. Removing this node leaves 1 managers out of 2. Without a Raft quorum your swarm will be inaccessible. The only way to restore a swarm that has lost consensus is to reinitialize it with `--force-new-cluster`. Use `--force` to suppress this message. [root@docker-node03 ~]# docker swarm leave -f Node left the swarm. [root@docker-node03 ~]#
提示:管理節點默認是不允許離開集群的,如果強制使用-f選項離開集群,會導致在其他管理節點無法正常管理集群;
[root@docker-node01 ~]# docker node ls Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online. [root@docker-node01 ~]#
提示:我們在docker-node01上現在就不能使用docker node ls 來查看集群節點列表了;解決辦法重新初始化集群;
[root@docker-node01 ~]# docker node ls
Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online.
[root@docker-node01 ~]# docker swarm init --advertise-addr 192.168.0.41
Error response from daemon: This node is already part of a swarm. Use "docker swarm leave" to leave this swarm and join another one.
[root@docker-node01 ~]# docker swarm init --force-new-cluster
Swarm initialized: current node (ynz304mbltxx10v3i15ldkmj1) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-2m9x12n102ca4qlyjpseobzik 192.168.0.41:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
[root@docker-node01 ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11
tzkm0ymzjdmc1r8d54snievf1 docker-node02 Unknown Active 19.03.11
aeo8j7zit9qkoeeft3j0q1h0z docker-node03 Down Active 19.03.11
rm3j7cjvmoa35yy8ckuzoay46 docker-node03 Unknown Active 19.03.11
[root@docker-node01 ~]#
提示:重新初始化集群不能使用docker swarm init –advertise-addr 192.168.0.41這種方式初始化,必須使用docker swarm init –force-new-cluster,該命令表示使用從當前狀態強制創建一個集群;現在我們就可以使用docker node rm 把down狀態的節點從集群刪除;
刪除down狀態的節點
[root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active 19.03.11 aeo8j7zit9qkoeeft3j0q1h0z docker-node03 Down Active 19.03.11 rm3j7cjvmoa35yy8ckuzoay46 docker-node03 Down Active 19.03.11 [root@docker-node01 ~]# docker node rm aeo8j7zit9qkoeeft3j0q1h0z rm3j7cjvmoa35yy8ckuzoay46 aeo8j7zit9qkoeeft3j0q1h0z rm3j7cjvmoa35yy8ckuzoay46 [root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active 19.03.11 [root@docker-node01 ~]#
docker node promote:把指定節點提升為管理節點
[root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active 19.03.11 [root@docker-node01 ~]# docker node promote docker-node02 Node docker-node02 promoted to a manager in the swarm. [root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active Reachable 19.03.11 [root@docker-node01 ~]#
docker node demote:把指定節點降級為work節點
[root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active Reachable 19.03.11 [root@docker-node01 ~]# docker node demote docker-node02 Manager docker-node02 demoted in the swarm. [root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active 19.03.11 [root@docker-node01 ~]#
docker node update:更新指定節點
[root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Active Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active 19.03.11 [root@docker-node01 ~]# docker node update docker-node01 --availability drain docker-node01 [root@docker-node01 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ynz304mbltxx10v3i15ldkmj1 * docker-node01 Ready Drain Leader 19.03.11 tzkm0ymzjdmc1r8d54snievf1 docker-node02 Ready Active 19.03.11 [root@docker-node01 ~]#
提示:以上命令把docker-node01的availability屬性更改為drain,這樣更改后docker-node01的資源就不會被調度到用來運行容器;
為docker swarm集群添加圖形界面
[root@docker-node01 docker]# docker run --name v1 -d -p 8888:8080 -e HOST=192.168.0.41 -e PORT=8080 -v /var/run/docker.sock:/var/run/docker.sock docker-registry.io/test/visualizer Unable to find image 'docker-registry.io/test/visualizer:latest' locally latest: Pulling from test/visualizer cd784148e348: Pull complete f6268ae5d1d7: Pull complete 97eb9028b14b: Pull complete 9975a7a2a3d1: Pull complete ba903e5e6801: Pull complete 7f034edb1086: Pull complete cd5dbf77b483: Pull complete 5e7311667ddb: Pull complete 687c1072bfcb: Pull complete aa18e5d3472c: Pull complete a3da1957bd6b: Pull complete e42dbf1c67c4: Pull complete 5a18b01011d2: Pull complete Digest: sha256:54d65cbcbff52ee7d789cd285fbe68f07a46e3419c8fcded437af4c616915c85 Status: Downloaded newer image for docker-registry.io/test/visualizer:latest 3c15b186ff51848130393944e09a427bd40d2504c54614f93e28477a4961f8b6 [root@docker-node01 docker]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3c15b186ff51 docker-registry.io/test/visualizer "npm start" 6 seconds ago Up 5 seconds (health: starting) 0.0.0.0:8888->8080/tcp v1 [root@docker-node01 docker]#
提示:我上面的命令是從私有倉庫中下載的鏡像,原因是互聯網下載太慢了,所以我提前下載好,放在私有倉庫中;有關私有倉庫的搭建使用,請參考https://www.cnblogs.com/qiuhom-1874/p/13061984.html或者https://www.cnblogs.com/qiuhom-1874/p/13058338.html;在管理節點上運行visualizer容器后,我們就可以直接訪問該管理節點地址的8888端口,就可以看到當前容器的情況;如下圖
提示:從上面的信息可以看到當前集群有一個管理節點和兩個work節點;現目前集群里沒有運行任何容器;
在docker swarm運行服務
[root@docker-node01 ~]# docker service create --name myweb docker-registry.io/test/nginx:latest i0j6wvvtfe1360ibj04jxulmd overall progress: 1 out of 1 tasks 1/1: running [==================================================>] verify: Service converged [root@docker-node01 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS i0j6wvvtfe13 myweb replicated 1/1 docker-registry.io/test/nginx:latest [root@docker-node01 ~]# docker service ps myweb ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 99y8towew77e myweb.1 docker-registry.io/test/nginx:latest docker-node03 Running Running 1 minutes ago [root@docker-node01 ~]#
提示:docker service create 表示在當前swarm集群環境中創建一個服務;以上命令表示在swarm集群上創建一個名為myweb的服務,用docker-registry.io/test/nginx:latest鏡像;默認情況下只啟動一個副本;
提示:可以看到當前集群中運行了一個myweb的容器,並且運行在docker-node03這台主機上;
在swarm 集群上創建多個副本服務
[root@docker-node01 ~]# docker service create --replicas 3 --name web docker-registry.io/test/nginx:latest mbiap412jyugfpi4a38mb5i1k overall progress: 3 out of 3 tasks 1/3: running [==================================================>] 2/3: running [==================================================>] 3/3: running [==================================================>] verify: Service converged [root@docker-node01 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS i0j6wvvtfe13 myweb replicated 1/1 docker-registry.io/test/nginx:latest mbiap412jyug web replicated 3/3 docker-registry.io/test/nginx:latest [root@docker-node01 ~]#docker service ps web ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 1rt0e7u4senz web.1 docker-registry.io/test/nginx:latest docker-node02 Running Running 28 seconds ago 31ll0zu7udld web.2 docker-registry.io/test/nginx:latest docker-node02 Running Running 28 seconds ago l9jtbswl2x22 web.3 docker-registry.io/test/nginx:latest docker-node03 Running Running 32 seconds ago [root@docker-node01 ~]#
提示:–replicas選項用來指定期望運行的副本數量,該選項會在集群上創建我們指定數量的副本,即便我們集群中有節點宕機,它始終會創建我們指定數量的容器在集群上運行着;
測試:把docker-node03關機,看看我們運行的服務是否會遷移到節點2上呢?
docker-node03關機前
docker-node03關機后
提示:從上面的截圖可以看到,當節點3宕機后,節點3上跑的所有容器,會全部遷移到節點2上來;這就是創建容器時用–replicas選項的作用;總結一點,創建服務使用副本模式,該服務所在節點故障,它會把對應節點上的服務遷移到其他節點上;這裏需要提醒一點的是,只要集群上的服務副本滿足我們指定的replicas的數量,即便故障的節點恢復了,它是不會把服務遷移回來的;
[root@docker-node01 ~]# docker service ps web ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 1rt0e7u4senz web.1 docker-registry.io/test/nginx:latest docker-node02 Running Running 15 minutes ago 31ll0zu7udld web.2 docker-registry.io/test/nginx:latest docker-node02 Running Running 15 minutes ago t3gjvsgtpuql web.3 docker-registry.io/test/nginx:latest docker-node02 Running Running 6 minutes ago l9jtbswl2x22 \_ web.3 docker-registry.io/test/nginx:latest docker-node03 Shutdown Shutdown 23 seconds ago [root@docker-node01 ~]#
提示:我們在管理節點查看服務列表,可以看到它遷移服務就是把對應節點上的副本停掉,然後在其他節點創建一個新的副本;
服務伸縮
[root@docker-node01 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS i0j6wvvtfe13 myweb replicated 1/1 docker-registry.io/test/nginx:latest mbiap412jyug web replicated 3/3 docker-registry.io/test/nginx:latest [root@docker-node01 ~]# docker service scale myweb=3 web=5 myweb scaled to 3 web scaled to 5 overall progress: 3 out of 3 tasks 1/3: running [==================================================>] 2/3: running [==================================================>] 3/3: running [==================================================>] verify: Service converged overall progress: 5 out of 5 tasks 1/5: running [==================================================>] 2/5: running [==================================================>] 3/5: running [==================================================>] 4/5: running [==================================================>] 5/5: running [==================================================>] verify: Service converged [root@docker-node01 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS i0j6wvvtfe13 myweb replicated 3/3 docker-registry.io/test/nginx:latest mbiap412jyug web replicated 5/5 docker-registry.io/test/nginx:latest [root@docker-node01 ~]# docker service ps myweb web ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS j7w490h2lons myweb.1 docker-registry.io/test/nginx:latest docker-node02 Running Running 12 minutes ago 1rt0e7u4senz web.1 docker-registry.io/test/nginx:latest docker-node02 Running Running 21 minutes ago 99y8towew77e myweb.1 docker-registry.io/test/nginx:latest docker-node03 Shutdown Shutdown 5 minutes ago en5rk0jf09wu myweb.2 docker-registry.io/test/nginx:latest docker-node03 Running Running 31 seconds ago 31ll0zu7udld web.2 docker-registry.io/test/nginx:latest docker-node02 Running Running 21 minutes ago h1hze7h819ca myweb.3 docker-registry.io/test/nginx:latest docker-node03 Running Running 30 seconds ago t3gjvsgtpuql web.3 docker-registry.io/test/nginx:latest docker-node02 Running Running 12 minutes ago l9jtbswl2x22 \_ web.3 docker-registry.io/test/nginx:latest docker-node03 Shutdown Shutdown 5 minutes ago od3ti2ixpsgc web.4 docker-registry.io/test/nginx:latest docker-node03 Running Running 31 seconds ago n1vur8wbmkgz web.5 docker-registry.io/test/nginx:latest docker-node03 Running Running 31 seconds ago [root@docker-node01 ~]#
提示:docker service scale 命令用來指定服務的副本數量,從而實現動態伸縮;
服務暴露
[root@docker-node01 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS i0j6wvvtfe13 myweb replicated 3/3 docker-registry.io/test/nginx:latest mbiap412jyug web replicated 5/5 docker-registry.io/test/nginx:latest [root@docker-node01 ~]# docker service update --publish-add 80:80 myweb myweb overall progress: 3 out of 3 tasks 1/3: running [==================================================>] 2/3: running [==================================================>] 3/3: running [==================================================>] verify: Service converged [root@docker-node01 ~]#
提示:docker swarm集群中的服務暴露和docker裏面的端口暴露原理是一樣的,都是通過iptables 規則表或LVS規則實現的;
提示:我們可以在管理節點上看到對應80端口已經處於監聽狀態,並且在iptables規則表中多了一項訪問本機80端口都DNAT到172.18.0.2的80上了;其實不光是在管理節點,在work節點上相應的iptables規則也都發生了變化;如下
提示:從上面的規則來看,我們訪問節點地址的80端口,都會DNAT到172.18.0.2的80;
提示:從上面是显示結果看,我們不難得知在docker-node02運行myweb容器的內部地址是10.0.0.7,那為什麼我們訪問172.18.0.2是能夠訪問到容器內部的服務呢?
測試:我們在docker-node02追蹤查看nginx容器的訪問日誌,看看到容器的IP地址是那個?
[root@docker-node02 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2134e1b2c689 docker-registry.io/test/nginx:latest "/docker-entrypoint.…" 24 minutes ago Up 24 minutes 80/tcp nginx.1.ych7y3ugxp6o592pbz5k2i412 [root@docker-node02 ~]# docker logs -f nginx.1.ych7y3ugxp6o592pbz5k2i412 /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/ /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh 10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf 10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh /docker-entrypoint.sh: Configuration complete; ready for start up 10.0.0.3 - - [21/Jun/2020:02:37:11 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-" 172.18.0.1 - - [21/Jun/2020:02:38:35 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-" 10.0.0.2 - - [21/Jun/2020:02:53:32 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-" 10.0.0.2 - - [21/Jun/2020:02:53:58 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-" ^C [root@docker-node02 ~]#
提示:我們在管理節點上訪問172.18.0.2在node2節點上看到的日誌是10.0.0.2的ip訪問到nginx服務;這是為什麼呢?其實原因就是在每個節點上都有一個ingress-sbox容器,該容器的地址就10.0.0.2;不同節點上的ingress-sbox的地址都不同,所以我們訪問不同節點地址,在nginx上看到地址也就不同;如下圖所示
提示:訪問不同的節點地址,在nginx日誌上記錄的IP各不相同
提示:從上面的截圖可以了解到每個節點的ingress-sbox容器的地址各不相同,但他們都把網關指向10.0.0.1,這意味着各個節點容器通信就可以基於這個網關來進行,從而實現了swarm集群上的容器間通信能夠基於ingress網絡進行;現在還有一個問題就是172.18.0.0/18的網絡是怎麼和10.0.0.0/24的網絡通信的?
提示:從上面的截圖可以看到,在管理節點上有兩個網絡名稱空間,一個id為0,而id為0的網絡名稱空間中有veth0和vxlan0這兩個網卡;而veth0和vxlan0都是橋接到br0上的,br0的地址就是10.0.0.1/24;vxlan的vlan id為4096;結合上面nginx的日誌,不難想到
我們訪問管理節點上的80,通過iptables規則把流量轉發給docker-gwbridge網絡上;現在我們還不清楚docker-gwbridge網絡上那個名稱空間的網絡,但是我們清楚知道在容器內部有兩張網卡,一張是eth0,一張是eth1,而eth1就是橋接到docker-gwbridge網絡上,這也就意味着容docker-gwbridge網絡的名稱空間和容器內部的eth1網絡名稱空間相同;
提示:從上面的截圖看,1-u5mwgfq7rb這個名稱的網絡名稱空間有三張網卡,分別是eth0,eth1和vxlan0,它們都是橋接在br0這個網卡上;而上面管理節點也在1-u5mwgfq7rb這個網絡名稱空間,並且它們中的vxlan0的vlan id都是4096,這意味着管理節點上的vxlan0可以同node2上的vxlan0直接通信(相同網絡名稱空間中的相同VLAN id是可以直接通信的),而vxlan0又是直接橋接到br0這塊網卡,所以我們在nginx日誌中能夠看到ingress-sbox容器的地址在訪問nginx;這其中的原因是ingress-sbox的網關就是br0;其實node3也是相同邏輯,不同節點上的容器間通信都是走vxlan0,與外部通信走eth1—->然後通過SNAT走docker-gwbridge—->物理網卡出去;
提示:一個容器上有兩個網絡,一個是eth0 ingress網絡,一個是eth1屬於docker-gwbridge網絡,兩者都屬於同一容器中的網絡名稱空間,所以我們訪問172.18.0.2就會通過ingress-sbox容器把源地址更改為docker-gwbridge上的ingress-sbox的地址,從而我們在看nginx日誌,就會看到10.0.0.2的地址;ingress-sbox容器作用我們可以理解為做SNAT的作用;
測試:訪問管理節點的80服務看看是否能夠訪問到nginx提供的頁面呢?
[root@docker-node02 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b829991d6966 docker-registry.io/test/nginx:latest "/docker-entrypoint.…" About an hour ago Up About an hour 80/tcp myweb.1.ilhkslrlnreyo6xx5j2h9isjb 8c2965fbdc27 docker-registry.io/test/nginx:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp web.2.pthe8da2n45i06oee4n7h4krd b019d663e48e docker-registry.io/test/nginx:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp web.3.w26gqpoyysgplm7qwhjbgisiv a7c1afd76f1f docker-registry.io/test/nginx:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp web.1.ho0d7u3wensl0kah0ioz1lpk5 [root@docker-node02 ~]# docker exec -it myweb.1.ilhkslrlnreyo6xx5j2h9isjb bash root@b829991d6966:/# cd /usr/share/nginx/html/ root@b829991d6966:/usr/share/nginx/html# ls 50x.html index.html root@b829991d6966:/usr/share/nginx/html# echo "this is docker-node02 index page" >index.html root@b829991d6966:/usr/share/nginx/html# cat index.html this is docker-node02 index page root@b829991d6966:/usr/share/nginx/html#
提示:以上是在docker-node02節點上對運行的nginx容器的主頁進行了修改,接下我們訪問管理節點的80端口,看看是否能夠訪問得到work節點上的容器,它們會有什麼效果?是輪詢?還是一直訪問一個容器?
提示:可以看到我們訪問管理節點的80端口,會輪詢的訪問到work節點上的容器;用瀏覽器測試可能存在緩存的問題,我們可以用curl命令測試比較準確;如下
[root@docker-node03 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f43fdb9ec7fc docker-registry.io/test/nginx:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp myweb.3.pgdjutofb5thlk02aj7387oj0 4470785f3d00 docker-registry.io/test/nginx:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp myweb.2.uwxbe182qzq00qgfc7odcmx87 7493dcac95ba docker-registry.io/test/nginx:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp web.4.rix50fhlmg6m9txw9urk66gvw 118880d300f4 docker-registry.io/test/nginx:latest "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp web.5.vo7c7vjgpf92b0ryelb7eque0 [root@docker-node03 ~]# docker exec -it myweb.2.uwxbe182qzq00qgfc7odcmx87 bash root@4470785f3d00:/# cd /usr/share/nginx/html/ root@4470785f3d00:/usr/share/nginx/html# echo "this is myweb.2 index page" > index.html root@4470785f3d00:/usr/share/nginx/html# cat index.html this is myweb.2 index page root@4470785f3d00:/usr/share/nginx/html# exit exit [root@docker-node03 ~]# docker exec -it myweb.3.pgdjutofb5thlk02aj7387oj0 bash root@f43fdb9ec7fc:/# cd /usr/share/nginx/html/ root@f43fdb9ec7fc:/usr/share/nginx/html# echo "this is myweb.3 index page" >index.html root@f43fdb9ec7fc:/usr/share/nginx/html# cat index.html this is myweb.3 index page root@f43fdb9ec7fc:/usr/share/nginx/html# exit exit [root@docker-node03 ~]#
提示:為了訪問方便看得出效果,我們把myweb.2和myweb.3的主頁都更改了內容
[root@docker-node01 ~]# for i in {1..10} ; do curl 192.168.0.41; done
this is myweb.3 index page
this is docker-node02 index page
this is myweb.2 index page
this is myweb.3 index page
this is docker-node02 index page
this is myweb.2 index page
this is myweb.3 index page
this is docker-node02 index page
this is myweb.2 index page
this is myweb.3 index page
[root@docker-node01 ~]#
提示:通過上面的測試,我們在使用–publish-add 暴露服務時,就相當於在管理節點創建了一個load balance;
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
Boosting 算法的特點在於:將表現一般的弱分類器通過組合變成更好的模型。代表自然就是我們的隨即森林了。
GBDT和Adaboost是boost算法中比較常見的兩種,這裏主要講解Adaboost。
Adaboost算法的核心就是兩個權重。對於數據有一個權重,權重大的數據計算的損失就大;然後對於每一個弱分類器有一個權重,這個權重就是每一個弱分類器最終投票的比重。
【先給出Adaboost關鍵的公式】:
\(\alpha_1=\frac{1}{2}ln(\frac{1-\epsilon_1}{\epsilon_1})\) 分類器的投票權重
\(W_i=W_ie^{-\alpha_i y_i \hat{h}(x_i)}\) 更新樣本的權重
【隨即森林中最終投票每一個弱分類器的比重相同】
大概流程就是,現在有一個數據集,然後每個數據的比重都相同,然後訓練了好幾個不同的弱分類器。
算法的流程:
這裏給一個具體計算的例子:
假設這裡有10個數據:
加號和減號分別代表不同的類別。然後每個類別有5個樣本。
下面會給出3個弱分類器:
這三個分類器分別是\(h_1(x),h_2(x),h_3(x)\)
圖中畫圈的數據就是分類錯誤的數據。可以發現每個弱分類器都分錯了3個。下面開始Adaboost的算法。
先計算三個弱分類器的錯誤率,因為一開始每個樣本的權重都是0.1,每個分類器又都錯了3個樣本,所以錯誤率都是0.3。這裏就隨機選取第一個分類器作為錯誤率最低的那個好了。
我們這裏通過第一個【某種算法】計算第一個弱分類器在最終的投票權重:
\(\alpha_1=\frac{1}{2}ln(\frac{1-\epsilon_1}{\epsilon_1})=0.5*ln(\frac{0.7}{0.3})=0.4236\)
然後通過這個\(\alpha_1=0.4236\)來更新每一個樣本的權重。這也就是上面提到的第二個【某種算法】:
\(W(i)=W(i)*e^{-\alpha y_i \hat {h}(x_i)}\)
這啥意思的,現在假設第一個樣本+1,這個樣本的權重是0.1(更新前),然後這個樣本在第一個分類器中是非類正確的,所以\(y_i \hat{h}(x_i)=1\),所以這個樣本更新后的權重就是\(0.1e^{-0.4236}=0.0655\)
當然,對於+3這個樣本,第一個分類器就分類錯誤,所以\(y_i \hat{h}(x_i)=-1\),所以呢這個樣本更新后的權重就是:\(0.1e^{0.4236}=0.1527\)
下面經過第一個分類器之後的樣本的權重:
然後再計算每一個分類器的基於更新之後樣本權重的錯誤率:
這一次選的是第二個分類器,然後計算它的\(\alpha_2\),然後再更新每一個樣本的權重值:
然後是再尋找錯誤率最低的分類器:
到這一步的時候,我們已經有了\(\alpha_1,\alpha_2,\alpha_3\),所以我們的adaboost已經得到了所有分類器的投票權重,所以最終的模型投票公式就是:
喜歡的話請關注我們的微信公眾號~【你好世界煉丹師】。
微信搜索公眾號:你好世界煉丹師。期待您的關注。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※南投搬家公司費用需注意的眉眉角角,別等搬了再說!
※新北清潔公司,居家、辦公、裝潢細清專業服務
※教你寫出一流的銷售文案?
看完官方文檔,閱讀了一些 Newtonsoft 源碼,對它有了新的認識,先總結 六個超經典又實用的特性,同大家一起分享,廢話不多說,快來一起看看吧~~~
如果你直接使用 JsonConvert.SerializeObject的話,默認情況下所有的json是擠壓在一塊的,特別不方便閱讀,如下所示:
static void Main(string[] args)
{
var reportModel = new ReportModel()
{
ProductName = "法式小眾設計感長裙氣質顯瘦純白色仙女連衣裙",
TotalPayment = 100,
TotalCustomerCount = 2,
TotalProductCount = 333
};
var json = JsonConvert.SerializeObject(reportModel);
System.Console.WriteLine(json);
}
}
public class ReportModel
{
public string ProductName { get; set; }
public int TotalCustomerCount { get; set; }
public decimal TotalPayment { get; set; }
public int TotalProductCount { get; set; }
}
那怎麼辦呢? JsonConvert中提供了一個 Formatting.Indented 用來格式化json,這樣在 debug 的過程中就非常友好,改造如下:
如果你寫過給 App 提供數據的後端服務,我相信你對手機流量這個詞特別敏感,往往一個 Model 上有十幾個字段,但需要傳給 App 可能就 三四個字段,這就造成了巨大的流量浪費,如下圖:
static void Main(string[] args)
{
var reportModel = new ReportModel()
{
ProductName = "法式小眾設計感長裙氣質顯瘦純白色仙女連衣裙",
TotalPayment = 100
};
var json = JsonConvert.SerializeObject(reportModel, Formatting.Indented);
System.Console.WriteLine(json);
}
從圖中可以看到,TotalCustomerCount 和 TotalProductCount 這兩個字段就沒必要了,Netnewsoft 中提供了 DefaultValueHandling.Ignore 剔除默認值的枚舉,太實用了,改造如下:
var json = JsonConvert.SerializeObject(reportModel, Formatting.Indented,
new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Ignore
});
每一套編程語言都有各自偏好的命名法,比如 js 中都喜歡採用 駝峰命名法,在 mysql 中我見過最多的 蛇形命名法,而我們在 C# 中序列化的屬性一般都是大寫字母開頭,比如你看到的 特性二 中的字段,那這裏就存在問題了,有沒有辦法兼容一下,給 js 就用 駝峰,給 mysql 就用 蛇形,這樣顯得對別人友好一些,不是嘛,接下來看看怎麼改造。
var json = JsonConvert.SerializeObject(reportModel, Formatting.Indented,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
var json = JsonConvert.SerializeObject(reportModel, Formatting.Indented,
new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
});
如果你和第三方系統進行過對接開發,通常都會遇到這個問題,就拿 OpenTaobao 來說,我的Model總不能按照它文檔這樣定義吧,而且字段名稱也不可能做到完全一致,如下圖:
所以這裏面必然要存在一個 Mapping 的過程,這就可以用 JsonProperty -> propertyName 幫你搞定,為了方便演示,我還是用 reportModel 吧。
static void Main(string[] args)
{
var json = "{'title':'法式小眾設計感長裙氣質顯瘦純白色仙女連衣裙','customercount':1000,'totalpayment':100.0,'productcount':10000}";
var reportModel = JsonConvert.DeserializeObject<ReportModel>(json);
}
public class ReportModel
{
[JsonProperty("title")] public string ProductName { get; set; }
[JsonProperty("customercount")] public int TotalCustomerCount { get; set; }
[JsonProperty("totalpayment")] public decimal TotalPayment { get; set; }
[JsonProperty("productcount")] public int TotalProductCount { get; set; }
}
可能有些朋友對這兩個概念不是特別了解,這裏我僅显示 Model 中的 ProductName 為例講解一下:
MemberSerialization.OptOut 配合 JsonIgnore
static void Main(string[] args)
{
var reportModel = new ReportModel()
{
ProductName = "法式小眾設計感長裙氣質顯瘦純白色仙女連衣裙",
TotalPayment = 100
};
var json = JsonConvert.SerializeObject(reportModel, Formatting.Indented);
System.Console.WriteLine(json);
}
[JsonObject(MemberSerialization.OptOut)]
public class ReportModel
{
public string ProductName { get; set; }
[JsonIgnore] public int TotalCustomerCount { get; set; }
[JsonIgnore] public decimal TotalPayment { get; set; }
[JsonIgnore] public int TotalProductCount { get; set; }
}
MemberSerialization.OptIn 配合 JsonProperty
[JsonObject(MemberSerialization.OptIn)]
public class ReportModel
{
[JsonProperty] public string ProductName { get; set; }
public int TotalCustomerCount { get; set; }
public decimal TotalPayment { get; set; }
public int TotalProductCount { get; set; }
}
這個特性當初打破了我對 Newtonsoft 的認知觀,不知道您呢? 通常我們都會認為 一個 json 對應一個 model,一個 model 對應一個 json,居然還可以多個 json 對應一個 model 的情況,這就有意思了,場景大家可以自己想一想哈,這裏使用 PopulateObject 方法就可以輕鬆幫你搞定,接下來看看怎麼寫這個代碼:
static void Main(string[] args)
{
var json1 = "{'ProductName':'法式小眾設計感長裙氣質顯瘦純白色仙女連衣裙'}";
var json2 = "{'TotalCustomerCount':1000,'TotalPayment':100.0,'TotalProductCount':10000}";
var reportModel = new ReportModel();
JsonConvert.PopulateObject(json1, reportModel);
JsonConvert.PopulateObject(json2, reportModel);
}
是不是有點意思
為了怕影響閱讀體驗,這一篇就先總結六個供大家欣賞,Newtonsoft 這玩意確實非常強大,太多的東西需要去挖掘,希望本篇對你有幫助,謝謝。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※新北清潔公司,居家、辦公、裝潢細清專業服務
※別再煩惱如何寫文案,掌握八大原則!
※教你寫出一流的銷售文案?
※超省錢租車方案
※FB行銷專家,教你從零開始的技巧
之前的兩篇文章 Nginx 變量介紹以及利用 Nginx 變量做防盜鏈 講的是 Nginx 有哪些變量以及一個常見的應用。那麼如此靈活的 Nginx 怎麼能不支持自定義變量呢,今天的文章就來說一下自定義變量的幾個模塊以及 Nginx 的 keepalive 特性。
ngx_http_map_module 默認編譯進 Nginx,通過 --without-http_map_module 禁用Syntax: map string $variable { ... }
Default: —
Context: http
Syntax: map_hash_bucket_size size;
Default: map_hash_bucket_size 32|64|128;
Context: http
Syntax: map_hash_max_size size;
Default: map_hash_max_size 2048;
Context: http
我們主要看一下 map string $variable { ... } 這個指令。所謂類似 switch case 的語法是指,string 的值可以有多個,可以根據 string 值的不同,來給 $variable 賦不同的值。
大家看到上面這些規則可能都有些暈,廢話不多說,直接來看一個實戰配置文件就懂了。
這裏我們有一個配置文件,在這個文件裏面我們定義了兩個 map 塊,分別配置了兩個變量,$name 和 $mobile,$name 中包含 hostnames 指令。
map $http_host $name {
hostnames;
default 0;
~map\.ziyang\w+\.org.cn 1;
*.ziyang.org.cn 2;
map.ziyang.com 3;
map.ziyang.* 4;
}
map $http_user_agent $mobile {
default 0;
"~Opera Mini" 1;
}
server {
listen 10001;
default_type text/plain;
location /{
return 200 '$name:$mobile\n';
}
}
下面看一下實際的請求:
test_nginx curl -H "Host: map.ziyang.org.cn" 127.0.0.1:10001
2:0
為什麼會返回 2:0 呢?我們來看一下匹配順序。
map.ziyang.org.cn 有三個規則可以生效,分別是:
而泛域名是優先於正則表達式的,* 在前的泛域名優先於在後面的泛域名,因此最終匹配到的就是:
而第二個變量 $mobile 自然走的是 default 規則,不用多說。
這就是 map 模塊的作用,大家可以多嘗試一下。
下面再來看一個與 map 模塊有點類似的 split_clients 模塊,這個模塊也是通過生成新的變量來完成 AB 測試功能的,它可以按照變量的值,按照百分比的方式,生成新的變量。
ngx_http_split_clients_module,默認編譯進 Nginx,通過 --without-http_split_clients_module 禁用Syntax: split_clients string $variable { ... }
Default: —
Context: http
split_clients 的指令與 map 是非常相似的,可以看一下前面的介紹,這裏不再贅述了。
下面這個配置,來看下有沒有啥問題:
split_clients "${http_testcli}" $variant {
0.51% .one;
20.0% .two;
50.5% .three;
40% .four;
* "";
}
細心的同學可能已經發現了,所有的百分比相加已經超過了 100%,所以 Nginx 直接會拋出一個錯誤,禁止執行。
test_nginx ./sbin/nginx -s reload
nginx: [emerg] percent total is greater than 100% in /Users/mtdp/myproject/nginx/test_nginx/conf/example/17.map.conf:31
然後將 40% .four; 這一行給屏蔽掉再試試看:
test_nginx curl -H "testcli: split_clients.ziyang.com" --resolve "split_clients.ziyang.com:80:127.0.0.1" http://split_clients.ziyang.com
ABtestfile.three
正常執行。
geo 模塊與前面兩個模塊也很相似,不同之處在於,這個模塊是基於 IP 地址或者子網掩碼這樣的變量值來生成新的變量的。
功能:根據 IP 地址創建新變量
模塊: ngx_http_geo_module,默認編譯進 Nginx,通過 --without-http_geo_module 禁用
指令
Syntax: geo [$address] $variable { ... }
Default: —
Context: http
如果 geo 指令后不輸入 $address,那麼默認使用 $remote_addr 變量作為 IP 地址
{} 內的指令匹配:優先最長匹配
通過 IP 地址及子網掩碼的方式,定義 IP 範圍,當 IP 地址在範圍內時新變量使用其後的參數值
default 指定了當以上範圍都未匹配上時,新變量的默認值
通過 proxy 指令指定可信地址(參考 realip 模塊),此時 remote_addr 的值為 X-Forwarded-For 頭部值中最後一個 IP 地址
proxy_recursive 允許循環地址搜索
include,優化可讀性
delete 刪除指定網絡
geo $country {
default ZZ;
#include conf/geo.conf;
#proxy 172.18.144.211;
127.0.0.0/24 US;
127.0.0.1/32 RU;
10.1.0.0/16 RU;
192.168.1.0/24 UK;
}
問題:以下命令執行時,變量 country 的值各為多少?(proxy 實際上為客戶端地址,這裏設置為本機的局域網地址即可,我這裡是 172.18.144.211)
curl -H ‘X-Forwarded-For: 10.1.0.0,127.0.0.2’ geo.ziyang.com
curl -H ‘X-Forwarded-For: 10.1.0.0,127.0.0.1’ geo.ziyang.com
curl -H ‘X-Forwarded-For: 10.1.0.0,127.0.0.1,1.2.3.4’ geo.ziyang.com
結果如下:
```shell
test_nginx curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.2' geo.ziyang.com
US
test_nginx curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.1' geo.ziyang.com
RU
test_nginx curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.1,1.2.3.4' geo.ziyang.com
ZZ
這裏可以看出來,匹配規則實際上是遵循最長匹配的規則的。
geoip 模塊可以根據 IP 地址生成對應的地址變量,用法與前面的也都類似,Nginx 是基於 MaxMind 數據庫來生成對應的地址的。
ngx_http_geoip_module,默認未編譯進 Nginx,通過 --with-http_geoip_module 禁用使用這個模塊是需要安裝 MaxMind 庫的,安裝步驟如下:
--with-http_geoip_module 參數指令
Syntax: geoip_country file; # 指定國家類的地址文件
Default: —
Context: http
Syntax: geoip_proxy address | CIDR;
Default: —
Context: http
變量
指令
Syntax: geoip_city file;
Default: —
Context: http
變量
前面說的都是 Nginx 的變量相關的內容,其實 Nginx 還有一個很具有特色的模塊,那就是 keepalive 模塊,由於內容不是很多,所以我就直接寫到這篇文章裏面了,單寫一篇顯得內容不夠哈。
這裏指的是 HTTP 的 keepalive,TCP 也有 keepalive,後面會說。
而且是對客戶端的 keepalive,不是對上游服務器的。
功能:多個 HTTP 請求通過復用 TCP 連接,可以實現以下功能:
Connection 頭部
Keepalive 頭部:timeout=n,單位是秒,表示連接至少保持 n 秒
對客戶端行為控制的指令:
Syntax: keepalive_disable none | browser ...;
Default: keepalive_disable msie6;
Context: http, server, location
Syntax: keepalive_requests number;
Default: keepalive_requests 100;
Context: http, server, location
Syntax: keepalive_timeout timeout [header_timeout];
Default: keepalive_timeout 75s;
Context: http, server, location
keepalive_disable 設置為 none 表示對所有瀏覽器啟用 keepalive,msie6 表示在老版本 MSIE 上禁用 keepalivekeepalive_requests 設置允許保持 keepalive 的請求的數量keepalive_timeout 表示超時時間好了,關於 Nginx 的模塊介紹就已經全部介紹完了,有興趣的同學可以去翻我前面的系列文章。當然還有一部分重要的內容還沒有介紹,那就是關於 Nginx 的反向代理和負載均衡部分,這塊咱們單獨抽出來說,別著急,馬上乾貨就出來。
本文首發於我的個人博客:iziyang.github.io,所有配置文件我已經放在了 Nginx 配置文件,大家可以自取。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※新北清潔公司,居家、辦公、裝潢細清專業服務
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※超省錢租車方案
※教你寫出一流的銷售文案?
※網頁設計最專業,超強功能平台可客製化
前言:實際項目中經常遇到消息消費失敗了,要進行消息的重發。比如支付消息消費失敗后,要分不同時間段進行N次的消息重發提醒。
特別注意代碼與配置文件中的註釋,各個使用說明都已經詳細寫在配置文件中
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cloudstream</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ①關鍵配置:引入stream-rabbit 依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- ②關鍵配置:由於stream是基於spring-cloud的,所以這裏要引入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
注意各個配置的縮進格式,別搞錯了
server:
port: 8081
spring:
application:
name: stream-demo
#rabbitmq連接配置
rabbitmq:
host: 127.0.0.1
port: 5672
username: admin
password: 123456
cloud:
stream:
bindings:
#消息生產者,與DelayDemoTopic接口中的DELAY_DEMO_PRODUCER變量值一致
delay-demo-producer:
#①定義交換機名
destination: demo-delay-queue
#消息消費者,與DelayDemoTopic接口中的DELAY_DEMO_CONSUMER變量值一致
delay-demo-consumer:
#定義交換機名,與①一致,就可以使發送和消費都指向一個隊列
destination: demo-delay-queue
#分組,這個配置可以開啟消息持久化、可以解決在集群環境下重複消費的問題。
#比如A、B兩台服務器集群,如果沒有這個配置,則A、B都能收到同樣的消息,如果有該配置則只有其中一台會收到消息
group: delay-consumer-group
consumer:
#最大重試次數,默認為3。不使用默認的,這裏定義為1,由我們程序控制發送時間和次數
maxAttempts: 1
rabbit:
bindings:
#消息生產者,與DelayDemoTopic接口中的DELAY_DEMO_PRODUCER變量值一致
delay-demo-producer:
producer:
#②申明為延遲隊列
delayedExchange: true
#消息消費者,與DelayDemoTopic接口中的DELAY_DEMO_CONSUMER變量值一致
delay-demo-consumer:
consumer:
#申明為延遲隊列,與②的配置的成對出現的
delayedExchange: true
#開啟死信隊列
autoBindDlq: true
#死信隊列中消息的存活時間
dlqTtl: 60000
/**
* 定義延遲消息通道
*/
public interface DelayDemoTopic {
/**
* 生產者,與yml文件配置對應
*/
String DELAY_DEMO_PRODUCER = "delay-demo-producer";
/**
* 消費者,與yml文件配置對應
*/
String DELAY_DEMO_CONSUMER = "delay-demo-consumer";
/**
* 定義消息消費者,在@StreamListener監聽消息的時候用到
* @return
*/
@Input(DELAY_DEMO_CONSUMER)
SubscribableChannel delayDemoConsumer();
/**
* 定義消息發送者,在發送消息的時候用到
* @return
*/
@Output(DELAY_DEMO_PRODUCER)
MessageChannel delayDemoProducer();
}
/**
* 配置消息的binding
*
*/
@EnableBinding(value = {DelayDemoTopic.class})
@Component
public class MessageConfig {
}
/**
* 發送消息
*/
@RestController
public class SendMessageController {
@Autowired
DelayDemoTopic delayDemoTopic;
@GetMapping("send")
public Boolean sendMessage(BigDecimal money) throws JsonProcessingException {
Message<BigDecimal> message = MessageBuilder.withPayload(money)
//設置消息的延遲時間,首次發送,不設置延遲時間,直接發送
.setHeader(DelayConstant.X_DELAY_HEADER,0)
//設置消息已經重試的次數,首次發送,設置為0
.setHeader(DelayConstant.X_RETRIES_HEADER,0)
.build();
return delayDemoTopic.delayDemoProducer().send(message);
}
}
@Component
@Slf4j
public class DelayDemoTopicListener {
@Autowired
DelayDemoTopic delayDemoTopic;
/**
* 監聽延遲消息通道中的消息
* @param message
*/
@StreamListener(value = DelayDemoTopic.DELAY_DEMO_CONSUMER)
public void listener(Message<BigDecimal> message) {
//獲取重試次數
int retries = (int)message.getHeaders().get(DelayConstant.X_RETRIES_HEADER);
//獲取消息內容
BigDecimal money = message.getPayload();
try {
String now = DateUtils.formatDate(new Date(),"yyyy-MM-dd HH:mm:ss");
//模擬:如果金額大於200,則消息無法消費成功;金額如果大於100,則重試3次;如果金額小於100,直接消費成功
if (money.compareTo(new BigDecimal(200)) == 1){
throw new RuntimeException(now+":金額超出200,無法交易。");
}else if (money.compareTo(new BigDecimal(100)) == 1 && retries <= 3) {
if (retries == 0) {
throw new RuntimeException(now+":金額超出100,消費失敗,將進入重試。");
}else {
throw new RuntimeException(now+":金額超出100,當前第" + retries + "次重試。");
}
}else {
log.info("消息消費成功!");
}
}catch (Exception e) {
log.error(e.getMessage());
if (retries < DelayConstant.X_RETRIES_TOTAL){
//將消息重新塞入隊列
MessageBuilder<BigDecimal> messageBuilder = MessageBuilder.fromMessage(message)
//設置消息的延遲時間
.setHeader(DelayConstant.X_DELAY_HEADER,DelayConstant.ruleMap.get(retries + 1))
//設置消息已經重試的次數
.setHeader(DelayConstant.X_RETRIES_HEADER,retries + 1);
Message<BigDecimal> reMessage = messageBuilder.build();
//將消息重新發送到延遲隊列中
delayDemoTopic.delayDemoProducer().send(reMessage);
}else {
//超過重試次數,做相關處理(比如保存數據庫等操作),如果拋出異常,則會自動進入死信隊列
throw new RuntimeException("超過最大重試次數:" + DelayConstant.X_RETRIES_TOTAL);
}
}
}
}
目前寫在一個常量類里,實際項目中,通常會配置在配置文件中
public class DelayConstant {
/**
* 定義當前重試次數
*/
public static final String X_RETRIES_HEADER = "x-retries";
/**
* 定義延遲消息,固定值,該配置放到消息的header中,會開啟延遲隊列
*/
public static final String X_DELAY_HEADER = "x-delay";
/**
* 定義最多重試次數
*/
public static final Integer X_RETRIES_TOTAL = 5;
/**
* 定義重試規則,毫秒為單位
*/
public static final Map<Integer,Integer> ruleMap = new HashMap(){{
put(1,1000);
put(2,2000);
put(3,3000);
put(4,4000);
put(5,5000);
}};
}
經過以上配置和實現就可完成模擬的重發場景。
http://127.0.0.1:8081/send?money=10,可以看到控制台中輸出:消息消費成功!
http://127.0.0.1:8081/send?money=110,可以看到控制台中輸出:2020-06-20 10:59:42:金額超出100,消費失敗,將進入重試。
2020-06-20 10:59:43:金額超出100,當前第1次重試。
2020-06-20 10:59:45:金額超出100,當前第2次重試。
2020-06-20 10:59:48:金額超出100,當前第3次重試。
消息消費成功!
http://127.0.0.1:8081/send?money=110,可以看到控制台中輸出: 由於本文用到了延遲隊列,需要在rabbitMQ中安裝延遲插件,具體安裝方式,可以查看:延遲隊列安裝參考
以上示例都可以通過我的GitHub獲取完整的代碼.
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※教你寫出一流的銷售文案?
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※回頭車貨運收費標準
※別再煩惱如何寫文案,掌握八大原則!
※超省錢租車方案
※產品缺大量曝光嗎?你需要的是一流包裝設計!