討論這個議題似乎有些抽象,當我們的系統初次上線時,實際上有高達 80% 的時間處於低流量狀態。我們就舉例像是:購物網、社群平台這類特定類型的網站,比較有可能面臨高流量的情況。
然而,我們不能等到問題發生後才急忙改變架構,那樣的後果不堪設想。既然如此,為何不在一開始就規劃一個能夠應對未來可能出現的高流量情況的穩健架構呢?
設計一套能夠應對高流量情況的系統,需要從多個維度進行考慮與規劃。為了確保系統的穩定性與擴展性,以下是一些實用且被廣泛認可的策略和最佳做法:
- 水平擴展:通過在多個服務器上分散負載,水平擴展允許系統隨著需求增加而增加更多的資源。這可以通過增加更多的應用伺服器或資料庫節點來達成,從而有效應對高流量的挑戰,並保障系統在需求增加時的靈活擴展。
- 負載均衡:負載均衡器能夠將入站流量平均分配至多個服務器,以防止任何單一節點的過載,從而提升系統的整體可用性與穩定性。Nginx、HAProxy 以及 Amazon AWS 的 Application Load Balancer 等都是常見的選擇。
- 快取策略:適當地使用快取可以顯著減少對後端資料庫的直接請求,從而提高系統的響應速度和整體性能。Redis 和 Memcached 是極佳的快取解決方案。對於靜態內容,如圖片和前端資源,使用 CDN 進行快取也能有效減輕後端的壓力。
- 資料庫性能優化:透過合理使用索引、資料庫分區以及資料庫分片等技術,可以顯著提升資料庫的處理能力與查詢效率。此外,根據應用的具體需求選擇最合適的資料庫系統(關係型或非關係型)也至關重要。
- 採用微服務架構:將一個大型應用拆分成多個小的、獨立的微服務,可以使系統更易於管理與擴展。每個微服務都可以獨立部署和擴展,從而使系統能夠更靈活地應對各個服務的具體需求。
- 限流與熔斷:限流可以預防系統因突發的大流量而過載,熔斷則可以保護系統在某個部分出現故障時,避免故障蔓延導致整個系統的崩潰。這兩種機制對於維持系統的穩定運行至關重要。
透過這些策略的實施,不僅可以提升系統對高流量的承受能力,還能保證在面臨不同規模的用戶需求時,系統的平滑運行和高可用性。
在實務上
當你負責的系統已經穩定運行了數月並且有了一定程度的流量,或者當你被賦予任務去設計一個能夠應對高流量的系統時,那麼「水平擴展」和「負載均衡」無疑是非常直接且有效的策略。
這兩個策略能夠透過增加『資源』來達成將流量分散的效果,而具體要如何分流,則需視系統的瓶頸所在來決定,可能的焦點包括:
- 網站服務:若網站本身的請求處理能力不足,可以透過增加更多的網站服務器並透過負載均衡器分散請求來提升整體處理能力。
- 資料庫系統:資料庫是常見的系統瓶頸之一,可以考慮透過資料庫集群、讀寫分離或資料庫分片等策略來增強其處理高流量的能力。
- 網路架構:網路設備與架構也可能成為流量瓶頸,此時可以透過升級網路設備或優化網路架構來改善。
補充一下
關於為什麼「快取」可能不是所有情況下的首選解決方案:
實際上,是否採用快取要看你的系統主要服務是什麼。像是涉及到實時更新的「銷售類」網站,顯示的是商品的即時庫存,這種情況下用快取就不太合適,因為資訊需要實時反映最新狀態。
另一方面,對於以內容展示為主的網站,比如說:像是「方格子」,快取就非常有用了。畢竟,當一篇文章成為熱門內容,被大量訪問時,文章本身的內容是不會改變的,這時候使用快取能有效減輕伺服器壓力。至於文章的互動部分,如留言和點讚等,則可以採用其他方法進行動態更新,不必依賴快取。
所以,選擇是否使用快取,得根據系統的具體需求和特點來決定,並非所有場景都適用。
水平擴展 (Horizontal Scaling)
在談到水平擴展 (Horizontal Scaling)的具體做法時,確保應用的無狀態運作是關鍵。以下是針對在 Laravel 框架中實踐無狀態應用服務的一些建議,以助於應對高流量情境:
- 無狀態應用服務:讓你的 Laravel 應用成為無狀態的,這樣就能在多台機器間分散使用者請求。重點在於,要將 session 資訊存放在像是 Redis 或 Memcached 這樣的中央儲存系統,確保各服務間的狀態同步。
- Session 的集中式管理:將 session 存放在集中式的儲存系統,不論使用者接觸到哪一台伺服器,都能獲得一致的 session 資訊。Laravel 提供了容易設置的選項來實現這一點:
'driver' => env('SESSION_DRIVER', 'redis'),
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
],
- 避免使用本地存儲:對於需要被多個應用實例訪問的資料,不應該將其存儲在本地檔案系統。改用雲儲存服務如 Amazon S3 或 Google Cloud Storage,或者是使用網絡檔案系統(NFS)來實現資料的共享。
- 應用邏輯與狀態分離:盡量將應用邏輯與用戶狀態分離開來,將必要的狀態資訊存儲於資料庫或其他中央管理的服務。
- 採用無狀態認證方式:如 JWT 等無狀態認證機制可以跨服務共享使用者身份,無需在每次請求時都驗證狀態。
- 應用的容器化:使用 Docker 等容器技術部署應用,可以確保在不同環境中應用的一致性,並且容器的快速部署與銷毀特性非常適合動態擴展。
- 無狀態的 API 設計:確保 API 的每次請求都是自包含的,即包含了完成請求所需的所有資訊,這樣 API 就可以在多個伺服器上無縫運作。
- 利用消息隊列與後台處理:對於耗時的任務,將其放入後台隊列中處理,可以減輕前端伺服器的壓力,並且隊列服務本身也可以根據任務量進行擴展。
- 性能優化:使用各種中間件(middleware)、快取和數據預加載等技術,減少對狀態的依賴,從而降低單點壓力,提升整體性能。
透過上述的策略和技術,你的 Laravel 應用將更加適合於水平擴展,從而提高應對高流量情境的能力。
提到的這些建議,只是入門的一步。實際上,每個系統的情境都有其獨特性,不可能單靠套用幾個策略就能解決所有問題。要真正優化系統,除了這些大方向的架構調整外,還需要深入到程式碼層面,細心審視每一行程式的效能,以及資料存取方式的合理性。只有透過全面的檢視和不斷的調整,才能使系統更加穩健,應對各種挑戰。
註:負載均衡是另一種應用/傳輸層的結構處理,以後有機會再發篇文章說明~