導航:首頁 > 源碼編譯 > 演算法說明文檔

演算法說明文檔

發布時間:2023-03-02 13:25:00

㈠ 圖像演算法工程師的職責概述內容

圖像演算法工程師需要協助完成項目的系統集成測試、版本交付等工作,對項目實施和維護提供支持。下面是我為您精心整理的圖像演算法工程師的職責概述內容。

圖像演算法工程師的職責概述內容1

職責:

1.負責原有圖像演算法的改進和新演算法的開發;

2.根據項目需求,通過研讀相關領域文獻尋找解決問題的最優演算法;

3.跟蹤技術前沿,並對演算法性能進行模擬驗證;

4.能夠搭建計算機視覺演算法軟硬體測試環境,對演算法具體性能進行測試;

5.負責編寫演算法測試相關的技術文檔以及專利相關的文檔;

任職資格:

1.模式識別,圖像信號處理、計算機、通信、應用數學等計算機相關專業碩士及以上學歷;

2.優秀的應屆畢業生或2年以上圖像演算法領域工作經驗;

3.熟練C/C++編程語言,熟悉OpenCV的基本演算法,有良好的代碼習慣;

4.熟練掌握計算機視覺和圖像處理相關的基本演算法及應用,在圖像分割、增強、分類、識別等方面有深厚的演算法基礎;

5.英文良好,有較強的文獻閱讀和演算法實現能力;

6.優秀的分析問題和解決問題的能力,對解決具有挑戰性的問題充滿激情;

7、具備以下情況之一者優先:

圖像演算法工程師的職責概述內容2

職責:

1、負責公司業務中貨架圖像識別模型學習,維護,更新等工作

2、根據產品研發實際需求,能夠獨立開發相關的數學模型建立,以及模型的迭代輸出實現;

3、產品上線後,持續分析線上識別准確性等指標,優化識別引擎、提高識別效率和成功率;

4、持續跟蹤AI的新演算法新技術,結合應用場景,為公司業務與技術規劃提供決策支持;

任職要求:

1、計算機、機器學習、圖像處理、模式識別等相關方向碩士研究生及以上學歷

2、2年以上圖像識別技術研發經驗,熟悉深度學習理論框架

3、熟悉C++、Python,熟悉Tensorflow等主流的機器學習框架;

4、良好的英文技術文獻閱讀能力,能獨立進行英文文獻調研、分析和總結;

5、有責任心和團隊精神,樂於溝通和合作。

圖像演算法工程師的職責概述內容3

職責:

1、負責公司產品圖像處理演算法的維護、新產品圖像處理演算法的預研和開發;

2、對臨床醫學圖像問題的反饋進行分析、根據臨床需求進行演算法調整;

3、編寫演算法詳細說明文檔,以及演算法相關文件的規范化輸出;

4、編寫公司產品圖像處理演算法的相關發明專利和軟體著作權;

5、醫學影像行業內最新圖像處理技術的收集、分析與實現。

任職要求:

1、碩士及以上學歷,數學或計算機等相關專業;

2、精通C++, Matlab, Python編程語言中的至少其中一項,有良好的編碼習慣和技術文檔編寫能力;

3、有扎實的數學基礎,有演算法研究和實現的實際項目經驗;

4、有良好的英文閱讀能力,英語要求六級或同等能力者;

5、有較強的團隊合作意識和良好的邏輯分析能力;

6、有CT、MRI圖像的分割,配准,或AI/深度學習研究經驗者優先。

圖像演算法工程師的職責概述內容4

職責:

1.參與公司視覺系統研發工作;

2.基於OpenCV、halcon平台,負責參與開發視覺系統的各功能模塊,如模板匹配、blob分析、邊緣檢測、特徵提取、瑕疵檢測等;

3.負責機器視覺系統圖像處理、分析及識別演算法設計、實現及現場調試。

職位要求:

1.三年以上以C++開發經驗,一年以上以OpenCV、halcon開發經驗,熟悉VS2008以上開發平台;

2.有較好的數學基礎,動手能力強,能夠根據視覺論文編寫出針對具體問題的視覺演算法。

3.有一定的視覺開發經驗和項目經歷,熟悉window操作系統,Socket及串口通信、多線程編程、SqlSever2008等操作;

4.對機器視覺有濃厚的興趣,有良好的抽象思維和邏輯思維能力,獨立分析問題和解決問題的能力;

5.具備現場獨立調試視覺的能力

圖像演算法工程師的職責概述內容5

職責:

1、參與公司圖像處理相關的工作

2、依照產品需求,給出解決方案,模型搭建,部署等

3、獨立完成計算機視覺演算法的開發及優化升級

任職資格:

1、具有1年以上的圖像處理實際工作經驗

2、精通 C/C++,具有較強的問題分析及編程實現能力

3、精通 OpenCV 的使用,同時熟悉 Matlab 更佳

4、熟練 linux 開發環境,能解決 linux 環境配置的常見問題

5、積極、主動,良好的溝通能力及團隊合作精神

6、良好的英文閱讀能力,能直接讀懂英文資料

㈡ K-近鄰演算法簡介

1.K-近鄰(KNearestNeighbor,KNN)演算法簡介 :對於一個未知的樣本,我們可以根據離它最近的k個樣本的類別來判斷它的類別。

以下圖為例,對於一個未知樣本綠色小圓,我們可以選取離它最近的3的樣本,其中包含了2個紅色三角形,1個藍色正方形,那麼我們可以判斷綠色小圓屬於紅色三角形這一類。
我們也可以選取離它最近的5個樣本,其中包含了3個藍色正方形,2個紅色三角形,那麼我們可以判斷綠色小圓屬於藍色正方形這一類。

3.API文檔

下面我們來對KNN演算法中的參數項做一個解釋說明:

'n_neighbors':選取的參考對象的個數(鄰居個數),默認值為5,也可以自己指定數值,但不是n_neighbors的值越大分類效果越好,最佳值需要我們做一個驗證。
'weights': 距離的權重參數,默認uniform。
'uniform': 均勻的權重,所有的點在每一個類別中的權重是一樣的。簡單的說,就是每個點的重要性都是一樣的。
'distance':權重與距離的倒數成正比,距離近的點重要性更高,對於結果的影響也更大。
'algorithm':運算方法,默認auto。
'auto':根絕模型fit的數據自動選擇最合適的運算方法。
'ball_tree':樹模型演算法BallTree
'kd_tree':樹模型演算法KDTree
'brute':暴力演算法
'leaf_size':葉子的尺寸,默認30。只有當algorithm = 'ball_tree' or 'kd_tree',這個參數需要設定。
'p':閔可斯基距離,當p = 1時,選擇曼哈頓距離;當p = 2時,選擇歐式距離。
n_jobs:使用計算機處理器數目,默認為1。當n=-1時,使用所有的處理器進行運算。

4.應用案例演示
下面以Sklearn庫中自帶的數據集--手寫數字識別數據集為例,來測試下kNN演算法。上一章,我們簡單的介紹了機器學習的一般步驟:載入數據集 - 訓練模型 - 結果預測 - 保存模型。這一章我們還是按照這個步驟來執行。
[手寫數字識別數據集] https://scikit-learn.org/stable/moles/generated/sklearn.datasets.load_digits.html#sklearn.datasets.load_digits

5.模型的方法
每一種模型都有一些它獨有的屬性方法(模型的技能,能做些什麼事),下面我們來了解下knn演算法常用的的屬性方法。

