成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

請求量太大下游扛不住怎么辦?進來學一招

開發 前端
如果只是改邏輯合并一下請求,吭哧吭哧改代碼就完了,也不值得寫這篇文章了,如何改最少的代碼來實現合并請求才是最難的。

背景

這個問題簡單說一下背景,如果不明白可以看上篇文章 ,不想看也沒關系,這是個通用的解法,后面我會總結抽象下。

在上篇文章的最后提到對每個摘除的地址做決策時,需要順序執行,且每一個要摘除的地址都要實時獲取該集群的地址信息,以便做出是否需要兜底的決策。

當被摘除的機器非常多時,獲取地址信息的請求量就會非常大,對注冊中心造成了不小的壓力。

請求數據源的接口如下所示(其中 cuuid 是集群的 id)

type Read interface {
ListClusterEndpoints(ctx context.Context, cuuid string) ([]ptypes.Endpoint, error)
}

相信大家也能理解這個非常簡單的背景并且能想到一些解法。每次決策需要按 cuuid 獲取集群,也就是單個單個地獲取實時集群地址信息,由于是實時信息,緩存首先排除,其次自然而然地能想到如果能將請求合并一下,是不是就能解決請求量大的問題?

難點

如果只是改邏輯合并一下請求,吭哧吭哧改代碼就完了,也不值得寫這篇文章了,如何改最少的代碼來實現合并請求才是最難的。

解法

那天遇到這個問題,晚上輾轉反側想到了這個解法,其實主要也是參考 Go http client 的實現,都說看源碼沒用,這不就是用處么?

Read? 數據源接口定義保持不變,也就是上層的業務代碼完全不用改,只需要把 ListClusterEndpoints 的實現換掉。

我們可以用一個隊列把每個請求入隊,入隊列以后,調用方阻塞,然后起一些協程去隊列里取一批請求參數,發起批量請求,響應之后喚醒阻塞的調用方。

圖片

為此,我們實現一個可以阻塞并被其他協程喚醒的工具:

type token struct {
value interface{}
err error
}

type Token chan token

func NewToken() Token {
return make(Token, 1)
}

func (t Token) Done(value interface{}, err error) {
t <- token{value: value, err: err}
}

func (t Token) Wait(timeout time.Duration) (value interface{}, err error) {
if timeout <= 0 {
tk := <-t
return tk.value, tk.err
}

select {
case tk := <-t:
return tk.value, tk.err
case <-time.After(timeout):
return nil, ErrTokenTimeout
}
}

其次,定義隊列和其他參數:

type DataSource struct {
paramCh chan param
readTimeout time.Duration
concurrency int
step int
}

type param struct {
cuuid string
token Token
}

替換掉原來 ListClusterEndpoints 的實現:

func (p *DataSource) ListClusterEndpoints(ctx context.Context, cuuid string) ([]ptypes.Endpoint, error) {
req := param{
cuuid: cuuid,
token: NewToken(),
}

select {
case p.paramCh <- req:
default:
return nil, fmt.Errorf("list cluster endpoints write channel failed")
}

value, err := req.token.Wait(p.readTimeout)
if err != nil {
return nil, err
}
eps, ok := value.([]ptypes.Endpoint)
if !ok {
return nil, fmt.Errorf("value is not endpoints")
}
return endpoints, nil
}

再起幾個協程來處理任務:

func (p *DataSource) startListClusterEndpointsLoop() {
for i := 0; i < p.concurrency; i++ {
go func() {
for {
reqs := p.getListClusterEndpointsReqFromChan()
p.doBatchListClusterEndpoints(reqs)
}
}()
}
}

最關鍵的是 getListClusterEndpointsReqFromChan 的實現,既不能讓協程空跑,這樣太消耗cpu,又要能及時地取到一批參數,我們采取的方法是先阻塞地獲取一個參數,如果沒數據則阻塞,如果有數據,繼續取,直到數量達到上限或者取不到數據為止,此時這一批數據就可以批量地進行調用了。

func (p *DataSource) getListClusterEndpointsReqFromChan() []param {
reqs := make([]param, 0)
select {
case req := <-p.paramCh:
reqs = append(reqs, req)
for i := 1; i < p.step; i++ {
select {
case reqNext := <-p.paramCh:
reqs = append(reqs, reqNext)
default:
break
}
}
}
return reqs
}

最后

這個方法很簡單,但是有一些要注意的地方,得做好監控,比如調用方單個請求的QPS、RT,實際批量請求的QPS、RT,這樣才好計算出處理協程開多少個合適,還有隊列寫入失敗、隊列長度等等監控,當容量不足時及時做出調整。

責任編輯:武曉燕 來源: 捉蟲大師
相關推薦

2020-10-20 08:01:30

MySQL密碼Windows

2021-11-22 11:30:37

JavaScript代碼瀏覽器

2023-10-06 20:52:47

2020-07-24 07:38:20

Nginx并發量日志

2021-10-08 08:58:35

MySQL函數脫敏

2021-06-01 22:20:07

私鑰互聯網安全

2021-11-28 17:01:49

工業公司網絡攻擊黑客

2021-04-16 23:33:48

區塊鏈安全私鑰

2018-05-23 16:56:40

戴爾

2021-03-06 09:54:22

PythonHTTP請求頭

2013-07-30 11:24:33

SAP“簡化IT 一招

2022-05-10 07:31:49

消息隊列CPUQPS

2021-10-17 07:14:08

Windows 11操作系統微軟

2023-09-08 15:48:13

2021-01-29 07:45:27

if-else代碼數據

2017-05-31 12:52:55

大數據數據文件解決思路

2017-11-02 13:35:39

2020-08-13 13:41:31

Python數據密度散點圖

2018-10-10 14:34:27

ARM嵌入式系統硬件

2013-05-03 11:21:27

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区二区成人 | 日韩一区二区在线播放 | 亚洲一区二区久久久 | 超黄视频网站 | 成人三级视频在线观看 | 亚洲精选久久 | 日韩成人专区 | 无码日韩精品一区二区免费 | 亚洲高清视频一区二区 | 欧美国产一区二区 | 天天操人人干 | 国产精品久久久久aaaa九色 | 岛国av免费看 | a视频在线观看 | 91免费在线视频 | 五月免费视频 | 欧美日韩三级 | 欧美日韩高清免费 | 日韩一区二区在线视频 | 91精品国模一区二区三区 | 亚洲人人舔人人 | 91n成人| 动漫www.被爆羞羞av44 | 综合第一页| 91精品国产91久久久久久最新 | 日本激情视频中文字幕 | 国产精品免费一区二区三区四区 | 国产精品一区二区三区免费观看 | 国产日韩精品视频 | 一区二区三区高清 | 极品销魂美女一区二区 | 久久亚洲综合 | 午夜影院在线免费观看视频 | 青春草在线 | 亚洲精品一 | 黄网免费看 | 一级毛片中国 | 国产成人av一区二区三区 | 91精品一区二区三区久久久久 | 欧美成人a∨高清免费观看 色999日韩 | 最新中文字幕第一页视频 |