假設有個動態的id過濾規則,規則包含多個需要過濾的id(用逗號分開)以及id區間(用-代表區間);
實現全域性過濾器,對入參的id判斷是否命中過濾規則;
id為long型別,配置中心返回的規則是string型別。
規則示例:
1,3,5,7-10
規則意思需要過濾 1,3,5,7,8,9,10
咋一看需求挺簡單,下面實現方式也直接簡單
讀取配置
解析配置
規則判斷
//配置中心,用於讀取規則配置
private config config = configservice.
getconfig()
;/**
* 過濾器主方法
*/public
boolean
ismatch
(long id)
}return range.
equals
(string.
valueof
(id));
});}
對以上實現進行基準測試,測試用例mock的規則:「1, 3, 5, 7, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100-120, 200-220, 300-320」
@benchmarkmode
(mode.sampletime)
@warmup
(iterations =
2, time =2)
@measurement
(iterations =
5, time =5)
@threads(1
)@fork(1
)@outputtimeunit
(timeunit.nanoseconds)
@benchmark
public
void
testmatchbystr()
測試結果
benchmark mode cnt score error units
benchmarktest.testmatchbystr sample 867795 1843.805 ± 21.407 ns/op
benchmarktest.testmatchbystr:testmatchbystr·p0.00 sample 1000.000 ns/op
benchmarktest.testmatchbystr:testmatchbystr·p0.50 sample 1800.000 ns/op
benchmarktest.testmatchbystr:testmatchbystr·p0.90 sample 1800.000 ns/op
benchmarktest.testmatchbystr:testmatchbystr·p0.95 sample 1900.000 ns/op
benchmarktest.testmatchbystr:testmatchbystr·p0.99 sample 2900.000 ns/op
benchmarktest.testmatchbystr:testmatchbystr·p0.999 sample 4696.000 ns/op
benchmarktest.testmatchbystr:testmatchbystr·p0.9999 sample 24021.158 ns/op
benchmarktest.testmatchbystr:testmatchbystr·p1.00 sample 1243136.000 ns/op
結果看上去還不錯,99.99%都在0.02毫秒內。
考慮到這是乙個全域性的過濾器,承載了全站的所有請求,並且是高併發業務,計算速度應該在允許的條件下追求最快,於是陷入思考,是否可以有更優實現?計算已經很簡單了,貌似沒有可以刪減的;仔細看字串解析部分計算是不是可以提前做?不需要放在每次的過濾判斷中,下面試著分離計算:
初始化時就解析配置為list+map,並監聽配置變更
過濾器使用已解析好的集合進行規則判斷
//這裡實際是使用了apollo的配置變更事件,提前將字串轉換成為集合
private set
configarray =
newhashset
<
>()
;private map
configrange =
newhashmap
<
>()
;/**
* 在初始化中讀取並監聽變更,提前把配置解析
*/public
void
init()
});}
/** * 解析apollo配置
*/private
void
resolveconfig
(string configstr)
}else})
;}this
.configarray = tmpconfigarray;
this
.configrange = tmpconfigrange;
}/**
* 過濾器主方法
*/public
boolean
ismatch
(long id)
if(configarray.
size()
>0)
if(configrange.
size()
>0)
return
false
;}
對以上實現進行基準測試,mock同樣的規則:「1, 3, 5, 7, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100-120, 200-220, 300-320」
測試規則
@benchmarkmode
(mode.sampletime)
@warmup
(iterations =
2, time =2)
@measurement
(iterations =
5, time =5)
@threads(1
)@fork(1
)@outputtimeunit
(timeunit.nanoseconds)
@benchmark
public
void
testmatchbylist()
測試結果
benchmark mode cnt score error units
benchmarktest.testmatchbylist sample 581645 67.503 ± 5.858 ns/op
benchmarktest.testmatchbylist:testmatchbylist·p0.00 sample ≈ 0 ns/op
benchmarktest.testmatchbylist:testmatchbylist·p0.50 sample 100.000 ns/op
benchmarktest.testmatchbylist:testmatchbylist·p0.90 sample 100.000 ns/op
benchmarktest.testmatchbylist:testmatchbylist·p0.95 sample 100.000 ns/op
benchmarktest.testmatchbylist:testmatchbylist·p0.99 sample 100.000 ns/op
benchmarktest.testmatchbylist:testmatchbylist·p0.999 sample 300.000 ns/op
benchmarktest.testmatchbylist:testmatchbylist·p0.9999 sample 3283.540 ns/op
benchmarktest.testmatchbylist:testmatchbylist·p1.00 sample 815104.000 ns/op
從基準結果看,快了10倍。在耗時很低的情況下,依然降低了10倍,雖然只是節省1000ns/op;不積跬步,無以至千里;如果能在每個業務開發時都稍微思考節省一點點,對於高併發系統的效能提公升還是很有價值的。
總結:在高併發系統中應盡量將使用者請求鏈中的計算做到最精簡,當然需要考慮下投入產出比。
關於乙個profiler工具的設計
我目前想對之前寫的乙個profiler工具進行重構,和大家 一下我設想的一些思路。首先,在設計中包含乙個重要的概念 軌跡。1 軌跡的定義 在系統中,任何程式的執行都有可能留下軌跡 資料庫的呼叫會留下sql軌跡,事務的軌跡,http請求會留下訪問的軌跡,程式的方法執行也可以留下軌跡 2 軌跡的管理 軌...
乙個ID引起的血案
最近用asp寫程式時,剛開始支援的資料庫是access,程式裡有一段 是往資料庫裡新添一條記錄,方法為先建立乙個recordset,然後用addnew和update方法來實現資料新增。addnew之後便能取得新增記錄的id號。後來程式移植到伺服器上時,由於伺服器安裝的是sql server 2000...
生成乙個唯一的id
php uniqid 函式可用於生成不重複的唯一識別符號,該函式基於微秒級當前時間戳。在高併發或者間隔時長極短 如迴圈 的情況下,會出現大量重複資料。即使使用了第二個引數,也會重複,最好的方案是結合md5函式來生成唯一id。php uniqid 生成不重複唯一標識方法一 這種方法會產生大量的重複資料...