JAVA系統中常用算法歸類:
一、限流算法
每個API接口都是有訪問上限的,當訪問頻率或者併發量超過其承受範圍時候,我們就必須考慮限流來保證接口的可用性或者降級可用性.即接口也需要安裝上保險絲,以防止非預期的請求對系統壓力過大而引起的系統癱瘓.
漏桶算法,令牌桶算法,計數算法
- 漏桶算法
漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進入到漏桶裏,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然後就拒絕請求,可以看出漏桶算法能強行限制數據的傳輸速率
- 令牌桶算法
令牌桶算法(Token Bucket)和 Leaky Bucket 效果一樣但方向相反的算法,更加容易理解.隨着時間流逝,系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶裏加入Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了.新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務.
- 計數算法
按照設定的速率對每個請求進行計算,然後滑動窗口來判斷是否超過限制。
相關組件
- Google開源工具包Guava提供了限流工具類RateLimiter
- (網關限流)Spring Cloud Zuul RateLimit項目Github地址 https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit
- eureka組件實現的令牌桶限流
應用場景
- 接口調用頻率限制
- 程序邏輯執行速率控制
- 事務超頻處理
- 網關請求限流
二、分佈式算法
負載均衡算法:
- Hash算法,一致性hash(環形hash),虛擬節點
常用的算法:是對hash結果取餘數 (hash() mod N ):對機器編號從0到N-1,按照自定義的 hash()算法,對每個請求的hash()值按N取模,得到餘數i,然後將請求分發到編號爲i的機器。
1、算法簡述
一致性哈希算法(Consistent Hashing Algorithm)是一種分佈式算法,常用於負載均衡。Memcached client也選擇這種算法,解決將key-value均勻分配到衆多Memcached server上的問題。它可以取代傳統的取模操作,解決了取模操作無法應對增刪Memcached Server的問題(增刪server會導致同一個key,在get操作時分配不到數據真正存儲的server,命中率會急劇下降)。
簡單來說,一致性哈希將整個哈希值空間組織成一個虛擬的圓環,如假設某哈希函數H的值空間爲0 - (2^32)-1(即哈希值是一個32位無符號整形),整個哈希空間環如下:
整個空間按順時針方向組織。0和(2^32)-1在零點中方向重合。
下一步將各個服務器使用H進行一個哈希,具體可以選擇服務器的ip或主機名作爲關鍵字進行哈希,這樣每臺機器就能確定其在哈希環上的位置,這裏假設將上文中三臺服務器使用ip地址哈希後在環空間的位置如下:
接下來使用如下算法定位數據訪問到相應服務器:將數據key使用相同的函數H計算出哈希值h,通根據h確定此數據在環上的位置,從此位置沿環順時針“行走”,第一臺遇到的服務器就是其應該定位到的服務器。
例如我們有A、B、C、D四個數據對象,經過哈希計算後,在環空間上的位置如下:
根據一致性哈希算法,數據A會被定爲到Server 1上,D被定爲到Server 3上,而B、C分別被定爲到Server 2上。
2、容錯性與可擴展性分析
下面分析一致性哈希算法的容錯性和可擴展性。現假設Server 3宕機了:
可以看到此時A、C、B不會受到影響,只有D節點被重定位到Server 2。一般的,在一致性哈希算法中,如果一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即順着逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。
下面考慮另外一種情況,如果我們在系統中增加一臺服務器Memcached Server 4:
此時A、D、C不受影響,只有B需要重定位到新的Server 4。一般的,在一致性哈希算法中,如果增加一臺服務器,則受影響的數據僅僅是新服務器到其環空間中前一臺服務器(即順着逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。
綜上所述,一致性哈希算法對於節點的增減都只需重定位環空間中的一小部分數據,具有較好的容錯性和可擴展性。
3、虛擬節點
一致性哈希算法在服務節點太少時,容易因爲節點分部不均勻而造成數據傾斜問題。例如我們的系統中有兩臺服務器,其環分佈如下:
此時必然造成大量數據集中到Server 1上,而只有極少量會定位到Server 2上。爲了解決這種數據傾斜問題,一致性哈希算法引入了虛擬節點機制,即對每一個服務節點計算多個哈希,每個計算結果位置都放置一個此服務節點,稱爲虛擬節點。具體做法可以在服務器ip或主機名的後面增加編號來實現。例如上面的情況,我們決定爲每臺服務器計算三個虛擬節點,於是可以分別計算“Memcached Server 1#1”、“Memcached Server 1#2”、“Memcached Server 1#3”、“Memcached Server 2#1”、“Memcached Server 2#2”、“Memcached Server 2#3”的哈希值,於是形成六個虛擬節點:
- 輪循算法
多個節點輪流選擇,輪詢負載表現爲每個節點依次路由。
利用JDK的concurrent包下 AtomicInteger 原子操作類的CAS原理,進行整數自增,然後對節點數取模,得到本次輪詢的索引。
- 最少連接算法
逐個考察Server,如果Server被tripped了,則忽略,在選擇其中ActiveRequestsCount最小的server。該策略性能較差
- 隨機算法
隨機產生實例選擇的索引,算法簡單,性能高,負載在小範圍內不均勻
- 加權法
環形hash算法+虛擬節點,使請求根據權重值,不均勻分佈。
- 響應速度算法
每次路由記錄響應時間,選擇響應時間最短的實例進行路由。保證不同硬件環境下實例可資源均衡利用。
相關組件:
- nginx 負載均衡配置
- springcloud-ribbon 負載均衡
分佈式計算算法:
- Paxos算法
Paxos主要用於保證分佈式存儲中副本(或者狀態)的一致性。副本要保持一致,那麼,所有副本的更新序列就要保持一致。解決分佈式條件下的一致性問題,
Paxos解決這一問題利用的是選舉,少數服從多數的思想,只要2N+1個節點中,有N個以上同意了某個決定,則認爲系統達到了一致
https://www.cnblogs.com/esingchan/p/3917718.html
相關組件:
- Google的Chubby、MegaStore
- Spanner數據庫
- Hadoop中的ZooKeeper
- Raft算法
過去, Paxos一直是分佈式協議的標準,但是Paxos難於理解,更難以實現,Google的分佈式鎖系統Chubby作爲Paxos實現曾經遭遇到很多坑。
來自Stanford的新的分佈式協議研究稱爲Raft,它是一個爲真實世界應用建立的協議,主要注重協議的落地性和可理解性。
https://www.jdon.com/artichect/raft.html
三、排序算法
https://www.cnblogs.com/guoyaohua/p/8600214.html
- 冒泡排序
冒泡排序是一種簡單的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。
public static void bubbleSort(int[] numbers) { int temp = 0; int size = numbers.length; for(int i = 0 ; i < size-1; i ++) { for(int j = 0 ;j < size-1-i ; j++) { if(numbers[j] > numbers[j+1]) //交換兩數位置 { temp = numbers[j]; numbers[j] = numbers[j+1]; numbers[j+1] = temp; } } } }
- 選擇排序
選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
/** * 選擇排序 * @param array * @return */ public static int[] selectionSort(int[] array) { if (array.length == 0) return array; for (int i = 0; i < array.length; i++) { int minIndex = i; for (int j = i; j < array.length; j++) { if (array[j] < array[minIndex]) //找到最小的數 minIndex = j; //將最小數的索引保存 } int temp = array[minIndex]; array[minIndex] = array[i]; array[i] = temp; } return array; }
- 插入排序
插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工作原理是通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,通常採用in-place排序(即只需用到O(1)的額外空間的排序),因而在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間。
/** * 插入排序 * @param array * @return */ public static int[] insertionSort(int[] array) { if (array.length == 0) return array; int current; for (int i = 0; i < array.length - 1; i++) { current = array[i + 1]; int preIndex = i; while (preIndex >= 0 && current < array[preIndex]) { array[preIndex + 1] = array[preIndex]; preIndex--; } array[preIndex + 1] = current; } return array; }
- 希爾排序
希爾排序是希爾(Donald Shell)於1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序經過改進之後的一個更高效的版本,也稱爲縮小增量排序,同時該算法是衝破O(n2)的第一批算法之一。它與插入排序的不同之處在於,它會優先比較距離較遠的元素。希爾排序又叫縮小增量排序。
/** * 希爾排序 * * @param array * @return */ public static int[] ShellSort(int[] array) { int len = array.length; int temp, gap = len / 2; while (gap > 0) { for (int i = gap; i < len; i++) { temp = array[i]; int preIndex = i - gap; while (preIndex >= 0 && array[preIndex] > temp) { array[preIndex + gap] = array[preIndex]; preIndex -= gap; } array[preIndex + gap] = temp; } gap /= 2; } return array; }
- top k 算法(小頂堆算法)
問題描述:有N(N>>10000)個整數,求出其中的前K個最大的數。(稱作Top k或者Top 10)
問題分析:由於(1)輸入的大量數據;(2)只要前K個,對整個輸入數據的保存和排序是相當的不可取的。
可以利用數據結構的最小堆來處理該問題。
最小堆如圖所示,對於每個非葉子節點的數值,一定不大於孩子節點的數值。這樣可用含有K個節點的最小堆來保存K個目前的最大值(當然根節點是其中的最小數值)。
每次有數據輸入的時候可以先與根節點比較。若不大於根節點,則捨棄;否則用新數值替換根節點數值。並進行最小堆的調整。
實現代碼以及說明:
package AlgorithmTopK; public class TopK { public int[] createHeapOther(int a[], int k) { int[] result = new int[k]; for (int i = 0; i < k; i++) { result[i] = a[i]; } for (int i = 1; i < k; i++) { int child = i; int parent = (i - 1) / 2; int temp = a[i]; while (parent >= 0 &&child!=0&& result[parent] >temp) { result[child] = result[parent]; child = parent; parent = (parent - 1) / 2; } result[child] = temp; } return result; } public int[] createHeap(int input[], int K) { //創建小根堆,複雜度最壞是nlgK int heap[] = new int[K]; for(int i=0;i<K;i++) heap[i] = input[i]; for(int i = 1;i < heap.length;i++) { int child = i; int parent = (child-1) / 2; while(parent >= 0 && child!=0 && heap[parent] > heap[child]) { int temp = heap[child]; heap[child] = heap[parent]; heap[parent] = temp; child = parent; parent = (parent - 1) / 2; } } return heap; } public void insertHeap(int heap[], int value) { heap[0] = value; int parent = 0; while(parent<heap.length) { //這個循環複雜度最壞是 logK int lchild = parent*2 + 1; int rchild = parent*2 + 2; int minIndex = parent; //指向左右兒子中最小的 if(lchild < heap.length && heap[lchild] < heap[parent]) minIndex = lchild; if(rchild < heap.length && heap[rchild] < heap[minIndex]) minIndex = rchild; if(minIndex == parent) { break; } else { int temp = heap[minIndex]; heap[minIndex] = heap[parent]; heap[parent] = temp; parent = minIndex; } } } public int[] getTopKByHeap(int input[], int K) { int result[] = createHeap(input, K); //複雜度最壞是 O(nlgK) for(int i=K;i<input.length;i++) { if(input[i] > result[0]) insertHeap(result, input[i]); //複雜度最壞是O(nlgK),而且內存消耗就K,不然海量數據排序,內存放不下,得用歸併排序,最好最壞平均都是 O(nlgn) } return result; } public static void main(String[] args) { int a[] = {4,3,5,1,2,8,9,10};//待找TOP K 的排海量數據N int result[] = new TopK().getTopKByHeap(a, 6); System.out.print("TOP K is :"); for(int resultItem : result) { System.out.print(resultItem + " "); } }
四、信息安全算法
加解密算法:
算法選擇:對稱加密AES,非對稱加密: ECC,消息摘要: MD5,數字簽名:DSA
對稱加密算法(加解密密鑰相同)
名稱 |
密鑰長度 |
運算速度 |
安全性 |
資源消耗 |
DES |
56位 |
較快 |
低 |
中 |
3DES |
112位或168位 |
慢 |
中 |
高 |
AES |
128、192、256位 |
快 |
高 |
低 |
非對稱算法(加密密鑰和解密密鑰不同)
名稱 |
成熟度 |
安全性(取決於密鑰長度) |
運算速度 |
資源消耗 |
RSA |
高 |
高 |
慢 |
高 |
DSA |
高 |
高 |
慢 |
只能用於數字簽名 |
ECC |
低 |
高 |
快 |
低(計算量小,存儲空間佔用小,帶寬要求低) |
散列算法比較
名稱 |
安全性 |
速度 |
SHA-1 |
高 |
慢 |
MD5 |
中 |
快 |
對稱與非對稱算法比較
名稱 |
密鑰管理 |
安全性 |
速度 |
對稱算法 |
比較難,不適合互聯網,一般用於內部系統 |
中 |
快好幾個數量級(軟件加解密速度至少快100倍,每秒可以加解密數M比特數據),適合大數據量的加解密處理 |
非對稱算法 |
密鑰容易管理 |
高 |
慢,適合小數據量加解密或數據簽名 |
算法選擇(從性能和安全性綜合)
對稱加密: AES(128位),
非對稱加密: ECC(160位)或RSA(1024),
消息摘要: MD5
數字簽名:DSA
輕量級:TEA、RC系列(RC4),Blowfish (不常換密鑰)
速度排名(個人估測,未驗證):IDEA <DES <GASTI28<GOST<AES<RC4<TEA<Blowfish
簡單的加密設計: 用密鑰對原文做 異或,置換,代換,移位
名稱 |
數據大小(MB) |
時間(s) |
平均速度MB/S |
評價 |
DES |
256 |
10.5 |
22.5 |
低 |
3DES |
256 |
12 |
12 |
低 |
AES(256-bit) |
256 |
5 |
51.2 |
中 |
Blowfish |
256 |
3.7 |
64 |
高 |
表5-3 單鑰密碼算法性能比較表 |
名稱 |
實現方式 |
運算速度 |
安 全 性 |
改進措施 |
應用場合 |
DES |
40-56bit 密鑰 |
一般 |
完全依賴密鑰,易受窮舉搜索法攻擊 |
雙重、三重DES,AES |
適用於硬件實現 |
IDEA |
128bit密鑰 8輪迭代 |
較慢 |
軍事級,可抗差值分析和相關分析 |
加長字長爲32bit、密鑰爲256bit,採用232 模加、232+1模乘 |
適用於ASIC設計 |
GOST |
256bit密鑰 32輪迭代 |
較快 |
軍事級 |
加大迭代輪數 |
S盒可隨機祕 密選擇,便於軟件實現 |
Blowfish |
256-448bit 密鑰、16輪迭代 |
最快 |
軍事級、可通過改變密鑰長度調整安全性 |
|
適合固定密鑰場合,不適合常換密鑰和智能卡 |
RC4 |
密鑰長度可變 |
快DESl0倍 |
對差分攻擊和線性攻擊具有免疫能力,高度非線性 |
密鑰長度放寬到64bit |
算法簡單,易於編程實現 |
RC5 |
密鑰長度和迭代輪數均可變 |
速度可根據 三個參數的 值進行選擇 |
六輪以上時即可抗線性攻擊、通過調整字長、密鑰長度和迭代輪數可以在安全性和速度上取得折中 |
引入數據相倚轉 |
適用於不同字長的微處理器 |
CASTl28 |
密鑰長度可變、16輪迭代 |
較快 |
可抵抗線性和差分攻擊 |
增加密鑰長度、形成CAST256 |
適用於PC機和 UNIX工作站 |
常見加密算法
1、DES(Data Encryption Standard):對稱算法,數據加密標準,速度較快,適用於加密大量數據的場合;
2、3DES(Triple DES):是基於DES的對稱算法,對一塊數據用三個不同的密鑰進行三次加密,強度更高;
3、RC2和RC4:對稱算法,用變長密鑰對大量數據進行加密,比 DES 快;
4、IDEA(International Data Encryption Algorithm)國際數據加密算法,使用 128 位密鑰提供非常強的安全性;
5、RSA:由 RSA 公司發明,是一個支持變長密鑰的公共密鑰算法,需要加密的文件塊的長度也是可變的,非對稱算法;
6、DSA(Digital Signature Algorithm):數字簽名算法,是一種標準的 DSS(數字簽名標準),嚴格來說不算加密算法;
7、AES(Advanced Encryption Standard):高級加密標準,對稱算法,是下一代的加密算法標準,速度快,安全級別高,在21世紀AES 標準的一個實現是 Rijndael 算法;
8、BLOWFISH,它使用變長的密鑰,長度可達448位,運行速度很快;
9、MD5:嚴格來說不算加密算法,只能說是摘要算法;
10、PKCS:The Public-Key Cryptography Standards (PKCS)是由美國RSA數據安全公司及其合作伙伴制定的一組公鑰密碼學標準,其中包括證書申請、證書更新、證書作廢表發佈、擴展證書內容以及數字簽名、數字信封的格式等方面的一系列相關協議。
11、SSF33,SSF28,SCB2(SM1):國家密碼局的隱蔽不公開的商用算法,在國內民用和商用的,除這些都不容許使用外,其他的都可以使用;
12、ECC(Elliptic Curves Cryptography):橢圓曲線密碼編碼學。
13、TEA(Tiny Encryption Algorithm)簡單高效的加密算法,加密解密速度快,實現簡單。但安全性不如DES,QQ一直用tea加密