乙個關於全域性ID過濾工具的效能優化實踐

2021-10-05 10:31:06 字數 4733 閱讀 1210

假設有個動態的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 生成不重複唯一標識方法一 這種方法會產生大量的重複資料...