當我們在系統設計的時候會遇到高併發的問題,因此如何解決這個問題,我們應該從哪些方面考慮??下面我們來詳細說明下:
第一、硬件層面
1:服務器:
服務器用更大的內存,更好的硬盤,提供更好,更多的服務器,生成服務器集羣。
2:帶寬
從相應的開發商裏,獲取足夠的帶寬。會消耗不少毛爺爺。
第二、軟件層面:
1.應用服務器這塊肯定是最先遭不住的,所以先搞一個niginx做負責均衡(輪詢,加權輪詢),後臺多部署幾臺應用服務器,
將請求均勻的分發到每臺服務器上,就是所謂的集羣部署;
2.有了集羣部署後,應用服務器的壓力暫時抗住了,現在就輪到數據庫這塊遭不住了。這時候就要考慮分庫分表了,
還有就是讀寫分離,將所有寫的壓力搞到主庫master上,讀的壓力搞到從庫slave。就是分庫分表+讀寫分離;
還有給數據庫相應的字段加索引。提高數據庫的查詢速度。
3.如果訪問量繼續擴大,那就橫向擴展數據庫服務器,但是又要考慮到money的問題,所以這也行不通。再把緩存服務器(redis)
搞起,做他一個緩存集羣。
4.上一步的緩存集羣只是解決了讀的問題,其實寫的壓力還是很大,這個時候就要引入消息中間鍵了,像各種mq之類的
東東。因爲有些業務上雖然一次要寫入很多條數據,但是不是都是立馬要寫入的。舉個例子,像用戶付款後,先把付錢
相關的數據存了,其他記錄放在mq裏,系統空閒了再慢慢來存。
第三:詳細設計中的問題
1:分佈式事務解決方案?
解決這種問題,有兩種方式
(1):本地消息表
是國外的 ebay 搞出來的這麼一套思想。
這個大概意思是這樣的:
- A 系統在自己本地一個事務裏操作同時,插入一條數據到消息表;
- 接着 A 系統將這個消息發送到 MQ 中去;
- B 系統接收到消息之後,在一個事務裏,往自己本地消息表裏插入一條數據,同時執行其他的業務操作,如果這個消息已經被處理過了,那麼此時這個事務會回滾,這樣保證不會重複處理消息;
- B 系統執行成功之後,就會更新自己本地消息表的狀態以及 A 系統消息表的狀態;
- 如果 B 系統處理失敗了,那麼就不會更新消息表狀態,那麼此時 A 系統會定時掃描自己的消息表,如果有未處理的消息,會再次發送到 MQ 中去,讓 B 再次處理;
- 這個方案保證了最終一致性,哪怕 B 事務失敗了,但是 A 會不斷重發消息,直到 B 那邊成功爲止。
這個方案說實話最大的問題就在於嚴重依賴於數據庫的消息表來管理事務啥的,會導致如果是高併發場景咋辦呢?咋擴展呢?所以一般確實很少用。
(2):可靠消息最終一致性方案
這個的意思,就是乾脆不要用本地的消息表了,直接基於 MQ 來實現事務。比如阿里的 RocketMQ 就支持消息事務。
大概的意思就是:
- A 系統先發送一個 prepared 消息到 mq,如果這個 prepared 消息發送失敗那麼就直接取消操作別執行了;
- 如果這個消息發送成功過了,那麼接着執行本地事務,如果成功就告訴 mq 發送確認消息,如果失敗就告訴 mq 回滾消息;
- 如果發送了確認消息,那麼此時 B 系統會接收到確認消息,然後執行本地的事務;
- mq 會自動定時輪詢所有 prepared 消息回調你的接口,問你,這個消息是不是本地事務處理失敗了,所有沒發送確認的消息,是繼續重試還是回滾?一般來說這裏你就可以查下數據庫看之前本地事務是否執行,如果回滾了,那麼這裏也回滾吧。這個就是避免可能本地事務執行成功了,而確認消息卻發送失敗了。
- 這個方案裏,要是系統 B 的事務失敗了咋辦?重試咯,自動不斷重試直到成功,如果實在是不行,要麼就是針對重要的資金類業務進行回滾,比如 B 系統本地回滾後,想辦法通知系統 A 也回滾;或者是發送報警由人工來手工回滾和補償。
- 這個還是比較合適的,目前國內互聯網公司大都是這麼玩兒的,要不你舉用 RocketMQ 支持的,要不你就自己基於類似 ActiveMQ?RabbitMQ?自己封裝一套類似的邏輯出來,總之思路就是這樣子的。
2:session一致性問題
(1)什麼是session和session一致性
簡單說一下session和session一致性。服務爲訪問他的用戶構造了一組信息,稱之爲會話(session),當該用戶在限定時間內每次發起http訪問時,服務端能自動感知到是該用戶在發起訪問,稱之爲會話保持(session一致性)
(2):session一致的方法。
IP哈希
使用反向代理(nginx),對用戶的IP進行hash操作,確保唯一IP地址每次都訪問一個相同的服務。
Session複製
把每個用戶的session都同步複製到集羣中的每一個服務節點,這樣無論用戶訪問哪個服務節點,都能獲取到自己的session信息。
Session客戶端存儲
把session信息保存到客戶端的cookie中。
Session分佈式存儲
把session信息保存到後端的其它存儲中,例如mysql,redis,memcached等。
(3):微服務架構下的session一致性
A:如果我們採用了shiro作爲權限控制組件
構造ShiroConfig,需要包含SessionManager,SecurityManager對象,並且在filter中過濾掉不需要攔截的地址,比如Actutor的EndPonit信息;
B:採取redis存儲session
C:存儲在頁面中cookie或者session中
下面是從公衆號JavaGuide中拷貝的幾張圖。
(1):大型網站架構演化
(2)大型架構模式
(3):網站核心架構要素
(4):網站高性能架構
(5):網站可擴展架構
(6):網站安全架構