close

在這篇文章中,我將講解如何優化使用 Node.js 編寫的 API。

前提條件

想要充分了解本文內容,你必須了解以下概念:

Node.js 的設置與安裝
如何使用 Node 創建 API
如何使用 Postman
JavaScript 的 async/await 工作原理
Redis 的基礎操作
API 優化到底指的是什麼

優化包含了改善 API 的響應時間。響應時間越短,API 的速度越快。

我將在本文分享一些技巧,幫助你縮短響應時間、降低延遲、管理錯誤和吞吐量,並且最大限度地減少 CPU 和內存的使用。

如何優化 Node.js 的 API1. 始終使用異步函數

異步函數就像 JavaScript 的心臟。因此,優化 CPU 使用率的最佳方法就是編寫異步函數來執行非阻塞 I/O 操作。

I/O 操作包括對數據的讀和寫。它可以在數據庫、雲存儲或者任何本地磁盤上進行。

在大量使用 I/O 操作的應用使用異步函數可以提高效率。因為由於沒有阻塞 I/O,當一個請求在做輸入/輸出操作的時候,CPU 可以同時處理多個請求。

舉例如下:

varfs=require('fs');//執行阻塞I/Ovarfile=fs.readFileSync('/etc/passwd');console.log(file);//執行非阻塞I/Ofs.readFile('/etc/passwd',function(err,file){if(err)returnerr;console.log(file);});
使用 Node 包 fs 來處理文件
readFileSync() 是同步函數,會在執行完成前阻塞線程
readFile() 是異步函數,會立刻返回並在後台運行
2. 避免在 API 中使用 session 和 cookie,僅在 API 響應中發送數據

當我們使用 cookie 或者 session 來存儲臨時狀態的時候,會占用非常多的服務器內存。

現在通用無狀態 API,並且也有 JWT、OAuth 等驗證機制。驗證令牌保存在客戶端以便服務器管理狀態。

JWT 是基於 JSON 的用於 API 驗證的安全令牌。JWT 可以被看到,但一旦發送就無法修改。JWT 只是一個序列並沒有加密。OAuth 不是 API 或服務——相反,它是授權的開放標準。OAuth 是一組用於獲取令牌的標準步驟。

同時,也不要把時間浪費在使用 Node.js 來服務靜態文件。這方面 NGINX 和 Apache 做得更好。

使用 Node 搭建 API 的時候,不要在響應中發送完整的 HTML 頁面。當僅有數據通過 API 發送的時候,Node 服務得會更好。大部分 Node 應用都使用 JSON 數據。

3. 優化數據庫查詢

優化 Node API 的重要一環是優化查詢。特別是對於大型應用來說,我們需要多次查詢數據庫,所以一個糟糕的查詢會降低應用的整體性能。

索引是一種優化數據庫性能的方法,通過最小化處理查詢時所需的磁盤訪問次數來實現。它是一種數據結構技術,用於快速定位和訪問數據庫中的數據。索引是使用幾個數據庫列創建的。

假設我們有一個沒有索引的數據庫模式,並且數據庫包含 100 萬條記錄。與帶有索引的模式相比,使用沒有索引的模式做一個簡單的 find(查找)查詢將掃描更多的記錄來找到匹配的記錄。

沒有索引的查詢
>db.user.find({email:'ofan@skyshi.com'}).explain("executionStats")
有索引的查詢
>db.getCollection("user").createIndex({"email":1},{"name":"email_1","unique":true}){"createdCollectionAutomatically":false,"numIndexesBefore":1,"numIndexesAfter":2,"ok":1}

兩者之間掃描文件的數量相差巨大 ~ 1038:

方法掃描文件沒有索引1039有索引1
4. 使用 PM2 集群模式優化 API

PM2 是為 Node.js 應用程序設計的生產流程管理器。它內置了負載平衡器,允許應用程序在不修改代碼的情況下,作為多個進程運行。

使用 PM2 時的應用停機時間幾乎為零。總體來說,PM2 確實可以提升 API 性能和並發性。

在生產環境中部署代碼並運行以下命令以查看 PM2 集群如何在所有可用 CPU 上進行擴展:

pm2startapp.js-i05. 減少 TTFB(第一字節時間)

第一字節時間是一種測量方式,用作表示 Web 服務器或者其他網絡資源的響應時間。TTFB 測量從用戶或客戶發出 HTTP 請求到客戶的瀏覽器收到頁面的第一個字節的時間。

所有用戶訪問瀏覽器的同一頁面加載速度不可能在 100 毫秒之內,這僅僅是因為服務器和用戶之間的物理距離。

我們可以通過使用 CDN 和全球本地數據中心緩存內容來減少第一個字節的時間。這有助於用戶以最小的延遲訪問內容。你可以從 Cloudflare 提供的 CDN 解決方案開始着手。

6. 使用帶日誌的錯誤腳本

監視 API 是否正常工作最好的辦法是記錄行為,於是記錄日誌就派上用場。

一個常見的辦法是將記錄打印在控制台上(使用console.log())。

比console.log()更高效的方法是使用 Morgan、Buyan 和 Winston。我將在這裡以 Winston 為例。

如何使用 Winston 記錄 – 功能
支持 4 個可以自由選擇的日誌等級,如:info、error、verbose、debug、silly 和 warn
支持查詢日誌
簡單的分析
可以使用相同的類型進行多個 transports 輸出
捕獲並記錄 uncaughtException

可以使用以下命令行設置 Winston:

npminstallwinston--save

這裡是使用 Winston 記錄的基本配置:

