在整理網路上的惡意IP來源,整理完成後大約有四萬多個IP需要組合成一個長字串,使用 API 的方式送進設備中進行過濾流量。但要把四萬個IP打包成一行,一開始使用參數讓字串連結在一起後,寫入回記憶體中IP數量大時,等起來的時間真是不惶多讓,所幸找到簡化的運算方式,把研究思路寫下來做個紀錄。
從網路上取回來的IP列表如下,單純的一排只有IP的資訊。
AA.65.44.17
AA.15.29.151
AA.208.220.122
AA.208.220.226
AA.234.220.195
AA.234.220.197
AA.236.201.110
AA.236.201.88
AA.249.28.195
AA.253.41.111
AA.253.41.98
而設備需要把上面的IP列表組合成下列字串內容,在透過API的方式餵入。
{"ip":"AA.65.44.17"},{"ip":"AA.15.29.151"},{"ip":"AA.208.220.122"},{"ip":"AA.208.220.226"},{"ip":"AA.234.220.195"},{"ip":"AA.234.220.197"},{"ip":"AA.236.201.110"},{"ip":"AA.236.201.88"},{"ip":"AA.249.28.195"},{"ip":"AA.253.41.111"},{"ip":"AA.253.41.98"}
一開始想法很簡單就是做標準的迴圈讀擋程式碼如下,讀取每行後進行字串拼接,全部完成後存入 tmp 下方的 TOR_IPs 檔案中。
#!/bin/sh
Linechange(){
Readiplist2=NULL
for ip in $(cat 檔案)
do
Readiplist1='{"ip":"'$ip'"}'
if [ $Readiplist2 != NULL ]; then
Readiplist2=$Readiplist2","$Readiplist1
#如果是第二個字串會開始拼接文字
else
Readiplist2=$Readiplist1
#判斷為第一個字串時直接放入Readiplist2
fi
done
echo "$Readiplist2"
}
echo -n $(Linechange) > /tmp/TOR_IPs.txt
一開始在小於一萬筆內資料時,字串拼接只需要花幾分鐘等待,但超過一萬筆後時間會呈現等比級數的成長。
猜測原因是來自變數 Readiplist2 越來越長的情形下,每一次組合字串時時須從記憶體內讀取出來字越多時間越長,組合完成後字串需要再次存入記憶體內時間也變長。
測試筆數達4萬五千筆資料時間花了1個小時左右,每一次存取平均值在0.08秒。
根據數據可以猜測每一次差距都是幾毫秒,但長期讀寫下來累積時間來會非常驚人,尤其是在未來會根據不同的資料來源、行為再做出全重的計算,數量只會越來越多。
在分析寫入的文字內容基本上不會超過25個字元後,想到以log方式寫入硬碟中是否可以進一步縮減整體時間,避免了讀寫的時間複雜度逐漸拉高等問題。修正後的程式碼如下
#!/bin/sh
Linechange(){
Readiplist2=NULL
for ip in $(cat 檔案)
do
Readiplist1='{"ip":"'$ip'"}'
if [ $Readiplist2 != NULL ]; then
echo -n ","$Readiplist1 >> $PDList
#字串直接寫入檔案
else
Readiplist2=$Readiplist1
#字串寫入參數讓之後判斷為需要加上逗號
echo -n $Readiplist1 > $PDList
#字串直接寫入檔案
fi
done
}
#參數設定
PDList=/tmp/TOR_IPs.txt
#呼叫函數
Linechange
在上述版本中直接把IP取出後存入參數內,在根據判斷後決定是否要加上”,”號,判斷式完成後直接寫入硬碟中。
Shell Script 執行時檔案會如下方方式產生
{"ip":"AA.65.44.17"}
{"ip":"AA.65.44.17"},{"ip":"AA.15.29.151"}
{"ip":"AA.65.44.17"},{"ip":"AA.15.29.151"},{"ip":"AA.208.220.122"}
修改程式碼後速度基本上4萬五千筆從一個小時運算時間減少至一分鐘內就搞定了,速度提升時間節省了99%以上。
在長字串做大量拼接在一起時就可以用這樣的方式,可以有效地節省時間。
這中間也遇到不少事情,例如說:使用 Curl 這指令有長度限制,也是卡關了一些時間,這又是另外一個故事了。