1. 程式人生 > >Java處理網際網路高併發(專題精講)

Java處理網際網路高併發(專題精講)

引子:
高併發是網際網路應用的一大特點,也是網際網路應用不可避免的一個問題;比如 淘寶雙11購物狂歡節,京東618購物促銷節,12306春節火車票,促銷,秒殺等;

解決高併發問題是一個系統工程,需要站在全域性高度統籌謀劃,從多個角度進行架構設計,在實踐中,我們探索、總結和提煉出來了很多應對高併發的方案或者說手段,分別如下:

***A.硬體解決方案:
方式一: 單體應用----單體應用也叫集中式應用;
產品或者網站初期,通常功能較少,使用者量也不多,所以一般按照單體應用進行設計和開發;
按照經典的MVC三層架構設計,使用單臺數據庫,快取也不是必須的,應用系統和資料庫部署在同一臺伺服器上;
隨著應用系統功能的增加,訪問使用者的增多,單臺伺服器已無法承受那麼多的訪問流量;
此時,根據業務情況、訪問流量等因素,在成本也不高的情況下,可以採用提升硬體配置的方式來解決;
能通過提升硬體解決的時候,也需要提升硬體效能;
方式二: 單體應用垂直擴容----單體應用垂直擴容:指的是提升伺服器硬體配置;CPU從32位提升為64位;記憶體從64GB提升為256GB(比如快取伺服器);磁碟從HDD(Hard Disk Drive)提升為SSD(固態硬碟(Solid State Drives)),有大量讀寫的應用;磁碟擴容,1TB擴充套件到2TB,比如檔案系統;千兆網絡卡提升為萬兆網絡卡;但是不管怎麼提升硬體效能,硬體效能的提升不可能永無止盡,所以最終還是要靠分散式解決;

*** B.快取解決方案:快取可以說是處理高併發,優化系統性能首先要考慮的一個因素;它是解決效能問題的利器,就像一把瑞士軍刀,鋒利強大;
方式一:HTTP快取(包括 瀏覽器快取、Nginx快取、CDN快取)
瀏覽器快取-> 瀏覽器快取是指當我們使用瀏覽器訪問一些網站頁面或者HTTP服務時,根據伺服器端返回的快取設定響應頭將響應內容快取到瀏覽器,下次可以直接使用快取內容或者僅需要去伺服器端驗證內容是否過期即可,這樣可以減少瀏覽器和伺服器之間來回傳輸的資料量,節省頻寬,提升效能;比如新浪:http://www.sina.com.cn/,第二次重新整理訪問,返回響應碼為304,表示頁面內容沒有修改過,瀏覽器快取的內容還是最新的,不需要從伺服器獲取,直接讀取瀏覽器快取即可;


DateFormat format = new SimpleDateFormat(“EEE,MMM yyyy HH: mm: ss ‘GMT’”, Locale. US);
//當前時間
long now = System.currentTimeMillis() * 1000 * 1000;
response.addHeader( “Date”, format.format(new Date()));
//過期時間http 1. 0支援
response.addHeader(“Expires”, format.format (new Date(now+ 20 * 1000)));
//文件生存時間http 1.1支援
response.addHeader(“Cache-Control”, “max-age=20”);

Nginx快取->Nginx提供了expires指令來實現快取控制,比如:
location /static {
root /opt/static/;
expires 1d;
}
當用戶訪問時,Nginx攔截到請求後先從Nginx本地快取查詢資料,如果有並且沒有過期,則直接返回快取內容;啟用壓縮,減少資料傳輸量;
gzip on; #開啟gzip壓縮輸出

CDN快取-> CDN的全稱是Content Delivery Network,即內容分發網路。CDN的基本原理是廣泛採用各種快取伺服器,將這些快取伺服器分佈到使用者訪問相對集中的地區或網路中,在使用者訪問網站時,利用全域性負載技術將使用者的訪問指向距離最近的工作正常的快取伺服器上,由快取伺服器直接響應使用者請求。

縱觀整個寬頻服務的價值鏈,內容提供商和使用者位於整個價值鏈的兩端,中間依靠網路服務提供商將其串接起來。隨著網際網路工業的成熟和商業模式的變革,在這條價值鏈上的角色越來越多也越來越細分。其目的是使使用者可就近取得所需內容,解決 Internet網路擁擠的狀況,提高使用者訪問網站的響應速度。在這裡插入圖片描述著名的廠商:(帝聯科技)http://www.dnion.com/