6.knn演算法的優缺點
優點:
簡單,效果還不錯,適合多分類問題
缺點:
效率低(因為要計算預測樣本距離每個樣本點的距離,然後排序),效率會隨著樣本量的增加而降低。

㈢ React的diff演算法詳解

一、什麼是diff演算法?

為了增強用戶體驗,React從版本16開始將 同步更新 重構成了 可中斷的非同步更新 ,即採用了新的Reconciler(協調器,用於找出變化的組件),而新的Reconciler中採用了fiber架構。fiber架構的原理在此不再詳細解釋,我們目前只需要知道fiber節點可以保存dom信息,fiber節點構成的樹叫fiber樹,而更新dom是要用到『雙緩存技術』,即比較舊的fiber樹與此次要渲染的jsx對象,返回新的fiber樹進行渲染。 在舊fiber樹與jsx對象比較時,決定哪些節點要復用的過程,就是diff演算法

由於diff本身也會帶來性能消耗,為了降低演算法復雜度,React對diff做了 三個預設限制

更新後

如果沒有key會走第二條限制,有了key,react就可以判斷div和p節點是存在的,可以復用,只需要交換順序。

diff演算法會根據不同的jsx對象執行不同的處理函數,根據jsx對象的不同,我們可以分為兩類

1.JSX對象(之後都用newChildren表示)的類型為object、number、string,代表同級只有一個節點
2. newChildren的類型為Array,代表同級有多個節點。

二、單節點diff

對於單節點diff,用一個流程圖就可以解釋

更新後

由於 key的默認值為null ,所以更新前與更新後滿足key相同且元素類型不同,那麼我們要刪除更新前的三個div節點,新增p節點

三、多節點diff

對於多節點diff, 我們要 遍歷newChildren和oldFiber 進行比較。由於React團隊發現dom節點一般有更新,增加,刪除這三種操作,而更新更為頻繁,所以他們設置更新的優先順序高於增加刪除。基於以上原因,在多節點diff演算法的實現中有兩層遍歷, 第一層遍歷處理更新的節點,第二層遍歷處理更新以外的節點

第一層遍歷

遍歷newChildren與oldFiber, 判斷節點是否可復用,如果可以復用,則繼續遍歷。
如果不能復用,分為兩種情況:

第二層遍歷

第二層遍歷從第一層遍歷的結束位開始
第一層遍歷結束後有4種結果

首先我們要判斷newChildren中遍歷到的節點,在oldFiber中是否存在,基於此,React將oldFiber中的節點以key-oldfiber 鍵值對的形式存在Map中,只需要newChildren的key,就可以判斷oldFiber中有沒有相應的節點。

如果oldFiber中沒有相應的節點,則將newChildren生成的fiber打上placement標記

如果有相應的節點,將它的索引記為oldIndex,與上一次可復用節點在oldFiber的索引位置lastPlacedIndex比較,如果每次可復用的節點在上一次可復用右邊就說明位置沒有變化 ,即

oldIndex >=lastPlacedIndex, 說明相對位置沒有變化 ,那麼令lastPlacedIndex=oldIndex
oldIndex<lastPlacedIndex, 代表本節點需要向右移動
例如:

參考文檔
React技術揭秘 (iamkasong.com)

㈣ md5 演算法程序+詳細注釋,高分求教!

MD5加密演算法簡介

