對比
如果使用nodejs來搭建Service服務,那麼我們首選express
或者koa
,而fastify
告訴我們一個資料:
Framework | Version | Router? | Requests/sec |
---|---|---|---|
hapi | 18.1.0 | ✓ | 29,998 |
Express | 4.16.4 | ✓ | 38,510 |
Restify | 8.0.0 | ✓ | 39,331 |
Koa | 2.7.0 | ✗ | 50,933 |
Fastify | 2.0.0 | ✓ | 76,835 |
- | |||
http.Server |
10.15.2 | ✗ | 71,768 |
從資料中可以看出,Koa
的效能遠大於express
。當然,它的測試基於簡單的單路由測試。不過由此我們可以看到fastify
的效能遠大於Koa
。相信使用過fastify
的小夥伴都會對它的效能速度感到驚訝。其實原理很簡單,就是請求的URL快速匹配Callback。如何做到,理論上也很簡單,就是找尋它的最短路徑來匹配。所以一般能夠快速匹配的,都是通過空間換時間的方式來達到效果。
這裡,我還想告訴大家一點,fastify
並不是最快的。
主角
今天的主角就是koa-rapid-router。為什麼我們會以KOA
打頭呢?因為這篇文章目的其實是與koa-router
的比較,而不是fastify
。而此路由架構,也是為了在使用KOA
的時候能夠接近fastify
的效能(經過測試,沒有超過fastify
,KOA
本身的效能也有問題)。
接下來,我們會丟擲一系列的測試資料來告訴大家Koa-router
的效能是何其糟糕。我們分別使用這樣的原則來測試
- 向每個架構注入10000個靜態路由,測試最末尾的那個。
- 使用相同的測試命令
autocannon -c 100 -d 40 -p 10 <url>
- 對比靜態路由和動態路由效能上的差距
測試程式碼全部在這裡
靜態路由對比
我們寫入如下的程式碼
for (let i = 0; i < 10000; i++) {
router.get('/uuid/' + (i + 1), async (ctx) => ctx.body = 'ok');
vrouter.get('/uuid/' + (i + 1), (res) => res.end('ok'));
route_2.get('/interface/api/uuid/' + (i + 1), async (ctx) => ctx.body = 'ok');
fastify.get('/interface/api/uuid/' + (i + 1), (request, reply) => reply.send('ok'));
}
複製程式碼
接著進行測試 npm run test
,得到資料:
Preview:
Results
command | architecture | Latency | Req/Sec | Bytes/Sec |
---|---|---|---|---|
test:koa | koa + koa-router |
245.07 ms | 394.25 | 56 kB |
test:fast | fastify |
1.96 ms | 49324 | 7 MB |
test:rapid | koa + koa-rapid-router |
2.17 ms | 44828.8 | 6.37 MB |
test:http | http + koa-rapid-router |
1.64 ms | 58911.2 | 5.95 MB |
從資料上得出結論,koa-router
在有10000個路由的時候,它的效能超級低下,只能達到平均的394.25
,意思就是每秒只能處理394.25個請求,多來就不行。而koa + koa-rapid-router
則處理到了44828.8個。同樣是使用KOA
模型,差距很明顯。我做了分析,主要是koa-router
內部迴圈比較多導致的。在10000個請求迴圈過程中,效率非常低下。而我們如何做到達到44828.8的效能,主要我們在記憶體中維護了一份靜態路由列表,能讓程式以最快的速度找到我們需要的callback。
對比fastify
,可以看出,KOA
本身效能的問題很大。
大家一定會問,對比靜態路由Koa-router
肯定沒有優勢,那麼我們來對比動態路由。
動態路由對比
我們寫入如下程式碼
router.get('/zzz/{a:number}', async (ctx) => ctx.body = 'ok');
vrouter.get('/zzz/{a:number}', (res) => res.end('ok'));
route_2.get('/interface/api/zzz/:a(\\d+)', async (ctx) => ctx.body = 'ok');
fastify.get('/interface/api/zzz/:a', (request, reply) => reply.send('ok'));
複製程式碼
我們將這段程式碼加入到10000個靜態路由程式碼的後面,修正測試的路徑,我們得到如下資料:
Results
command | architecture | Latency | Req/Sec | Bytes/Sec |
---|---|---|---|---|
test:koa | koa + koa-router |
220.29 ms | 441.75 | 62.7 kB |
test:fast | fastify |
1.9 ms | 50988.65 | 7.24 MB |
test:rapid | koa + koa-rapid-router |
2.32 ms | 41961.6 | 5.96 MB |
test:http | http + koa-rapid-router |
1.82 ms | 53160.8 | 5.37 MB |
動態路由的對比從一定程度上可以看出koa-router
的糟糕之處,不論是靜態路由還是動態路由,它都基本穩定在400左右的qps。而koa + koa-rapid-router
稍有下降,fastify
一如既往的穩定。但是從http + koa-rapid-router
模型上看,rapid完全超越fastify
。koa + koa-rapid-router
與koa + koa-router
對比,效能大概是100倍的樣子。如果我們可以這樣認定,如果我們需要高併發,但是還是使用koa
的生態的話,koa + koa-rapid-router
是最佳選擇。如果我們完全追求效能,不考慮生態的話,那麼fastify
首選。
有人會問,那麼為什麼http + koa-rapid-router
不使用,它可是比fastify
更快的路由?那是因為,http + koa-rapid-router
需要單獨建立生態,暫時無法做到大規模使用,也許到最後,我們可以用上新的基於koa-rapid-router
的企業級服務架構。這也是我正在思考的。
結尾
我們所造的輪子的效能是不可能超越http
模組的效能,我們只能無限接近它。這就像光速的道理一樣,只能接近,無法等於。高效能架構主要還是在於理念模型,跟數學息息相關。
專案開源在 github.com/cevio/koa-r… 有興趣的小夥伴關注下,謝謝。