朱革 余浩源 王先全 周錫祥



摘要:隨著互聯網的迅速發展普及以及手機App的快速壯大,服務器需要承受的用戶訪問量變得越來越大,加上現在用戶對上網體驗的重視,這些都對當前服務器設計提出了新的挑戰。單一服務器架構已經不適合用來承受高并發請求,前幾年流行的SSM框架能通過集群提高性能,但單體應用不適合做大型系統,且僅通過服務器集群帶來的性能提升也會遇到瓶頸,故需要新的架構來滿足當前的需求。從集群、分布式、數據庫幾個方面入手對服務器設計進行分析,提出適合高并發環境的服務器架構。
關鍵詞:高并發;集群;分布式;架構
中圖分類號:TP311? ? ? 文獻標識碼:A
文章編號:1009-3044(2021)12-0069-04
Abstract: With the rapid development and popularization of the Internet and the rapid growth of mobile phone apps. The server has to bear more and more user visits, and now users attach importance to the Internet experience, which all pose new challenges to the current server architecture. The single server architecture is no longer suitable for handling high concurrent requests, The popular SSM frameworks of the past few years can improve performance through clustering, but monolithic applications are not suitable for large system, and the performance improvement only through server clusters will also encounter bottlenecks, therefore, a new architecture is needed to meet the current needs. Analyze server design from the aspects of cluster, distributed, and database, and proposes a server architecture suitable for high-concurrency environments.
Key words: high concurrency; cluster; distributed; framework
1 背景
互聯網發展初期對服務器的要求不高,通常在一臺服務器上搭建服務就能夠滿足需求,隨著上網人數的增多以及網速的提高,服務器需要承受的壓力也越來越大。比如淘寶在雙十一凌晨0點的時候所承受的訪問量巨大,12306鐵路購票網站曾經就出現過由于服務器壓力過大系統崩潰的情況,解決服務器在高并發情況下的性能低下問題是必要的。增強服務器的并發能力除了提升硬件性能,參數調優等方式外,還能通過優化服務器的架構來提高系統的性能,增強系統的容災性與穩定性。本文從集群、分布式、數據庫等方面來對服務器架構設計進行分析,為高性能服務器的設計提供一種可行的方案。
2 架構優化的必要性
對服務器性能進行提升有多種方式,進行硬件的升級是最為簡單的方式,但這種方式成本高且不能從根本上解決問題[1]。對單體應用進行集群部署能有效地提高系統的并發能力與穩定性,但單體應用存在著不易迭代及部署效率低等問題,并發到了一定程度僅靠集群并不能完全解決性能問題,此時就需要從其他部分進優化,單靠某一個技術無法滿足需求,所以需要對服務器架構進行設計。
3 設計方案
在實際的使用中,影響服務器性能的因素有很多,架構的不同會使服務器的性能有很大的差距。本節主要從集群、分布式的使用以及數據庫方面進行分析。
3.1 使用服務器集群
客戶端發送多個請求到單個服務器,服務器對請求進行處理并且連接數據庫,處理完畢后再將結果返回到客戶端。這種架構的成本低,代碼編寫也較為簡單,是比較適合早期并發較少的情況的。但隨著現在請求并發變得越來越大,如果想要繼續采取單一服務器來處理請求,就得提升服務器的性能,隨著摩爾定律[2]逐漸失效,硬件性能的提升已經跟不上需求的提升,就算不考慮成本將硬件的配置提升到最好仍然不能滿足需求,那通過提升硬件配置這條縱向解決問題的道路就走不通。服務器集群[3]這種通過橫向增加服務器數量的方式就正好能夠解決這個問題。
3.1.1 服務器集群的作用
服務器集群就是將多個做同種服務的服務器集中起來,利用多個計算機來進行并行的計算以提高性能,每個服務器是一個節點,所有節點構成了集群[4]。當單個服務器處理高并發請求遇到瓶頸的時候使用集群可以提升系統的處理能力,當集群的性能不夠時也可以適當增加節點數量。對客戶端而言,訪問服務器的集群就和訪問一個服務器是一樣的,客戶端不用專門為此做配置。
3.1.2 用負載均衡服務器搭建服務器集群
服務器集群是由多個服務器組成,但是用戶的請求到底由哪個服務器來進行處理,需要一個調度者來進行調度,這個調度者就是負載均衡服務器。用戶的請求先發送給負載均衡服務器,然后服務器再根據相應的設置以及當時負載的情況,來選出一個合適的節點對請求進行處理。
本文選用Nginx作為負載均衡服務器,它可以通過反向代理來實現軟件負載均衡,反向代理時客戶端不需要做任何配置[5]。如圖1所示客戶端對代理是無感知的,請求發送到了反向代理服務器,由反向代理服務器去選擇目標服務器,從目標服務器獲取返回的數據后再返回給客戶端,客戶端訪問的實際是代理服務器的地址,而不是直接訪問的被隱藏了的真實服務器地址。
使用Nginx作為負載均衡服務器首先需要對其配置文件nginx.conf進行修改,在http塊中添加:
upstream myserver {
server 192.168.0.1:8080;
server 192.168.0.2:8080;
server 192.168.0.3:8080;
}
表示使用默認的負載均衡策略輪詢將請求分配給三個服務。然后將http塊里的server部分修改為:
server{
listen 80;
server_name localhost;
location /{
proxy_pass http://myserver ;
}
......
其中listen為監聽的端口,server_name為監聽的地址,proxy_pass則為請求的轉向,值為前面upstream定義的服務器列表。除輪詢外常見的負載均衡策略還有Weight、ip_hash、fair、least_conn等。
3.2 采用分布式架構
前面講了通過服務器集群來提高性能,當訪問量越來越大,單一應用增加服務器數量所帶來的性能提升變得越來越小,這時候就需要用到分布式架構了,從圖2中可以看出一般服務器集群中的單體架構和分布式系統間的區別。
分布式與集群相比,分布式是一種工作方式而集群則是一種物理形態。將同一個業務放到不同的機器上以提高性能與可用性就能稱為集群,而分布式則是將不同的業務或者一個業務拆分成多個子業務部署到不同的服務器上通過交換信息的方式來進行協作[6]。分布式是通過縮短單個任務的執行時間來提升工作效率,集群則是通過提高相同時間并行執行的任務數量來提高效率的,所以當增加集群服務器數量提升的性能有限時可通過分布式來提升性能。
3.2.1 微服務的概念
簡單地來說微服務[7]就是很小的服務,甚至功能可以單一到只做一件事,一個操作。單體應用將它的所有功能放在一個進程里執行,能夠在多個服務器里復制這單體應用實現擴展。而微服務則將它的每個功能元素放在不同的服務中,通過跨服務器的分發這些服務進行擴展,對需要進行復制的功能元素進行復制也可以實現性能提升。
分布式也屬于微服務,兩者的概念比較相似但是也有一些差別。分布式是一種讓分散的機器相互協助完成業務的手段,微服務相比一般的分布式則對服務進行更細粒度的切分,使得整個系統更加易于迭代且并行度更高。
3.2.2 分布式服務中應解決的問題
在微服務架構中隨著功能模塊的增多,代碼和配置文件變得越來越冗雜,為解決這個問題可使用Spring Boot來快速構建微服務應用,它能幫助開發者解決開發中大部分的配置問題,大部分配置都可以用java類加上注解來代替。Spring Boot能簡化Spring的開發,它為Spring整合了許多第三方技術,去繁從簡約定大于配置,十分適合微服務開發[8]。它的特點有:
1)能快速創建獨立運行的Spring項目以及集成了主流的框架。
2)擁有內嵌的Servlet容器,方便運行。
3)簡化了Maven配置。
4)無代碼生成,不需要配置XML文件。
5)有大量的自動配置,簡化開發,當然也能修改默認值。
微服務雖然帶來了許多好處,同時也使得運維變得更加復雜,監控更加困難,分布式的復雜性也提高了。比較明顯的幾個問題是:
①服務間的調用問題。各個服務間實現調用需要知道服務所在的地址,服務不會放在固定的機器上,故不能將要調用的目標服務地址寫死在配置文件里。
②配置問題。微服務要將單體服務中的業務拆分成多個粒度較小的子服務,這樣就會導致系統中出現大量的服務,如果仍然為每個服務都提供單獨的配置文件則會出現配置信息冗余。
③系統的穩定性問題。由于微服務間的調用是鏈性的,如果在整個調用鏈中某個服務出現了問題則整個系統則會雪崩式癱瘓。
3.2.3 分布式服務設計
分布式服務中存在的問題使其構建變得復雜,可以使用Spring Cloud[9]來解決這些問題,它提供很多工具用來進行分布式系統的構建,比如:服務發現,配置管理,熔斷,微代理,智能路由,控制總線,全局鎖,一次性token,決策競選,分布式session,集群狀態等。
Spring Cloud Alibaba[10]是阿里巴巴推出的解決方案,使用其中的Nacos、Sentinel、Seata組件來解決前面所提到的問題。
服務間調用的地址問題可以使用Nacos中的服務注冊發現來解決,在服務的配置中添加如下代碼:
spring:
application:
name: provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
該配置指定了Nacos的地址以及該服務的服務名。
將服務提供者注冊進Nacos后,服務消費者可以用restTemplate或OpenFeign等來請求調用該服務中的功能。其原理是在服務器啟動的時候會將服務器的服務地址通信地址等用別名注冊進注冊中心,而另一邊服務的消費者則以該別名在注冊中心中獲取到服務的真實通信地址。
配置問題同樣也可以使用Nacos來解決,使用其配置管理功能來充當配置中心。需要注意的是在使用配置中心管理配置時,springboot的配置文件需要使用bootstrap.yml,這是因為在項目初始化時需要保證配置先從配置中心拉取,拉取配置后項目才能正常啟動,application.yml是用戶級的資源配置項,而bootstrap.yml是系統級的,優先級更高。
bootstrap.yml中相關配置代碼寫法:
spring:
application:
name: config
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yaml
group: DEV_GROUP
namespace: 077e4037-3b40-4d4a-801e-c8d
4012a815f
使用Sentinel從流量切入,在流量控制、熔斷降級等維度來維持服務的穩定性。流量控制是監控應用流量的QPS或并發線程數來避免瞬時高流量沖垮服務器。熔斷就好比保險絲,當請求滿足設定的異常條件就會熔斷掉服務,而不是一直等待到此服務超時。降級則是對服務進行有策略的降級,可以理解為保證核心業務正常運行的兜底方法。
啟動好Sentinel與服務并且進行過一次請求后登錄Sentinel控制臺就可以看到相關服務并且對其進行控制。圖3為新增流控規則的面板,可以看到閾值類型有QPS和線程數兩種,QPS是指當調用該api的QPS達到閾值的時候,進行限流。線程數則是當調用該api的線程數達到閾值的時候,進行限流。
新增降級規則的面板為圖4,降級策略有RT、異常比例以及異常數。RT為平均響應時間,滿足平均響應時間超出閾值且在時間窗口內通過的請求數大于一定值兩個條件時觸發降級。異常比例和異常數則分別是發生異常的比例和異常的個數超過閾值時觸發降級。
3.3 數據庫優化方案
在高并發的環境下,對數據庫的操作頻繁,如果只對單實例數據庫進行簡單的操作無法支撐系統的正常運行,所以需要對數據庫的使用進行設計。
3.3.1 索引優化分析
數據庫執行查詢語句時會根據條件進行全表掃描,正確的使用索引可以提高查詢的效率[11]。索引的本質是數據結構,它能夠幫助數據庫高效的獲取數據,但其本身也會占用磁盤空間。使用索引時每次對表進行更新操作時需要更新索引,故會降低表的更新速度。當表的數據特別多時,索引能帶來的性能提升會降低,這就需要合理的設計索引,以免影響性能的提升甚至帶來負面效果[12]。
索引在使用中也需要注意避免索引失效,在mysql中可以使用explain關鍵字來看mysql如何處理sql語句,其使用方法是Explain + SQL語句:
EXPLAIN SELECT * FROM t1,t2,t3 WHERE t1.id=t2.id AND t2.id=t3.id
圖5為執行Explain的結果,可以看到內容并不是一般查詢語句的格式。其中各個字段都有其含義:
1)id:查詢序列號,表示查詢中執行的順序,值相同則從上往下執行,值不同則id越大的越先執行。
2)select_type:查詢的類型。
3)table:此行數據與哪張表相關。
4)type:表示訪問的類型,較為重要的一個指標,能顯示出數據庫引擎查找表的方式。
5)possible_keys:可能應用在這張表上的索引。
6)key:實際使用的索引。
7)key_len:索引中使用的字節數,檢查是否充分利用上了索引。
8)ref:索引被使用的列。
9)rows:顯示mysql認為執行查詢時需檢查的行數。
3.3.2 讀寫分離
一個系統對數據庫的讀寫需求通常不同,讓單一數據庫處理讀和寫操作可能會使其承受的壓力過大??梢允褂弥鲝膹椭苼韺崿F讀寫分離,讓主服務器只執行寫操作,從服務器執行讀操作,以此分散服務器的壓力[13]。
如圖6所示,主從復制的原理是從數據庫slave通過讀取主數據庫master的binlog然后執行一遍master執行過的操作從而達到主從同步的效果。可以將其中的過程分為三個部分:1)master將每個會改變數據的操作串行的記錄進二進制日志binlog中,將這些記錄稱為二進制日志事件。2)slave從master拷貝二進制日志事件到它的中繼日志中。3)slave按照順序執行中繼日志中的事件,將master的改變應用到自己的數據庫中。
3.3.3 分表存儲
在數據量特別大的情況下,數據庫的讀取性能會有較大的降低,為了解決這個問題可以將一張表的數據拆分到多張表上存儲[14]。
切分有垂直切分與水平切分,根據業務對表進行分類然后分布到不同的數據庫上,這屬于對數據的垂直切分。切分時可按照模塊進行分類,有時也可以根據數據量的大小來切分。垂直切分并不會縮表,所以依然存在著單庫的瓶頸,這時就需要用到水平切分。水平切分是根據某個字段的一些規則將原本放在一個表里的數據放在不同的數據庫里,就相當于對表按照數據行來進行切分。
隨著數據量的增大,先對表進行水平切分,此時利用多塊硬盤來提升IO性能與存儲空間,成本比較低。數據庫到了需要垂直切分的階段,此時修改數據庫結構的主要原因已經不是數據量而是整個業務系統不能承受壓力了。若過早對數據庫進行垂直切分需要重新構建業務系統,工作量大,而水平切分不需要對業務做大量修改,所以在實際應用時建議先考慮水平切分,然后再做垂直切分。
4 結束語
高并發服務器的設計需要從多方面考慮,若某個部分出現短板就會影響整體性能。本文從服務器集群、分布式、數據庫幾個方面入手設計服務器架構,以提升服務器的可用性與并發性能。提高服務器性能的方式并不唯一,且不同的使用情景需要不同的架構,除了文中的方案外,還可從其他部分優化,比如使用緩存、動靜分離、對熱點部分進行優化、對表的設計進行優化、對jvm等參數進行調優、使用CDN等等?;ヂ摼W正在高速發展,這使得高并發的場景變得越來越多。雖然各自解決高并發問題使用的技術各不相同,但是遇到的各種問題有很多都是類似的,故能夠對別人的解決方案進行參考,然后找到自己解決問題的思路。希望本文提供的方案能夠有助于大家對高并發服務器搭建知識的學習。
參考文獻:
[1] 王亞楠,吳華瑞,黃鋒.高并發Web應用系統的性能優化分析與研究[J].計算機工程與設計,2014,35(8)5:2976-2981.
[2] 逄健,劉佳.摩爾定律發展述評[J].科技管理研究,2015,35(15):46-50.
[3] 吳海明.基于Linux高可用性負載均衡集群技術的研究與應用[J].科技創新與應用,2018(36):17-18.
[4] 李海軍.服務器集群技術綜述[J].電腦知識與技術,2013,9(22):5018-5020.
[5] 劉金秀,陳怡華,谷長樂.基于Nginx的高可用Web系統的架構研究與設計[J].現代信息科技,2019,3(11):94-97.
[6] 金磐石.分布式架構在銀行核心業務系統的應用[J].計算機系統應用,2017,26(6):46-52.
[7] 趙然,朱小勇.微服務架構評述[J].網絡新媒體技術,2019,8(1):58-61,65.
[8] 熊永平.基于SpringBoot框架應用開發技術的分析與研究[J].電腦知識與技術,2019,15(36):76-77.
[9] 周永圣,侯峰裕,孫雯,等.基于SpringCloud微服務架構的進銷存管理系統的設計與實現[J].工業控制計算機,2018,31(11):129-130,133.
[10] 方永敢.微服務架構的研究及小區生活服務平臺的實現[D].成都:電子科技大學,2020.
[11] 母鳳雯.數據庫索引技術概述[J].電腦知識與技術,2017,13(25):9-11,13.
[12] 王麗娟,靳繼紅.基于MySQL的查詢優化技術研究[J].電腦知識與技術,2017,13(30):35-36.
[13] 劉建宏.MySQL數據庫優化與集群[J].數字通信世界,2017(7):47.
[14] 韋美雁,段華斌,周新林.大數據環境下的MySQL優化技術探討[J].現代計算機(專業版),2018(30):68-72.
【通聯編輯:謝媛媛】