一、綜述
MD5的全稱是message-digest algorithm 5(信息-摘要演算法),在90年代初由mit laboratory for computer science和rsa data security inc的ronald l. rivest開發出來,經md2、md3和md4發展而來。它的作用是讓大容量信息在用數字簽名軟體簽署私人密匙前被"壓縮"成一種保密的格式(就是把一 個任意長度的位元組串變換成一定長的大整數)。不管是md2、md4還是md5,它們都需要獲得一個隨機長度的信息並產生一個128位的信息摘要。雖然這些 演算法的結構或多或少有些相似,但md2的設計與md4和md5完全不同,那是因為md2是為8位機器做過設計優化的,而md4和md5卻是面向32位的電 腦。這三個演算法的描述和c語言源代碼在internet rfcs 1321中有詳細的描述(http://www.ietf.org/rfc/rfc1321.txt),這是一份最權威的文檔,由ronald l. rivest在1992年8月向ieft提交。

rivest在1989年開發出md2演算法。在這個演算法中,首先對信 息進行數據補位,使信息的位元組長度是16的倍數。然後,以一個16位的檢驗和追加到信息末尾。並且根據這個新產生的信息計算出散列值。後來,rogier 和chauvaud發現如果忽略了檢驗和將產生md2沖突。md2演算法的加密後結果是唯一的--既沒有重復。
為了加強演算法的安全性, rivest在1990年又開發出md4演算法。md4演算法同樣需要填補信息以確保信息的位元組長度加上448後能被512整除(信息位元組長度mod 512 = 448)。然後,一個以64位二進製表示的信息的最初長度被添加進來。信息被處理成512位damg?rd/merkle迭代結構的區塊,而且每個區塊要 通過三個不同步驟的處理。den boer和bosselaers以及其他人很快的發現了攻擊md4版本中第一步和第三步的漏洞。dobbertin向大家演示了如何利用一部普通的個人電 腦在幾分鍾內找到md4完整版本中的沖突(這個沖突實際上是一種漏洞,它將導致對不同的內容進行加密卻可能得到相同的加密後結果)。毫無疑問,md4就此 被淘汰掉了。
盡管md4演算法在安全上有個這么大的漏洞,但它對在其後才被開發出來的好幾種信息安全加密演算法的出現卻有著不可忽視的引導作用。除了md5以外,其中比較有名的還有sha-1、ripe-md以及haval等。
一年以後,即1991年,rivest開發出技術上更為趨近成熟的md5演算法。它在md4的基礎上增加了"安全-帶子"(safety-belts)的 概念。雖然md5比md4稍微慢一些,但卻更為安全。這個演算法很明顯的由四個和md4設計有少許不同的步驟組成。在md5演算法中,信息-摘要的大小和填充 的必要條件與md4完全相同。den boer和bosselaers曾發現md5演算法中的假沖突(pseudo-collisions),但除此之外就沒有其他被發現的加密後結果了。
van oorschot和wiener曾經考慮過一個在散列中暴力搜尋沖突的函數(brute-force hash function),而且他們猜測一個被設計專門用來搜索md5沖突的機器(這台機器在1994年的製造成本大約是一百萬美元)可以平均每24天就找到一 個沖突。但單從1991年到2001年這10年間,竟沒有出現替代md5演算法的md6或被叫做其他什麼名字的新演算法這一點,我們就可以看出這個瑕疵並沒有 太多的影響md5的安全性。上面所有這些都不足以成為md5的在實際應用中的問題。並且,由於md5演算法的使用不需要支付任何版權費用的,所以在一般的情 況下(非絕密應用領域。但即便是應用在絕密領域內,md5也不失為一種非常優秀的中間技術),md5怎麼都應該算得上是非常安全的了。

二、演算法的應用

md5的典型應用是對一段信息(message)產生信息摘要(message-digest),以防止被篡改。比如,在unix下有很多軟體在下載的時候都有一個文件名相同,文件擴展名為.md5的文件,在這個文件中通常只有一行文本,大致結構如:
md5 (tanajiya.tar.gz) =
這就是tanajiya.tar.gz文件的數字簽名。md5將整個文件當作一個大文本信息,通過其不可逆的字元串變換演算法,產生了這個唯一的md5信 息摘要。如果在以後傳播這個文件的過程中,無論文件的內容發生了任何形式的改變(包括人為修改或者下載過程中線路不穩定引起的傳輸錯誤等),只要你對這個 文件重新計算md5時就會發現信息摘要不相同,由此可以確定你得到的只是一個不正確的文件。如果再有一個第三方的認證機構,用md5還可以防止文件作者的 "抵賴",這就是所謂的數字簽名應用。
md5還廣泛用於加密和解密技術上。比如在unix系統中用戶的密碼就是以md5(或其它類似的算 法)經加密後存儲在文件系統中。當用戶登錄的時候,系統把用戶輸入的密碼計算成md5值,然後再去和保存在文件系統中的md5值進行比較,進而確定輸入的 密碼是否正確。通過這樣的步驟,系統在並不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統的合法性。這不但可以避免用戶的密碼被具有系統管理員許可權的 用戶知道,而且還在一定程度上增加了密碼被破解的難度。
正是因為這個原因,現在被黑客使用最多的一種破譯密碼的方法就是一種被稱為"跑字 典"的方法。有兩種方法得到字典,一種是日常搜集的用做密碼的字元串表,另一種是用排列組合方法生成的,先用md5程序計算出這些字典項的md5值,然後 再用目標的md5值在這個字典中檢索。我們假設密碼的最大長度為8位位元組(8 bytes),同時密碼只能是字母和數字,共26+26+10=62個字元,排列組合出的字典的項數則是p(62,1)+p(62,2)….+p (62,8),那也已經是一個很天文的數字了,存儲這個字典就需要tb級的磁碟陣列,而且這種方法還有一個前提,就是能獲得目標賬戶的密碼md5值的情況 下才可以。這種加密技術被廣泛的應用於unix系統中,這也是為什麼unix系統比一般操作系統更為堅固一個重要原因。

三、演算法描述

對md5演算法簡要的敘述可以為:md5以512位分組來處理輸入的信息,且每一分組又被劃分為16個32位子分組,經過了一系列的處理後,演算法的輸出由四個32位分組組成,將這四個32位分組級聯後將生成一個128位散列值。
在md5演算法中,首先需要對信息進行填充,使其位元組長度對512求余的結果等於448。因此,信息的位元組長度(bits length)將被擴展至n*512+448,即n*64+56個位元組(bytes),n為一個正整數。填充的方法如下,在信息的後面填充一個1和無數個 0,直到滿足上面的條件時才停止用0對信息的填充。然後,在在這個結果後面附加一個以64位二進製表示的填充前信息長度。經過這兩步的處理,現在的信息字 節長度=n*512+448+64=(n+1)*512,即長度恰好是512的整數倍。這樣做的原因是為滿足後面處理中對信息長度的要求。
md5中有四個32位被稱作鏈接變數(chaining variable)的整數參數,他們分別為:a=0x01234567,b=0x89abcdef,c=0xfedcba98,d=0x76543210。
當設置好這四個鏈接變數後,就開始進入演算法的四輪循環運算。循環的次數是信息中512位信息分組的數目。
將上面四個鏈接變數復制到另外四個變數中:a到a,b到b,c到c,d到d。
主循環有四輪(md4隻有三輪),每輪循環都很相似。第一輪進行16次操作。每次操作對a、b、c和d中的其中三個作一次非線性函數運算,然後將所得結 果加上第四個變數,文本的一個子分組和一個常數。再將所得結果向右環移一個不定的數,並加上a、b、c或d中之一。最後用該結果取代a、b、c或d中之 一。
以一下是每次操作中用到的四個非線性函數(每輪一個)。

f(x,y,z) =(x&y)|((~x)&z)
g(x,y,z) =(x&z)|(y&(~z))
h(x,y,z) =x^y^z
i(x,y,z)=y^(x|(~z))
(&是與,|是或,~是非,^是異或)

這四個函數的說明:如果x、y和z的對應位是獨立和均勻的,那麼結果的每一位也應是獨立和均勻的。
f是一個逐位運算的函數。即,如果x,那麼y,否則z。函數h是逐位奇偶操作符。

假設mj表示消息的第j個子分組(從0到15),
<< ff(a,b,c,d,mj,s,ti) 表示 a=b+((a+(f(b,c,d)+mj+ti)
<< gg(a,b,c,d,mj,s,ti) 表示 a=b+((a+(g(b,c,d)+mj+ti)
<< hh(a,b,c,d,mj,s,ti) 表示 a=b+((a+(h(b,c,d)+mj+ti)
<< ii(a,b,c,d,mj,s,ti) 表示 a=b+((a+(i(b,c,d)+mj+ti)
<< 這四輪(64步)是:

第一輪

ff(a,b,c,d,m0,7,0xd76aa478)
ff(d,a,b,c,m1,12,0xe8c7b756)
ff(c,d,a,b,m2,17,0x242070db)
ff(b,c,d,a,m3,22,0xc1bdceee)
ff(a,b,c,d,m4,7,0xf57c0faf)
ff(d,a,b,c,m5,12,0x4787c62a)
ff(c,d,a,b,m6,17,0xa8304613)
ff(b,c,d,a,m7,22,0xfd469501)
ff(a,b,c,d,m8,7,0x698098d8)
ff(d,a,b,c,m9,12,0x8b44f7af)
ff(c,d,a,b,m10,17,0xffff5bb1)
ff(b,c,d,a,m11,22,0x895cd7be)
ff(a,b,c,d,m12,7,0x6b901122)
ff(d,a,b,c,m13,12,0xfd987193)
ff(c,d,a,b,m14,17,0xa679438e)
ff(b,c,d,a,m15,22,0x49b40821)

第二輪

gg(a,b,c,d,m1,5,0xf61e2562)
gg(d,a,b,c,m6,9,0xc040b340)
gg(c,d,a,b,m11,14,0x265e5a51)
gg(b,c,d,a,m0,20,0xe9b6c7aa)
gg(a,b,c,d,m5,5,0xd62f105d)
gg(d,a,b,c,m10,9,0x02441453)
gg(c,d,a,b,m15,14,0xd8a1e681)
gg(b,c,d,a,m4,20,0xe7d3fbc8)
gg(a,b,c,d,m9,5,0x21e1cde6)
gg(d,a,b,c,m14,9,0xc33707d6)
gg(c,d,a,b,m3,14,0xf4d50d87)
gg(b,c,d,a,m8,20,0x455a14ed)
gg(a,b,c,d,m13,5,0xa9e3e905)
gg(d,a,b,c,m2,9,0xfcefa3f8)
gg(c,d,a,b,m7,14,0x676f02d9)
gg(b,c,d,a,m12,20,0x8d2a4c8a)

第三輪

hh(a,b,c,d,m5,4,0xfffa3942)
hh(d,a,b,c,m8,11,0x8771f681)
hh(c,d,a,b,m11,16,0x6d9d6122)
hh(b,c,d,a,m14,23,0xfde5380c)
hh(a,b,c,d,m1,4,0xa4beea44)
hh(d,a,b,c,m4,11,0x4bdecfa9)
hh(c,d,a,b,m7,16,0xf6bb4b60)
hh(b,c,d,a,m10,23,0xbebfbc70)
hh(a,b,c,d,m13,4,0x289b7ec6)
hh(d,a,b,c,m0,11,0xeaa127fa)
hh(c,d,a,b,m3,16,0xd4ef3085)
hh(b,c,d,a,m6,23,0x04881d05)
hh(a,b,c,d,m9,4,0xd9d4d039)
hh(d,a,b,c,m12,11,0xe6db99e5)
hh(c,d,a,b,m15,16,0x1fa27cf8)
hh(b,c,d,a,m2,23,0xc4ac5665)

第四輪

ii(a,b,c,d,m0,6,0xf4292244)
ii(d,a,b,c,m7,10,0x432aff97)
ii(c,d,a,b,m14,15,0xab9423a7)
ii(b,c,d,a,m5,21,0xfc93a039)
ii(a,b,c,d,m12,6,0x655b59c3)
ii(d,a,b,c,m3,10,0x8f0ccc92)
ii(c,d,a,b,m10,15,0xffeff47d)
ii(b,c,d,a,m1,21,0x85845dd1)
ii(a,b,c,d,m8,6,0x6fa87e4f)
ii(d,a,b,c,m15,10,0xfe2ce6e0)
ii(c,d,a,b,m6,15,0xa3014314)
ii(b,c,d,a,m13,21,0x4e0811a1)
ii(a,b,c,d,m4,6,0xf7537e82)
ii(d,a,b,c,m11,10,0xbd3af235)
ii(c,d,a,b,m2,15,0x2ad7d2bb)
ii(b,c,d,a,m9,21,0xeb86d391)

常數ti可以如下選擇:
在第i步中,ti是4294967296*abs(sin(i))的整數部分,i的單位是弧度。(4294967296等於2的32次方)
所有這些完成之後,將a、b、c、d分別加上a、b、c、d。然後用下一分組數據繼續運行演算法,最後的輸出是a、b、c和d的級聯。
當你按照我上面所說的方法實現md5演算法以後,你可以用以下幾個信息對你做出來的程序作一個簡單的測試,看看程序有沒有錯誤。

md5 ("") =
md5 ("a") =
md5 ("abc") =
md5 ("message digest") =
md5 ("abcdefghijklmnopqrstuvwxyz") =
md5 ("") =
md5 ("1234567890") =

如果你用上面的信息分別對你做的md5演算法實例做測試,最後得出的結論和標准答案完全一樣,那我就要在這里象你道一聲祝賀了。要知道,我的程序在第一次編譯成功的時候是沒有得出和上面相同的結果的。

四、MD5的安全性

md5相對md4所作的改進:

1. 增加了第四輪;

2. 每一步均有唯一的加法常數;

3. 為減弱第二輪中函數g的對稱性從(x&y)|(x&z)|(y&z)變為(x&z)|(y&(~z));

4. 第一步加上了上一步的結果,這將引起更快的雪崩效應;

5. 改變了第二輪和第三輪中訪問消息子分組的次序,使其更不相似;

6. 近似優化了每一輪中的循環左移位移量以實現更快的雪崩效應。各輪的位移量互不相同。

㈤ DPDK ACL演算法介紹

DPDK提供了三種classify演算法:最長匹配LPM、精確匹配(Exact Match)和通配符匹配(ACL)。

其中的ACL演算法,本質是步長為8的Multi-Bit Trie,即每次可匹配一個位元組。一般來說步長為n時,Trie中每個節點的出邊為2^n,但DPDK在生成run-time structures時,採用DFA/QRANGE/SINGLE這幾種不同的方式進行數據結構的壓縮,有效去除了冗餘的出邊。本文將為大家介紹ACL演算法的基本原理,主要內容包括:trie樹的構造、運行時的node array生成和匹配原理。對於ACL介面的使用,參考DPDK的官方文檔即可。

ACL規則主要面向的是IP流量中的五元組信息,即IP/PORT/PROTO,演算法在這個基礎上進行了抽象,提供了三種類型的匹配區域:

熟悉這三種類型的使用後,完全可以用它們去匹配網路報文的其它區域,甚至將其應用到其它場景中。

具體來說,rte_acl_field_def有5個成員:type、size、field_index、input_index、offset。
如果要深入理解演算法,可以思考這幾個欄位的意義,或者換個角度來看:

對於規則的定義,要注意如下兩點:

比如定義了5個field,那麼請給出每一個的具體定義:

像field[1]中IP和mask都為0,表示匹配所有的IP地址;field[3]中range由0到65535,表示匹配所有。類似這樣的全匹配一定要顯示的定義出來,因為如果不明確定義,這些欄位的值取決於編譯器的,最後編譯的ACL規則很可能與原有設想存在偏差。

如果在規則中,對於某個field不進行限制,對於不同type的field,規則書寫時有一定差異:
對於BITMASK和MASK類型,全0代表匹配所有,如上例中的field[0]、field[1];
對於RANGE,則按照上述field[3]中的形式定義。

規則定義好後,會轉換為trie樹並最終合並到一起。
實際處理過程中,build_trie函數會自底向上的將rule中的每個field轉換為node,然後將這些node合並生成這條rule的trie,最後將這個trie與已有的trie進行merge,最終生成整個rule set的trie。

tire由node組成,其主要數據成員如下:

node中values成員用於記錄匹配信息,ptrs則用於描述node的出邊,用於指向轉換後的node。

values採用bitmap進行壓縮,其數據結構為struct rte_acl_bitset values; 一個byte取值范圍是[0,255],可通過256個bit位來進行對應,並實現byte值的快速查找:即通過第x位的bit值是否為1來判斷是否包含數值x(0 <= x < 256)。

num_ptrs用於描述出邊數目,ptrs即為實際的出邊,它記錄了其匹配值values和匹配後的節點指針。
match_flag和mrt則用於記錄匹配結果,trie樹中葉子節點一定是記錄匹配結果的節點。

trie樹其詳細結構比較復雜,這里將其結構進行簡化,如下所示:

上圖的trie樹有4個node,通過ptrs進行指向,values欄位為匹配值的bitmap表示,為了表述的簡潔,後續會採用simple的方式進行描述。
在trie simple中,實心節點表示匹配節點,邊上的數字代表匹配值(為便於閱讀,採用實際值而不再是bitmap形式),…代表其它匹配值。

不同type的field,轉換為node的方式會有所不同。
目前提供的3種類型:BITMASK描述一個byte的匹配,支持mask模式;MASK用於描述4個byte的匹配,支持mask模式;RANGE描述2個byte的匹配,此時mask表示上限。
field到node的轉換,見build_trie中的for循環,具體轉換函數則參考:

對於BITMASK,如{.value.u8 = 6, .mask_range.u8 = 0xff,},它最後的轉換形式如下:

構造field的node時,總會在結尾添加一個空的end節點,最後一個field除外(它是match node)。在for循環中每完成了一個field的解析後,會將其合並到root中,從而生成這個rule的trie。
合並前,也會先構造一個空的end node(見build_trie函數中,while循環下的root創建),讓它與field構成的node頭合並,因為不相交,所以merge時會將匹配信息合並到end node並釋放原有的頭,並將field鏈的end節點返回(保存到end_prev中),下次合並時,就用此end節點與新的node頭合並。
循環遍歷完所有的field後,這些node就串聯起來了,構成這個rule的trie。

對於多個rule,每次構造完成後會merge到整體的trie中。
這里詳細介紹下merge演算法原理,其實仔細閱讀acl_merge_trie函數的注釋即可。

對於node A和node B的merge, acl_merge_trie函數返回一個節點,這個節點指向它們路徑的交集。
這里給出三個例子用於展示merge前後的變化。為了減少狀態點,構造rte_acl_field_def如下:

示例1:

acl_rules[1]為trie A,acl_rules[0]對應trie B,最終trie B合並到trie A上,具體如下:

1和1』合並時,因為level為0,所以1』直接合並到1中;
4和4』合並時,因為節點無交集,所以創建新節點c1(node 4的拷貝),並將4'上的邊拷貝到c1中。

示例2,rule類別相同,但優先順序不同:

acl_rules[1]為trie A,acl_rules[0]對應trie B,最終trie B合並到trie A上,具體如下:

6和6』是match node,類別相同,且6的優先順序為2大於6』的優先順序。
6和6』合並時,直接返回6。而前面創建的新節點,如d1,已包含5』的所有邊(非ACL_INTERSECT_B),所以最終返回5,free d1。
同理依次往上回溯,a4,b3,c2,也依次被釋放,最終merge的trie即為原來的trie A。

示例3,rule類別不同,優先順序相同:

acl_rules[1]為trie A,acl_rules[0]對應trie B,最終trie B合並到trie A上,具體如下:

6和6』是match node,因為類別不同,所以最終創建了新node e1,這也導致示例3和示例2最終merge結果的不同。

合並是一個遞歸的過程,逆向思考構造過程會有助於理解演算法。另外,在build_trie之前會sort_rule,匹配范圍更大的rule會放到前面優先構造trie,個人為這樣node A包含node B的概率更大,這可能也是merge時創建的node C是A的拷貝而不是B的拷貝的原因,因為這樣出現ACL_INTERSECT_B的概率相對較低。

一些說明:

trie樹構造完成後,會將其由指針跳轉的形式轉換為等效的數組索引形式,即node array,既可讓匹配數據更緊湊,也可提高匹配演算法的效率。
採用node array的方式進行狀態點的壓縮是很常見的優化方式,比如snort裡面的ac演算法(acsmx.c):

筆者也曾經做過類似的優化,通過將出邊由指針方式修改為索引方式,整個匹配tree的內存佔用只需要原來的1/5。
將指針方式轉換為node array形式是優化的第一步,對於Next[256]出邊又可以採用多種壓縮方式,比如snort中新的ac演算法(acsmx2.c),就採用了Sparse rows和Banded rows等多種壓縮方式,但其原理是將出邊進行映射轉換,本質上還是做DFA狀態跳轉。

DPDK對邊的壓縮方式與上述類似,不過它優化的粒度更細,不同type的node有不同的壓縮方式:

比如在示例三中,node 1為DFA節點(根節點強制使用DFA方式),2、3、a5、b4、c3、d2為QRANGE,4、5為SINGLE,6、e1為MATCH。
2、3、a5、b4雖然在圖上僅有一條有效邊,但它不為SINGLE,因為對於無效的匹配其實也會有出邊,所以它的真實出邊數目並不唯一,只有像4、5這類全匹配節點才是真正的SINGLE節點。

在構造node array前,會調用acl_calc_counts_indices函數更新node的node type,fanout等信息。
node type依據其fanout值決定,fanout計算見acl_count_fanout函數,其原理是:

比如對於示例3中的d2節點:

fanout計算完成後,若其值為1則為SINGLE節點,(1, 5]為QRANGE節點,(5, 256]為DFA節點。
注意:對於trie樹的root節點,不論fanout值為多少,會強制將其構造為DFA節點,且其fanout值會重新計算。

type和fanout計算完成後,會統計各類節點數目,信息保存在acl_calc_counts_indices傳入的counts參數中,隨後rte_acl_gen依據這些信息將整塊的node array內存分配出來,其布局大致如下:

Data indexes中用於保存在rte_acl_field_def中定義的offset;
Results對應match node,用於保存匹配結果。
Trans table包含整個匹配過程中的跳轉點:

靜態將整塊node array分配完成後,就需要依據trie 樹的node信息填充Trans table和Results了,具體過程見acl_gen_node函數;Data indexes的填充則在acl_set_data_indexes中完成。

2.2中的內存布局大致描繪了各種類型節點的分布情況,DFAs內部由一個一個的DFA節點組成,QUADs和SINGLEs也一樣,都是由相同類型的節點構成。
對於每一個節點,其結構則類似如下形式:

DFA節點的fanout一般為4,出邊數為fanout*RTE_ACL_DFA_GR64_SIZE;(圖中畫的為fanout為4的情況,256條出邊)
QUAD節點的fanout不超過5,即為節點的出邊數不超過5;(圖中畫的為fanout為4的情況)
SINGLE節點只有一個出邊;
圖中的trans即為這個節點的出邊,它本質是一個uint64的數據結構,通過trans和input信息即可計算得到下一個節點的index,從而實現匹配跳轉。trans中不同bit位包含著豐富的信息,具體見acl.h中的說明即可。

高32位對於不同類型的節點有不同的解釋:

低32位:

在實際處理過程中,通過高32位與input_byte計算得到index,與低32位中的addr,即可快速定位到下一個trans:trans_table + (addr+index)。
這里的處理其實與傳統的DFA跳轉差別很大,傳統處理時,next = node[『input』],跳轉到下一個節點,然後採用next[『input』]進行跳轉和匹配,即使有數據結構的壓縮,跳轉目標仍是狀態點。但DPDK中,跳轉時直接採用trans_table + (addr+index),直接找到了狀態點的邊(trans),而不是到狀態點。

跳轉表具體構建時,採用acl_gen_node函數完成:

匹配的過程與跳轉表的構建其實是互為一體的,如何構建跳轉表就決定了如何進行匹配。

在2.3節,對於跳轉的形式已進行了說明,具體可閱讀rte_acl_classify_scalar函數:跳轉時直接採用trans_table + (addr+index),直接找到了狀態點的邊(trans),而不是到狀態點。

對於具體的匹配過程,還有一點需要注意,即GET_NEXT_4BYTES的使用,每次匹配時候都會去4BTYTES進行匹配,這也是為什麼定義input fields時要求4位元組連續。比如我在dpdk-dev郵件組中問的這個 問題 。

解決4位元組連續,可以通過定義相同的input_index來解決,比如像郵件中提到的設置sport/dport的input_index相同,這是因為data indexes的構造取決於input_index,見acl_build_index函數;同時field_index不同、input_index相同時可避免對field區間的優化(如果優化,將某個field去掉了,這時4位元組匹配會失效)。郵件中的問題,正是因為field[3]被優化掉後,4位元組連續匹配出現問題。

在特定的場合還必須通過指定.size為32來解決,即使type類型為BITMASK,見DPDK的ACL文檔中關於 tos示例的說明 。

另外再說下field_index,前面提出一個問題:field_index是否多餘?
答案是不多餘,因為演算法中會對field進行優化,如果不指定field_index欄位,這個優化就無法進行了,具體的優化處理見acl_rule_stats函數。
優化過程中要進行input_index的判斷,這是因為相同的input_index可以有多個field,但其中只有某個field是completely wild時應避免進行優化。只有相同input_index的所有field都是completely wild時,才應該將這個field優化掉。

上面的一系列說明,都是針對GET_NEXT_4BYTES每次匹配四個位元組的匹配進行的補充說明。

匹配的具體過程,這里用圖形的方式進行簡要說明,為了能有多種類型的node,這里構造規則如下:

trie樹如下所述:

對應的node array如下圖所示:

假設輸入數據為:proto 16, ip 192.12.8.8,則transition跳轉方式如上圖紅線所示:

注意:node array中indexes、DFA0和idle省略了。

關於trie樹相關的理論知識參考 這里 。

本文主要介紹了DPDK的ACL演算法,詳細描述了如何由規則生成trie,並將trie轉換為node array的過程,在文末通過示例介紹了具體的匹配過程。文章旨在介紹ACL演算法的基本思路,希望對大家能有所幫助。

㈥ 演算法課程設計報告

題目中要求的功能進行敘述分析,並且設計解決此問題的數據存儲結構,(有些題目已經指定了數據存儲的,按照指定的設計),設計或敘述解決此問題的演算法,描述演算法建議使用流程圖,進行演算法分析指明關鍵語句的時間復雜度。
給出實現功能的一組或多組測試數據,程序調試後,將按照此測試數據進行測試的結果列出來 。
對有些題目提出演算法改進方案,比較不同演算法的優缺點。
如果程序不能正常運行,寫出實現此演算法中遇到的問題,和改進方法;
2 對每個題目要有相應的源程序(可以是一組源程序,即詳細設計部分):
源程序要按照寫程序的規則來編寫。要結構清晰,重點函數的重點變數,重點功能部分要加上清晰的程序注釋。
程序能夠運行,要有基本的容錯功能。盡量避免出現操作錯誤時出現死循環;
3 最後提供的主程序可以象一個應用系統一樣有主窗口,通過主菜單和分級菜單調用課程設計中要求完成的各個功能模塊,調用後可以返回到主菜單,繼續選擇其他功能進行其他功能的選擇。最好有窗口展示部分。
4 課程設計報告:(保存在word 文檔中,文件名要求 按照"姓名-學號-課程設計報告"起名,如文件名為"張三-001-課程設計報告".doc )按照課程設計的具體要求建立的功能模塊,每個模塊要求按照如下幾個內容認真完成;
其中包括:
a)需求分析:
在該部分中敘述,每個模塊的功能要求
b)概要設計
在此說明每個部分的演算法設計說明(可以是描述演算法的流程圖),每個程序中使用的存儲結構設計說明(如果指定存儲結構請寫出該存儲結構的定義。
c)詳細設計
各個演算法實現的源程序,對每個題目要有相應的源程序(可以是一組源程序,每個功能模塊採用不同的函數實現)
源程序要按照寫程序的規則來編寫。要結構清晰,重點函數的重點變數,重點功能部分要加上清晰的程序注釋。
d)調試分析
測試數據,測試輸出的結果,時間復雜度分析,和每個模塊設計和調試時存在問題的思考(問題是哪些?問題如何解決?),演算法的改進設想。
5. 課設總結: (保存在word 文檔中)總結可以包括 : 課程設計 過程的收獲、遇到問題、遇到問題解決問題過程的思考、程序調試能力的思考、對數據結構這門課程的思考、在課程設計過程中對C課程的認識等內容;
6.實驗報告的首頁請參考如下格式:

課程設計實驗
起止日期:20 -20 學年 學期
系別 班級 學號 姓名
實驗題目 □設計性 □綜合性
自我評價
教師評語 能夠實現實驗要求的功能 □全部 □部分演算法有新意 □有 □一般程序運行通過 □全部 □部分 演算法注釋說明 □完善 □僅有功能說明介面參數說明 □有 □無按期上交列印文檔資料及源程序 □所有 □部分綜合設計說明報告結構 □合理 □不合理用戶使用說明 □完整 □不全現場演示操作有準備 □有 □無問題解答流暢 □流暢 □不流暢獨立完成實驗 □能 □不能體現團隊合作精神。 □能夠 □不能
成績

這是張表格,過來時沒調整好,不過應該看得明白。我們是這樣寫的,你可以參考一下。

㈦ Raft 演算法(詳細版)

在分布式系統中,一致性演算法至關重要。在所有一致性演算法中,Paxos 最負盛名,它由萊斯利·蘭伯特(Leslie Lamport)於 1990 年提出,是一種基於消息傳遞的一致性演算法,被認為是類似演算法中最有效的。

Paxos 演算法雖然很有效,但復雜的原理使它實現起來非常困難,截止目前,實現 Paxos 演算法的開源軟體很少,比較出名的有 Chubby、LibPaxos。此外,Zookeeper 採用的 ZAB(Zookeeper Atomic Broadcast)協議也是基於 Paxos 演算法實現的,不過 ZAB 對 Paxos 進行了很多改進與優化,兩者的設計目標也存在差異——ZAB 協議主要用於構建一個高可用的分布式數據主備系統,而 Paxos 演算法則是用於構建一個分布式的一致性狀態機系統。

由於 Paxos 演算法過於復雜、實現困難,極大地制約了其應用,而分布式系統領域又亟需一種高效而易於實現的分布式一致性演算法,在此背景下,Raft 演算法應運而生。

Raft 演算法在斯坦福 Diego Ongaro 和 John Ousterhout 於 2013 年發表的《In Search of an Understandable Consensus Algorithm》中提出。相較於 Paxos,Raft 通過邏輯分離使其更容易理解和實現,目前,已經有十多種語言的 Raft 演算法實現框架,較為出名的有 etcd、Consul 。

根據官方文檔解釋,一個 Raft 集群包含若干節點,Raft 把這些節點分為三種狀態:Leader、 Follower、Candidate,每種狀態負責的任務也是不一樣的。正常情況下,集群中的節點只存在 Leader 與 Follower 兩種狀態。

Leader(領導者) :負責日誌的同步管理,處理來自客戶端的請求,與Follower保持heartBeat的聯系;

Follower(追隨者) :響應 Leader 的日誌同步請求,響應Candidate的邀票請求,以及把客戶端請求到Follower的事務轉發(重定向)給Leader;

Candidate(候選者) :負責選舉投票,集群剛啟動或者Leader宕機時,狀態為Follower的節點將轉為Candidate並發起選舉,選舉勝出(獲得超過半數節點的投票)後,從Candidate轉為Leader狀態。

通常,Raft 集群中只有一個 Leader,其它節點都是 Follower。Follower 都是被動的,不會發送任何請求,只是簡單地響應來自 Leader 或者 Candidate 的請求。Leader 負責處理所有的客戶端請求(如果一個客戶端和 Follower 聯系,那麼 Follower 會把請求重定向給 Leader)。

為簡化邏輯和實現,Raft 將一致性問題分解成了三個相對獨立的子問題。

選舉(Leader Election) :當 Leader 宕機或者集群初創時,一個新的 Leader 需要被選舉出來;

日誌復制(Log Replication) :Leader 接收來自客戶端的請求並將其以日誌條目的形式復制到集群中的其它節點,並且強制要求其它節點的日誌和自己保持一致;

安全性(Safety) :如果有任何的伺服器節點已經應用了一個確定的日誌條目到它的狀態機中,那麼其它伺服器節點不能在同一個日誌索引位置應用一個不同的指令。

根據 Raft 協議,一個應用 Raft 協議的集群在剛啟動時,所有節點的狀態都是 Follower。由於沒有 Leader,Followers 無法與 Leader 保持心跳(Heart Beat),因此,Followers 會認為 Leader 已經下線,進而轉為 Candidate 狀態。然後,Candidate 將向集群中其它節點請求投票,同意自己升級為 Leader。如果 Candidate 收到超過半數節點的投票(N/2 + 1),它將獲勝成為 Leader。

第一階段:所有節點都是 Follower。

上面提到,一個應用 Raft 協議的集群在剛啟動(或 Leader 宕機)時,所有節點的狀態都是 Follower,初始 Term(任期)為 0。同時啟動選舉定時器,每個節點的選舉定時器超時時間都在 100~500 毫秒之間且並不一致(避免同時發起選舉)。

第二階段:Follower 轉為 Candidate 並發起投票。

沒有 Leader,Followers 無法與 Leader 保持心跳(Heart Beat),節點啟動後在一個選舉定時器周期內未收到心跳和投票請求,則狀態轉為候選者 Candidate 狀態,且 Term 自增,並向集群中所有節點發送投票請求並且重置選舉定時器。

注意,由於每個節點的選舉定時器超時時間都在 100-500 毫秒之間,且彼此不一樣,以避免所有 Follower 同時轉為 Candidate 並同時發起投票請求。換言之,最先轉為 Candidate 並發起投票請求的節點將具有成為 Leader 的「先發優勢」。

第三階段:投票策略。

節點收到投票請求後會根據以下情況決定是否接受投票請求(每個 follower 剛成為 Candidate 的時候會將票投給自己):

請求節點的 Term 大於自己的 Term,且自己尚未投票給其它節點,則接受請求,把票投給它;

請求節點的 Term 小於自己的 Term,且自己尚未投票,則拒絕請求,將票投給自己。

第四階段:Candidate 轉為 Leader。

一輪選舉過後,正常情況下,會有一個 Candidate 收到超過半數節點(N/2 + 1)的投票,它將勝出並升級為 Leader。然後定時發送心跳給其它的節點,其它節點會轉為 Follower 並與 Leader 保持同步,到此,本輪選舉結束。

注意:有可能一輪選舉中,沒有 Candidate 收到超過半數節點投票,那麼將進行下一輪選舉。

在一個 Raft 集群中,只有 Leader 節點能夠處理客戶端的請求(如果客戶端的請求發到了 Follower,Follower 將會把請求重定向到 Leader) ,客戶端的每一個請求都包含一條被復制狀態機執行的指令。Leader 把這條指令作為一條新的日誌條目(Entry)附加到日誌中去,然後並行得將附加條目發送給 Followers,讓它們復制這條日誌條目。

當這條日誌條目被 Followers 安全復制,Leader 會將這條日誌條目應用到它的狀態機中,然後把執行的結果返回給客戶端。如果 Follower 崩潰或者運行緩慢,再或者網路丟包,Leader 會不斷得重復嘗試附加日誌條目(盡管已經回復了客戶端)直到所有的 Follower 都最終存儲了所有的日誌條目,確保強一致性。

第一階段:客戶端請求提交到 Leader。

如下圖所示,Leader 收到客戶端的請求,比如存儲數據 5。Leader 在收到請求後,會將它作為日誌條目(Entry)寫入本地日誌中。需要注意的是,此時該 Entry 的狀態是未提交(Uncommitted),Leader 並不會更新本地數據,因此它是不可讀的。

第二階段:Leader 將 Entry 發送到其它 Follower

Leader 與 Followers 之間保持著心跳聯系,隨心跳 Leader 將追加的 Entry(AppendEntries)並行地發送給其它的 Follower,並讓它們復制這條日誌條目,這一過程稱為復制(Replicate)。

有幾點需要注意:

1. 為什麼 Leader 向 Follower 發送的 Entry 是 AppendEntries 呢?

因為 Leader 與 Follower 的心跳是周期性的,而一個周期間 Leader 可能接收到多條客戶端的請求,因此,隨心跳向 Followers 發送的大概率是多個 Entry,即 AppendEntries。當然,在本例中,我們假設只有一條請求,自然也就是一個Entry了。

2. Leader 向 Followers 發送的不僅僅是追加的 Entry(AppendEntries)。

在發送追加日誌條目的時候,Leader 會把新的日誌條目緊接著之前條目的索引位置(prevLogIndex), Leader 任期號(Term)也包含在其中。如果 Follower 在它的日誌中找不到包含相同索引位置和任期號的條目,那麼它就會拒絕接收新的日誌條目,因為出現這種情況說明 Follower 和 Leader 不一致。

3. 如何解決 Leader 與 Follower 不一致的問題?

在正常情況下,Leader 和 Follower 的日誌保持一致,所以追加日誌的一致性檢查從來不會失敗。然而,Leader 和 Follower 一系列崩潰的情況會使它們的日誌處於不一致狀態。Follower可能會丟失一些在新的 Leader 中有的日誌條目,它也可能擁有一些 Leader 沒有的日誌條目,或者兩者都發生。丟失或者多出日誌條目可能會持續多個任期。

要使 Follower 的日誌與 Leader 恢復一致,Leader 必須找到最後兩者達成一致的地方(說白了就是回溯,找到兩者最近的一致點),然後刪除從那個點之後的所有日誌條目,發送自己的日誌給 Follower。所有的這些操作都在進行附加日誌的一致性檢查時完成。

Leader 為每一個 Follower 維護一個 nextIndex,它表示下一個需要發送給 Follower 的日誌條目的索引地址。當一個 Leader 剛獲得權力的時候,它初始化所有的 nextIndex 值,為自己的最後一條日誌的 index 加 1。如果一個 Follower 的日誌和 Leader 不一致,那麼在下一次附加日誌時一致性檢查就會失敗。在被 Follower 拒絕之後,Leader 就會減小該 Follower 對應的 nextIndex 值並進行重試。最終 nextIndex 會在某個位置使得 Leader 和 Follower 的日誌達成一致。當這種情況發生,附加日誌就會成功,這時就會把 Follower 沖突的日誌條目全部刪除並且加上 Leader 的日誌。一旦附加日誌成功,那麼 Follower 的日誌就會和 Leader 保持一致,並且在接下來的任期繼續保持一致。

第三階段:Leader 等待 Followers 回應。

Followers 接收到 Leader 發來的復制請求後,有兩種可能的回應:

寫入本地日誌中,返回 Success;

一致性檢查失敗,拒絕寫入,返回 False,原因和解決辦法上面已做了詳細說明。

需要注意的是,此時該 Entry 的狀態也是未提交(Uncommitted)。完成上述步驟後,Followers 會向 Leader 發出 Success 的回應,當 Leader 收到大多數 Followers 的回應後,會將第一階段寫入的 Entry 標記為提交狀態(Committed),並把這條日誌條目應用到它的狀態機中。

第四階段:Leader 回應客戶端。

完成前三個階段後,Leader會向客戶端回應 OK,表示寫操作成功。

第五階段,Leader 通知 Followers Entry 已提交

Leader 回應客戶端後,將隨著下一個心跳通知 Followers,Followers 收到通知後也會將 Entry 標記為提交狀態。至此,Raft 集群超過半數節點已經達到一致狀態,可以確保強一致性。

需要注意的是,由於網路、性能、故障等各種原因導致「反應慢」、「不一致」等問題的節點,最終也會與 Leader 達成一致。

前面描述了 Raft 演算法是如何選舉 Leader 和復制日誌的。然而,到目前為止描述的機制並不能充分地保證每一個狀態機會按照相同的順序執行相同的指令。例如,一個 Follower 可能處於不可用狀態,同時 Leader 已經提交了若乾的日誌條目;然後這個 Follower 恢復(尚未與 Leader 達成一致)而 Leader 故障;如果該 Follower 被選舉為 Leader 並且覆蓋這些日誌條目,就會出現問題,即不同的狀態機執行不同的指令序列。

鑒於此,在 Leader 選舉的時候需增加一些限制來完善 Raft 演算法。這些限制可保證任何的 Leader 對於給定的任期號(Term),都擁有之前任期的所有被提交的日誌條目(所謂 Leader 的完整特性)。關於這一選舉時的限制,下文將詳細說明。

在所有基於 Leader 機制的一致性演算法中,Leader 都必須存儲所有已經提交的日誌條目。為了保障這一點,Raft 使用了一種簡單而有效的方法,以保證所有之前的任期號中已經提交的日誌條目在選舉的時候都會出現在新的 Leader 中。換言之,日誌條目的傳送是單向的,只從 Leader 傳給 Follower,並且 Leader 從不會覆蓋自身本地日誌中已經存在的條目。

Raft 使用投票的方式來阻止一個 Candidate 贏得選舉,除非這個 Candidate 包含了所有已經提交的日誌條目。Candidate 為了贏得選舉必須聯系集群中的大部分節點。這意味著每一個已經提交的日誌條目肯定存在於至少一個伺服器節點上。如果 Candidate 的日誌至少和大多數的伺服器節點一樣新(這個新的定義會在下面討論),那麼它一定持有了所有已經提交的日誌條目(多數派的思想)。投票請求的限制中請求中包含了 Candidate 的日誌信息,然後投票人會拒絕那些日誌沒有自己新的投票請求。

Raft 通過比較兩份日誌中最後一條日誌條目的索引值和任期號,確定誰的日誌比較新。如果兩份日誌最後條目的任期號不同,那麼任期號大的日誌更加新。如果兩份日誌最後的條目任期號相同,那麼日誌比較長的那個就更加新。

如同 4.1 節介紹的那樣,Leader 知道一條當前任期內的日誌記錄是可以被提交的,只要它被復制到了大多數的 Follower 上(多數派的思想)。如果一個 Leader 在提交日誌條目之前崩潰了,繼任的 Leader 會繼續嘗試復制這條日誌記錄。然而,一個 Leader 並不能斷定被保存到大多數 Follower 上的一個之前任期里的日誌條目 就一定已經提交了。這很明顯,從日誌復制的過程可以看出。

鑒於上述情況,Raft 演算法不會通過計算副本數目的方式去提交一個之前任期內的日誌條目。只有 Leader 當前任期里的日誌條目通過計算副本數目可以被提交;一旦當前任期的日誌條目以這種方式被提交,那麼由於日誌匹配特性,之前的日誌條目也都會被間接的提交。在某些情況下,Leader 可以安全地知道一個老的日誌條目是否已經被提交(只需判斷該條目是否存儲到所有節點上),但是 Raft 為了簡化問題使用了一種更加保守的方法。

當 Leader 復制之前任期里的日誌時,Raft 會為所有日誌保留原始的任期號,這在提交規則上產生了額外的復雜性。但是,這種策略更加容易辨別出日誌,即使隨著時間和日誌的變化,日誌仍維護著同一個任期編號。此外,該策略使得新 Leader 只需要發送較少日誌條目。

raft 的讀寫都在 leader 節點中進行,它保證了讀的都是最新的值,它是符合強一致性的(線性一致性),raft 除了這個還在【客戶端交互】那塊也做了一些保證,詳情可以參考論文。但是 zookeeper 不同,zookeeper 寫在 leader,讀可以在 follower 進行,可能會讀到了舊值,它不符合強一致性(只考慮寫一致性,不考慮讀一致性),但是 zookeeper 去 follower 讀可以有效提升讀取的效率。

對比於 zab、raft,我們發現他們選舉、setData 都是需要過半機制才行,所以他們針對網路分區的處理方法都是一樣的。

一個集群的節點經過網路分區後,如一共有 A、B、C、D、E 5個節點,如果 A 是 leader,網路分區為 A、B、C 和 D、E,在A、B、C分區還是能正常提供服務的,而在 D、E 分區因為不能得到大多數成員確認(雖然分區了,但是因為配置的原因他們還是能知道所有的成員數量,比如 zk 集群啟動前需要配置所有成員地址,raft 也一樣),是不能進行選舉的,所以保證只會有一個 leader。

如果分區為 A、B 和 C、D、E ,A、B 分區雖然 A 還是 leader,但是卻不能提供事務服務(setData),C、D、E 分區能重新選出 leader,還是能正常向外提供服務。

1)我們所說的日誌(log)與狀態機(state machine)不是一回事,日誌指還沒有提交到狀態機中的數據。
2)新 leader 永遠不會通過計算副本數量提交舊日誌,他只能復制舊日誌都其他 follower 上,對於舊日誌的提交,只能是新 leader 接收新的寫請求寫新日誌,順帶著把舊日誌提交了。

㈧ CACHE替換演算法有哪幾種,分別簡要說明

其代表演算法有:①Hybrid演算法:演算法對Cache中的每一個對象賦予一個效用函數,將效用最小的對象替換出Cache;②LowestRelativeValue演算法:將效用值最低的對象替換出Cache;③(LCNR)演算法:該演算法使用一個關於文檔訪問頻次、傳輸時間和大小的推理函數來確定替換文檔;④Bolot等人提出了一種基於文檔傳輸時間代價、大小、和上次訪問時間的權重推理函數來確定文檔替換;⑤SizeAdjustLRU(SLRU)演算法:對緩存的對象按代價與大小的比率進行排序,並選取比率最小的對象進行替換

擴展知識:
Cache是一種根據程序局部性原則,通過小容量速度快的存儲器緩存部分數據,以減少處理器對慢速大容量存儲器的訪問次數,從而提升處理器取指效率的機制。Cache替換演算法是指當Cache缺失發生後,Cache按某種機制選中高速緩存中的某個地址進行數據更新。Cache替換演算法對Cache的命中率有較大的影響。目前主流的Cache替換演算法有偽隨機、先進先出(FIFO——First In First Out)和最近最少使用(LRU——Least Recently Used)等。相較於偽隨機和先進先出演算法,LRU演算法更符合程序局部性原則(當前執行的程序代碼,在不久後會再次訪問該代碼段),Cache的命中率更高,但其硬體資源消耗非常大。

傳統的LRU演算法對Cache的每一路進行統計,在需要替換時,將最近最少被使用的那一路替換。由於傳統LRU演算法的數據使用頻率統計為向上計數,故其計數器計數位寬較大,且需要額外的機制來處理計數溢出的情況。

㈨ 演算法文檔怎麼寫

雞數=輸入雞數*2-輸入免數/2免數=輸入兔數/2-輸入雞數

閱讀全文

與演算法說明文檔相關的資料

熱點內容
加密電梯卡怎麼復制到蘋果手機上 瀏覽:304
php獲取數據類型 瀏覽:915
新概念c51單片機 瀏覽:326
刪除文件的命令行 瀏覽:981
java編程軟體eclipse 瀏覽:198
番茄app怎麼完成簽約流程 瀏覽:725
ibm伺服器如何進u盤啟動 瀏覽:185
網路驅動重啟命令 瀏覽:446
入職聯想程序員 瀏覽:155
linux拷貝目錄下所有文件 瀏覽:46
androidwebview測試 瀏覽:234
java數組效率 瀏覽:496
java我的世界怎麼免費開伺服器 瀏覽:520
被刪了的app如何找回 瀏覽:358
冒險島飛花院伺服器什麼時間開的 瀏覽:864
old引擎視頻編譯 瀏覽:936
三小虎語音包文件夾 瀏覽:169
安卓區王者怎麼轉移蘋果多少錢 瀏覽:542
怎麼學好電腦的文字編程 瀏覽:400
武俠版pdf 瀏覽:776