方式二:應用快取(包括:記憶體快取、磁碟快取、程式碼元件、伺服器)
記憶體快取->在記憶體中快取資料;效率高,速度快;應用重啟快取丟失
磁碟快取->在磁碟快取資料;讀取效率較之記憶體快取稍低;應用重啟快取不會丟失;
程式碼元件-> Guava、Ehcache
伺服器->Redis、MemCache
方式三:多級快取,在整個應用系統的不同層級進行資料的快取,多層次快取,來提升訪問效率;比如:CDN -> Nginx -> Redis -> DB
(補充小知識,前端快取分為瀏覽器快取和cdn快取)
內容包括:經常需要讀取的資料、頻繁訪問的資料、熱點資料快取、IO瓶頸資料、計算昂貴的資料、無需實時更新的資料。
***C:叢集解決方案
叢集部署,也叫單體應用水平擴容;
1. 應用層面:原來通過部署一臺伺服器提供服務,現在就多部署幾臺,那麼服務的能力就會提升;部署了多臺伺服器,但是使用者訪問入口只能是一個,比如www.web.com,所以就需要負載均衡;負載均衡是應用叢集擴容後的必須步驟;叢集部署後,使用者的會話session狀態要保持的話,就需要實現session共享;
2. 資料庫層面:將資料庫伺服器和應用伺服器分離;
看應用是多讀還是多寫的應用,如果是多讀的應用,可通過資料庫主從架構解決,寫資料時操作主庫,讀資料時操作從庫;如果應用是多寫的應用,可通過互為主從的模式進行解決;
***D.拆分解決方案
1.應用拆分: 分散式—>單體應用,隨著業務的發展,應用功能的增加,單體應用就逐步變得非常龐大,很多人維護這麼一個系統,開發、測試、上線都會造成很大問題,比如程式碼衝突,程式碼重複,邏輯錯綜混亂,程式碼邏輯複雜度增加,響應新需求的速度降低,隱藏的風險增大,所以需要按照業務維度進行應用拆分,採用分散式開發(所以在網際網路公司架構師是很重要的);應用拆分之後,就將原來在同一程序裡的呼叫變成了遠端方法呼叫,此時就需要使用到一些遠端呼叫技術:htppClient、hessian、dubbo、webservice等;
可能面臨的挑戰:
隨著呼叫的錯綜複雜,我們需要對應用進行服務化,比如SOA,註冊中心、服務治理。
隨著訪問流量進一步增大,那麼解決方案也就越來越複雜,限流、降級、基礎化、通用化等;
中等網際網路公司大約發展到服務化、註冊中心就差不多了;
通過應用拆分和服務化之後,擴容就變得容易,如果此時系統處理能力跟不上,只需要增加伺服器即可;
2.資料庫拆分
分為:垂直拆分和水平拆分
按照業務維度把相同型別的表放在一個數據庫,另一些表放在另一個數據庫,這種方式的拆分叫垂直拆分,
也就是在不同庫建不同表,把表分散到各個資料庫;比如 產品、訂單、使用者 三類資料以前在一個數據庫中,現在可以用三個資料庫,分別為 產品資料庫、訂單資料庫、使用者資料庫;這樣可以將不同的資料庫部署在不同的伺服器上,提升單機容量和效能問題,也解決多個表之間的IO競爭問題;
根據資料行的特點和規則,將表中的某些行切分到一個數據庫,而另外的某些行又切分到另一個數據庫,這種方式的拆分叫水平拆分;
單庫單表在資料量和流量增大的過程中,大表往往會成為效能瓶頸,所以資料庫要進行水平拆分;
主從複製/讀寫分離/SQL優化/索引優化;
一主多從:讀多寫少,讀多的應用前面還需要新增快取,比如Redis/Memcache;互為主從:寫多讀少的應用;採用MyCat分庫分表/讀寫分離,降低開發難度;
E.靜態化
對於一些訪問量大,更新頻率較低的資料,可直接定時生成靜態html頁面,供前端訪問,而不是訪問jsp;(用到的技術:freemaker、velocity、thymeleaf),頁面靜態化首先可以大大提升訪問速度,不需要去訪問資料庫或者快取來獲取資料,瀏覽器直接載入html頁即可;頁面靜態化可以提升網站穩定性,如果程式或資料庫出了問題,靜態頁面依然可以正常訪問;
F.動靜分離
採用比如Nginx實現動靜分離,Nginx負責代理靜態頁面;Nginx的效率極高,利用它處理靜態資源,可以為後端伺服器分擔壓力;
G.佇列
佇列是資料結構中的一種線性表,從一端插入資料,從另一端刪除資料,先進先出;在實際專案中:
不是所有的處理都必須要實時處理;
不是所有的請求都必須要實時告訴使用者結果;
不是所有的請求都必須100%一次性處理成功;
不知道哪個系統需要我的協助來實現它的業務處理,保證最終一致性,不需要強一致性;這些情況下,我們都可以考慮使用佇列來處理;
佇列的作用就是:
非同步處理/系統解耦/流量削峰;
非同步處理是使用佇列的一個主要原因,比如註冊成功了,發優惠券/送積分/送紅包/發簡訊/發郵件等操作都可以非同步處理;
使用佇列實現系統解耦,比如支付成功了,發訊息通知物流系統,發票系統,庫存系統等,而無需直接呼叫這些系統;
使用佇列流量削峰,比如併發下單、秒殺等,可以考慮使用佇列將請求暫時入隊,通過快取+佇列的方式將流量削平,變成平緩請求進行處理;使用佇列的削峰,主要是將高峰流量變成平緩流量進行非同步處理,避免應用系統因瞬間的巨大壓力而壓垮;
請求佇列,比如web訪問,我們可以採用請求佇列對請求進行限流,當請求流量大於佇列長度後,拋棄請求,特別是在一些流量高峰時段,保護後端服務不會被突然的巨大流量壓垮服務;常見的訊息佇列產品:ActiveMQ/kafka/RabbitMQ/RocketMQ/Redis佇列
H.池化
在實際開發中,我們經常會採用一些池化技術,減少資源消耗,提升系統性能;比如(物件池、資料庫連線池、Redis連線池、HttpClient連線池、執行緒池)。
1.物件池:通過複用物件,減少物件建立和垃圾收集器回收物件的資源開銷;可以採用commons-pool2實現;
2. 資料庫連線池:Druid/DBCP/C3P0
3. Redis連線池:JedisPool(內部基於commons-pool2)
4. HttpClient連線池:PoolingClientConnectionManager
5.執行緒池: Executors.newFixedThreadPool(8);
Executors.newSingleThreadExecutor();
Executors.newScheduledThreadPool(10);
I.優化
(包括:JVM優化、Tomcat優化、Java程式優化、資料庫優化、Nginx優化、Linux優化、網路優化)
1.JVM優化:設定JVM引數
-server -Xmx4g -Xms4g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70(解釋說明
:-server VM有兩種執行模式Server與Client,兩種模式的區別在於,Client模式啟動速度較快,Server模式啟動較慢;但是啟動進入穩定期長期執行之後Server模式的程式執行速度比Client要快很多;

-Xmx2g 最大堆大小

-Xms2g 初始堆大小

-Xmn256m 堆中年輕代大小;

-XX:PermSize設定非堆記憶體初始值,預設是實體記憶體的1/64;由XX:MaxPermSize設定最大非堆記憶體的大小,預設是實體記憶體的1/4.

-Xss 每個執行緒的Stack大小

-XX:+DisableExplicitGC,這個引數作用是禁止程式碼中顯示呼叫GC。程式碼如何顯示呼叫GC呢,通過System.gc()函式呼叫。如果加上了這個JVM啟動引數,那麼程式碼中呼叫System.gc()沒有任何效果,相當於是沒有這行程式碼一樣。

-XX:+UseConcMarkSweepGC 併發標記清除(CMS)收集器,CMS收集器也被稱為短暫停頓併發收集器;

-XX:+CMSParallelRemarkEnabled 降低標記停頓;

-XX:+UseCMSCompactAtFullCollection:使用併發收集器時,開啟對年老代的壓縮.

-XX:LargePageSizeInBytes 指定 Java heap 的分頁頁面大小

-XX:+UseFastAccessorMethods 原始型別的快速優化

-XX:+UseCMSInitiatingOccupancyOnly 使用手動定義的初始化定義開始CMS收集

-XX:CMSInitiatingOccupancyFraction 使用cms作為垃圾回收使用70%後開始CMS收集;

2.設定JVM引數,可以參考JVM優化引數:
在tomcat的bin目錄下的startup.sh中設定jvm引數:

JAVA_OPTS="-server -XX:+PrintGCDetails -Xmx4g -Xms4g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70"
3.Java程式優化
不要重複建立太多物件;流/檔案/連線 一定要記得在finally塊中關閉;
少用重量級同步鎖synchronized;不要在迴圈體中使用try/catch;多定義區域性變數,少定義成員變數;…
4.資料庫優化
資料庫伺服器優化:資料庫伺服器的引數設定,偏DBA;
資料庫索引優化:建立索引的欄位儘量的小,最好是數值;儘量在唯一性高的欄位上建立索引,不要在性別這種唯一性很低的欄位上建立索引;
SQL優化:資料搜尋引擎solr/elasticsearch
5.Nginx優化
調整配置檔案引數 :
worker_processes 16;

events {
worker_connections 4096;
multi_accept on;
use epoll;
}
6.Linux優化:優化核心引數
7.網路優化:頻寬、路由器等方面
J.壓測
在系統上線前,需要對系統各個環節進行壓力測試,發現系統的瓶頸點,然後進行調優;
即便我們的優化工作已經做得很好了,但依然也會存在一些風險因素,比如網路不穩定,機房故障,所以我們需要提前有故障預備方案,比如多機房部署容災、路由切換等;
即便我們的故障預備方案做好了,還需要提前進行演練,以確保預案的有效性;
壓力測試工具:Apache JMeter/Webbench等;
技術總監、架構師牽頭,測試團隊、技術團隊、運維團隊 共同完成;

後語******
以上就是全部了,覺得不錯,點個贊拿走不謝!