中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

提升node.js中使用redis的性能遇到問題怎么辦

發布時間:2021-07-21 11:16:57 來源:億速云 閱讀:138 作者:小新 欄目:web開發

這篇文章主要為大家展示了“提升node.js中使用redis的性能遇到問題怎么辦”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“提升node.js中使用redis的性能遇到問題怎么辦”這篇文章吧。

問題初現

某基于node.js開發的業務系統向外提供了一個dubbo服務,提供向第三方緩存查詢、設置多項業務數據并聚合操作結果。在QPS達到800時(兩臺虛擬機,每臺機器4Core8G4node進程),在監控平臺上出現了非常多的slow rt警告,平均接口響應達到60+ms,請求報警率達到80%+。

為找到造成該服務吞吐量過低的罪魁禍首,業務人員在請求日志中打點了所有查詢緩存的操作,結果顯示每個請求查詢緩存耗時在50-100ms之間跳動。查詢了redis-server的監控數據發現,不存在server端的慢查詢,在整個監控區間內服務端處理時間在40us徘徊,因此排除了redis-server的處理能力不足原因;

通過登錄內網機器進行不斷測試到對應redis server機器的端到端時延發現內部局域網的帶寬、時延與抖動足夠正常,都不是造成該問題的原因。

因此,錯誤原因定位到了調用redis client的業務代碼以及redis client的I/O性能。

本文中提到的node redis client采用的基于node-redis封裝的二方包,因此問題排查也基于node-redis這個模塊。

瓶頸在哪

為了在本地模擬線上環境的并發,可以做一個不是很嚴謹的測試:

async ()=>{
  let dd = Date.now()
  let arr = []
  for(let i=0;i<200;i++){
    arr.push(new Promise((res,rej)=>{
      let hrtime = process.hrtime();
      client.send_command('get',['key'], function(e,r) {
      let diff = process.hrtime(hrtime);
      let cost = (diff[0] * NS_PER_SEC + diff[1])/1000000;
      console.log(`final: ${cost} ms`)
      res();
      });
    }));
  }
  await Promise.all(arr)
  console.log('ops/sec:',200*1000/(Date.now() - dd),Date.now() - dd);
}

會發現每個請求的rt都會比前一個請求來的大

提升node.js中使用redis的性能遇到問題怎么辦

 最后一個請求的rt竟然達到了257 ms!雖然在node單進程像示例代碼那樣并發執行200次get請求是非常少見而且愚蠢的(關于示例代碼的優化在在下節講述),但是針對這個示例必須找到請求delay增加的原因。

 為此繼續分析,redis client采用的是單連接模式,底層采用的非阻塞網絡I/O,socket.recv()在node層面是通過監聽socket的data事件完成的,因此先分析redis-client讀性能如何:

提升node.js中使用redis的性能遇到問題怎么辦

上圖每段日志的含義分別表示:

- data events trigger times: socket data事件觸發的次數
- data event start from prevent event: data事件距離上次觸發的時間間隔
- data events exec time(ms): 本次事件處理函數執行時間

 上圖只是截取了最初的請求日志,發現當第6次觸發data事件時,竟然距離上次觸發事件隔了35ms,在隨后的請求中會復

現這種現象,因此這也就導致了在并發200次查詢請求時,每個請求的rt都會隨之增大,并且有些響應之間間隔了30ms。

從表象看造成問題在于redis-server發送的響應不是一個數據塊,而是多個數據塊導致觸發socket的data事件過多,而且data事件抖動過大導致響應之間存在30ms的突變(data事件是無法同時觸發兩次的,每次data事件處理函數執行完后才能繼續觸發下一個data事件);當然也有可能和socket寫入(即發送req)有關,如緩存請求等。為了繼續探查,監控與socket寫入相關的接口 **_write()**,記錄每次寫入socket的數據時距離上一次寫入的間隔:

提升node.js中使用redis的性能遇到問題怎么辦

可見,在使用redis-client發送請求時,write方法也不是瓶頸。

采用同樣方法,對socket的push()(該方法觸發socket的data事件)進行監控,發現socket的數據到達間隔抖動非常大:

提升node.js中使用redis的性能遇到問題怎么辦

 因此,造成redis-client并發請求下響應rt抖動較大的情況與單連接下響應數據到達本地的時刻有關,具體可能與底層libuv的緩存策略有關(筆者并未再往下探查)。

提升node.js中使用redis的性能遇到問題怎么辦

在一個node實例中通過一個單連接與redis server通信,在高并發下會出現排隊等待響應的情況,并且有可能會出現響應rt雪崩效應(如上文demo所示),因此需要盡可能減少或緩存客戶端的請求數量,進行批量發送。

調優

1. pipeline(涉及到寫模式及時序)
2. script

對于pipeline方式,redis server是默認支持的。通俗點說,pipeline可以合并一系列請求一次發送,并將這些請求對應的結果一次性拿到。因此這種方式可以有效減少響應次數,從而減少socket觸發data事件的次數,盡可能快的拿到響應體。

提升node.js中使用redis的性能遇到問題怎么辦

 需要強調的是,在node中,是通過底層socket的**_writev**實現一次發送多條redis命令的,_writev又叫做聚合寫,它支持將不同緩沖區的多條數據通過一次系統調用寫入目標流,因此性能上比每次寫單個緩沖區的單個數據來的好得多。在node的Writeable對象中,有cork和uncork方法,通過這兩個方法可以在node write stream中緩存多條數據,通過_writev一次性發送。

關于 _writev的數據結構

redis在拿到數據后,根據resp協議解析出命令集合緩存在隊列中,直到收到exec命令,開始批量執行命令集,并將所有命令執行的結果轉換為數組返回給redis client。這樣就可以通過一次寫、一次讀實現高性能I/O。

async ()=>{
  let dd = Date.now()
  let batch = await client.batch();
  for(let i=0;i<200;i++){
    batch.get('vdWeex_com.koudai.weidian.buyer_1');
  }
  let rt = await batch.exec();
  process.exit();
}

而對于script方法,則是由redis client傳入script命令,在server端執行script邏輯,批量執行命令,并返回結果。同樣是一次寫、一次讀。

收獲

1. node socket默認采用writev 集合寫
2. 無依賴批量請求采用pipeline
3. eval script解決有依賴批量請求
4. redis高性能體現在服務端處理能力,但瓶頸往往出現在客戶端,因此增強客戶端I/O能力與并發并行多客戶端才是高并發解決方案

以上是“提升node.js中使用redis的性能遇到問題怎么辦”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

会昌县| 河北区| 班戈县| 湘西| 子长县| 东兰县| 临桂县| 商丘市| 上饶市| 平遥县| 墨竹工卡县| 威信县| 沙洋县| 色达县| 侯马市| 绍兴市| 莱阳市| 水富县| 剑川县| 汽车| 塘沽区| 安新县| 民和| 东海县| 铁岭市| 耿马| 左贡县| 呈贡县| 遂溪县| 和林格尔县| 靖宇县| 曲沃县| 白河县| 靖安县| 阜新| 施甸县| 林口县| 府谷县| 扬州市| 个旧市| 明星|