constwinston=require('winston');letlogger=newwinston.Logger({transports:[newwinston.transports.File({level:'verbose',timestamp:newDate(),filename:'filelog-verbose.log',json:false,}),newwinston.transports.File({level:'error',timestamp:newDate(),filename:'filelog-error.log',json:false,})]});logger.stream={write:function(message,encoding){logger.info(message);}};7. 使用 HTTP/2 而不是 HTTP

除了上述使用的這些技巧,我們還可以使用 HTTP/2 而不是 HTTP,因為它具備以下優勢:

多路復用
頭部壓縮
服務器推送
二進制格式

它專注提高性能,並解決 HTTP 的問題。它使網頁瀏覽更快、更容易,並且消耗更少的帶寬。

8. 並行任務

使用 async.js 來運行任務。並行任務對 API 的性能有很大改善,它減少了延遲並最大限度地減少了阻塞操作。

並行意味着同時運行多個任務。當你並行任務的時候,不需要控制程序的執行順序。

以下是一個數組異步並行的簡單例子:

constasync=require("async");//使用對象而不是數組async.parallel({task1:function(callback){setTimeout(function(){console.log('TaskOne');callback(null,1);},200);},task2:function(callback){setTimeout(function(){console.log('TaskTwo');callback(null,2);},100);}},function(err,results){console.log(results);//結果相當於:{task2:2,task1:1}});

在以上例子中,我們使用了 async.js 以異步的形式執行了兩個任務。task 1 需要 200 毫秒完成,但是 task 2 不需要等待 task 1 完成後再執行 – 它在設定的 100 毫秒後執行。

並行任務對 API 的性能有很大的影響。它減少了延遲並最大限度地減少了阻塞操作。

9. 使用 Redis 緩存應用

Redis 是 Memcached 的高級版本。它通過在服務器的主內存中存儲和檢索數據來優化 API 響應時間。它提高了數據庫查詢的性能,也減少了訪問延遲。

在下面的代碼片段中,我們分別調用了不使用 Redis 和使用 Redis 的 API,並比較了響應時間。

響應時間差異巨大~ 899.37 毫秒:

方法響應時間不使用 Redis900ms使用 Redis0.621ms

以下是不使用 Redis 的 Node:

'usestrict';//定義需要的所有依賴項constexpress=require('express');constresponseTime=require('response-time')constaxios=require('axios');//加載Express框架varapp=express();//創建在響應頭中添加X-Response-Time的中間件app.use(responseTime());constgetBook=(req,res)=>{letisbn=req.query.isbn;leturl=`https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}`;axios.get(url).then(response=>{letbook=response.data.itemsres.send(book);}).catch(err=>{res.send('Thebookyouarelookingforisnotfound!!!');});};app.get('/book',getBook);app.listen(3000,function(){console.log('Yournodeisrunningonport3000!!!')});

以下是使用 Redis 的 Node:

'usestrict';//定義需要的所有依賴項constexpress=require('express');constresponseTime=require('response-time')constaxios=require('axios');constredis=require('redis');constclient=redis.createClient();//加載Express框架varapp=express();//創建在響應頭中添加X-Response-Time的中間件app.use(responseTime());constgetBook=(req,res)=>{letisbn=req.query.isbn;leturl=`https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}`;returnaxios.get(url).then(response=>{letbook=response.data.items;//設置string-key:緩存中的 isbn。以及緩存的內容: title//設置緩存的過期時間為1個小時(60分鐘)client.setex(isbn,3600,JSON.stringify(book));res.send(book);}).catch(err=>{res.send('Thebookyouarelookingforisnotfound!!!');});};constgetCache=(req,res)=>{letisbn=req.query.isbn;//對照服務器的redis檢查緩存數據client.get(isbn,(err,result)=>{if(result){res.send(result);}else{getBook(req,res);}});}app.get('/book',getCache);app.listen(3000,function(){console.log('Yournodeisrunningonport3000!!!'))};總結

在本指南中,我們了解了如何優化 Node.js API 的響應時間。

JavaScript 重度依賴函數,因此,使用異步函數可以使腳本運行得更快並且不阻塞。

除此之外,我們還可以使用緩存記憶(Redis)、數據庫索引、TTFB 和 PM2 集群來提高響應速度。

最後請記住,注意路由的安全性並儘可能優化路由也很重要。我們不能為了提高 API 響應速度而妥協掉安全性。因此,在 Node.js 中構建優化的 API 時,應該保留所有標準安全檢查。

Happycoding!

原文鏈接:https://www.freecodecamp.org/news/how-to-optimize-nodejs-apis/

作者:Kayode Adeniyi

譯者:PapayaHUANG

在線貢獻者交流會預告
在線貢獻者交流會將於北京時間 2022 年 9 月 11 日周日上午 10:00 - 11:00 開展(每兩周一次,都在這個時間段開展)。
歡迎大家添加小助手微信 fcczhongguo,加入會議室。
開源公益社區 freeCodeCamp.org 自 2014 年成立以來,以「幫助人們免費學習編程」為使命,創建了大量免費的編程教程,包括交互式課程、視頻課程、文章等。我們正在幫助全球數百萬人學習編程,希望讓世界上每個人都有機會獲得免費的優質的編程教育資源,成為開發者或者運用編程去解決問題。
✨ ✨ ✨
年度總結丨2021 年世界各地的開發者在 freeCodeCamp 學習 21 億分鐘(4000 年)
freeCodeCamp 教育公益下一個目標:免費提供大學計算機科學學士學位
參與 freeCodeCamp 開源社區翻譯協作,幫助全世界人們用母語免費學習編程

點擊「閱讀原文」
在freeCodeCamp 專欄了解更多
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()