當前位置:首頁 > 計算機
  • 小白必讀:計算機網絡入門

    很久很久之前,你不與任何其他電腦相連接,孤苦伶仃。 直到有一天,你希望與另一台電腦 B 建立通信,於是你們各開了一個網口,用一根網線連接了起來。 用一根網線連接起來怎麼就能"通信"了呢?我可以給你講 IO、講中斷、講緩衝區,但這不是研究網絡時該關心的問題。 第一層 首先,你要給所有的連接到交換機的設備,都起個名字。原來你們叫 ABCD,但現在需要一個更專業的,全局唯一的名字作為標識,你把這個更高端的名字稱為 MAC 地址。 這樣,A 在發送數據包給 B 時,只要在頭部拼接一個這樣結構的數據,就可以了。 B 在收到數據包後,根據頭部的目標 MAC 地址信息,判斷這個數據包的確是發給自己的,於是便收下。 第二層 交換機內部維護一張 MAC 地址表,記錄着每一個 MAC 地址的設備,連接在其哪一個端口上。 MAC 地址 端口 bb-bb-bb-bb-bb-bb 1 cc-cc-cc-cc-cc-cc 3 aa-aa-aa-aa-aa-aa 4 dd-dd-dd-dd-dd-dd 5 你給這個通過這樣傳輸方式而組成的小範圍的網絡,叫做以太網。 假如在 MAC 地址表為空時,你給 B 發送瞭如下數據 由於這個包從端口 4 進入的交換機,所以此時交換機就可以在 MAC地址表記錄第一條數據: 交換機看目標 MAC 地址(bb-bb-bb-bb-bb-bb)在地址表中並沒有映射關係,於是將此包發給了所有端口,也即發給了所有機器。 MAC:bb-bb-bb-bb-bb-bb 端口:1 但是你要注意,上面那根紅色的線,最終在 MAC 地址表中可不是一條記錄呀,而是要把 EFGH 這四台機器與該端口(端口6)的映射全部記錄在表中。 左邊的交換機 右邊的交換機 MAC 地址 端口 bb-bb-bb-bb-bb-bb 1 cc-cc-cc-cc-cc-cc 1 aa-aa-aa-aa-aa-aa 1 dd-dd-dd-dd-dd-dd 1 ee-ee-ee-ee-ee-ee 2 ff-ff-ff-ff-ff-ff 3 gg-gg-gg-gg-gg-gg 4 hh-hh-hh-hh-hh-hh 6 但很遺憾,人是貪婪的動物,很快,電腦的數量就發展到幾千、幾萬、幾十萬。 交換機已經無法記錄如此龐大的映射關係了。 那我可不可以讓那根紅色的網線,接入一個新的設備,這個設備就跟電腦一樣有自己獨立的 MAC 地址,而且同時還能幫我把數據包做一次轉發呢? 好了,現在交換機的 MAC 地址表中,只需要多出一條 MAC 地址 ABAB 與其端口的映射關係,就可以成功把數據包轉交給路由器了,這條搞定。 不難想到這樣一個點子,假如電腦 C 和 D 的 MAC 地址擁有共同的前綴,比如分別是 D 的 MAC 地址:FFFF-FFFF-DDDD 這樣是否可行呢?答案是否定的。 00-16-EA-AE-3C-40 那如果你希望像上面那樣根據目標MAC地址的特地格式來轉發(FFFF-FFFF-?開頭的),那你就需要要求某一子網下統統買一個遞四方香港製造的設備,或者你就需要要求遞四方香港在生產網絡設備燒錄 MAC 地址時,提前按照你規劃好的子網結構來定 MAC 地址,並且日後這個網絡的結構都不能輕易改變。 於是你發明了一個新的地址,給每一台機器一個 32 位的編號,如: 你覺得有些不清晰,於是把它分成四個部分,中間用點相連。 你還覺得不清晰,於是把它轉換成 10 進制。 最後你給了這個地址一個響亮的名字,IP 地址。現在每一台電腦,同時有自己的 MAC 地址,又有自己的 IP 地址,只不過 IP 地址是軟件層面上的,可以隨時修改,MAC 地址一般是無法修改的。 那交給路由器之後,路由器又是怎麼把數據包準確轉發給指定設備的呢? 我們先給上面的組網方式中的每一台設備,加上自己的 IP 地址 現在兩個設備之間傳輸,除了加上數據鏈路層的頭部之外,還要再增加一個網絡層的頭部。 A ~ 路由器這段的包如下: 路由器到 C 這段的包如下: 好了,上面説的兩種情況(A->B,A->C),相信細心的讀者應該會有不少疑問,下面我們一個個來展開。 A 給 C 發數據包,怎麼知道是否要通過路由器轉發呢? 如果源 IP 與目的 IP 處於一個子網,直接將包通過交換機發出去。 好,那現在只需要解決,什麼叫處於一個子網就好了。 這兩個是我們人為規定的,即我們想表示,對於 192.168.0.1 來説: 那對於計算機來説,怎麼表達這個意思呢?於是人們發明了子網掩碼的概念 這表示,將源 IP 與目的 IP 分別同這個子網掩碼進行與運算,相等則是在一個子網,不相等就是在不同子網,就這麼簡單。 A電腦:192.168.0.1 & 255.255.255.0 = 192.168.0.0 B電腦:192.168.0.2 & 255.255.255.0 = 192.168.0.0 C電腦:192.168.1.1 & 255.255.255.0 = 192.168.1.0 D電腦:192.168.1.2 & 255.255.255.0 = 192.168.1.0 A 如何知道,哪個設備是路由器? 上一步 A 通過是否與 C 在同一個子網內,判斷出自己應該把包發給路由器,那路由器的 IP 是多少呢? 對 A 來説,A 只能直接把包發給同處於一個子網下的某個 IP 上,所以發給路由器還是發給某個電腦,對 A 來説也不關心,只要這個設備有個 IP 地址就行。 路由器如何知道C在哪裏? 現在 A 要給 C 發數據包,已經可以成功發到路由器這裏了,最後一個問題就是,路由器怎麼知道,收到的這個數據包,該從自己的哪個端口出去,才能直接(或間接)地最終到達目的地 C 呢。 這個表就叫路由表。 不同於 MAC 地址表的是,路由表並不是一對一這種明確關係,我們下面看一個路由表的結構。 目的地址 子網掩碼 下一跳 端口 192.168.0.0 255.255.255.0 0 192.168.0.254 255.255.255.255 0 192.168.1.0 255.255.255.0 1 192.168.1.254 255.255.255.255 1 這就很好理解了,路由表就表示,192.168.0.xxx 這個子網下的,都轉發到 0 號端口,192.168.1.xxx 這個子網下的,都轉發到 1 號端口。下一跳列還沒有值,我們先不管 答案:arp 答案很簡單,在網絡層,我需要把 IP 地址對應的 MAC 地址找到,也就是通過某種方式,找到 192.168.0.2 對應的 MAC 地址 BBBB。 一開始的時候這個表是空的,電腦 A 為了知道電腦 B(192.168.0.2)的 MAC 地址,將會廣播一條 arp 請求,B 收到請求後,帶上自己的 MAC 地址給 A 一個響應。此時 A 便更新了自己的 arp 表。 好了,總結一下,到目前為止就幾條規則 電腦視角: 交換機視角: 路由器視角: 如果你嗅覺足夠敏鋭,你應該可以感受到下面這句話: 涉及到的三張表分別是 這三張表是怎麼來的 知道了以上這些,目前網絡上兩個節點是如何發送數據包的這個過程,就完全可以解釋通了! 也就是説找來找去,最終必須能映射到一個端口號,然後從這個端口號把數據包發出去。 目的地址 下一跳 端口 192.168.0.0/24 0 192.168.0.254/32 0 192.168.1.0/24 1 192.168.1.254/32 1 192.168.2.0/24 192.168.100.5 192.168.100.0/24 2 192.168.100.4/32 2 思考一分鐘... 詳細過程動畫描述: 詳細過程文字描述: 2. A 通過 ARP 找到 默認網關 192.168.0.254 的 MAC 地址。 5. 數據包來到了路由器 1,發現其目標 IP 地址是 192.168.2.2,查看其路由表,發現了下一跳的地址是 192.168.100.5 7. 此時路由器 2 收到了數據包,看到其目的地址是 192.168.2.2,查詢其路由表,匹配到端口號為 1,準備從 1 號口把數據包送出去。 9. 交換機 3 收到了數據包,發現目的 MAC 地址為 FFFF,查詢其 MAC 地址表,發現應該從其 6 號端口出去,於是從 6 號端口把數據包發出去。 更詳細且精準的過程: 至此,經過物理層、數據鏈路層、網絡層這前三層的協議,以及根據這些協議設計的各種網絡設備(網線、集線器、交換機、路由器),理論上只要擁有對方的 IP 地址,就已經將地球上任意位置的兩個節點連通了。 —— 全文完 ——

    時間:2021-01-15 關鍵詞: 網絡 計算機

  • 什麼水平才能任教清華計算機系?

    這天,我正在工位上例行做白日夢,清華大學計算機系的招聘信息,就正正好好蹦進了我眼睛裏。 我不禁好奇膽邊生:想去貴系嗑鹽,得到達怎樣的標準?待遇又如何? 畢竟不久前才讓人大AI學院凡爾賽了一把,着實心動了好一陣子呢。(手動狗頭) 話不多説,直接來研究一下招聘信息~ 一開始,貴系先謙虛地用最新數據,介紹了一下自己當前的情況: USNews2021全球計算機系排名第四,QS 2020全球計算機排名13。 6位院士,53位國家級帽子人才。 就問你心動不心動吧。 這次招聘信息共發佈了四類崗位: 教研系列教師崗位 研究系列教師崗位 教學系列教師崗位 博士後 並且註明了,此招聘信息長期有效,沒有截止日期。 其中,研究系列教師的2個崗位,招收方向是面對全球氣候變化的超級計算機系統。 那麼,作為中國TOP2高校裏風頭最盛的專業之一,貴系對應聘者的要求具體是什麼呢? 教研系列教師崗位、研究系列教師崗位這兩個崗位的招聘要求相同,大致如下: 具有計算機專業相關的博士學位 有博士後研究經歷,或者2年以上研究大學或研究機構工作經歷 如申請準聘副教授、長聘副教授、長聘教授崗位,需要有國外內一流大學相關學科副高(含副高)以上職稱 在相關領域國內外取得一定的影響力研究成果 以上兩個崗位,均未提及對海外留學經歷的要求。 教學系列教師崗位則主要承擔基礎課、專業課等的全英語教學職責。 需要滿足上述前兩點之外,還需要滿足三年以上海外知名高校學習或工作經歷,具有副教授(含)以上職稱。 這三個職位的待遇如何? 具體薪資沒有明確公開, 只是説「提供領域內有競爭力的薪酬」。 但是! 可以享受住房、子女入學的福利待遇!!! 這兩項,不禁讓人想起了人大高瓴學院AI教師的招聘標準,在帝都,解決了住房和入學,其他一切或許都是小問題了。 相對前者而言,對博士後的招聘要求最為寬鬆。 只需在國內外知名高校博士畢業三年以內,年齡在35歲(不含)以下的。要在相關領域有代表性成果,科研能力強。 當然,薪酬待遇也不如前面三者,僅在站期間可以享受入住博士後公寓,子女入學入園等福利。 但是,博士後除了國家的資助外,還可以申請清華「水木學者」支持計劃。 所謂「水木學者」計劃,是清華大學為吸引優秀青年學者,培育各領域拔尖人才特設的人才計劃。 2021年,計劃支持人數不超過200人。 這個水木學者的待遇也算十分優厚了,根據他們官網的公告,收入零零散散算起來,一年也有40多萬了…… 年薪(税前)30萬元(兩年) 享受社會保險、職業年金、公積金、住房補貼等約12.3萬元 可優先租住週轉房或享受租房補貼4.2萬元/年 享受教師子女入園入學政策、醫療政策 按照博士後制度辦理本人及家屬的户籍遷移 可參加清華大學教師職業培訓,可申請高水平國際學術會議資助 博士後合作導師可根據申請人的綜合情況給予額外資助 在站滿三年可申請評定副高級職稱 這麼優厚的待遇條件,那麼對於申請者有什麼要求呢? 在國外(境外)世界大學綜合排名或學科排名前100的高校獲得博士學位,且尚未回國工作的博士畢業生;或在國內雙一流建設學校、學科獲得博士學位的全日制博士畢業生 35週歲以下 獲得博士學位不超過三年,2021年應屆博士畢業生優先 進校後須全職在清華大學工作 不過説實在的,寫在紙面上的崗位要求,只是「及格線」。 具體「有影響力」的學術成果指什麼?有沒有海外學術經歷有多大影響? 想要將這些標準進一步「量化」,還是得向已經邁過門檻的青年教師們取取經。 據我們的不完全統計,清華計算機系2017-2020年入職的幾位青年教師中,研究方向主要集中在兩個方向上:高性能計算和人工智能。 這也可以從招聘信息上看出傾向。 雖然這一次在教研崗上貴系沒有直接點明對研究方向的要求,但2018年9月版本的招聘信息中,提到在「高性能計算機系統與應用」、「下一代互聯網體系結構」和「人工智能與智能計算」3個方向設置了3個教研系列教師崗位。 具體到科研成績上,舉個例子,在高性能計算方面,助理研究員甘霖就見證過中國超算的多個「首次」: 2016年,他和團隊為中國超算首次摘下世界高性能計算應用領域最高獎「戈登·貝爾」獎; 2018年,在清華大學計算機系擔任博士後的他,獲得IEEE高性能計算傑出新人獎,成為該獎設立以來首位獲獎的中國人。 而在人工智能方面,年年頂會常相見自不必説,有人論文引用量1700+,h指數超過20;有人是博士後創新人才支持計劃(博新計劃)獲得者,博士後期間享受國家每人兩年60萬元資助…… 值得一提的是,翻一翻清華計算機系青年教師們的履歷,就會發現海外留學經歷確實並非必要條件。他們當中的大多數,是在清華大學取得了計算機博士學位。 好了,情況就是這些。 終於,在上清華還是上北大之後,我又面臨了人生新選擇:選清華計算機系還是選人大高瓴學院呢?(手動狗頭) 參考鏈接://mp.weixin.qq.com/s/ok_vN-sAOdxtcefr1NTupA END 本文經AI新媒體量子位(ID:QbitAI)授權轉載,轉載請聯繫出處 蕾師師 煢煢 發自 凹非寺 版權歸原作者所有,如有侵權,請聯繫刪除。 ▍ 推薦閲讀 國內MCU能替代國外產品嗎?MCU的未來又將如何? STM32價格瘋長下,盤點STM32的國產替代者 選微處理器MPU,還是單片機MCU?兩者區別詳解 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2021-01-14 關鍵詞: 電子信息 計算機

  • 二進制原碼/反碼/補碼詳解,不懂的請看過來

    真值與機器值 真值很好理解,就是十進制的數字前面再加上正負號,這是人類可以簡單識別的數字,比如 0、±16、±1084、±10.34、±100.453 等,而正數前面的+符號可以省略。機器值從字面理解就是機器(計算機)識別的值,實際上也確實是這個意思。 計算機中通過高低電平表示1或者0,這樣就可以表示一個二進制的數值。一個1或者0表示的數值位稱為一個bit,而計算機中存儲和傳輸數據的最小單位是一個字節(byte)也就是8個bit,所以説計算機所有計算本質上都是基於二進制。 在計算機中,我們可以使用1個或者多個字節存儲一個數,但無論是多少個字節,其大小肯定是固定的,同時其所能表示的數值的範圍也是固定的。比如説對使用1個字節存儲的數進行計算或者傳輸,那麼這個數所能表示的最小值為00000000最大值為11111111,轉換為十進制為0 ~ 255。那麼無論對這個數做了什麼計算,無論計算之後的結果為多少都不能超出這個範圍,同理使用2個字節存儲的數範圍為0 ~ 65535。 由於很多時候一個數據需要使用2個或者2個以上的字節表示,那麼這種數據無論是存儲還是傳輸的時候都會有一個順序的問題,也就是大小端對齊(字節序)問題。在存儲時高位字節在前為大端對齊,反之為小端對齊。在數據傳輸時先傳輸高位字節為大端字節序,反之為小端字節序。目前絕大多數平台內部都是小端對齊的方式存儲數據,而大多數通信協議卻都是用大端字節序傳輸數據,所以這一點值得注意一下。 符號位與數值位 計算機中使用二進制存儲傳輸和計算數值,但是不能只有數值,計算的時候還得有正負之分。在計算機中使用最高bit位的數值來表示正負號,這個bit位稱作符號位。 計算機中符號位的值為0表示這個數為正數,符號位值為1表示這個樹為負數。由於符號位表示符號所以其不表示具體的值,除開符號位剩餘的bit位用來表示數值也就是數值位。比如1個字節的整數00000001,其中最高bit(最左邊)位的0為符號位,表示這個數為正數,數值位為1,所以其真值為1。同理2個字節的整數00000000_0000001,其真值也是1。 原碼、反碼和補碼 計算機只識別機器碼,其實也就是二進制數,並且使用最高bit位表示符號位。那麼兩個真值為8和-8的8位整數,它們在計算機內部的機器值是否就分別是00001000和10001000?其實並不是,這只是8和-8的原碼,而機器算計中的機器值是使用補碼存儲和計算的。 計算機中,正數的原碼、反碼和補碼是一樣的,所以上面那個例子中,真值為8的8位整數的機器值確實是00001000,但是-8就不是這麼回事了。負數的首先將原碼數值位按位取反得到反碼,然後再將反碼數值位加1之後則得到補碼。我們來看一下-8這個例子,其原碼為10001000,數值位按位取反之後的反碼為11110111,然後數值位加1之後的補碼為11111000。所以真值為-8的8位整數在計算機中的機器值為11111000,我們來看下面這張表: 注:int8為8bit位整數佔用1byte,int16為16bit位整數佔用2byte。 剛説的是原碼轉補碼的步驟,其實補碼轉原碼的步驟是一樣的。首先正數的原碼補碼是一樣的不需要轉換,我們看負數11111000,首先將數值位按位取反得到10000111,然後再將數值位加1得到10001000。我們再來看一個8位的整數10000000,是不是發現這個數原碼和補碼是一樣的,那麼這個看起來像是“-0”的數是怎麼回事呢?其實可以將這個數看成是一個特殊值,它的真實含義就是最小值。8位的這種“-0”的真值為-128,16位的這種“-0”真值為-32768。所以只需要記住100...000這種補碼就是最小值就行,我們看下面的這張表: 有兩對8bit位的整數4、8和4、-8,我們分別看一下他們在計算機中是怎麼做加法計算的。首先看4和8的補碼分別為00000100和00001000,只需要將每個bit位相加就行,結果為00001100,其真值為12。我們再來4和-8的計算,它們補碼分別為00000100和11111000,然後將它們按位相加(注意符號位也要做加法)得到11111100,其原碼為10000100,真值為-4。 再來看一下減法計算,比如8bit位的整數-8減去4,首先可以將4處理一下可以變為(-8) + (-4),這樣是不是就又變為了加法了?-8和-4的補碼分別為11111000和11111100,將它們按位相加得到補碼11110100(注意這是8位的整數,超出部分發生了溢出),轉換成原碼為10001100,真值為-12。 再來看一下乘法,比如8bit位的整數-8乘以13,他們的補碼分別為11111000和00001101。其中-8為被乘數,13為乘數,並且乘數有8個bit位,需要將被乘數按位與和位計算8次然後將結果相加,看如下分析: 被乘數的第0個bit位值為1,將被乘數乘以1然後左移0位得到:11111000; 被乘數的第1個bit位值為0,將被乘數乘以0然後左移1位得到:00000000; 被乘數的第2個bit位值為1,將被乘數乘以1然後左移2位得到;11100000; 被乘數的第3個bit位值為1,將被乘數乘以1然後左移3位得到;11000000; 被乘數的第4個bit位值為0,將被乘數乘以0然後左移4位得到;00000000; 被乘數的第5個bit位值為0,將被乘數乘以0然後左移5位得到;00000000; 被乘數的第6個bit位值為0,將被乘數乘以0然後左移6位得到;00000000; 被乘數的第7個bit位值為0,將被乘數乘以0然後左移7位得到;00000000; 由此可以得計算得到8組補碼(注意上面做位移涉及到的整數溢出,只能是8個bit位),然後將它們做加法得到10011000(也存在整數溢出)轉換為原碼為11101000,真值為-104。 至於除法則是使用交替加減法的方式,本文只是對計算原理做一下擴展,這裏不再繼續深入做介紹,如果有想了解的可以自行上網查詢。 通過上面的分析可以知道,使用補碼可以將所有計算都轉化為加法計算,這樣可以讓計算機底層對於整數計算變得簡單,反碼屬於歷史遺留,因為其存在±0的問題。 END 節選自《二進制小總結》 //www.cnblogs.com/lbole/p/14260496.html 版權歸原作者所有,如有侵權,請聯繫刪除。 ▍ 推薦閲讀 成功為華為“續命:中國芯片之父張汝京 一個工程師的“噩夢”:剛分清CPU和GPU,卻發現還有…… 這位“華為天才少年”,竟然要我用“充電寶”打《只狼》 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2021-01-13 關鍵詞: 二進制 計算機

  • 強悍DIY:發燒友自制雙屏筆記本

    關於雙屏 Windows 設備,目前市面上已經有相關產品,但是這些產品動輒需要花費數千美元的價格,並且形式上和 MacBook 的 Touch Bar 相類似,只是那一條觸控條變成了面積更大的觸控屏。因此,微軟自己推出的雙屏設備,能夠帶來的新的應用場景是一個值得期待的方向。下面來看看記錄了完整 DIY 過程的視頻: 在微軟的雙屏 Surface 設備發佈之前,就有 DIY 發燒友自制了一台雙屏筆記本,並且想法還不錯。這位發燒友從 eBay 上購買到一塊顯示屏、電路板等產品,然後經過簡單組裝,使得所買的零部件成為一塊插上電源連接顯示信號就能夠工作的獨立顯示屏,接下來就是把這塊獨立的顯示屏和筆記本組裝到一起,通過 Windows 內置的投屏功能,連接上自制的顯示器,實現雙屏顯示。 雖然這位發燒友做得事情原理簡單,但是其所製造的獨一款的雙屏筆記本卻是能夠提供更多應用場景的思路。自制顯示屏通過支架固定在筆記本A面,通過鉸鏈實現翻轉,兩個屏幕可以同一個方向工作,也可以反向工作。 END 來源:趣無盡 版權歸原作者所有,如有侵權,請聯繫刪除。 ▍ 推薦閲讀 成功為華為“續命:中國芯片之父張汝京 一個工程師的“噩夢”:剛分清CPU和GPU,卻發現還有…… 這位“華為天才少年”,竟然要我用“充電寶”打《只狼》 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2021-01-08 關鍵詞: 筆記本 DIY 計算機

  • 原來PCIe這麼簡單,一定要看!

    硬盤是大家都很熟悉的設備,一路走來,從HDD到SSD,從SATA到NVMe,作為NVMe SSD的前端接口,PCIe再次進入我們的視野。作為x86體系關鍵的一環,PCIe標準歷經PCI,PCI-X和PCIe,走過近30年時光。其中Host發現與查找設備的方式卻一脈沿襲,今天我們先來聊一聊PCIe設備在一個系統中是如何發現與訪問的。 首先我們來看一下在x86系統中,PCIe是什麼樣的一個體系架構。下圖是一個PCIe的拓撲結構示例,PCIe協議支持256個Bus, 每條Bus最多支持32個Device,每個Device最多支持8個Function,所以由BDF(Bus,device,function)構成了每個PCIe設備節點的身份證號。 PCIe體系架構一般由root complex,switch,endpoint等類型的PCIe設備組成,在root complex和switch中通常會有一些embeded endpoint(這種設備對外不出PCIe接口)。這麼多的設備,CPU啓動後要怎麼去找到並認出它們呢? Host對PCIe設備掃描是採用了深度優先算法,其過程簡要來説是對每一個可能的分支路徑深入到不能再深入為止,而且每個節點只能訪問一次。我們一般稱這個過程為PCIe設備枚舉。枚舉過程中host通過配置讀事物包來獲取下游設備的信息,通過配置寫事物包對下游設備進行設置。 第一步,PCI Host主橋掃描Bus 0上的設備(在一個處理器系統中,一般將Root complex中與Host Bridge相連接的PCI總線命名為PCI Bus 0),系統首先會忽略Bus 0上的embedded EP等不會掛接PCI橋的設備,主橋發現Bridge 1後,將Bridge1 下面的PCI Bus定為 Bus 1,系統將初始化Bridge 1的配置空間,並將該橋的Primary Bus Number 和 Secondary Bus Number寄存器分別設置成0和1,以表明Bridge1 的上游總線是0,下游總線是1,由於還無法確定Bridge1下掛載設備的具體情況,系統先暫時將Subordinate Bus Number設為0xFF。 第二步,系統開始掃描Bus 1,將會發現Bridge 3,並發現這是一個switch設備。系統將Bridge 3下面的PCI Bus定為Bus 2,並將該橋的Primary Bus Number 和 Secondary Bus Number寄存器分別設置成1和2,和上一步一樣暫時把Bridge 3 的Subordinate Bus Number設為0xFF。 第三步,系統繼續掃描Bus 2,將會發現Bridge 4。繼續掃描,系統會發現Bridge下面掛載的NVMe SSD設備,系統將Bridge 4下面的PCI Bus定為Bus 3,並將該橋的Primary Bus Number 和 Secondary Bus Number寄存器分別設置成2和3,因為Bus3下面掛的是端點設備(葉子節點),下面不會再有下游總線了,因此Bridge 4的Subordinate Bus Number的值可以確定為3。 第四步,完成Bus 3的掃描後,系統返回到Bus 2繼續掃描,會發現Bridge 5。繼續掃描,系統會發現下面掛載的NIC設備,系統將Bridge 5下面的PCI Bus設置為Bus 4,並將該橋的Primary Bus Number 和 Secondary Bus Number寄存器分別設置成2和4,因為NIC同樣是端點設備,Bridge 5的Subordinate Bus Number的值可以確定為4。 第五步,除了Bridge 4和Bridge 5以外,Bus2下面沒有其他設備了,因此返回到Bridge 3,Bus 4是找到的掛載在這個Bridge下的最後一個bus號,因此將Bridge 3的Subordinate Bus Number設置為4。Bridge 3的下游設備都已經掃描完畢,繼續向上返回到Bridge 1,同樣將Bridge 1的Subordinate Bus Number設置為4。 第六步,系統返回到Bus0繼續掃描,會發現Bridge 2,系統將Bridge 2下面的PCI Bus定為Bus 5。並將Bridge 2的Primary Bus Number 和 Secondary Bus Number寄存器分別設置成0和5, Graphics card也是端點設備,因此Bridge 2 的Subordinate Bus Number的值可以確定為5。 至此,掛在PCIe總線上的所有設備都被掃描到,枚舉過程結束,Host通過這一過程獲得了一個完整的PCIe設備拓撲結構。 系統上電以後,host會自動完成上述的設備枚舉過程。除一些專有系統外,普通系統只會在開機階段進行進行設備的掃描,啓動成功後(枚舉過程結束),即使插入一個PCIe設備,系統也不會再去識別它。 在linux操作系統中,我們可以通過lspci –v -t命令來查詢系統上電階段掃描到的PCIe設備,執行結果會以一個樹的形式列出系統中所有的pcie設備。如下圖所示,其中黃色方框中的PCIe設備是北京憶芯科技公司(Bejing Starblaze Technology Co., LTD.)推出的STAR1000系列NVMe SSD主控芯片,圖中顯示的9d32是Starblaze在PCI-SIG組織的註冊碼,1000是設備系列號。 STAR1000設備的BDF也可以從上圖中找出,其中bus是0x3C,device是0x00,function是0x0,BDF表示為3C:00.0,與之對應的上游端口是00:1d.0。 我們可以通過“lspci –xxx –s 3C:00.0”命令來列出該設備的PCIe詳細信息(技術發燒友或數字控請關注該部分)。這些內容存儲在PCIe配置空間,它們描述的是PCIe本身的特性。如下圖所示(低位地址0x00在最左邊),可以看到這是一個非易失性存儲控制器,0x00起始地址是PCIe的Vendor ID和Device ID。Class code 0x010802表示這是一個NVMe存儲設備。0x40是第一組capability的指針,如果你需要查看PCIe的特性,就需要從這個位置開始去查詢,在每組特徵的頭字段都會給出下一組特性的起始地址。從0x40地址開始依次是power management,MSI中斷,鏈路控制與狀態,MSI-X中斷等特性組。這兒特別列出了鏈路特徵中的一個0x43字段,表示STAR1000設備是一個x4lane的鏈接,支持PCIe Gen3速率(8Gbps)。 當然也可以使用lspci –vvv –s 3C:00.0命令來查看設備特性,初學者看到下面的列表也就一目瞭然了。 Host在枚舉設備的同時也會對設備進行配置,每個PCIe設備都會指定一段CPU memory訪問空間,從上面的圖中我們可以看到這個設備支持兩段訪問空間,一段的大小是1M byte,另一段的大小是256K byte,系統會分別指定它們的基地址。基地址配置完成以後,Host就可以通過地址來對PCIe memory空間進行訪問了。 PCIe memory空間關聯的是PCIe設備物理功能,對於STAR1000系列芯片而言,物理功能是NVMe,memory中存放的是NMVe的控制與狀態信息,對於NMVe的控制以及工作狀態的獲取,都需要通過memory訪問來實現。 下面以NVMe命令下發為例簡單描述PCIe設備的memory訪問。NVMe命令下發的基本操作是1)Host寫doorbell寄存器,此時使用PCIe memory寫請求。如下圖所示,host發出一個memory write(MWr)請求,該請求經過switch到達要訪問的NVMe SSD設備。 這個請求會被端點設備接收並執行2)NVMe讀取命令操作。如下圖所示,此時NVMe SSD作為請求者,發出一個memory read(MRd)請求,該請求經過Switch到達Host,Host作為完成者會返回一個完成事物包(CplD),將訪問結果返回給NVMe SSD。 這樣,一個NVMe的命令下發過程就完成了。同樣,NVMe的其他操作比如各種隊列操作,命令與完成,數據傳輸都是通過PCIe memory訪問的方式進行的,此處不再詳述。 通過上面的描述,相信能夠幫助大家瞭解PCIe的設備枚舉和memory空間訪問。以後會繼續與大家探討PCIe的其他內容,比如PCIe的協議分層,鏈路建立,功耗管理等等。目前PCIe協議還正在不斷的快速演進中,2017年發佈的PCIe Gen4標準,每條Serdes支持的速率已經達到16Gbps,Gen5也在加速制定中,其速率會再翻一倍達到32Gbps。Starblaze會緊跟技術的發展趨勢,提供速率更高,性能更好更穩定的NVMe SSD系列產品。 END 來源:人人都是極客 版權歸原作者所有,如有侵權,請聯繫刪除。 ▍ 推薦閲讀 成功為華為“續命:中國芯片之父張汝京 一個工程師的“噩夢”:剛分清CPU和GPU,卻發現還有…… 這位“華為天才少年”,竟然要我用“充電寶”打《只狼》 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2021-01-07 關鍵詞: PCIe 計算機

  • 元旦期間,整理了12道計算機網絡面試題

    時間:2021-01-05 關鍵詞: TCP 計算機

  • 2020大學生專業薪資排名一覽

    聲明: 部分內容來源於網絡,僅供讀者學術交流,版權歸原作者所有。 前些日子,《2020年中國大學生就業報告》發佈,報告中顯示,2019屆本科畢業生月入5440元,本科計算機類、高職鐵道運輸類專業領跑薪酬榜。 剔除通貨膨脹因素的影響外,與2015屆相比,五年來本科生起薪漲幅為23.6%;高職畢業生平均月收入為4295元,與2015屆相比,五年來高職生起薪漲幅為15.7%。 大學畢業生升學比例持續上升。本科生國內讀研比例從2015屆的13.5%上升至2019屆的15.2%,高職畢業生讀本科的比例從2015屆的4.7%上升到2019屆的7.6%。 具體來説,計算機類、電子信息類、自動化類等本科專業畢業生薪資較高,2019屆平均月收入分別為6858元、6145元、5899元;鐵道運輸類、計算機類、水上運輸類等高職專業畢業生薪資較高,2019屆平均月收入分別為5109元、4883元、4763元。 下面我們來看看具體排行名單↓↓↓ 月收入前10專業類 其中,本科、高職“綠牌”專業: 綠牌專業指的是失業量較小,就業率、薪資和就業滿意度綜合較高的專業,為需求增長型專業。行業需求增長是造就綠牌專業的主要因素,連續綠牌説明相關專業就業優勢具有持續性。 本科、高職“紅牌”專業: 紅牌專業指的是失業量較大,就業率、薪資和就業滿意度綜合較低的專業。這與相關專業畢業生供需矛盾有關。紅綠牌專業反映的是全國總體情況,各省區、各高校情況可能會有差別。 畢業生熱門就職行業 1)本科畢業生 2019屆本科畢業生就業比例最大的行業類是“教育業”(就業比例:15.9%),同時與2017屆相比增幅也較高,為8.2%。具體來看,在“教育業”的就業增長主要是“民辦中小學及教輔機構”(2019屆就業比例:7.6%)、“公辦中小學教育機構”(2019屆就業比例:6.1%)的需求增長,較2017屆增幅分別為20.6%、7%。 其他就業比例較大且增長較多的行業類還有建築業(2019屆就業比例:8.9%),信息傳輸、軟件和信息技術服務業(2019屆就業比例:8.9%),各類專業設計與諮詢服務業(2019屆就業比例:5.8%),文化、體育和娛樂業(2019屆就業比例:4.6%),人才需求分別較2017屆增長6%、4.7%、7.4%、18%。 2)高職畢業生 2019屆高職畢業生就業比例較大的行業類是建築業(就業比例:11.1%)、教育業(就業比例:7.8%)。與2017屆相比,到“教育業”就業的高職生比例增幅也較高,為20%。具體來看,在“教育業”的就業增長主要是“教輔及培訓機構”(2019屆就業比例:2.9%)、“幼兒與學前教育機構”(2019屆就業比例:2.5%)的需求增長,較2017屆增幅分別為26.1%、19%。 其他就業比例較大且增長較多的行業類還有信息傳輸、軟件和信息技術服務業(2019屆就業比例:5.8%),居民服務、修理和其他服務業(2019屆就業比例:4.7%),住宿和餐飲業(2019屆就業比例:3.9%),人才需求分別較2017屆增長13.7%、6.8%、21.9%。 最掙錢的行業排名 今年5月,國家統計局公佈2019年城鎮非私營單位和城鎮私營單位分行業就業人員年平均工資數據!2018年和2019年,年平均工資最高的行業都是信息傳輸、軟件和信息技術服務業。我們可以通過下列圖表看看近兩年平均工資較高的行業具體有哪些。 城鎮非私營單位 分行業就業人員年平均工資 (單位:元,%)↓↓↓ 數據來源:國家統計局 城鎮私營單位 分行業就業人員年平均工資 (單位:元,%) ↓↓↓ 數據來源:國家統計局 -END- | 整理文章為傳播相關技術,版權歸原作者所有 | | 如有侵權,請聯繫刪除 | 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2021-01-04 關鍵詞: 電子信息 大學生 計算機

  • 2020大學生專業薪資排名一覽,你到平均工資了嗎?

    前些日子,《2020年中國大學生就業報告》發佈,報告中顯示,2019屆本科畢業生月入5440元,本科計算機類、高職鐵道運輸類專業領跑薪酬榜。 剔除通貨膨脹因素的影響外,與2015屆相比,五年來本科生起薪漲幅為23.6%;高職畢業生平均月收入為4295元,與2015屆相比,五年來高職生起薪漲幅為15.7%。 大學畢業生升學比例持續上升。本科生國內讀研比例從2015屆的13.5%上升至2019屆的15.2%,高職畢業生讀本科的比例從2015屆的4.7%上升到2019屆的7.6%。 具體來説,計算機類、電子信息類、自動化類等本科專業畢業生薪資較高,2019屆平均月收入分別為6858元、6145元、5899元;鐵道運輸類、計算機類、水上運輸類等高職專業畢業生薪資較高,2019屆平均月收入分別為5109元、4883元、4763元。 下面我們來看看具體排行名單↓↓↓ 月收入前10專業類 其中,本科、高職“綠牌”專業: 綠牌專業指的是失業量較小,就業率、薪資和就業滿意度綜合較高的專業,為需求增長型專業。行業需求增長是造就綠牌專業的主要因素,連續綠牌説明相關專業就業優勢具有持續性。 本科、高職“紅牌”專業: 紅牌專業指的是失業量較大,就業率、薪資和就業滿意度綜合較低的專業。這與相關專業畢業生供需矛盾有關。紅綠牌專業反映的是全國總體情況,各省區、各高校情況可能會有差別。 畢業生熱門就職行業 1)本科畢業生 2019屆本科畢業生就業比例最大的行業類是“教育業”(就業比例:15.9%),同時與2017屆相比增幅也較高,為8.2%。具體來看,在“教育業”的就業增長主要是“民辦中小學及教輔機構”(2019屆就業比例:7.6%)、“公辦中小學教育機構”(2019屆就業比例:6.1%)的需求增長,較2017屆增幅分別為20.6%、7%。 其他就業比例較大且增長較多的行業類還有建築業(2019屆就業比例:8.9%),信息傳輸、軟件和信息技術服務業(2019屆就業比例:8.9%),各類專業設計與諮詢服務業(2019屆就業比例:5.8%),文化、體育和娛樂業(2019屆就業比例:4.6%),人才需求分別較2017屆增長6%、4.7%、7.4%、18%。 2)高職畢業生 2019屆高職畢業生就業比例較大的行業類是建築業(就業比例:11.1%)、教育業(就業比例:7.8%)。與2017屆相比,到“教育業”就業的高職生比例增幅也較高,為20%。具體來看,在“教育業”的就業增長主要是“教輔及培訓機構”(2019屆就業比例:2.9%)、“幼兒與學前教育機構”(2019屆就業比例:2.5%)的需求增長,較2017屆增幅分別為26.1%、19%。 其他就業比例較大且增長較多的行業類還有信息傳輸、軟件和信息技術服務業(2019屆就業比例:5.8%),居民服務、修理和其他服務業(2019屆就業比例:4.7%),住宿和餐飲業(2019屆就業比例:3.9%),人才需求分別較2017屆增長13.7%、6.8%、21.9%。 最掙錢的行業排名 今年5月,國家統計局公佈2019年城鎮非私營單位和城鎮私營單位分行業就業人員年平均工資數據!2018年和2019年,年平均工資最高的行業都是信息傳輸、軟件和信息技術服務業。我們可以通過下列圖表看看近兩年平均工資較高的行業具體有哪些。 城鎮非私營單位 分行業就業人員年平均工資 (單位:元,%)↓↓↓ 數據來源:國家統計局  城鎮私營單位 分行業就業人員年平均工資 (單位:元,%) ↓↓↓ 數據來源:國家統計局  END 來源:大魚機器人 版權歸原作者所有,如有侵權,請聯繫刪除。 ▍ 推薦閲讀 成功為華為“續命:中國芯片之父張汝京 一個工程師的“噩夢”:剛分清CPU和GPU,卻發現還有…… 這位“華為天才少年”,竟然要我用“充電寶”打《只狼》 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2021-01-04 關鍵詞: 電子信息 計算機

  • 亂花漸欲迷人眼:淺談關於分佈式存儲的五大“謊言”

    要説近幾年存儲這條街最靚的仔,莫過於分佈式存儲了。 自誕生以來,分佈式存儲就被視為存儲的未來,被萬眾期待。然而分佈式存儲起步於寒門,最早應用於互聯網日誌、企業備份歸檔、開發測試等場景,追求極致成本,性能和可靠性卻不敢恭維。但它還算爭氣,憑藉多年的打怪練級,越來越多地開始承載自動駕駛研發、超高清編輯、運營商5G網絡雲等企業的關鍵業務,走上變成高富帥、贏取白富美的道路。 正所謂人紅是非多,存儲領域也是一樣,坊間一直流傳着關於分佈式存儲的各種流言蜚語。好事的筆者今天就來探尋一番,揭開謊言背後的真相。 謊言一:分佈式存儲就是軟件+服務器 目測分佈式存儲領域有兩個派系,一派是SDS(軟件定義存儲),一派是軟硬一體。前者以vSAN、Ceph為代表,以軟件遞四方香港為主在推廣。分佈式存儲軟件+通用服務器組合,打着重定義存儲市場的旗幟,頗有“有王侯將相寧有種乎”的氣勢;另一派是存儲老牌存儲玩家的產品,他們以軟硬一體為主,如Isilon、HCP等。 楚河漢界已然形成,未來誰主沉浮? 誰主沉浮很難判斷,不過我們不妨從產業動態窺見一斑。雖然用過的客户都詬病分佈式存儲軟件+服務器的方式存在各種兼容性、可靠性、可維護性的問題,但筆者認為SDS這種模式會長期存在,尤其是在傳統的低端領域,如備份歸檔、開發測試環境。而越往高端走,軟硬一體越是佔據主流。業界主流遞四方香港也在持續推出軟硬一體的產品: 國外,DELL&EMC的PowerScale(Isilon)、DDN的EXAScaler等產品; 國內XSKY、浪潮等Ceph系開源遞四方香港也推出了軟硬件一體產品如XScaler Express。尤其有趣的是XSKY,這可是國內Ceph系軟件定義存儲的旗幟啊,你品,你細品。 另一方面,筆者也注意到近些年運營商開展了大規模的分佈式存儲集採,有文件的有塊的。以中國移動2019~2020年分佈式塊存儲集採為例,3個標段中2個為軟硬一體採購,佔比超過了90%;電信集團2020年集採也放棄了分佈式存儲純軟件採購,首次全面轉向軟硬一體。從這個角度看,用户在嘗試了多種採購模式之後,也開始逐步轉變到軟硬一體的道路上來,尤其是大型用户。 #真相#:分佈式存儲誕生初期主要是純軟形態,近十年來,隨着分佈式存儲逐步進入企業市場,軟硬一體的形成已經成為主流。 謊言二:分佈式存儲就是低價值存儲 如文章開頭講到的,分佈式存儲早期主要用於互聯網日誌、企業備份歸檔、開發測試等場景,這些場景無疑是低價值場景,成本是第一訴求甚至唯一訴求。 然此一時彼一時,士別三日也當刮目相看。 首先是分佈式存儲產品能力已今非昔比,逐漸具備了承載企業高價值業務的關鍵能力,例如毫秒級時延、TB/s級帶寬、雙活/3DC業務級容災、端到端DIF等,無論是結構化數據還是非結構化數據的承載,功能完備性的最大短板早已補齊。 其次,分佈式存儲已走入眾多高價值關鍵業務,如運營商BOM業務、金融渠道類業務以及超算、油藏探測HPC等高價值業務,通過大規模應用實踐來檢驗成色。(小道消息,某些高價值場景全閃出貨價達到1000美金/TB以上啦) 看完當下還要看看未來,筆者認為未來高價值的非結構化數據場景,分佈式存儲的版圖還會持續擴展。如自動駕駛訓練、4K/8K超高清、5G日誌留存等場景,對多協議訪問、極致帶寬、擴展性訴求強烈,分佈式存儲已然成為這些未來業務的首選架構。 #真相#:分佈式存儲早期主要用在備份歸檔等低價值、強成本訴求的場景,如今隨着企業級能力的提升,分佈式存儲也逐步成為企業生產系統的承載平台,尤其是面向海量非結構數據場景,全面支撐文件資源池和HPC等高價值場景。 謊言三:開源架構是分佈式存儲的未來 筆者對開源從來是持開放、支持的態度,正是因為開源的存在,IT產業才能有今天的繽紛色彩;也正是因為開源,存儲這個高大上的產品,才走進了更多的尋常百姓家。 但如果説存儲的未來在開源,我不敢苟同。 開源分佈式存儲軟件的出現,一定程度上降低了存儲的門檻,小公司可快速包裝出存儲產品,帶動服務器銷售。但產品同質化問題是所有開源不得不面臨的問題,由於架構限制,很難在不動架構的情況下,真正做出差異化競爭力。互聯網類公司、部分科研機構,以及有技術情結和充足資金投入的客户可能選擇開源,而對於金融、電信運營商、大企業商用HPC、政府等對可靠性、性能、安全合規有要求的企業,開源從來不是第一選擇,因為數據太重要了。(聽説,國內某知名銀行曾經投入500人基於開源軟件搭建分佈式存儲,投入巨大且無法達銀行業務對性能、可靠性、易運維訴求,最終於2年後放棄。) 也有認為開源更自主可控的。筆者認為開源給了用户一定的自主權,但和自主可控是兩個概念。據SNYK 2019 年開源安全狀況報告説明,開源軟件漏洞在兩年時間內增加了88%,開源風險的解決強依賴於社區版本發佈,不能及時規避。同時,近兩年國際形勢的大變化,給開放著稱的開源蒙上了一層陰影。 此外,業界TOP主流分佈式存儲產品均是閉源架構,如PowerScale(Isilon)、Spectrum Scale、Nutanix、OceanStor Pacific、VSAN、HCP,翻看了一下三個月前發佈的IDC市場份額報告,TOP5分佈式存儲遞四方香港中,基於開源二次開發的遞四方香港份額僅佔18%。 #真相#:開源只是部分遞四方香港的商業選擇,分佈式存儲產業的主流還是非開源,並且開源並不代表更加自主可控。 謊言四:分佈式存儲可全面取代企業外置存儲 這是一個在存儲領域爭論最大的問題。 正所謂長江後浪推前浪,分佈式存儲快速增長是不可否認的,這從各大遞四方香港的業績報告和分析師報告就能看出來,但想要把企業外置存儲這個前浪拍死在沙灘上還是步子邁太大,不現實。 企業外置存儲在相當長的一段時間內,仍然是主流。它主要面向企業傳統應用如ERP/CRM/HIS等,數據量不大但對可靠性、性能有極致要求,如銀行Core-Banking,從可靠性、生態層面,分佈式存儲都不是最佳選擇。分佈式存儲主要面向海量數據、新興業務場景,如HPC/EDA、大數據,這類場景以二進制文件、視頻、圖片等非結構化數據為主,數據量極大。所以從場景來看,二者場景是有明確區隔的,按場景並存是最好的選擇。 從技術的角度,分佈式存儲的發力點在大規模的擴展性,基於此逐步優化性能、可靠性,讓海量數據存得下、用得起;集中式存儲的技術方向在於保持穩定性的基礎上,利用更快的介質、更低時延的網絡為核心業務提供加速,讓業務更穩、效率更高。因此,從技術方向上來看,二者也是各有側重的。 #真相#:分佈式存儲和企業外置存儲並非取代關係,二者相輔相成、互為補充。企業外置存儲主要面向結構化數據市場,分佈式存儲主要面對海量非結構化數據市場(高價值分佈式文件、分佈式對象)。 謊言五:分佈式存儲就是“雲” IT潮流滾滾向前,每隔幾年總是需要有一些新概念。 雲和分佈式存儲一樣,這些年正在大行其道、炙手可熱。Cloud First、All in Cloud、Cloud Native等概念風靡業界,CIO見面不提雲貌似都不好意思打招呼。早期人們也習慣把分佈式存儲叫做“雲存儲”,那麼分佈式存儲和雲是什麼關係? 筆者認為,分佈式存儲是一種技術架構,而云是一種商業模式。分佈式存儲可以被用作各種雲的數據底座,也可以單獨成為數據底座,是配合的關係,而非替換和包含關係。 其實,從與業界公有云遞四方香港的溝通了解到,公有云雖然近年來加強對傳統IT市場的轉換,但公有云數據增長更多還是來自新興場景。以美國市場為例,近年來AWS持續快速增長,但PowerScale(Isilon)作為Dell&EMC面向非結構化數據場景的主力產品,並且近60%的銷售收入來自於北美市場,依然保持以每年近20%的速度穩步增長。 從場景看,多數海量存儲場景,因客户數據安全、生態、招標模式等原因,以線下采購為主,公有云為輔助。以HPC為例,雲上HPC給業界提供了一種新的方式,讓更多用户能享受到公有云帶來的便利性。但實際上HPC主要用於創新研究,對數據安全要求高,上雲是有顧慮。參考Hyperion Research報告顯示,到2024年雲上HPC的收入佔比僅17.7%,收入佔比很低。 #真相# :分佈式存儲是一個產品,雲是一種商業模式是一種服務形式,二者不衝突,長期共存是未來。 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-12-31 關鍵詞: 分佈式存儲 計算機

  • 不低調的巨人,史玉柱

    —————END—————

    時間:2020-12-29 關鍵詞: 史玉柱 計算機

  • 可能是筆記本史上最難的考試,Evo認證有多嚴?

    可能是筆記本史上最難的考試,Evo認證有多嚴?

    性能、價格會是電腦的終極進化方向嗎?並不是,回頭看看當前的電腦行業,標準化生產帶來了的生產效率的極大提升,但也導致了千篇一律,體驗不夠完善,尤其是是筆記本電腦,不同產品的表現差異很大,消費者往往無所適從。 以往吸引消費者購買的優勢現在已經不湊效了,問題出在哪?套用一句話,那就是現在標準工業線生產的筆記本已經滿足不了人們羣眾日益豐富的需求了,無論是辦公還是娛樂。 筆記本電腦到了一個需要用工匠精神重塑消費者體驗的關鍵時刻了。 Intel變身大工匠,從雅典娜到Evo重新定義筆記本 從雅典娜到Evo認證,Intel對筆記本的要求是不斷提升的,越來越嚴格,這個變化就好像是車間主管到大工匠的升級一樣,以前只關心產量、效率,現在要精雕細琢,確保每枱筆記本電腦有這工藝品一樣的精緻。 給筆記本重新定標準容易,真正把標準付諸實施就是另外一回事了,大家都喜歡工匠精神,但是現在的工匠精神通常只針對小而美的東西,往往還是特殊定製的,產量不高,而Evo筆記本不同,那是奔着成百上千萬銷量的廣闊市場而去的,其中覆蓋超過 150多家合作伙伴。 不光有硬件,還要涉及到軟件生態上的優化,要根據特定操作系統和最常用的應用,尋找軟件級別的優化方法。 這個標準涉及到了筆記本中的幾乎所有關鍵部件,除了Intel的處理器之外,還包括顯示面板、觸控屏、ALS傳感器、麥克風、喇叭、攝像頭、WiFi模組、內存、SSD、電池、充電器、鍵盤、觸摸鍵盤等關鍵功能部件。 雖然我們整天説Intel Evo認證嚴苛,但相信沒幾個人見過到底有多嚴苛,這裏就來分享幾個具體的認證要求,可以看到Evo要求多麼繁瑣細緻。 在這樣嚴苛的標準下,英特爾和OEM也一同做了很多探索和創新,以滿足在真實使用場景下的續航指標。比如,我們曾攜手關鍵供應商及客户,研發推出了1瓦節能屏技術,可以將電池續航時間延長20%左右。通過開放實驗室的推廣,節能屏被廣泛採用在Evo平台上。 為了保證Evo認證筆記本的體驗始終如一,Intel這次也採取了極為嚴格的認證標誌及流暢,一切以用户的真實體驗為目標,至少25個項目的測試全部通過之後才能獲得Evo認證。僅僅單項脱靶,就會被淘汰,無緣Evo。可以説是筆記本史上最為嚴苛的考試認證!

    時間:2020-12-28 關鍵詞: 英特爾 計算機

  • 3w字帶你揭開WebSocket的神祕面紗~

    目錄 一. WebSocket 簡介 WebSocket 是一種基於 TCP 的網絡協議。在 2009 年誕生,於 2011 年被 IETF 定為標準 RFC 6455 通信標準,並由 RFC7936 補充規範。WebSocket API 也被 W3C 定為標準。 WebSocket 也是一種全雙工通信的協議,既允許客户端向服務器主動發送消息,也允許服務器主動向客户端發送消息。在 WebSocket 中,瀏覽器和服務器只需要完成一次握手,兩者之間就可以建立持久性的連接,進行雙向數據傳輸。 二. WebSocket 特點 連接握手階段使用 HTTP 協議; 協議標識符是 ws,如果採用加密則是 wss; 數據格式比較輕量,性能開銷小,通信高效; 沒有同源限制,客户端可以與任意服務器通信; 建立在 TCP 協議之上,服務器端的實現比較容易; 通過 WebSocket 可以發送文本,也可以發送二進制數據; 與 HTTP 協議有着良好的兼容性。默認端口也是 80 和 443,並且握手階段採用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器; 三. 為什麼需要 WebSocket? 談起為什麼需要 WebSocket 前,那得先了解在沒有 WebSocket 那段時間説起,那時候基於 Web 的消息基本上是靠 Http 協議進行通信,而經常有”聊天室”、”消息推送”、”股票信息實時動態”等這樣需求,而實現這樣的需求常用的有以下幾種解決方案: 1. 短輪詢(Traditional Polling) 短輪詢是指客户端每隔一段時間就詢問一次服務器是否有新的消息,如果有就接收消息。這樣方式會增加很多次無意義的發送請求信息,每次都會耗費流量及處理器資源。 優點:短連接,服務器處理簡單,支持跨域、瀏覽器兼容性較好。 缺點:有一定延遲、服務器壓力較大,浪費帶寬流量、大部分是無效請求。 2. 長輪詢(Long Polling) 長輪詢是段輪詢的改進,客户端執行 HTTP 請求發送消息到服務器後,等待服務器迴應,如果沒有新的消息就一直等待,知道服務器有新消息傳回或者超時。 這也是個反覆的過程,這種做法只是減小了網絡帶寬和處理器的消耗,但是帶來的問題是導致消息實時性低,延遲嚴重。而且也是基於循環,最根本的帶寬及處理器資源佔用並沒有得到有效的解決。 優點:減少輪詢次數,低延遲,瀏覽器兼容性較好。 缺點:服務器需要保持大量連接。 3. 服務器發送事件(Server-Sent Event) “ 目前除了 IE/Edge,其他瀏覽器都支持。 服務器發送事件是一種服務器向瀏覽器客户端發起數據傳輸的技術。一旦創建了初始連接,事件流將保持打開狀態,直到客户端關閉。該技術通過傳統的 HTTP 發送,並具有 WebSockets 缺乏的各種功能,例如”自動重新連接”、”事件ID” 及 “發送任意事件”的能力。 “ 服務器發送事件是單向通道,只能服務器向瀏覽器發送,因為流信息本質上就是下載。 優點:適用於更新頻繁、低延遲並且數據都是從服務端發到客户端。 缺點:瀏覽器兼容難度高。 總結 顯然,上面這幾種方式都有各自的優缺點,雖然靠輪詢方式能夠實現這些一些功能,但是其對性能的開銷和低效率是非常致命的,尤其是在移動端流行的現在。 現在客户端與服務端雙向通信的需求越來越多,且現在的瀏覽器大部分都支持 WebSocket。所以對實時性和雙向通信及其效率有要求的話,比較推薦使用 WebSocket。 四. WebSocket 連接流程 第一步 客户端先用帶有 Upgrade:Websocket 請求頭的 HTTP 請求,向服務器端發起連接請求,實現握手(HandShake)。 客户端 HTTP 請求的 Header 頭信息如下: Connection: UpgradeSec-WebSocket-Extensions: permessage-deflate; client_max_window_bitsSec-WebSocket-Key: IRQYhWINfX5Fh1zdocDl6Q==Sec-WebSocket-Version: 13Upgrade: websocket Connection: Upgrade 表示要升級協議。 Upgrade: Websocket 要升級協議到 websocket 協議。 Sec-WebSocket-Extensions: 表示客户端所希望執行的擴展(如消息壓縮插件)。 Sec-WebSocket-Key: 主要用於WebSocket協議的校驗,對應服務端響應頭的 Sec-WebSocket-Accept。 Sec-WebSocket-Version: 表示 websocket 的版本。如果服務端不支持該版本,需要返回一個 Sec-WebSocket-Versionheader,裏面包含服務端支持的版本號。 第二步 握手成功後,由 HTTP 協議升級成 Websocket 協議,進行長連接通信,兩端相互傳遞信息。 服務端響應的 HTTP Header 頭信息如下: Connection: upgradeSec-Websocket-Accept: TSF8/KitM+yYRbXmjclgl7DwbHk=Upgrade: websocket Connection: Upgrade 表示要升級協議。 Upgrade: Websocket 要升級協議到 websocket 協議。 Sec-Websocket-Accept: 對應 Sec-WebSocket-Key 生成的值,主要是返回給客户端,讓客户端對此值進行校驗,證明服務端支持 WebSocket。 五. WebSocket 使用場景 數據流狀態: 比如説上傳下載文件,文件進度,文件是否上傳成功。 協同編輯文檔: 同一份文檔,編輯狀態得同步到所有參與的用户界面上。 多玩家遊戲: 很多遊戲都是協同作戰的,玩家的操作和狀態肯定需要及時同步到所有玩家。 多人聊天: 很多場景下都需要多人蔘與討論聊天,用户發送的消息得第一時間同步到所有用户。 社交訂閲: 有時候我們需要及時收到訂閲消息,比如説開獎通知,比如説在線邀請,支付結果等。 股票虛擬貨幣價格: 股票和虛擬貨幣的價格都是實時波動的,價格跟用户的操作息息相關,及時推送對用户跟盤有很大的幫助。 六. WebSocket 中子協議支持 WebSocket 確實指定了一種消息傳遞體系結構,但並不強制使用任何特定的消息傳遞協議。而且它是 TCP 上的一個非常薄的層,它將字節流轉換為消息流(文本或二進制)僅此而已。由應用程序來解釋消息的含義。 與 HTTP(它是應用程序級協議)不同,在 WebSocket 協議中,傳入消息中根本沒有足夠的信息供框架或容器知道如何路由或處理它。因此,對於非常瑣碎的應用程序而言 WebSocket 協議的級別可以説太低了。 可以做到的是引導在其上面再創建一層框架。這就相當於當今大多數 Web 應用程序使用的是 Web 框架,而不直接僅使用 Servlet API 進行編碼一樣。 WebSocket RFC 定義了子協議的使用。在握手過程中,客户機和服務器可以使用頭 Sec-WebSocket 協議商定子協議,即使不需要使用子協議,而是用更高的應用程序級協議,但應用程序仍需要選擇客户端和服務器都可以理解的消息格式。且該格式可以是自定義的、特定於框架的或標準的消息傳遞協議。 Spring 框架支持使用 STOMP,這是一個簡單的消息傳遞協議,最初創建用於腳本語言,框架靈感來自 HTTP。STOMP 被廣泛支持,非常適合在 WebSocket 和 web 上使用。 七. 什麼是 STOMP 協議 (1). STOMP 協議概述 “ STOMP(Simple Text-Orientated Messaging Protocol)是一種簡單的面向文本的消息傳遞協議。 它提供了一個可互操作的連接格式,允許 STOMP 客户端與任意 STOMP 消息代理(Broker)進行交互。STOMP 協議由於設計簡單,易於開發客户端,因此在多種語言和多種平台上得到廣泛地應用。 (2). 簡單介紹可以分為以下幾點: STOMP 是基於幀的協議,其幀以 HTTP 為模型。 STOMP 框架由命令,一組可選的標頭和可選的主體組成。 STOMP 基於文本,但也允許傳輸二進制消息。 STOMP 的默認編碼為 UTF-8,但它支持消息正文的替代編碼的規範。 (3). STOMP 客户端是一種用户代理 作為生產者,通過 SEND 幀將消息發送到目標服務器上。 作為消費者,對目標地址發送 SUBSCRIBE 幀,並作為 MESSAGE 幀從服務器接收消息。 (4). STOMP 幀 STOMP 是基於幀的協議,其幀以 HTTP 為模型。STOMP 結構為: COMMANDheader1:value1header2:value2Body^@ 客户端可以使用 SEND 或 SUBSCRIBE 命令發送或訂閲消息,還可以使用 “destination” 頭來描述消息的內容和接收者。 這支持一種簡單的發佈-訂閲機制,可用於通過代理將消息發送到其他連接的客户端,或將消息發送到服務器以請求執行某些工作。 (5). Stomp 常用幀 STOMP 的客户端和服務器之間的通信是通過”幀“(Frame)實現的,每個幀由多”行“(Line)組成,其包含的幀如下: Connecting Frames: CONNECT(連接) CONNECTED(成功連接) Client Frames: SEND(發送) SUBSRIBE(訂閲) UNSUBSCRIBE(取消訂閲) BEGIN(開始) COMMIT(提交) ABORT(中斷) ACK(確認)) NACK(否認)) DISCONNECT(斷開連接)) Server Frames: MESSAGE(消息)) RECEIPT(接收)) ERROR(錯誤)) (6). Stomp 與 WebSocket 的關係 直接使用 WebSocket 就很類似於使用 TCP 套接字來編寫 Web 應用,因為沒有高層級的應用協議(wire protocol),因而就需要我們定義應用之間所發送消息的語義,還需要確保連接的兩端都能遵循這些語義。 同 HTTP 在 TCP 套接字上添加請求-響應模型層一樣,STOMP 在 WebSocket 之上提供了一個基於幀的線路格式層,用來定義消息語義。 (7). 使用 STOMP 作為 WebSocket 子協議的好處 無需發明自定義消息格式 在瀏覽器中 使用現有的stomp.js客户端 能夠根據目的地將消息路由到 可以使用成熟的消息代理(例如 RabbitMQ, ActiveMQ等)進行廣播的選項 使用 STOMP(相對於普通 WebSocket)使 Spring Framework 能夠為應用程序級使用提供編程模型,就像 Spring MVC 提供基於 HTTP 的編程模型一樣。 八. Spring 封裝的 STOMP 使用 Spring 的 STOMP 支持時,Spring WebSocket 應用程序充當客户端的 STOMP 代理。 消息被路由到 @Controller 消息處理方法或簡單的內存中代理,該代理跟蹤訂閲並向訂閲的用户廣播消息。 還可以將 Spring 配置為與專用的 STOMP 代理(例如 RabbitMQ,ActiveMQ等)一起使用,以實際廣播消息。在那種情況下,Spring 維護與代理的 TCP 連接,將消息中繼到該代理,並將消息從該代理向下傳遞到已連接的 WebSocket 客户端。 因此 Spring Web 應用程序可以依賴基於統一 HTTP 的安全性,通用驗證以及熟悉的編程模型消息處理工作。 Spring 官方提供的處理流圖: 上面中的一些概念關鍵詞: Message: 消息,裏面帶有 header 和 payload。 MessageHandler: 處理 client 消息的實體。 MessageChannel: 解耦消息發送者與消息接收者的實體 clientInboundChannel:用於從 WebSocket 客户端接收消息。 clientOutboundChannel:用於將服務器消息發送給 WebSocket 客户端。 brokerChannel:用於從服務器端、應用程序中向消息代理髮送消息 Broker: 存放消息的中間件,client 可以訂閲 broker 中的消息。 上面的設置包括3個消息通道: clientInboundChannel: 用於來自WebSocket客户端的消息。 clientOutboundChannel: 用於向WebSocket客户端發送消息。 brokerChannel: 從應用程序內部發送給代理的消息。 九. 示例一:實現簡單的廣播模式 WebSocket 常分為廣播與隊列模式,廣播模式是向訂閲廣播的用户發送信息,只要訂閲相關廣播就能收到對應信息。 隊列模式常用於點對點模式,為單個用户向另一個用户發送信息,這裏先介紹下廣播模式的實現示例。 1. Maven 引入相關依賴 這裏使用 Maven 工具管理依賴包,分別引入下面依賴: lombok: Lombok 工具依賴,便於生成實體對象的 Get 與 Set 方法。 spring-boot-starter-websocket:SpringBoot 實現 WebSocket 的依賴,裏面對 WebSocket 進行了一些列封裝,並且也包含了 SpringBoot Web 依賴。                             org.springframework.boot            spring-boot-starter-websocket                                    org.projectlombok            lombok            provided         2. 創建測試實體類 創建便於傳輸消息的實體類,裏面字段內容如下: import lombok.Data;@Datapublic class MessageBody {    /** 消息內容 */    private String content;    /** 廣播轉發的目標地址(告知 STOMP 代理轉發到哪個地方) */    private String destination;} 3. 創建 WebSocket 配置類 創建 WebSocket 配置類,配置進行連接註冊的端點 /mydlq 和消息代理前綴 /topic 及接收客户端發送消息的前綴 /app。 @Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {    /**     * 配置 WebSocket 進入點,及開啓使用 SockJS,這些配置主要用配置連接端點,用於 WebSocket 連接     *     * @param registry STOMP 端點     */    @Override    public void registerStompEndpoints(StompEndpointRegistry registry) {        registry.addEndpoint("/mydlq").withSockJS();    }    /**     * 配置消息代理選項     *     * @param registry 消息代理註冊配置     */    @Override    public void configureMessageBroker(MessageBrokerRegistry registry) {        // 設置一個或者多個代理前綴,在 Controller 類中的方法裏面發生的消息,會首先轉發到代理從而發送到對應廣播或者隊列中。        registry.enableSimpleBroker("/topic");        // 配置客户端發送請求消息的一個或多個前綴,該前綴會篩選消息目標轉發到 Controller 類中註解對應的方法裏        registry.setApplicationDestinationPrefixes("/app");    }} 4. 創建測試 Controller 類 創建 Controller 類,該類也類似於正常 Web 項目中 Controller 寫法一樣,在方法上面添加 @MessageMapping 註解,當客户端發送消息請求的前綴匹配上 WebSocket 配置類中的 /app 前綴後,會進入到 Controller 類中進行匹配,如果匹配成功則執行註解所在的方法內容。 @Controllerpublic class MessageController {    /** 消息發送工具對象 */    @Autowired    private SimpMessageSendingOperations simpMessageSendingOperations;    /** 廣播發送消息,將消息發送到指定的目標地址 */    @MessageMapping("/test")    public void sendTopicMessage(MessageBody messageBody) {        // 將消息發送到 WebSocket 配置類中配置的代理中(/topic)進行消息轉發        simpMessageSendingOperations.convertAndSend(messageBody.getDestination(), messageBody);    }} 5. 創建測試腳本 創建用於操作 WebSocket 的 JS 文件 app-websocket.js,內容如下: // 設置 STOMP 客户端var stompClient = null;// 設置 WebSocket 進入端點var SOCKET_ENDPOINT = "/mydlq";// 設置訂閲消息的請求前綴var SUBSCRIBE_PREFIX = "/topic"// 設置訂閲消息的請求地址var SUBSCRIBE = "";// 設置服務器端點,訪問服務器中哪個接口var SEND_ENDPOINT = "/app/test";/* 進行連接 */function connect() {    // 設置 SOCKET    var socket = new SockJS(SOCKET_ENDPOINT);    // 配置 STOMP 客户端    stompClient = Stomp.over(socket);    // STOMP 客户端連接    stompClient.connect({}, function (frame) {        alert("連接成功");    });}/* 訂閲信息 */function subscribeSocket(){    // 設置訂閲地址    SUBSCRIBE = SUBSCRIBE_PREFIX + $("#subscribe").val();    // 輸出訂閲地址    alert("設置訂閲地址為:" + SUBSCRIBE);    // 執行訂閲消息    stompClient.subscribe(SUBSCRIBE, function (responseBody) {        var receiveMessage = JSON.parse(responseBody.body);        $("#information").append("" + receiveMessage.content + "");    });}/* 斷開連接 */function disconnect() {    stompClient.disconnect(function() {        alert("斷開連接");    });}/* 發送消息並指定目標地址(這裏設置的目標地址為自身訂閲消息的地址,當然也可以設置為其它地址) */function sendMessageNoParameter() {    // 設置發送的內容    var sendContent = $("#content").val();    // 設置待發送的消息內容    var message = '{"destination": "' + SUBSCRIBE + '", "content": "' + sendContent + '"}';    // 發送消息    stompClient.send(SEND_ENDPOINT, {}, message);} 6. 創建 WebSocket HTML 創建用於展示 WebSocket 相關功能的 WEB HTML 頁面 index.html,內容如下:     Hello WebSocket                                                                                                            WebSocket 連接:                        進行連接                        斷開連接                                        訂閲地址:                                                                                    訂閲                                                                                發送的消息內容:                                        發送                                                    接收到的消息:                                                                             7. 啓動並進行測試 輸入地址 //localhost:8080/index.html 訪問測試的前端頁面,然後執行下面步驟進行測試: 點擊 進行連接按鈕,連接 WebSocket 服務端; 在訂閲地址欄輸入 訂閲地址(因為本人設置的訂閲地址和接收消息的地址是一個,所以隨意輸入); 點擊 訂閲按鈕訂閲對應地址的消息; 在發送消息內容的輸入框中輸入 hello world!,然後點擊 發送按鈕發送消息; 執行完上面步驟成後,可以觀察到成功接收到訂閲地址的消息,如下: 十. 示例二:實現點對點模式(引入 Spring Security 實現鑑權) 1. Maven 引入相關依賴 這裏使用 Maven 工具管理依賴包,分別引入下面依賴: lombok: Lombok 工具依賴,便於生成實體對象的 Get 與 Set 方法。 spring-boot-starter-websocket:SpringBoot 實現 WebSocket 的依賴,裏面對 WebSocket 進行了一些列封裝,並且也包含了 SpringBoot Web 依賴。 spring-boot-starter-security:Spring Security,這是一種基於 Spring AOP 和 Servlet 過濾器的安全框架。它提供全面的安全性解決方案,同時在 Web 請求級和方法調用級處理身份確認和授權。                             org.springframework.boot            spring-boot-starter-websocket                                    org.springframework.boot            spring-boot-starter-security                                    org.projectlombok            lombok            provided             2. 創建測試實體類 創建便於傳輸消息的實體類,裏面字段內容如下: @Datapublic class MessageBody {    /** 發送消息的用户 */    private String from;    /** 消息內容 */    private String content;    /** 目標用户(告知 STOMP 代理轉發到哪個用户) */    private String targetUser;    /** 廣播轉發的目標地址(告知 STOMP 代理轉發到哪個地方) */    private String destination;} 3. 創建 WebSocket 配置類 創建 WebSocket 配置類,配置進行連接註冊的端點/mydlq 和消息代理前綴 /queue 及接收客户端發送消息的前綴 /app。 @Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {    /**     * 配置 WebSocket 進入點,及開啓使用 SockJS,這些配置主要用配置連接端點,用於 WebSocket 連接     *     * @param registry STOMP 端點     */    @Override    public void registerStompEndpoints(StompEndpointRegistry registry) {        registry.addEndpoint("/mydlq").withSockJS();    }    /**     * 配置消息代理選項     *     * @param registry 消息代理註冊配置     */    @Override    public void configureMessageBroker(MessageBrokerRegistry registry) {        // 設置一個或者多個代理前綴,在 Controller 類中的方法裏面發生的消息,會首先轉發到代理從而發送到對應廣播或者隊列中。        registry.enableSimpleBroker("/queue");        // 配置客户端發送請求消息的一個或多個前綴,該前綴會篩選消息目標轉發到 Controller 類中註解對應的方法裏        registry.setApplicationDestinationPrefixes("/app");        // 服務端通知特定用户客户端的前綴,可以不設置,默認為user        registry.setUserDestinationPrefix("/user");    }} 5. 創建 Security 配置 Spring Security 的配置類,可以在該類中配置權限認證及測試的兩個用户相關信息: 測試用户名/密碼1:mydlq1/123456 測試用户名/密碼2:mydlq2/123456 @Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {    /**     * 設置密碼編碼的配置參數,這裏設置為 NoOpPasswordEncoder,不配置密碼加密,方便測試。     *     * @return 密碼編碼實例     */    @Bean    PasswordEncoder passwordEncoder() {        return NoOpPasswordEncoder.getInstance();    }    /**     * 設置權限認證參數,這裏用於創建兩個用於測試的用户信息。     *     * @param auth SecurityBuilder 用於創建 AuthenticationManager。     * @throws Exception 拋出的異常     */    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.inMemoryAuthentication()                .withUser("mydlq1")                .password("123456")                .roles("admin")                .and()                .withUser("mydlq2")                .password("123456")                .roles("admin");    }    /**     * 設置 HTTP 安全相關配置參數     *     * @param http HTTP Security 對象     * @throws Exception 拋出的異常信息     */    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                .anyRequest().authenticated()                .and()                .formLogin()                .permitAll();    }} 5. 創建測試 Controller 類 跟上面介紹廣播模式一樣,作用也是根據 WebSocket 配置類中 /app 前綴匹配後進入 Controller 類進行邏輯處理操作。 @Controllerpublic class MessageController {    @Autowired    private SimpMessageSendingOperations simpMessageSendingOperations;    /**     * 點對點發送消息,將消息發送到指定用户     */    @MessageMapping("/test")    public void sendUserMessage(Principal principal, MessageBody messageBody) {        // 設置發送消息的用户        messageBody.setFrom(principal.getName());        // 調用 STOMP 代理進行消息轉發        simpMessageSendingOperations.convertAndSendToUser(messageBody.getTargetUser(), messageBody.getDestination(), messageBody);    }} 6. 創建 WebSocket JS 創建用於操作 WebSocket 的 JS 文件 app-websocket.js,內容如下: // 設置 STOMP 客户端var stompClient = null;// 設置 WebSocket 進入端點var SOCKET_ENDPOINT = "/mydlq";// 設置訂閲消息的請求前綴var SUBSCRIBE_PREFIX = "/topic"// 設置訂閲消息的請求地址var SUBSCRIBE = "";// 設置服務器端點,訪問服務器中哪個接口var SEND_ENDPOINT = "/app/test";/* 進行連接 */function connect() {    // 設置 SOCKET    var socket = new SockJS(SOCKET_ENDPOINT);    // 配置 STOMP 客户端    stompClient = Stomp.over(socket);    // STOMP 客户端連接    stompClient.connect({}, function (frame) {        alert("連接成功");    });}/* 訂閲信息 */function subscribeSocket(){    // 設置訂閲地址    SUBSCRIBE = SUBSCRIBE_PREFIX + $("#subscribe").val();    // 輸出訂閲地址    alert("設置訂閲地址為:" + SUBSCRIBE);    // 執行訂閲消息    stompClient.subscribe(SUBSCRIBE, function (responseBody) {        var receiveMessage = JSON.parse(responseBody.body);        $("#information").append("" + receiveMessage.content + "");    });}/* 斷開連接 */function disconnect() {    stompClient.disconnect(function() {        alert("斷開連接");    });}/* 發送消息並指定目標地址 */function sendMessageNoParameter() {    // 設置發送的內容    var sendContent = $("#content").val();    // 設置待發送的消息內容    var message = '{"destination": "' + SUBSCRIBE + '", "content": "' + sendContent + '"}';    // 發送消息    stompClient.send(SEND_ENDPOINT, {}, message);} 7. 創建 WebSocket HTML 創建用於展示 WebSocket 相關功能的 WEB HTML 頁面 index.html,內容如下:     Hello WebSocket                                                                                                            WebSocket 連接:                        進行連接                        斷開連接                                        訂閲地址:                                                                                    訂閲                                                                                發送的消息內容:                                        發送                                                    接收到的消息:                                                                             8. 啓動並進行測試 為了方便測試,需要打開兩個不同類型瀏覽器(因為用户登錄後會存 Session,如果一個瀏覽器不同用户登錄會使之前 Session 失效)來進行測試,兩個瀏覽器同時輸入地址 //localhost:8080/index.html 訪問測試的前端頁面,然後可以看到並沒有進入 /index.html 頁面,而是跳轉到Spring Security 提供的登錄的 /login 頁面,如下: 兩個瀏覽器中都輸入用户名/密碼 mydlq1/123456 與 mydlq2/123456 進行登錄,然後會回到 /index.html 頁面,然後執行下面步驟進行測試: ”瀏覽器1”和”瀏覽器2”點擊”進行連接”按鈕,連接 WebSocket 服務端; ”瀏覽器1”和”瀏覽器2”中同時設置訂閲地址為”/abc”,然後點擊訂閲按鈕進行消息訂閲; ”瀏覽器1”(用户 mydlq1)設置發送目標用户為”/mydlq2”,”瀏覽器2”(用户 mydlq2)設置發送目標用户為”/mydlq1”; ”瀏覽器1”(用户 mydlq1)設置發送消息為Hi, I’m mydlq1,”瀏覽器2”(用户 mydlq2)設置發送消息為Hi, I’m mydlq2; 點擊發送按鈕發送消息; 執行完上面步驟成後,可以在兩個不同瀏覽器中觀察到如下內容: 十一. 示例三:實現點對點模式(根據請求頭 Header 實現鑑權) 1. Maven 引入相關依賴 “ 同示例二 2. 創建測試實體類 @Datapublic class MessageBody {    /** 發送消息的用户 */    private String from;    /** 消息內容 */    private String content;    /** 目標用户(告知 STOMP 代理轉發到哪個用户) */    private String targetUser;    /** 廣播轉發的目標地址(告知 STOMP 代理轉發到哪個地方) */    private String destination;}@Data@AllArgsConstructorpublic class User {    private String username;    private String token;} 3. 配置 WebSocket 通道攔截器 配置 WebSocket 通道攔截器,裏面添加兩個模擬用户: 用户 mydlq1, Token:123456-1 用户 mydlq2, Token:123456-2 /** * WebSocket 通道攔截器(這裏模擬兩個測試 Token 方便測試,不做具體 Token 鑑權實現) * * @author mydlq */public class MyChannelInterceptor implements ChannelInterceptor {    /** 測試用户與 token 1 */    private User mydlq1 = new User("","123456-1");    /** 測試用户與 token 2 */    private User mydlq2 = new User("","123456-2");    /**     * 從 Header 中獲取 Token 進行驗證,根據不同的 Token 區別用户     *     * @param message 消息對象     * @param channel 通道對象     * @return 驗證後的用户信息     */    @Override    public Message preSend(Message message, MessageChannel channel) {        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);        String token = getToken(message);        if (token!=null && accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) {            Principal user = null;            // 提前創建好兩個測試 token 進行匹配,方便測試            if (mydlq1.getToken().equals(token)){                user = () -> mydlq1.getUsername();            } else if (mydlq2.getToken().equals(token)){                user = () -> mydlq2.getUsername();            }            accessor.setUser(user);        }        return message;    }    /**     * 從 Header 中獲取 TOKEN     *     * @param message 消息對象     * @return TOKEN     */    private String getToken(Message message){        Map headers = (Map) message.getHeaders().get("nativeHeaders");        if (headers !=null && headers.containsKey("token")){            List token = (List)headers.get("token");            return String.valueOf(token.get(0));        }        return null;    }} 4. 創建 WebSocket 配置類 創建 WebSocket 配置類,配置進行連接註冊的端點 /mydlq 和消息代理前綴 /queue 及接收客户端發送消息的前綴 /app。 @Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {    /**     * 配置 WebSocket 進入點,及開啓使用 SockJS,這些配置主要用配置連接端點,用於 WebSocket 連接     *     * @param registry STOMP 端點     */    @Override    public void registerStompEndpoints(StompEndpointRegistry registry) {        registry.addEndpoint("/mydlq").withSockJS();    }    /**     * 配置消息代理選項     *     * @param registry 消息代理註冊配置     */    @Override    public void configureMessageBroker(MessageBrokerRegistry registry) {        // 設置一個或者多個代理前綴,在 Controller 類中的方法裏面發生的消息,會首先轉發到代理從而發送到對應廣播或者隊列中。        registry.enableSimpleBroker("/queue");        // 配置客户端發送請求消息的一個或多個前綴,該前綴會篩選消息目標轉發到 Controller 類中註解對應的方法裏        registry.setApplicationDestinationPrefixes("/app");        // 服務端通知特定用户客户端的前綴,可以不設置,默認為user        registry.setUserDestinationPrefix("/user");    }    /**     * 配置通道攔截器,用於獲取 Header 的 Token 進行鑑權     *     * @param registration 註冊通道配置類     */    @Override    public void configureClientInboundChannel(ChannelRegistration registration) {        registration.interceptors(new MyChannelInterceptor());    }} 5. 創建測試 Controller 類 @Controllerpublic class MessageController {    @Autowired    private SimpMessageSendingOperations simpMessageSendingOperations;    /**     * 點對點發送消息,將消息發送到指定用户     */    @MessageMapping("/test")    public void sendUserMessage(Principal principal, MessageBody messageBody) {        // 設置發送消息的用户        messageBody.setFrom(principal.getName());        // 調用 STOMP 代理進行消息轉發        simpMessageSendingOperations.convertAndSendToUser(messageBody.getTargetUser(), messageBody.getDestination(), messageBody);    }} 6. 創建 WebSocket JS 創建用於操作 WebSocket 的 JS 文件 app-websocket.js,內容如下: // 設置 STOMP 客户端var stompClient = null;// 設置 WebSocket 進入端點var SOCKET_ENDPOINT = "/mydlq";// 設置訂閲消息的請求地址前綴var SUBSCRIBE_PREFIX  = "/queue";// 設置訂閲地址var SUBSCRIBE = "";// 設置服務器端點,訪問服務器中哪個接口var SEND_ENDPOINT = "/app/test";/* 進行連接 */function connect() {    // 設置 SOCKET    var socket = new SockJS(SOCKET_ENDPOINT);    // 配置 STOMP 客户端    stompClient = Stomp.over(socket);    // 獲取 TOKEN    var myToken = $("#myToken").val();    // STOMP 客户端連接    stompClient.connect({token: myToken}, function (frame) {        alert("連接成功");    });}/* 訂閲信息 */function subscribeSocket(){    // 設置訂閲地址    SUBSCRIBE = SUBSCRIBE_PREFIX + $("#subscribe").val();    // 輸出訂閲地址    alert("設置訂閲地址為:" + SUBSCRIBE);    // 執行訂閲消息    stompClient.subscribe("/user" + SUBSCRIBE, function (responseBody) {        var receiveMessage = JSON.parse(responseBody.body);        console.log(receiveMessage);        $("#information").append("" + receiveMessage.content + "");    });}/* 斷開連接 */function disconnect() {    stompClient.disconnect(function() {        alert("斷開連接");    });}/* 發送消息並指定目標地址 */function sendMessageNoParameter() {    // 設置發送的內容    var sendContent = $("#content").val();    // 設置發送的用户    var sendUser = $("#targetUser").val();    // 設置待發送的消息內容    var message = '{"targetUser":"' + sendUser + '", "destination": "' + SUBSCRIBE + '", "content": "' + sendContent + '"}';    // 發送消息    stompClient.send(SEND_ENDPOINT, {}, message);} 7. 創建 WebSocket HTML 創建用於展示 WebSocket 相關功能的 WEB HTML 頁面 index.html,內容如下:     Hello WebSocket                                                                                    WebSocket 連接:                    進行連接                    斷開連接                                訂閲地址:                                                                    訂閲                                                    TOKEN 信息:                        發送的目標用户:                        發送的消息內容:                            發送                                接收到的消息:                                                     8. 啓動並進行測試 為了方便測試,需要打開兩個不同類型瀏覽器(這裏模擬通過 Header 傳 Token 的方式進行用户驗證,具體登錄邏輯不實現,而是直接使用事先配置好的兩個用户 Token 進行模擬)來進行測試,兩個瀏覽器同時輸入地址 //localhost:8080/index.html 訪問測試的前端頁面 ``/index.html` 如下: 瀏覽器1: 用户:mydlq1 Token:123456789-1 瀏覽器2: 登錄的用户:mydlq2 Token:123456789-2 兩個瀏覽器中都執行下面步驟進行測試: 瀏覽器1和 瀏覽器2點擊 進行連接按鈕,連接 WebSocket 服務端; 瀏覽器1和 瀏覽器2中同時設置訂閲地址為 /abc,然後點擊訂閲按鈕進行消息訂閲; 瀏覽器1(用户 mydlq1)在 TOken 信息一欄中填寫模擬用户 mydlq1 的 Token 串, 瀏覽器2(用户 mydlq2)填寫模擬用户 mydlq2 的 Token 串; 瀏覽器1(用户 mydlq1)設置發送目標用户為 /mydlq2, 瀏覽器2(用户 mydlq2)設置發送目標用户為 /mydlq1; 瀏覽器1(用户 mydlq1)設置發送消息為 Hi, I’m mydlq1, 瀏覽器2(用户 mydlq2)設置發送消息為 Hi, I’m mydlq2; 點擊 發送按鈕發送消息; 執行完上面步驟成後,可以在兩個不同瀏覽器中觀察到如下內容: 十二. SpringBoot 結合 WebSocket 的常用方法示例 1. WebSocket 開啓跨域選項 WebSocket 配置類,裏面設置允許跨域,內容如下: @Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {    @Override    public void configureMessageBroker(MessageBrokerRegistry registry) {        registry.enableSimpleBroker("/queue");        registry.setApplicationDestinationPrefixes("/app");    }    @Override    public void registerStompEndpoints(StompEndpointRegistry registry) {        registry.addEndpoint("/mydlq")        // 設置允許跨域,設置為"*"則為允許全部域名        .setAllowedOrigins("*")        .withSockJS();    }} 2. WebSocket 用户上、下線監聽 創建 WebSocket 用户上線、下線處理器,內容如下: @Configurationpublic class HttpWebSocketHandlerDecoratorFactory implements WebSocketHandlerDecoratorFactory {    /**     * 配置 webSocket 處理器     *     * @param webSocketHandler webSocket 處理器     * @return webSocket 處理器     */    @Override    public WebSocketHandler decorate(WebSocketHandler webSocketHandler) {        return new WebSocketHandlerDecorator(webSocketHandler) {            /**             * websocket 連接時執行的動作             * @param session    websocket session 對象             * @throws Exception 異常對象             */            @Override            public void afterConnectionEstablished(final WebSocketSession session) throws Exception {                // 輸出進行 websocket 連接的用户信息                if (session.getPrincipal() != null) {                    String username = session.getPrincipal().getName();                    System.out.println("用户:" + username + "上線");                    super.afterConnectionEstablished(session);                }            }            /**             * websocket 關閉連接時執行的動作             * @param session websocket session 對象             * @param closeStatus 關閉狀態對象             * @throws Exception 異常對象             */            @Override            public void afterConnectionClosed(final WebSocketSession session, CloseStatus closeStatus) throws Exception {                // 輸出關閉 websocket 連接的用户信息                if (session.getPrincipal() != null) {                    String username = session.getPrincipal().getName();                    System.out.println("用户:" + username + "下線");                    super.afterConnectionClosed(session, closeStatus);                }            }        };    }} WebSocket 配置類中實現 configureWebSocketTransport() 方法,將上面 WebSocket 處理器加到其中,如下: @Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {    @Override    public void configureMessageBroker(MessageBrokerRegistry registry) {        registry.enableSimpleBroker("/queue");        registry.setApplicationDestinationPrefixes("/app");    }    @Override    public void registerStompEndpoints(StompEndpointRegistry registry) {        registry.addEndpoint("/mydlq").withSockJS();    }    /**     * 添加 WebSocket 用户上、下線監聽器     */    @Override    public void configureWebSocketTransport(WebSocketTransportRegistration registry) {        registry.addDecoratorFactory(new HttpWebSocketHandlerDecoratorFactory());    }} 十三. 總結 本文從原理到實踐詳細的介紹了WebSocket,希望你們喜歡..... 特別推薦一個分享架構+算法的優質內容,還沒關注的小夥伴,可以長按關注一下:長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-12-27 關鍵詞: 網絡 計算機

  • 數據處理,你不得不會的「正則表達式」

    若要判斷一個輸入的QQ號是否有效,你會如何處呢? 首先你得分析一下其對應規則,依次列出: 長度大於5,小於等於11; 首位不能為0; 是否為純數字? 規則既列,接着就該嘗試實現了,那麼用什麼來表示字符串呢?在C++中,最容易想到的就是string了,其中提供了許多成員函數可以處理字符串,所以有了如下實現: 1std::string qq; 2std::cin >> qq; 3 4// 1. 判斷位數是否合法 5if (qq.length() >= 5 && qq.length() 

    時間:2020-12-27 關鍵詞: 數據 計算機

  • 一個依賴搞定Spring Boot反爬蟲,防止接口盜刷!

    來源:oschina.net/news/112586/kk-anti-reptile-released 正文   kk-anti-reptile 是適用於基於 spring-boot 開發的分佈式系統的反爬蟲組件。 系統要求 基於 spring-boot 開發(spring-boot1.x, spring-boot2.x均可) 需要使用 redis 工作流程 kk-anti-reptile 使用基於 Servlet 規範的的 Filter 對請求進行過濾,在其內部通過 spring-boot 的擴展點機制,實例化一個 Filter,並注入到 Spring 容器 FilterRegistrationBean 中,通過 Spring 注入到 Servlet 容器中,從而實現對請求的過濾。 在 kk-anti-reptile 的過濾 Filter 內部,又通過責任鏈模式,將各種不同的過濾規則織入,並提供抽象接口,可由調用方進行規則擴展。 Filter 調用則鏈進行請求過濾,如過濾不通過,則攔截請求,返回狀態碼 509,並輸出驗證碼輸入頁面,輸出驗證碼正確後,調用過濾規則鏈對規則進行重置。 目前規則鏈中有如下兩個規則 ip-rule ip-rule 通過時間窗口統計當前時間窗口內請求數,小於規定的最大請求數則可通過,否則不通過。時間窗口、最大請求數、ip 白名單等均可配置。 ua-rule ua-rule 通過判斷請求攜帶的 User-Agent,得到操作系統、設備信息、瀏覽器信息等,可配置各種維度對請求進行過濾。 命中規則後 命中爬蟲和防盜刷規則後,會阻斷請求,並生成接除阻斷的驗證碼,驗證碼有多種組合方式,如果客户端可以正確輸入驗證碼,則可以繼續訪問 驗證碼有中文、英文字母+數字、簡單算術三種形式,每種形式又有靜態圖片和 GIF 動圖兩種圖片格式,即目前共有如下六種,所有類型的驗證碼會隨機出現,目前技術手段識別難度極高,可有效阻止防止爬蟲大規模爬取數據 接入使用 後端接入非常簡單,只需要引用 kk-anti-reptile 的 maven 依賴,並配置啓用 kk-anti-reptile 即可加入 maven 依賴 <dependency>    <groupId>cn.keking.projectgroupId>    <artifactId>kk-anti-reptileartifactId>    <version>1.0.0-SNAPSHOTversion>dependency> 配置啓用 kk-anti-reptile anti.reptile.manager.enabled=true 前端需要在統一發送請求的 ajax 處加入攔截,攔截到請求返回狀態碼 509 後彈出一個新頁面,並把響應內容轉出到頁面中,然後向頁面中傳入後端接口 baseUrl 參數即可,以使用 axios 請求為例: import axios from 'axios';import {baseUrl} from './config';axios.interceptors.response.use(  data => {    return data;  },  error => {    if (error.response.status === 509) {      let html = error.response.data;      let verifyWindow = window.open("","_blank","height=400,width=560");      verifyWindow.document.write(html);      verifyWindow.document.getElementById("baseUrl").value = baseUrl;    }  });export default axios; 注意 apollo-client 需啓用 bootstrap 使用 apollo 配置中心的用户,由於組件內部用到 @ConditionalOnProperty,要在 application.properties/bootstrap.properties 中加入如下樣例配置,(apollo-client 需要 0.10.0 及以上版本)詳見 apollo bootstrap 説明 apollo.bootstrap.enabled = true 需要有 Redisson 連接如果項目中有用到 Redisson,kk-anti-reptile 會自動獲取 RedissonClient 實例對象; 如果沒用到,需要在配置文件加入如下 Redisson 連接相關配置: spring.redisson.address=redis://192.168.1.204:6379spring.redisson.password=xxx 配置一覽表 在 spring-boot 中,所有配置在配置文件都會有自動提示和説明,如下圖: 所有配置都以 anti.reptile.manager 為前綴,如下為所有配置項及説明: 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝

    時間:2020-12-27 關鍵詞: 軟件 計算機

  • 32位、64位操作系統系統差異對比

    轉自 | 程序喵大人 想必大家都遇到過這樣的問題:安裝某個軟件的時候,出現提示選擇32位版本還是64位版本? 我們也可以查看自己的電腦是32位還是64位系統: Windows Linux 大家可能知道32位和64位和系統有關,但其實 32 vs 64 可以有多重含義。 一般情況下,有以下幾種可能: PU 、 程序 、 操作系統 。 今天我來給大家一一介紹下: CPU 首先,我們要先從什麼是位數講起。 計算機中的位數指的是CPU一次能處理的最大位數。在Intel由16位的286升級到386的時候,為了和16位系統兼容,它先推出的是386SX,這種CPU內部預算為32位,外部數據傳輸為16位。直到386DX以後,所有的CPU在內部和外部都是32位的了。 有些人往往會弄不清在計算機中出現的“位”和Byte,KB,MB等有何關係,8位等於一字節Byte,即8bit=1B。32位處理器每次最多處理4Byte(32bit),同理,64位處理器每次最多處理 8Byte(64bit) 。 32位架構的CPU數據總線寬度是32位,每次可以傳輸32位數據,可以計算4個字節。 64位架構的CPU數據總線寬度是64位,每次可以傳輸64位數據,可以計算8個字節。 數據總線 數據總線是CPU與內存或其它器件之間的數據傳輸的通道,數據總線的寬度決定了CPU和外界的數據傳輸速度,每根線可以傳輸1位二進制數據,32根線每次就可以傳輸32位數據,64根線每次就可以傳輸64位數據。除了數據總線外還有地址總線和控制總線。 地址總線 CPU通過地址總線來指定存儲單元,地址總線的寬度決定了CPU所能訪問的最大內存空間大小,1根地址線能訪問的內存空間是1bit,32根線訪問的最大內存空間是4G,64根線...太大了。 控制總線 CPU通過控制總線對外部器件進行控制,主要通過控制總線來傳輸控制信號和時序信號,控制總線是各種信號線的集合,是計算機各部件之間傳送數據、地址和控制信息的公共通道,控制總線的寬度決定了CPU對外部器件的控制能力。 總體來説 ,CPU作為總線的主控,通過控制總線向各個外部器件發送控制信號,通過地址總線訪問內存地址,通過數據總線傳輸數據。 CPU的位數越大,可以計算的數值就越大,64位CPU可以執行更大數字的運算,但這個優勢在普通應用上不太明顯,普通應用也沒必要進行太大數字的運算,但是對於數值計算較多的應用就非常明顯。同時64位CPU有更大的尋址空間。 運算速度不同:64位CPU的通用寄存器數據寬度是64位,處理器依次可以讀取64位數據,比32位多一倍,運算速度理論上會提升一倍。像兩個不同的打工人一樣,打工人A一次性可以搬32塊磚,打工人B一次性可以搬64塊磚,誰搬磚的速度快,自然你更喜歡用誰。由於運算速度提升,64位CPU可以在多任務中運行順暢,來回切換也不會卡頓,王者榮耀都順暢多了。 那麼有朋友要問了,既然位數越高處理器運算速度越快,為什麼不用128位、256位的CPU?因為位數越高,處理器芯片的設計也就越複雜,當前的科技水平還無法制造這麼複雜的CPU。 程序 指的是32位程序和64位程序。 32位指令的程序一般來説可以在64位機器上運行,可以兼容。 64位指令的程序不可以在32位機器上運行,因為32位的寄存器存不下64位的指令。 注意其實還有16位的程序,但16位的程序不能運行在64位的機器上,因為沒有提供兼容機制。 操作系統 操作系統其實也是程序,64位的操作系統使用的是64位的指令,不能安裝在32位機器上。 設計初衷不同 64位操作系統的設計初衷是為了滿足需要大量內存和複雜浮點數運算的需求,一般用在科學計算、人工智能、平面設計、視頻處理、3D動畫和遊戲數據庫以及各種網絡服務器等領域中。 安裝環境不同 64位操作系統只能安裝在64位CPU的機器上,同時需要配合64位的程序才能發揮最佳性能,32位操作系統既可以安裝在32位CPU的機器上,也可以安裝在64位CPU的機器上,但沒啥意義,64位的性能會被大打折扣。 尋址能力不同 32位操作系統最多可以尋址2的32次方即4,294,967,296字節,約4GB內存,4GB的內存就現在而言在很多服務端程序上都是不夠用的,而64位操作系統理論上可以尋址2的64次方即18,446,744,073,709,551,616字節超過1億GB內存,但這只是理論上,由於不同架構的CPU設計不同,所以尋址能力也有錯差別。 32位操作系統和64位操作系統下數據類型對應的字節大小也是不同的,正常數據類型對應的字節數應該是CPU位數決定的,但實際上貌似是由編譯器決定的,看下錶: 32位操作系統 64位操作系統 char 1個字節 1個字節 short int 2個字節 2個字節 int 4個字節 4個字節 unsigned int 4個字節 4個字節 float 4個字節 4個字節 double 8個字節 8個字節 long(指針大小) 4個字節 8個字節 long long 8個字節 8個字節 unsigned long 4個字節 8個字節 20世紀和21世紀早期製造的計算機大多都是32位的機器,現如今大多都是64位的機器了,但為了兼容32位機器,一般編程都會開發出兩個版本,例如打包一個Android SDK,SDK內部一般都會包含32位和64位的動態鏈接庫,iOS的Framework內部也會有32位和64位的靜態鏈接庫。 總結 32位處理器的優點唯一值得一提就是它可以與20世紀末和21世紀初開發的所有舊程序兼容。64位處理器可以提升程序的性能,更好的利用64位操作系統的特性。64位的處理器有256TB的虛擬內存,其中128TB分配給了用户空間,另外128TB分配給了內核空間(不同架構的CPU虛擬內存的設計大小不同,AMD的x86-64架構的CPU只有最低48位才會在地址轉換時被使用,所以總的虛擬地址空間為2的48次方即256TB,這裏涉及到多級頁表中的四級頁表,後續程序喵會介紹)。 要實現真正意義上的64位計算,光有64位的處理器是不行的,還必須得有64位的操作系統以及64位的應用軟件才行,三者缺一不可,缺少其中任何一種要素都是無法實現64位計算的。在64位處理器方面,Intel和AMD兩大處理器遞四方香港都發布了多個系列多種規格的64位處理器; 參考資料: //blog.51cto.com/zliang90/1282301 //www.zhihu.com/question/19862280 //blog.csdn.net/qing101hua/article/details/80763764 //www.guru99.com/32-bit-vs-64-bit-operating-systems.html //www.educba.com/32-bit-vs-64-bit-operating-system/ //www.werecoverdata.com/blog/whats-better-64-bit-vs-32-bit/ //www.geeksforgeeks.org/difference-32-bit-64-bit-operating-systems/ ------------ END ------------ 關注

    時間:2020-12-27 關鍵詞: 嵌入式 計算機

  • 如果MySQL磁盤滿了,會發生什麼?

    來源://testerhome.com/topics/23049 問題 使用命令發現磁盤使用率為100%了,還剩幾十兆。 一系列神操作 備份數據庫,刪除實例、刪除數據庫表、重啓mysql服務.結果磁盤空間均未釋放 怎麼辦 網上查了很多資源,説要進行磁盤碎片化整理。原因是datafree佔據的空間太多啦。具體可以通過這個sql查看。 SELECT CONCAT(TRUNCATE(SUM(data_length)/1024/1024,2),'MB') AS data_size,CONCAT(TRUNCATE(SUM(max_data_length)/1024/1024,2),'MB') AS max_data_size,CONCAT(TRUNCATE(SUM(data_free)/1024/1024,2),'MB') AS data_free,CONCAT(TRUNCATE(SUM(index_length)/1024/1024,2),'MB') AS index_sizeFROM information_schema.tables WHERE TABLE_NAME = 'datainfo'; 這個是後來的圖了,之前的圖沒有留,當時顯示一張表裏的data_free都達到了20個G。 網上推薦的做法如下所示,對錶格進行碎片化整理。 ALTER TABLE datainfo ENGINE=InnoDB;ANALYZE TABLE datainfo;optimize table datainfo; 僵局 查看數據庫版本為5.562不支持inodb,要麼選擇升級數據庫。正在這時,有個不好的消息發生了,那張表格給刪掉了,但是磁盤空間還是沒有釋放啊。所以對錶進行碎片化整理的路也走不通了,因為表沒了。。。 後來的神操作 使用命令查看mysql安裝的位置和配置文件所在的地方 mysql 1118 945 0 14:28 ? 00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mysqld.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/lib/mysql/mysql.sock 關閉mysql service mysql stop   刪除datadir目錄下的ibdata1、ib_logfile0 ib_logfile1這些文件 移動mysql的啓動參數 mv /etc/my.cnf ./abc 重新啓動mysql 發現磁盤空間釋放了 service mysql start 磁盤空間終於釋放了 下一步數據庫還原 採用navicate備份工具,進行數據庫備份 備份成功後生成了,生成psc文件200409141055.psc。 新建一個數據庫實例,設置數據庫名和字符集 然後對備份數據庫進行還原,點擊還原 開始進行還原 第一次還原後發現還原後數據庫表建成功了,但是表裏面沒有數據。後來網上查找資料發現是,遇到錯誤就停止了。所以更改了還原的配置,再次進行還原。之前是這樣設置的 還原時當成一個事務進行了,遇到錯誤就停止了。更改配置 重新進行還原,數據庫裏的數據有了,並且驗證沒有問題。 問題解決 mysql碎片化產生的原因 表的存儲會出現碎片化,每當刪除了一行內容,該段空間就會變為被留空,而在一段時間內的大量刪除操作,會使這種留空的空間變得比存儲列表內容所使用的空間更大; 當執行插入操作時,MySQL會嘗試使用空白空間,但如果某個空白空間一直沒有被大小合適的數據佔用,仍然無法將其徹底佔用,就形成了碎片; 當MySQL對數據進行掃描時,它掃描的對象實際是列表的容量需求上限,也就是數據被寫入的區域中處於峯值位置的部分; 清除碎片的優點 降低訪問表時的IO,提高mysql性能,釋放表空間降低磁盤空間使用率 MySQL官方建議不要經常(每小時或每天)進行碎片整理,一般根據實際情況,只需要每週或者每月整理一次即可(我們現在是每月凌晨4點清理mysql所有實例下的表碎片)。 在OPTIMIZE TABLE運行過程中,MySQL會鎖定表。因此,這個操作一定要在網站訪問量較少的時間段進行。 清理student的105萬條數據, OPTIMIZE TABLE 庫.student;本地測試需要37秒。 自測 大家可以用這條語句看看自己的系統的datafree大不大 show table status from 表名; 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-12-25 關鍵詞: MySQL 計算機

  • 計算機網絡原理超詳解説

    計算機網絡原理超詳解説 前言 大家好,我是泰斗賢若如,一個專注於用大白話講解技術的號主,這次給大家分享計算機網絡原理的相關知識,我自認為文章內容已經很通俗易懂了,祝您閲讀愉快! 一、計算機網絡概述 時代不同了,大家現在生活都好了,家家户户基本都有電腦,而且我們都習慣了使用電腦辦公,打打遊戲,聊聊天等等。那我們一起來想一個場景:如果沒有了網絡,我們是不是就不能使用電腦進行聊天了呀。那網絡到底是如何幫助我們來完成網絡聊天的?下面我就跟大家聊聊計算機網絡到底是怎麼回事兒。 瞭解我的讀者都知道,我習慣先用自己的語言解釋,後説官方定義。這回我和以往反着來,先説官方定義,再解釋: 計算機網絡是由通信介質將地理位置不同的且相互獨立的計算機連接起來,實現數據通信與資源共享。 我們假設有兩個獨立且毫不相關的計算機,一台在青海,一台在河南,想要進行數據傳輸(聊天),沒網是不行的。這個網指的就是互聯網( Internet )。這個 Internet 它是一個通信協議。什麼是協議?打個比方,就好比我們打電話,在中國,中國有十幾億人,地大物博,全國各地都有自己的方言,還有些地方使用自己的民族語言,這時候想要良好的溝通就必須使用一個統一的標準,就是普通話。大家都講普通話,溝通起來就沒有問題了。那如果是不同國家進行溝通呢?我們可以選擇使用英語進行交流,那英語就是全世界國家通用的一個標準,計算機就好比是分佈在全世界各個角落的人,計算機之間通話也要找一個統一的標準,這個標準就是 Internet 標準,又叫做 Internet 協議。 二、網絡介紹及隔壁老王的故事 先給大家講一個虛構版隔壁老王的故事: 有一個人叫隔壁老王,他有一個愛好就是看電影。有一天,這個隔壁老王想看一部電影,可是電腦裏面存儲的電影太多了,他費了老大勁才從裏面找到,覺得很不爽。於是他想,我能不能把所有電影做個分類,把同一種類型的電影放在同一個文件夾下,然後把所有的文件夾整合在一起,自己寫個瀏覽器軟件,把文件夾信息放到瀏覽器上,到時候找電影的時候就好找了,只要找到相應的文件夾直接點進去就能找到電影了。老王説幹就幹,沒多久就把所有的文件夾整理好了,然後把所有文件夾放到同一個頁面上,到時候他想點哪個就點哪個,So easy!(那些網站可能就是這樣來的)。 這回出來一個新人物,就叫小王吧(不是小王八),老王就是住在小王隔壁,有一天小王看到老王電腦上有那麼多電影,就跟老王商量,能不能在他電腦上也弄個跟老王一樣的,讓他也看看。老王也不是個小氣的人,好東西就是要分享的嘛,想都沒想就答應了,給小王説在你電腦上插根線接到我電腦上,然後下載我這個軟件,直接訪問我的電腦就行了。小王很高興,他馬上回家按老王説的做,沒多久他的電腦上也能看老王電腦上的東西了。有了小王,就還會有小張,小李,互相插根網線,他們都能互相共享電影了。這樣,局域網也就產生了,就比如在一個公司或者一個學校用的網絡,都稱為局域網。那學校有很多呀,不同的學校用的局域網是不同的,不同的城市也有不同的局域網,以局域網為單位,散佈在一個區或一個城市的各個局域網加一起叫城域 網,然後全世界所有城市的城域網加一起就叫廣域網。 過了一段時間,老王去小王家,看到小王電腦裏的電影比自己的還多,而且還好看,然後老王跟小王説,把你的這些電影給我一份吧,我也想看看。不用想,小王肯定立馬答應了,説你自己弄吧,想看哪個隨便看。薑還是老的辣,老王想到一個好辦法,他跟小王説,我再找一台電腦,把我倆電腦上的資源全部放到那台電腦裏,我倆只要在那個電腦上插根線連到自己電腦上,就都能訪問那台電腦上的東西了,這樣豈不是很方便。小王一拍腦門,呀!我咋就沒想到,那還不趕緊的。老王另外找了一台電腦,然後他倆把自己的電腦和那台電腦用線連起來,再把自己電腦裏的所有資源全傳進那台電腦中,最後他倆就能共享資源了。(那台電腦就是服務器) 總結一下就是: 網絡按地域分類:根據參照物不同、類型不同分為 局域網:一個公司、一個家庭、一個學校······ 城域網:一個地區、一個城市······ 廣域網:一個國家、全世界······ 三、互聯網協議是如何分佈和設計的‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 我在上面説了 Internet 協議,互聯網協議按照功能的不同,分為 osi 七層, tcp / ip 五層, tcp / ip 四層協議。如下圖: osi 的七層協議體系結構的概念清楚,理論也比較完善,但它既複雜又不實用, ISO 制定的 osi 協議參考模型的過於龐大、複雜招致了許多批評。於此對照,由技術人員自己開發的 TCP / IP 協議獲得了更為廣泛的應用。因此,我們只需要弄明白 TCP / IP 五層協議 就能瞭解和明白計算機最底層的通信是怎麼回事。 四、TCP/IP五層協議 如圖,從最下方的物理層到最上方的應用層,對於我們用户而言,最直接的是應用層。從上到下每一層都依賴於下一層,所以我從最下一層開始給大家講解: 注意:每一層都運行着一個特定的協議,共同組合成互聯網協議 一、物理層 物理層主要是由雙絞線、光纜、電纜、無線電波組成,其作用很簡單,就是連接不同的計算機,並傳遞底層電信號,高電壓:1 ,低電壓:0 。 二、數據鏈路層 我們從物理層上接收或者發送單純的 0 、 1 是沒有意義的,為什麼呢?想想哈,我想給女朋友發送一句話:“你好漂亮”,那我們要把“你好漂亮”轉換成 01 之後,交給網卡,網卡就懵逼了,發給誰 ???不知道。那怎麼辦?必須要確定數據發給誰。就像我們的快遞一樣,是不是在外層包裝上有商家地址和個人地址,這樣我們不管是發還是收,都能準確定位了。網絡傳送數據也一樣,我們就在數據前面加上目標地址,為了能接收到回信,也要把自己的地址也加上。但是,如果數據和地址放在一起,又亂了,比如,我給你一堆 01 ,1010101000101010101 ,你也分不清哪裏是數據,哪裏是地址。這時我們就要對要發送的 01 進行分組,規定前面 xxx 位是地址,後面 xxx 位是數據,並且,大家想互相都能通信,就必須都遵守這樣的規則(協議),這個協議叫以太網協議。在以太網協議出現之前,各個公司都有自己的分組規則,後來都統一使用以太網協議了。 以太網協議規定:一組電信號構成一個數據包,叫幀,每一幀分為報頭( head )和數據( data ) 兩部分。 報頭(head):固定 18 個字節 發送者/源地址:6 個字節 接收者/目標地址:6 個字節 數據類型:6 個字節 數據( data ):最短 46 個字節,最長 1500 字節 數據包的具體內容(發送給女朋友的話/快遞貨物) 以太網協議中的地址叫 MAC 地址, MAC 地址是每台計算機唯一的物理地址,是被寫在網卡上的。以太網協議規定,每一台接收和發送數據的設備必須要裝有網卡,負責發送和接收數據的設備,發送端和接收端的地址,指的就是網卡的地址,即 MAC 地址。 MAC地址 MAC 地址是每個網卡在出廠的時候,由各個遞四方香港直接燒錄在網卡上的,而且,這個地址必須是全世界唯一的。MAC 地址是由 12 位 16 進制的數字表示(前六位是遞四方香港編號,後六位是流水線號),這樣不同的遞四方香港之間就不會產生衝突了,自己生產自己的就好了。 交換機 在這給大家介紹一個東西,我們説兩個電腦要通信要先連根線,但是如果電腦多了之後,電腦間通信連的線也就多了,這樣太亂了,我介紹的這個東西就是交換機,它是負責組件局域網,研究的是 MAC 地址,它有什麼用你看下面圖片中的接口就知道了 有了 MAC 地址,以太網就可以進行工作了.理論上講,我們可以和世界上的每一台連接了互聯網的計算機進行通信了,此時通信的方案是:廣播 廣播 廣播又是怎麼一回事?其實廣播的方式很原始,基本通信就是靠吼。就像你想跟女朋友求婚一樣,你會大喊:“ xxx ,嫁給我吧”,旁邊能聽到這句話的人有很多,但是隻有你女朋友會回覆你。其他人會把你當傻D一樣看待。沒錯,廣播就是這樣進行通信的。首先組織好了一個數據包之後,把這個數據包通過電信號發出去,這時整個網絡上所有的人都會收到你發的這條數據,然後看看這個數據是不是自己的。如果不是就當他不存在,如果是,就接收。雖然效率低點,但畢竟能通信了。 廣播帶來什麼問題呢?如果是在一個小的網絡環境裏。比方説,你們宿舍幾個人,一起玩 CS ,沒問題,你喊一嗓子,你室友也能迴應你,也就效率低點。但是如果你連接到全世界的互聯網上,還使用廣播的方式來通信,就不是效率問題了,而是一個巨大的通信災難。全世界 60 多億人,每個人吼一嗓子,每個人發送一條信息,那每個人都會收到 60 多億條信息,網絡瞬間癱瘓,這種問題被稱為廣播風暴,那如何解決呢? 三、網絡層 首先,我們要了解一個事情,世界大網絡(廣域網)是由一個一個的互相隔離的小型局域網(子網)組成的,不同的局域網之間使用路由來連接。 路由器 上面説的交換機是負責組建局域網,研究的是 MAC 地址,而路由器是負責組件廣域網,研究的是 IP 地址,這個 IP 地址下面我再解釋。 剛才説的廣播,只能在一個局域網內進行通信,不可以在大網絡上進行廣播,有了路由器,就避免了廣播風暴的問題。每個局域網被稱為一個廣播域,局域網和局域網之間使用路由的方式進行通信(向不同的廣播域/子網發送數據包),用路由器把一個局域 網裏的所有計算機劃分成一個個子網。 現在大家先想一想生活中,如果是之前説的廣播,就相當於在一間教室裏你正在上課,你要給某個女孩傳紙條,那你要在紙條外面寫上你和你要傳的女孩的名字,再在裏面寫上你要寫的話,寫好後折起來,你和女孩的名字在外,內容在內,然後讓同學一個一個幫你傳,每個同學在傳的時候都會看外面寫的名字,如果不是他,就繼續傳,直到傳到的人是你寫的那個女孩為止。那這間教室就相當於一個局域網。好,現在我們要實現局域網和局域網之間的通信,比如此時位於青海的你要給河南的朋友送東西,你不可能用廣播的方式了,你只能快遞,這個青海和河南以及還有很多不同的省份是我國的土地劃分中的一部分,每一個省也可以看作是一個個局域網,那在網絡中,我國就相當於廣域網,那不同的局域網是如何劃分的呢?MAC 地址是沒辦法區分的,因為  MAC 地址上只有遞四方香港的流水號,這就引出我要説的網絡層,網絡層引出了一套新的地址來區分不同的局域網/子網,這一套地址就是網絡地址。 規定網絡地址的協議叫 IP 協議,它定義的地址叫 IP 地址。其實跟我國的省份名差不多,繼續説送快遞,你要把送的東西包裝好,在外面寫上你自己的地址和省份地址,還有朋友的地址和省份地址,交給青海的快遞公司,然後青海的快遞公司轉交給河南的快遞公司,最後由河南的快遞公司分配給你朋友,你朋友就收到快遞了。在這有兩點需要注意: 你要同時寫兩個地址,自己的地址( MAC 地址)和省份地址( IP 地址),這樣就能確定所要 接收和發送人的具體的位置了。 青海的快遞公司和河南的快遞公司就相當於路由器 子網掩碼 在這給大家普及一下子網掩碼,我們上面説有了 IP 地址和 MAC 地址,我們就能讓任何計算機之間進行通信了,那現在再想想,如果我要用我的計算機給另外一台計算機實現通信,我是不是要判斷要通信的計算機是否和我的計算機在同一個 IP 地址中,相當於上面例子中我和我要送東西的朋友是否在一個省,這個時候就需要用子網掩碼,我拿着我的 IP 地址和對方的子網掩碼通過計算,判斷是否在同一個 IP 地址下,如果在同一個IP 地址下,我可以用廣播的形式進行通信,如果不在同一個 IP 地址下,我可以先把數據傳給我方的路由器,再由我方的路由器把數據傳給對方的路由器,最後由對方的路由器把數據傳給要接收數據的計算機。這樣説,再聯繫上面已經説過的,我想不難理解了。 IP地址 目前我們普遍使用的是 IPV4 ,它規定,一個網絡地址由 32 位二進制組成,把 32 位平均分成四份,每一份   8 位, 8 位最大能表示的數據是 255 ,所以IP地址的範圍:0.0.0.0255.255.255.255. 一個IP地址分為兩部分,分別是網絡位和主機位 網絡位用來標識不同的子網主機位用來標識子網下主機的編號 為什麼要分兩部分呢?很簡單,就好比你想寫信給你的女朋友,假設你女朋友的地址是西寧市平安路128號,那麼網絡位就會直接找到西寧市,主機位幫你找到你的女朋友。 網絡位和主機位是如何劃分的?使用子網掩碼來劃分。子網掩碼和IP地址差不多,都是由32位二進制數來表示,子網掩碼也分為網絡部分和主機部分,網絡部分由1組成,主機部分由0組成。 那説了這麼多,IP協議是如何發送數據的?協議規定,IP協議使用IP數據包進行發送 數據。IP數據包同樣把數據分為了兩部分,head和data,並且在發送數據的時候,直 接用IP數據包直接裝載以太網的data部分。 head:長度為 20 到 600 字節 data:最長為 65515 字節 而以太網數據包的“數據”部分,最長只有 1500 字節,因此,如果IP數據包超過了 1500 字節,它就需要分割成幾個以太網數據包,分開發送了。 再回顧一下啊,以太網的頭是包含了自己的 MAC 地址和目標 MAC 地址的,那如何查 找目標 MAC 地址呢?前輩們弄了一個叫 ARP 協議的東西專門來解析目標 MAC 地址。它是如何工作的?首先,它是數據鏈路層的東西,在我們發送一個數據包的時候是包含着對方的 IP 地址的。例如我(172.13.4.58)想發送一條數據給女朋友(172.13.4.90),首 先,我得先拿到女朋友的MAC地址才可以通信。此時,我們會先計算一下我和女朋友 是否在一個子網內(子網掩碼) 在一個子網內,直接廣播發送一個數據包 子網內的計算機發現了這個包之後會返回一個數據包並且帶有 MAC 地址,這樣就通過 IP 地址找到了目標主機的 MAC 地址,接下來就可以進行數據傳輸了。 不在一個子網內,單純的用廣播就不行了,因為廣播只是針對自己內網而言。那怎麼辦,此時會把數據包發給網關,由網關發給其它路由,這樣在整個萬維網裏就可以找到你想要的那個計算機的 MAC 地址了。 總結 ARP 就是通過 IP 地址來查找 MAC 地址的一套固定協議,它是數據鏈路層的內容。 網絡層的意義:定義了子網, 區分各個局域網 IP 地址:網絡地址 子網掩碼:計算是否是同一個子網 四、傳輸層 到目前為止,前三層內容已經可以進行數據傳輸了。但是,我們的一台計算機上可以 一次性運行多個網絡應用程序,比如, QQ 、微信 、 LOL 三個軟件一起運行,都要進行網絡傳輸,但是就前面學習的這三層內容,是沒辦法區分開數據是要發送給哪一個 軟件的。那怎麼辦?引入第四層,傳輸層,傳輸層定義了端口的概念,每一個網絡應 用程序佔用一個網絡端口,不同的程序就用端口把數據隔離,兩兩互相不影響。 端口:應用程序和網卡的關聯編號 傳輸層:建立端口到端口的通信。 傳輸層有兩種協議:TCP 和 UDP TCP協議 TCP 協議:可靠傳輸, TCP 數據包沒有長度限制,理論上可以無限長,但是為了保證網絡的 效率,通常 TCP 數據包的長度不會超過 IP 數據包的長度,以確保單個 TCP 數據包不必再分割。 TCP 頭放的主要是源端口和目標端口 UDP協議 UDP 協議:不可靠傳輸,“報頭”部分一共只有 8 個字節,總長度不超過 65535 字節,正好放 進一個 IP 數據包。 其實和郵信是一樣的,寫好地址,寫好接收人,直接裝進信封裏,丟進郵箱裏就不用你管了,對方什麼時候收,收沒收到,你不知道。 五、應用層 用户使用的都是應用程序,均工作於應用層,大家都可以開發自己的應用程序,數據多種多樣,必須規定好數據的組織形式。對於用於而言最直觀的就是應用層。 應用層:規定應用程序的數據格式 例:TCP 協議可以為各種各樣的程序傳遞數據,比如 Email 、 WWW 、 FTP 等,那麼, 必須有不同協議規定電子郵件、網頁、 FTP 數據的格式,這些應用程序協議就構成了“應用層”。 總結 以上是對TCP/IP5層協議的解讀,總結一下: 發送數據其實就是一個封裝數據的過程 最後從物理層發出,對方接收到了之後再自下而上一層一層打開拿到數據,以上內容就是一個網絡傳輸的大致過程,其中還有好多細節沒有闡述,但大家知道和了解以上內容,對開發而言足夠了。 特別推薦一個分享架構+算法的優質內容,還沒關注的小夥伴,可以長按關注一下: 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-12-25 關鍵詞: 互聯網 計算機

  • 一文讀懂Nginx

    原文鏈接://blog.csdn.net/yujing1314/article/details/107000737 Nginx知識網結構圖 Nginx是一個高性能的HTTP和反向代理服務器,特點是佔用內存少,併發能力強,事實上nginx的併發能力確實在同類型的網頁服務器中表現較好。 Nginx專為性能優化而開發,性能是其最重要的要求,十分注重效率,有報告Nginx能支持高達50000個併發連接數。 基礎概念 正向代理 局域網中的電腦用户想要直接訪問網絡是不可行的,只能通過代理服務器來訪問,這種代理服務就被稱為正向代理。 反向代理 客户端無法感知代理,因為客户端訪問網絡不需要配置,只要把請求發送到反向代理服務器,由反向代理服務器去選擇目標服務器獲取數據,然後再返回到客户端,此時反向代理服務器和目標服務器對外就是一個服務器,暴露的是代理服務器地址,隱藏了真實服務器IP地址。 負載均衡 客户端發送多個請求到服務器,服務器處理請求,有一些可能要與數據庫進行狡猾,服務器處理完畢之後,再將結果返回給客户端。 普通請求和響應過程: 但是隨着信息數量增長,訪問量和數據量飛速增長,普通架構無法滿足現在的需求。 我們首先想到的是升級服務器配置,可以由於摩爾定律的日益失效,單純從硬件提升性能已經逐漸不可取了,怎麼解決這種需求呢? 我們可以增加服務器的數量,構建集羣,將請求分發到各個服務器上,將原來請求集中到單個服務器的情況改為請求分發到多個服務器,也就是我們説的負載均衡。 圖解負載均衡: 假設有15個請求發送到代理服務器,那麼由代理服務器根據服務器數量,平均分配,每個服務器處理5個請求,這個過程就叫做負載均衡。 動靜分離 為了加快網站的解析速度,可以把動態頁面和靜態頁面交給不同的服務器來解析,加快解析的速度,降低由單個服務器的壓力。 動靜分離之前的狀態L: 動靜分離之後: Nginx如何在linux安裝 第一種:Linux系統 CentOS 7 64位 下載以下安裝包,用Xftp放入Linux系統。 第一步: 安裝pcre依賴 解壓壓縮文件,進入解壓之後的目錄執行./configure,然後執行make && make install。 查看是否安裝成功: [root@localhost pcre-8.37] # pcre-config --version 第二步:安裝其他依賴 [root@localhost pcre-8.37]# yum -y make zlib zlib-devel gcc-c++ libtool openssl openssl-devel 第三步:安裝Nginx 解壓Nginx,進入Nginx目錄,執行./configure: 執行make && make install: 去sbin文件夾下啓動Nginx 。 cd /usr/local/nginx/sbin 檢查是否啓動成功。 [root@localhost sbin]# ps -ef|grep nginx 第二種 Linux系統 Red Hat Enterprise Linux Server release 6.5 (Santiago) 提前需要準備的: Nginx源碼://nginx.org/en/download.html yum 安裝教程: //blog.csdn.net/yujing1314/article/details/97237644 gcc-c++: [root@localhost ~]yum install gcc-c++ 第三方開發包: [root@localhost ~]yum install -y pcre pcre-devel[root@localhost ~]yum install -y zlib zlib-devel[root@localhost ~]yum install -y openssl openssl-devel 安裝步驟: 第一步:把Nginx的源碼包上傳到Linux系統。 我使用的SecureCRT的sftp文件傳輸,直接把文件拖進去就OK了。 第二步:解壓縮 [root@localhost ~]tar zxf nginx-1.8.0.tar.gz 第三步:使用configure命令創建一makeFile文件 ./configure–prefix=/usr/local/nginx–pid-path=/var/run/nginx/nginx.pid–lock-path=/var/lock/nginx.lock–error-log-path=/var/log/nginx/error.log–http-log-path=/var/log/nginx/access.log–with-http_gzip_static_module–http-client-body-temp-path=/var/temp/nginx/client–http-proxy-temp-path=/var/temp/nginx/proxy–http-fastcgi-temp-path=/var/temp/nginx/fastcgi–http-uwsgi-temp-path=/var/temp/nginx/uwsgi–http-scgi-temp-path=/var/temp/nginx/scgi 第四步:上一步可能會報錯,因為缺少temp文件,如下創建即可 [root@localhost sbin]# mkdir /var/temp/nginx/client -p 第五步:make 直接輸入make。 第六步:make install 直接輸入make install。 開啓Nginx: [root@localhost sbin]# ./nginx 如何查看進程[root@bogon stefan]# ps aux|grep nginx: 關閉Nginx: [root@localhost sbin]# ./nginx -s stop 推薦使用: [root@localhost sbin]# ./nginx -s quit 測試 輸入你虛擬機的IP,如下圖就成功了。 如果測試失敗,注意查看虛擬機防火牆是否關閉 Nginx常用命令 查看版本: ./nginx -v 啓動: ./nginx 關閉(有兩種方式,推薦使用 ./nginx -s quit):  ./nginx -s stop ./nginx -s quit 重新加載Nginx配置: ./nginx -s reload Nginx的配置文件 配置文件分三部分組成。 1、全局塊 從配置文件開始到events塊之間,主要是設置一些影響nginx服務器整體運行的配置指令。 併發處理服務的配置,值越大,可以支持的併發處理量越多,但是會受到硬件、軟件等設備的制約。 2、events塊 影響nginx服務器與用户的網絡連接,常用的設置包括是否開啓對多workprocess下的網絡連接進行序列化,是否允許同時接收多個網絡連接等等。 支持的最大連接數: 3、http塊 諸如反向代理和負載均衡都在此配置。 location指令説明: 該語法用來匹配url,語法如下: location[ = | ~ | ~* | ^~] url{ } =:用於不含正則表達式的url前,要求字符串與url嚴格匹配,匹配成功就停止向下搜索並處理請求 ~:用於表示url包含正則表達式,並且區分大小寫 ~*:用於表示url包含正則表達式,並且不區分大小寫 ^~:用於不含正則表達式的url前,要求Ngin服務器找到表示url和字符串匹配度最高的location後,立即使用此location處理請求,而不再匹配 如果有url包含正則表達式,不需要有~開頭標識 反向代理實戰 配置反向代理 目的:在瀏覽器地址欄輸入地址www.123.com跳轉Linux系統Tomcat主頁面。 具體實現: 先配置Tomcat:因為比較簡單,此處不再贅敍。 並在Windows訪問: 具體流程: 修改之前: 配置: 再次訪問: 反向代理2 目標: 訪問//192.168.25.132:9001/edu/直接跳轉到192.168.25.132:8080 訪問//192.168.25.132:9001/vod/直接跳轉到192.168.25.132:8081 準備: 配置兩個Tomcat,端口分別為8080和8081,都可以訪問,端口修改配置文件即可。 新建文件內容分別添加8080!!!和8081!!! 響應如下: 具體配置: 重新加載Nginx: ./nginx -s reload 訪問: 實現了同一個端口代理,通過edu和vod路徑的切換顯示不同的頁面。 反向代理小結 第一個例子:瀏覽器訪問www.123.com,由host文件解析 出服務器IP地址 192.168.25.132 www.123.com, 然後默認訪問80端口,而通過Nginx監聽80端口代理到本地的8080端口上,從而實現了訪問www.123.com,最終轉發到tomcat 8080上去。 第二個例子: 訪問//192.168.25.132:9001/edu/直接跳轉到192.168.25.132:8080, 訪問//192.168.25.132:9001/vod/直接跳轉到192.168.25.132:8081, 實際上就是通過Nginx監聽9001端口,然後通過正則表達式選擇轉發到8080還是8081的Tomcat上去。 負載均衡實戰 修改nginx.conf: 重啓Nginx: ./nginx -s reload 在8081的tomcat的webapps文件夾下新建edu文件夾和a.html文件,填寫內容為8081!!!! 在地址欄回車,就會分發到不同的Tomcat服務器上。 負載均衡方式: 1、輪詢(默認) 2、weight,代表權,權越高優先級越高 3、fair,按後端服務器的響應時間來分配請求,相應時間短的優先分配 4、ip_hash,每個請求按照訪問IP的hash結果分配,這樣每一個訪客固定的訪問一個後端服務器,可以解決session 的問題 動靜分離實戰 什麼是動靜分離 把動態請求和靜態請求分開,不是講動態頁面和靜態頁面物理分離,可以理解為Nginx處理靜態頁面,Tomcat處理動態頁面。 動靜分離大致分為兩種:一、純粹將靜態文件獨立成單獨域名放在獨立的服務器上,也是目前主流方案;二、將動態跟靜態文件混合在一起發佈,通過Nginx分開。 動靜分離圖析: 實戰準備 準備靜態文件: 配置Nginx: Nginx高可用 如果Nginx出現問題: 解決辦法: 前期準備: 兩台Nginx服務器 安裝Keepalived 虛擬IP 安裝Keepalived [root@192 usr] # yum install keepalived -y [root@192 usr] # rpm -q -a keepalived keepalived-1.3.5-16.el7.x86_64 修改配置文件: [root@192 keepalived]# cd /etc/keepalived[root@192 keepalived]# vi keepalived.conf  分別將如下配置文件複製粘貼,覆蓋掉keepalived.conf。 虛擬IP為192.168.25.50。 對應主機IP需要修改的是: smtp_server 192.168.25.147(主)smtp_server 192.168.25.147(備) state MASTER(主) state BACKUP(備) global_defs {   notification_email {     acassen@firewall.loc     failover@firewall.loc     sysadmin@firewall.loc   }   notification_email_from Alexandre.Cassen@firewall.loc   smtp_server 192.168.25.147   smtp_connect_timeout 30   router_id LVS_DEVEL # 訪問的主機地址}vrrp_script chk_nginx {  script "/usr/local/src/nginx_check.sh"  # 檢測文件的地址  interval 2   # 檢測腳本執行的間隔  weight 2   # 權重}vrrp_instance VI_1 {    state BACKUP    # 主機MASTER、備機BACKUP        interface ens33   # 網卡    virtual_router_id 51 # 同一組需一致    priority 90  # 訪問優先級,主機值較大,備機較小    advert_int 1    authentication {        auth_type PASS        auth_pass 1111    }    virtual_ipaddress {        192.168.25.50  # 虛擬ip    }} 啓動: [root@192 sbin]# systemctl start keepalived.service 訪問虛擬IP成功。 關閉主機147的Nginx和Keepalived,發現仍然可以訪問。 原理解析 如下圖,就是啓動了一個Master,一個Worker,Master是管理員,Worker是具體工作的進程。 Worker如何工作: 小結 Worker數應該和CPU數相等 一個Master多個Worker可以使用熱部署,同時Worker是獨立的,一個掛了不會影響其他的。 特別推薦一個分享架構+算法的優質內容,還沒關注的小夥伴,可以長按關注一下: 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-12-25 關鍵詞: 互聯網 計算機

  • 為何Linus一個人就能寫出這麼強的系統,中國卻做不出來?

    來源:默然 + Coldwings(知乎) www.zhihu.com/question/63187737 他是誰,Linus! 林納斯・託瓦茲(Linus Torvalds, 1969 年~),著名程序員,Linux 內核的發明人及該計劃的合作者。 託瓦茲利用個人時間及器材創造出了這套當今全球最流行的操作系統內核之一。 現受聘於開放源代碼開發實驗室(OSDL:Open Source Development Labs, Inc),全力開發 Linux 內核。 Linus 研究生時期開始寫操作系統(大約是 91 年),那時候個人電腦(PC)雖然興起一些年了,但是還只是小部分程序員和狂熱愛好者的玩具。硬件基本上都靠自己攢,軟件也是用開源系統各種魔改。 所以普通人根本沒有折騰 PC 的動力和理由。 Unix 已經霸佔了許多生產力場景,唯一的缺點就是貴,而且很多發行版是閉源的。個人用户根本不要考慮。 那時候 Linus 自己攢了一套 386,但是找不到好用,廉價/免費的操作系統用。當時社區裏當然也有一票免費且開源的系統,但是要不就是兼容性差,要不就是各種坑,要不就是沒軟件,總之各種各樣的問題。而兼容性是最主要的問題,那時候的 CPU 不像現在的流行架構就那麼兩種(x86、Arm),那時候叫的上名的架構有十幾種,所以操作系統的兼容性是非常重要的。 Linus 開始自學操作系統,發現了一本很好的教材 《操作系統:設計與實現》 ,然後花了一個暑假看完,開始自己寫操作系統。 《操作系統:設計與實現》這本書的作者是塔雷鮑姆,寫書的時候已經是業界大牛了,他在大學為了教學操作系統,但苦於學生買不起太貴的 Unix 發行版,於是自己寫了一個兼容 Unix 標準操作系統(主要是兼容 POSIX 標準),叫 Minix(名字上就很對仗,Universe - mini)。 Minix 這個系統就是為了教學而生的,只要買了這本書,就免費郵寄一份 Minix 源代碼。Minix 為了方便教學,保持代碼的簡潔,塔雷鮑姆拒絕向裏面添加太多複雜的功能。所以 Minix 雖然實現得優雅,但是社區的玩家要自己日常用,要魔改很多東西。 對了,Minix 是微內核的。對,微內核的概念存在幾十年了,而不是 2019 年誕生的。 微內核的結構非常優雅,文件系統,內存管理,硬件驅動都是以進程形式存在的,而不是內核代碼,這意味着驅動掛了不會帶着內核一起掛。缺點就是系統調用開銷太大,以至於慢到無法接受。所以現在的桌面系統,沒有純微內核的。 對於教學系統來説,微內核不是問題,畢竟是教學,不是生產工具。 社區和 Linus 都很喜歡 Minix,但是都不滿足於 Minix 作者因為教學目的而放棄兼容性和可擴展性。於是 Linus 在自己運行 Minix 的 PC 開始了 Linux 的開發。Linux 是宏內核的。 Linux 本來不叫 Linux,Linus 是一個很內向的人,不好意思取這麼自戀的名字,但是合作者強烈建議用這個名字,所以就用了,x 代表 unix 的聯繫(都兼容 POSIX)。 提一句,Unix 不是為開源和免費而生的,而是為商業而生的,但是 Unix 的發展催生了最好的開源環境(比如 GNU)。 注意,這幾年的社區,屬於『軍閥混戰』,大家都缺一款好用的免費操作系統,但並不只是 Linus 想到要解決這個問題,理查德·斯托曼領導的 GNU 組織在 90 年代就一直醖釀一款免費的操作系統,因為他們的目的是與商業的 Unix 對抗,光有一個 GCC 編譯器還不夠,還需要有自己的操作系統。 社區裏各種技術人員也對 GNU 的操作系統非常期待,但是這玩意兒難產了。直到很久之後,Linux 流行起來後,依然沒做出來。 最開始的 Linux 版本,只有幾千行代碼,現在基礎紮實的 CS 本科生,花一段時間都可以看懂,甚至有些 OS 教材就是用 Linux 最初的版本來教學的,比如哈工大李志軍的課程。 Linus 從一開始就不斷在 minix 論壇上發佈自己的進展,搞得論壇上一堆人非常感興趣,於是許多人加入了開發,這時候 Linus 用郵件接收每個人的代碼,然後手動合併。沒過一段時間,minix 論壇上就全是討論 Linux 的了,雖然塔雷鮑姆不是個小氣的人,但是這麼搞也讓塔雷鮑姆很不爽 不久之後 Linus 和塔雷鮑姆之間爆發了一次衝突。 塔雷鮑姆在論壇上寫了一篇文章,論證微內核與宏內核的優缺點,其實主要是攻擊宏內核,説宏內核除了性能好點,全是缺點,説 Linux 過時了。Linus 是脾氣暴躁的人,在技術問題上從不妥協。於是開始嘲諷 minix。Linux 的可移植性比 minix 更好,而且免費開源(minix 需要買書後獲得)。 然後來來去去吵了很多次,具體可以看: //www.oreilly.com/openbook/opensources/book/appa.html 只是他們兩個當時誰也沒想到,這次論壇上的口水戰會成為幾十年後人們依然提起的操作系統之爭。 Linus 並不恨塔雷鮑姆,他説後來有一次去了塔雷鮑姆的演講,完了之後拿着那本書想要塔雷鮑姆的簽名,但是沒有等到人。 我想 Linus 多少還是尊敬他的,畢竟是自己學習操作系統的領路人。 在 Linus 和社區人員的努力下,通過擴展 GCC 支持 Linux,Linux 也兼容了越來越多的平台。其他各類軟件移植到 Linux 也就變得容易了,尤其是在那個許多軟件以源代碼方式發行的時代,只要有對應平台的編譯器,編譯一次就算移植好了。 趕上 PC 發展的浪潮,但還不夠,畢竟蘋果微軟不是好對付的,商業操作系統的易用性依然很強。 但是 Linux 開始被各類企業青睞了,因為不是每個企業都有錢花高價買一套 Unix 來用,或者是更貴的軟件 + 硬件一體的大型機來用。Linux 讓他們看到了省錢的希望。 GNU 開始支持 Linux,Linux 成為了 GNU 的官方操作系統,所以現在叫 GNU/Linux。 可以説 Linux 和 GCC 幾乎是最偉大的兩個開源項目。它們合起來就更強悍了。 GCC 讓軟件方便移植,Linux 軟件生態就好了,軟件生態好了用户就多,用户多了就讓硬件公司眼饞,各路硬件公司都為 Linux 開發驅動和各種擴展,以支持自家硬件,這樣用户就更多。這個倍增效應是很強的。 Linus 在項目達到一定規模後就不再親自寫代碼了,主要是合併代碼,畢竟全球那麼多人提交代碼,他一個人審核合併就夠忙了,親自寫代碼也沒時間。 直到有一天他覺得忙不過來了,審核代碼會遇到很多傻逼代碼和開發者,於是他開發了現在最流行的版本控制工具,git,字面意思就是飯桶。 不得不説,他對操作系統發展方向的把控是精準的。 總結一下,Linux 的成功,以下幾個條件必不可少: Linus 強大的開發能力; Linus 的項目管理能; Linus 對操作系統發展方向的把控; 一個羣雄割據,缺乏免費好用的操作系統的時代; 一個不僅羣雄割據,缺乏免費好用的操作系統,而且程序語言,操作系統理論,編譯器技術發展到一定程度,個人 PC 持續發展的時代; GNU 的系統沒做出來; Minix 死守着『教育』不放; GNU 的支持; 全世界硬件遞四方香港的支持; 全世界軟件遞四方香港的支持; 全世界開源開發者的巨大貢獻; 再總結一下,一個人的命運,當然要靠自我奮鬥,但也要考慮到歷史的進程。 另外一個網友 Coldwings 的觀點 説實話,是你想多了…… Linus確實在Linux的內核開發上做了很多工作,諸如項目發起,最初版內核的設計等等,但是你如今拿到手的任何一個Linux發行版中,包含了至少上百個GNU項目,無數的其它開源項目,以及數十萬人貢獻的代碼。這裏所言道的Linux,是指Linux這個內核,而內核這玩意不包括任何應用層,甚至那個黑框框命令行都並不是Linux的一部分。內核暴露的是硬件到軟件的抽象、任務和資源調度,給出的是調用系統的編程接口,僅此而已。 Linus所做的1991年的第一版內核有些什麼功能呢?簡單的説,是這樣的: 一個有着硬件平台限制,能夠運行起來的,與當時便不是特別熱門的叫做Minix的操作系統內核大部分功能兼容的內核。 要説能力,那是非常強的,因為二十多年後的今天的大學生們即使上了操作系統課程做大作業要寫個OS內核,通常也不會做得多全面,更不提完全兼容某標準(當然更可能是因為沒有做那麼精細的需求)。但若只是如此,Linux也就止步於「優秀的大學生課程作業」水平了。 真正有意義的是他把Linux扔網上與社區協作開發(後來加上了GPL協議),而後在2年之內有超過百人折騰這個內核。而在當時的商用環境普遍使用Unix而主要發行版的Unix都貴破天際的情況下,在4年後終於有人覺得這個開源內核加上GNU工具能夠起到替代部分Unix節省成本,才真正意義上火起來的。而彼時已經有上千人參與內核的開發,其中甚至有大批RH等公司的專職僱員。 沒錯,就這麼個內核,沒有任何人機交互,僅僅提供軟件運行環境的玩意,儘管最初雛形是Linus的作品,四年後已經是數千名對操作系統有研究的程序員共同開發的產物了。而這只是一個現有的Linux發行版中佔比重很小的一部分(儘管很重要),可以交互的命令行環境bash來自於GNU,圖形界面Gnome來自於GNU,聲音服務來自於GNU,顯示服務來自於GNU(近來的發行版中也有其它開源實現)……連編譯器都來自於GNU,而這些東西,Linus幾乎都沒有參與。 他是大神,是Linux之父,但是説Linux,尤其是現在廣泛使用的功能完整的Linux是他一個人開發的這種事情,是不存在的。 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝

    時間:2020-12-25 關鍵詞: 軟件 計算機

  • 是什麼讓Spring5放棄了使用Guava Cache?

    來源://albenw.github.io/posts/a4ae1aa2/ 概要 Caffeine是一個高性能,高命中率,低內存佔用,near optimal 的本地緩存,簡單來説它是Guava Cache的優化加強版,有些文章把Caffeine稱為“新一代的緩存”、“現代緩存之王”。本文將重點講解Caffeine的高性能設計,以及對應部分的源碼分析。 與Guava Cache比較 大家都知道,Spring5即將放棄掉Guava Cache作為緩存機制,而改用Caffeine作為新的本地Cache的組件,這對於Caffeine來説是一個很大的肯定。為什麼Spring會這樣做呢?其實在Caffeine的Benchmarks裏給出了好靚仔的數據,對讀和寫的場景,還有跟其他幾個緩存工具進行了比較,Caffeine的性能都表現很突出。 使用Caffeine Caffeine為了方便大家使用以及從Guava Cache切換過來(很有針對性啊~),借鑑了Guava Cache大部分的概念(諸如核心概念Cache、LoadingCache、CacheLoader、CacheBuilder等等),對於Caffeine的理解只要把它當作Guava Cache就可以了。 使用上,大家只要把Caffeine的包引進來,然後換一下cache的實現類,基本應該就沒問題了。這對與已經使用過Guava Cache的同學來説沒有任何難度,甚至還有一點熟悉的味道,如果你之前沒有使用過Guava Cache,可以查看Caffeine的官方API説明文檔,其中Population,Eviction,Removal,Refresh,Statistics,Cleanup,Policy等等這些特性都是跟Guava Cache基本一樣的。 下面給出一個例子説明怎樣創建一個Cache: private static LoadingCache cache = Caffeine.newBuilder()            //最大個數限制            .maximumSize(256L)            //初始化容量            .initialCapacity(1)            //訪問後過期(包括讀和寫)            .expireAfterAccess(2, TimeUnit.DAYS)            //寫後過期            .expireAfterWrite(2, TimeUnit.HOURS)            //寫後自動異步刷新            .refreshAfterWrite(1, TimeUnit.HOURS)            //記錄下緩存的一些統計數據,例如命中率等            .recordStats()            //cache對緩存寫的通知回調            .writer(new CacheWriter() {                @Override                public void write(@NonNull Object key, @NonNull Object value) {                    log.info("key={}, CacheWriter write", key);                }                @Override                public void delete(@NonNull Object key, @Nullable Object value, @NonNull RemovalCause cause) {                    log.info("key={}, cause={}, CacheWriter delete", key, cause);                }            })            //使用CacheLoader創建一個LoadingCache            .build(new CacheLoader() {                //同步加載數據                @Nullable                @Override                public String load(@NonNull String key) throws Exception {                    return "value_" + key;                }                //異步加載數據                @Nullable                @Override                public String reload(@NonNull String key, @NonNull String oldValue) throws Exception {                    return "value_" + key;                }            }); Caffeine的高性能設計 判斷一個緩存的好壞最核心的指標就是命中率,影響緩存命中率有很多因素,包括業務場景、淘汰策略、清理策略、緩存容量等等。如果作為本地緩存, 它的性能的情況,資源的佔用也都是一個很重要的指標。下面 我們來看看Caffeine在這幾個方面是怎麼着手的,如何做優化的。 (注:本文不會分析Caffeine全部源碼,只會對核心設計的實現進行分析,但我建議讀者把Caffeine的源碼都涉獵一下,有個overview才能更好理解本文。如果你看過Guava Cache的源碼也行,代碼的數據結構和處理邏輯很類似的。 源碼基於:(caffeine-2.8.0.jar) W-TinyLFU整體設計 上面説到淘汰策略是影響緩存命中率的因素之一,一般比較簡單的緩存就會直接用到LFU(Least Frequently Used,即最不經常使用)或者LRU(Least Recently Used,即最近最少使用),而Caffeine就是使用了W-TinyLFU算法。 W-TinyLFU看名字就能大概猜出來,它是LFU的變種,也是一種緩存淘汰算法。那為什麼要使用W-TinyLFU呢? LRU和LFU的缺點 LRU實現簡單,在一般情況下能夠表現出很好的命中率,是一個“性價比”很高的算法,平時也很常用。雖然LRU對突發性的稀疏流量(sparse bursts)表現很好,但同時也會產生緩存污染,舉例來説,如果偶然性的要對全量數據進行遍歷,那麼“歷史訪問記錄”就會被刷走,造成污染。 如果數據的分佈在一段時間內是固定的話,那麼LFU可以達到最高的命中率。但是LFU有兩個缺點,第一,它需要給每個記錄項維護頻率信息,每次訪問都需要更新,這是個巨大的開銷;第二,對突發性的稀疏流量無力,因為前期經常訪問的記錄已經佔用了緩存,偶然的流量不太可能會被保留下來,而且過去的一些大量被訪問的記錄在將來也不一定會使用上,這樣就一直把“坑”佔着了。 無論LRU還是LFU都有其各自的缺點,不過,現在已經有很多針對其缺點而改良、優化出來的變種算法。 TinyLFU TinyLFU就是其中一個優化算法,它是專門為了解決LFU上述提到的兩個問題而被設計出來的。 解決第一個問題是採用了Count–Min Sketch算法。 解決第二個問題是讓記錄儘量保持相對的“新鮮”(Freshness Mechanism),並且當有新的記錄插入時,可以讓它跟老的記錄進行“PK”,輸者就會被淘汰,這樣一些老的、不再需要的記錄就會被剔除。 下圖是TinyLFU設計圖(來自官方) 統計頻率Count–Min Sketch算法 如何對一個key進行統計,但又可以節省空間呢?(不是簡單的使用HashMap,這太消耗內存了),注意哦,不需要精確的統計,只需要一個近似值就可以了,怎麼樣,這樣場景是不是很熟悉,如果你是老司機,或許已經聯想到布隆過濾器(Bloom Filter)的應用了。 沒錯,將要介紹的Count–Min Sketch的原理跟Bloom Filter一樣,只不過Bloom Filter只有0和1的值,那麼你可以把Count–Min Sketch看作是“數值”版的Bloom Filter。 更多關於Count–Min Sketch的介紹請自行搜索。 在TinyLFU中,近似頻率的統計如下圖所示: 對一個key進行多次hash函數後,index到多個數組位置後進行累加,查詢時取多個值中的最小值即可。 Caffeine對這個算法的實現在FrequencySketch類。但Caffeine對此有進一步的優化,例如Count–Min Sketch使用了二維數組,Caffeine只是用了一個一維的數組;再者,如果是數值類型的話,這個數需要用int或long來存儲,但是Caffeine認為緩存的訪問頻率不需要用到那麼大,只需要15就足夠,一般認為達到15次的頻率算是很高的了,而且Caffeine還有另外一個機制來使得這個頻率進行衰退減半(下面就會講到)。如果最大是15的話,那麼只需要4個bit就可以滿足了,一個long有64bit,可以存儲16個這樣的統計數,Caffeine就是這樣的設計,使得存儲效率提高了16倍。 Caffeine對緩存的讀寫(afterRead和afterWrite方法)都會調用onAccesss方法,而onAccess方法裏有一句: frequencySketch().increment(key); 這句就是追加記錄的頻率,下面我們看看具體實現 //FrequencySketch的一些屬性//種子數static final long[] SEED = { // A mixture of seeds from FNV-1a, CityHash, and Murmur3    0xc3a5c85c97cb3127L, 0xb492b66fbe98f273L, 0x9ae16a3b2f90404fL, 0xcbf29ce484222325L};static final long RESET_MASK = 0x7777777777777777L;static final long ONE_MASK = 0x1111111111111111L;int sampleSize;//為了快速根據hash值得到table的index值的掩碼//table的長度size一般為2的n次方,而tableMask為size-1,這樣就可以通過&操作來模擬取餘操作,速度快很多,老司機都知道int tableMask;//存儲數據的一維long數組long[] table;int size;/** * Increments the popularity of the element if it does not exceed the maximum (15). The popularity * of all elements will be periodically down sampled when the observed events exceeds a threshold. * This process provides a frequency aging to allow expired long term entries to fade away. * * @param e the element to add */public void increment(@NonNull E e) {  if (isNotInitialized()) {    return;  }  //根據key的hashCode通過一個哈希函數得到一個hash值  //本來就是hashCode了,為什麼還要再做一次hash?怕原來的hashCode不夠均勻分散,再打散一下。  int hash = spread(e.hashCode());  //這句光看有點難理解  //就如我剛才説的,Caffeine把一個long的64bit劃分成16個等分,每一等分4個bit。  //這個start就是用來定位到是哪一個等分的,用hash值低兩位作為隨機數,再左移2位,得到一個小於16的值  int start = (hash & 3)  windowMaximum()) {    // The pending operations will adjust the size to reflect the correct weight    if (node == null) {      break;    }    //下一個節點    Node next = node.getNextInAccessOrder();    if (node.getWeight() != 0) {      //把node定位在probation區      node.makeMainProbation();      //從window區去掉      accessOrderWindowDeque().remove(node);      //加入到probation queue,相當於把節點移動到probation區(晉升了)      accessOrderProbationDeque().add(node);      candidates++;      //因為移除了一個節點,所以需要調整window的size      setWindowWeightedSize(windowWeightedSize() - node.getPolicyWeight());    }    //處理下一個節點    node = next;  }  return candidates;} evictFromMain方法: /** * Evicts entries from the main space if the cache exceeds the maximum capacity. The main space * determines whether admitting an entry (coming from the window space) is preferable to retaining * the eviction policy's victim. This is decision is made using a frequency filter so that the * least frequently used entry is removed. * * The window space candidates were previously placed in the MRU position and the eviction * policy's victim is at the LRU position. The two ends of the queue are evaluated while an * eviction is required. The number of remaining candidates is provided and decremented on * eviction, so that when there are no more candidates the victim is evicted. * * @param candidates the number of candidate entries evicted from the window space *///根據W-TinyLFU,從window晉升過來的要跟probation區的進行“PK”,勝者才能留下@GuardedBy("evictionLock")void evictFromMain(int candidates) {  int victimQueue = PROBATION;  //victim是probation queue的頭部  Node victim = accessOrderProbationDeque().peekFirst();  //candidate是probation queue的尾部,也就是剛從window晉升來的  Node candidate = accessOrderProbationDeque().peekLast();  //當cache不夠容量時才做處理  while (weightedSize() > maximum()) {    // Stop trying to evict candidates and always prefer the victim    if (candidates == 0) {      candidate = null;    }    //對candidate為null且victim為bull的處理    if ((candidate == null) && (victim == null)) {      if (victimQueue == PROBATION) {        victim = accessOrderProtectedDeque().peekFirst();        victimQueue = PROTECTED;        continue;      } else if (victimQueue == PROTECTED) {        victim = accessOrderWindowDeque().peekFirst();        victimQueue = WINDOW;        continue;      }      // The pending operations will adjust the size to reflect the correct weight      break;    }    //對節點的weight為0的處理    if ((victim != null) && (victim.getPolicyWeight() == 0)) {      victim = victim.getNextInAccessOrder();      continue;    } else if ((candidate != null) && (candidate.getPolicyWeight() == 0)) {      candidate = candidate.getPreviousInAccessOrder();      candidates--;      continue;    }    // Evict immediately if only one of the entries is present    if (victim == null) {      @SuppressWarnings("NullAway")      Node previous = candidate.getPreviousInAccessOrder();      Node evict = candidate;      candidate = previous;      candidates--;      evictEntry(evict, RemovalCause.SIZE, 0L);      continue;    } else if (candidate == null) {      Node evict = victim;      victim = victim.getNextInAccessOrder();      evictEntry(evict, RemovalCause.SIZE, 0L);      continue;    }    // Evict immediately if an entry was collected    K victimKey = victim.getKey();    K candidateKey = candidate.getKey();    if (victimKey == null) {      @NonNull Node evict = victim;      victim = victim.getNextInAccessOrder();      evictEntry(evict, RemovalCause.COLLECTED, 0L);      continue;    } else if (candidateKey == null) {      candidates--;      @NonNull Node evict = candidate;      candidate = candidate.getPreviousInAccessOrder();      evictEntry(evict, RemovalCause.COLLECTED, 0L);      continue;    }    //放不下的節點直接處理掉    if (candidate.getPolicyWeight() > maximum()) {      candidates--;      Node evict = candidate;      candidate = candidate.getPreviousInAccessOrder();      evictEntry(evict, RemovalCause.SIZE, 0L);      continue;    }    //根據節點的統計頻率frequency來做比較,看看要處理掉victim還是candidate    //admit是具體的比較規則,看下面    candidates--;    //如果candidate勝出則淘汰victim    if (admit(candidateKey, victimKey)) {      Node evict = victim;      victim = victim.getNextInAccessOrder();      evictEntry(evict, RemovalCause.SIZE, 0L);      candidate = candidate.getPreviousInAccessOrder();    } else {      //如果是victim勝出,則淘汰candidate      Node evict = candidate;      candidate = candidate.getPreviousInAccessOrder();      evictEntry(evict, RemovalCause.SIZE, 0L);    }  }}/** * Determines if the candidate should be accepted into the main space, as determined by its * frequency relative to the victim. A small amount of randomness is used to protect against hash * collision attacks, where the victim's frequency is artificially raised so that no new entries * are admitted. * * @param candidateKey the key for the entry being proposed for long term retention * @param victimKey the key for the entry chosen by the eviction policy for replacement * @return if the candidate should be admitted and the victim ejected */@GuardedBy("evictionLock")boolean admit(K candidateKey, K victimKey) {  //分別獲取victim和candidate的統計頻率  //frequency這個方法的原理和實現上面已經解釋了  int victimFreq = frequencySketch().frequency(victimKey);  int candidateFreq = frequencySketch().frequency(candidateKey);  //誰大誰贏  if (candidateFreq > victimFreq) {    return true;    //如果相等,candidate小於5都當輸了  } else if (candidateFreq  0) {    //增加window的大小    increaseWindow();  } else {    //減少window的大小    decreaseWindow();  }} 下面分別展開每個方法來解釋: /** Calculates the amount to adapt the window by and sets {@link #adjustment()} accordingly. */@GuardedBy("evictionLock")void determineAdjustment() {  //如果frequencySketch還沒初始化,則返回  if (frequencySketch().isNotInitialized()) {    setPreviousSampleHitRate(0.0);    setMissesInSample(0);    setHitsInSample(0);    return;  }  //總請求量 = 命中 + miss  int requestCount = hitsInSample() + missesInSample();  //沒達到sampleSize則返回  //默認下sampleSize = 10 * maximum。用sampleSize來判斷緩存是否足夠”熱“。  if (requestCount = 0) ? stepSize() : -stepSize();  //下次的調整大小:如果命中率的之差大於0.05,則重置為0.065 * maximum,否則按照0.98來進行衰減  double nextStepSize = (Math.abs(hitRateChange) >= HILL_CLIMBER_RESTART_THRESHOLD)      ? HILL_CLIMBER_STEP_PERCENT * maximum() * (amount >= 0 ? 1 : -1)      : HILL_CLIMBER_STEP_DECAY_RATE * amount;  setPreviousSampleHitRate(hitRate);  setAdjustment((long) amount);  setStepSize(nextStepSize);  setMissesInSample(0);  setHitsInSample(0);}/** Transfers the nodes from the protected to the probation region if it exceeds the maximum. *///這個方法比較簡單,減少protected區溢出的部分@GuardedBy("evictionLock")void demoteFromMainProtected() {  long mainProtectedMaximum = mainProtectedMaximum();  long mainProtectedWeightedSize = mainProtectedWeightedSize();  if (mainProtectedWeightedSize 

    時間:2020-12-25 關鍵詞: 嵌入式 計算機

首頁  上一頁  1 2 3 4 5 6 7 8 9 10 下一頁 尾頁
發佈文章

技術子站

更多

項目外包