分佈式系統的基礎知識
- 阿姆達爾定律
- 多線程交互模式
- 互不通訊,沒有交集,各自執行各自的任務和邏輯
- 基於共享容器(如隊列)協同的多線程模式->生產者-消費者->隊列
- 經過事件協同的多線程模式->如B線程須要等到某個狀態或事件發生後才能繼續工做,而這個狀態改變或者事件產生和A線程相關
- 網絡通訊基礎知識
- OSI、TCP/IP
- 網絡IO實現方式
- BIO
- NIO->Reactor模式
- AIO->Proactor模式
負載均衡
- 硬件負載均衡
- LVS等軟件的負載均衡
- 名稱服務
- 規則服務器
- Master-Worker
小結
1.一致性hash的算法思路(遊戲服務器進程/線程設計思路)(擴容/縮容平滑)/環形消息隊列disruptor
2.緩存服務器擴容或者縮容要儘可能平滑
3.消息中間件-MOM:Message-Oriented middleware,is software infrastructure focused on sending and receiving messages between distributed system.
4.兩個常被說起的好處:異步和解耦.
5.CountDownLatch不能循環使用,而CyclicBarrier能夠循環使用(從名字上看,【循環_cyclic】屏障)
大型網站及其架構演進過程
海量數據+高併發訪問量+複雜的業務和系統
- 數據庫與應用分離
- 應用服務器走向集羣(引入負載均衡器)
session問題
- Session Sticky
- Session Replication
- Session數據集中存儲
- Cookie Based
讀寫分離
- 引入讀庫->搜索引擎(站內搜索功能)->Search Cluster
- 專庫專用->數據垂直拆分
- 數據水平拆分
加快數據讀取
分佈式存儲系統
拆分應用
消息中間件
中間件
特定中間件是解決特定場景問題的組件,它可以讓軟件開發人員專一於本身應用的開發
- 遠程過程調用和對象訪問中間件->解決分佈式環境下應用的互相訪問問題
- 消息中間件:解決應用之間的消息傳遞、解耦、異步的問題
- 數據訪問中間件:解決應用訪問數據庫的共性問題的組件
構建Java中間件的基礎知識
- JVM/GC/內存堆佈局
- 併發
- 線程池/synchronized/ReentrantLock/volatile(可見性與操做互斥是兩件事情)/Atomics/wait、nofity、nofifyAll/CountDownLatch/
- CyclicBarrier/Semaphore/Exchanger/Future、FutureTask/
- 併發容器:CopyOnWrite、Concurrent
- 動態代理
- 反射
- 網絡通訊選擇
- BIO/NIO/AIO
- MINA、Netty
- 協議制定
中間件範疇
服務框架
運行期服務框架與應用和容器的關係
- 直接或者間接依賴的jar致使應用裏同一個jar包有不一樣的版本->產生衝突
- 將服務框架自身用到的類和應用用到的類都控制在User-Defined Class Loader級別->實現相互間隔離
- web容器對於多個web應用的處理以及osgi對於不一樣的bundle的處理都採用了相似的方法
服務調用者與服務提供者之間通信方式的選擇
- 調用者[服務框架]
- 並非每次調用遠程服務前都經過服務註冊查找中心來查找可用地址->而是把地址緩存在調用者本地->當有變化時主動從服務註冊查找中心發起通知->告訴調用者可用的服務提供者列表的變化
- 客戶端拿到可用的服務提供者的地址列表後->如何爲當次的調用進行選擇就是路由要解決的問題->隨機、輪訓、權重(通常指動態權重)
引入基於接口、方法、參數的路由
- 由於通常狀況下,一個集羣會提供多個服務,每一個服務又有多個方法.->須要細粒度的控制服務路由
- 如服務提供者有兩個服務-接口A和接口B->接口A中的某個方法1執行比較耗時->影響總體性能->排隊
- 隔離資源,使得快慢不一樣、重要級別不一樣的方法之間互不影響
服務調用端的流控處理
序列化與反序列化處理
- 具體制定通訊協議時->版本號、可擴展(擴展性、向後兼容性)屬性以及發起方支持能力的介紹(如是否支持壓縮等)
網絡通訊實現選擇
- 同步方式進行遠程調用使用nio
- io線程/數據隊列/通訊對象隊列/定時任務
- 請求線程發送數據->進入數據隊列->生成通訊對象(阻塞請求線程)->加入通訊對象隊列->請求線程等待(通訊對象用於喚醒請求線程)
- 數據隊列->io線程->socket鏈接->進行數據收發(須要發送的數據進入數據隊列後,這樣請求線程就不須要直接和socket鏈接打交道->複用socket鏈接)
- 若是遠程調用超時前有執行結果返回->io線程會通知通訊對象->通訊對象結束請求線程等待->結果傳送給請求線程
- 定時任務用於負責檢查通訊對象隊列中的哪些通訊對象已經超時->而後這些通訊對象會通知請求線程已經超時
- 支持多種異步服務調用方式
- OneWay->只管發送請求而不關心結果的方式->只須要把發送的數據放入數據隊列便可
- CallBack->請求發送方發送請求後會繼續執行本身的操做->等對方有響應時進行一個回調
- 請求方設置回調->加入回調對象隊列
- 收到服務提供者的返回後->io線程會通知回調對象->執行回調方法
- 對於超時->定時任務的方式->若是沒有返回->也須要執行回調對象的方法->告知已超時沒有結果
- 若是不引入新的線程->那麼回調的執行要麼是io線程中要麼是在定時任務的線程中
- [建議用新的線程執行回調->不要由於回調自己的代碼執行時間久等問題影響了io線程或者定時任務].
- Future->請求線程經過future來獲取通訊結果並直接控制超時->同上Future對象隊列 io線程依然是從數據隊列中獲得數據再進行通訊->獲得結果後會把它傳給Future
- 可靠異步->要保證異步請求可以在遠程被執行->消息中間件來完成這個保證
- 使用Future方式對遠程服務調用的優化
- 一個請求中調用多個遠程服務的狀況
- 按照調用順序把服務的請求發送給服務A,服務B,服務C->請求發送過去並不直接等待執行結果
- 而是直到服務C的請求也發出去後再來統一等待服務A、服務B和服務C的執行結果->而後再接着進行本地的數據處理
- 前提:所調用服務A、B、C之間並無相互的依賴關係
- 即並行調用優化->由於Future方式的支持
- 反序列化線程:通常是使用io線程,不過這樣會影響io線程的工做效率;另外一種方式是把反序列化工做從io線程轉移到其餘線程去作
服務提供端的設計與實現
- 服務端的工做
- IO線程通訊處理->反序列化的工做取決於具體實現,io線程或者工做線程中進行的方式都有
- 獲得反序列化的消息並定位服務後->調用服務通常在非io線程進行(工做線程)
- 執行不一樣服務的線程池隔離
- 服務提供端,工做線程池不止一個,而是多個,定位到服務後,根據服務名稱、方法、參數來肯定具體執行服務調用的是哪一個線程池.->這樣,不一樣線程池之間是隔離的->不會出現爭奪線程資源的狀況.
- 整個服務框架的功能分爲服務調用者和服務提供者兩方面,此外像序列化、協議、通訊等是公用的功能.在具體實現上,是把這些功能都放在一塊兒造成一個完成的服務框架->而不是分爲服務調用者框架和服務提供者框架
- 服務框架必須作到模塊化且可配置->模塊可替換->並留有必定的擴展點來擴展原有功能
服務升級
- 接口不變->內部的服務實現有變化->比較簡單,採用灰度發佈的方式驗證而後所有發佈就能夠了
- 接口中增長方法->也比較簡單,直接增長方法便可->須要使用新方法的調用者就使用新方法,原來的調用者繼續使用原來的方法便可
- 接口的某些方法修改調用的參數列表
- 對使用原來方法的代碼都進行修改->不太可行->由於要求咱們同時發佈多個系統
- 版本號解決->經常使用的方式->使用老方法的系統繼續調用原來版本的服務,而須要使用新方法的系統則使用新版本的服務
- 在設計方法上考慮參數的擴展性->可行,不太好->因參數列表可擴展通常就覺得是採用相似map的方式來傳遞參數->不直觀,而且對參數的校驗會比較複雜
實戰中的優化
- 服務的拆分
- 服務的粒度
- 優雅和實用的平衡
- 多調用一次就比以前多了一次網絡->一些功能直接在服務調用者的機器上實現會更加合適、經濟
- 如服務調用者直接讀緩存->大部分對數據的請求直接走一次緩存就能夠,只有少部分沒有命中緩存的數據讀取須要走服務提供者->而後再到數據庫進行讀取並插入緩存
- 分佈式環境的請求合併
- 分佈式鎖-->額外開銷->另一個思路->在服務調用端不是把請求隨機分發給服務提供者,而是根據必定的規則把一樣的請求發送到同一個服務提供者上->減小複雜性.
服務治理
- 服務信息、服務質量、服務容量、服務依賴、服務分佈、服務統計、服務元數據、服務查詢、服務報表、服務監視、服務上下線、服務路由、服務限流降級、服務歸組、服務線程池管理、機房規則、服務受權、
- ESB
- Enterprise Service Bus:企業服務總線
數據訪問層
數據庫減壓思路
- 優化應用,看看是否有沒必要要的壓力給了數據庫(應用優化)
- 看看有沒有辦法能夠下降對數據庫的壓力,例如引入緩存、加搜索引擎等
- 把數據庫的數據和訪問分到多臺數據庫上,分開支持->核心思路和邏輯
單機變爲多機後,事務如何處理
- 分佈式事務->XA/DTP-->AP(Application Program)->RM(Resource Manager)->TM(Transaction Manager)
- 兩階段提交->Prepare->Commit
- Prepare階段有一個節點資源失敗則Rollback
- 大型網站一致性的基礎理論-CAP/BASE
- CAP:Consitency,一致性、Availability,可用行、Partition-Tolerance,分區容忍性
- 分佈式系統中不能同時知足上面三項,CA、AP、CP
- 分佈式系統中,咱們通常選擇增強可用性和分區容忍性而犧牲一致性->首先先知足A和P,而後看如何解決C的問題.
- BASE模型
- Basically Available,基本可用,容許分區失敗
- Soft state,軟狀態,接受一段時間的狀態不一樣步
- Eventually consistent,最終一致,保證最終數據的狀態是一致的
- 對於C,咱們採用的方式和策略就是保持最終一致,也就是不保證數據發生變化後全部節點馬上一致,可是保證他們最終是一致的。在大型網站中,爲了更好的保持擴展性和可用性,通常都不會選擇強一致性,而是採用最終一致的策略來實現.
- 比兩階段提交更輕量一些的Paxos協議
- 集羣內數據一致性的算法實例
- 從工程上說,若是可以避免分佈式事務的引入,那麼仍是避免爲好;若是必定要引入分佈式事務,那麼能夠考慮最終一致的方法,而不是追求強一致。並且從實現上來講,咱們是經過補償的機制不斷重試,讓以前由於異常而沒有進行到底的操做繼續進行而不是回滾。若是仍是不能知足需求,那麼基於Paxos的算法會是一個不錯的選擇.
多機的Sequence問題與處理
- 惟一性
- 連續性
- 提供一個實現方案:把全部id集中放到一個地方進行管理,每臺機器使用時都從這個id生成器上取
跨庫查詢
數據訪問層的設計與實現
- 對外提供數據訪問層的方式
- 爲用戶提供專有API->不推薦->沒有通用性
- 通用方式->jdbc->數據層自身能夠做爲一個jdbc的實現,即暴露出jdbc的接口給應用
- 基於orm或者類orm接口的方式
- 按照數據層流程的順序看數據層設計
- SQL解析
- 經過SQL解析能夠獲得SQL中的關鍵信息,如代表、字段、where條件等;而在數據層中,一個很重要的事情是根據執行的SQL獲得被操做的表,根據參數及規則來肯定目標數據庫鏈接
- 規則處理階段
- 一致性 hash
- [把節點對應的哈希值變爲了一個範圍],而再也不是離散的.在一致性哈希中,咱們會把整個哈希值的範圍定義的很是大,而後把這個範圍分配給現有的節點
- 虛擬節點對一致性hash的改進
- 映射表與規則自定義方式
- 爲何要改寫SQL
- 如何選擇數據源
- Master/Slave
- 根據當前SQL的特色(讀、寫)、是否在事務中以及各個庫的權重規則,計算獲得此次SQL請求要訪問的數據庫
- 執行SQL和結果處理階段
- 實戰
- 複雜的鏈接管理
- 三層數據源的支持和選擇
- DataSouce/AtomDataSource/groupDataSource
- 獨立部署的數據訪問層實現方式
- 讀寫分離的挑戰和應對
- 數據結果相同,多從庫對應一主庫的場景
- 應用經過數據層訪問數據庫,經過消息系統就數據庫的更新送出消息通知->數據庫同步服務器得到消息通知後會進行數據的複製工做.分庫規則配置則負責在讀數據及數據同步服務器更新分庫時讓數據層知道分庫規則->數據同步服務器和DB主庫的交互主要是根據被修改或新增的數據主鍵來獲取內容,採用的是行復制的形式
- 比較優雅的方式是基於數據庫的日誌來進行數據的複製
- 主/備庫分庫方式不一樣的數據複製
- 非對稱複製->控制數據分發->如主庫按照買家id分庫,而備庫按照賣家id進行分庫
- 引入數據變動平臺
- 不少其餘場景也會關心數據的變動,除了複製到其餘數據庫,例如緩存的失效等->能夠考慮構建一個通用的平臺來管理和控制數據變動->
- 引入Extractor和Applier->Extractor負責把數據源變動的信息加入到數據分發平臺中,而Applier的做用是把這些變動應用到相應的目標上->中間的數據分發平臺中是由多個管道組成->進入到數據分發平臺的變動信息就是標準化、結構化的數據了-
- 如何作到數據平滑遷移
- 最大挑戰是,在遷移的過程當中又會有數據的變化(由於不少應用不能接受長時間的停機)->能夠考慮的方案是在開始進行數據遷移時記錄增量的日誌,在遷移結束後,再對增量的變化進行處理.->在最後,能夠要把要被遷移的數據的寫暫停,保證增量日誌都處理完畢後,再切換規則,放開全部的寫,完成遷移工做
消息中間件
消息中間件對應用的解耦
- 如登錄系統負責向消息中間件發送消息,而其餘的系統則向消息中間件來訂閱這個消息,而後完成本身的工做.
- 經過消息中間件解耦,登錄系統就不用關心到底有多少個系統須要知曉登錄成功這件事了,而不用關心如何通知它們,只須要把登錄成功這件事轉化爲一個消息發送到消息中間件就能夠了
- landon:和事件解耦同樣,如遊戲中玩家升級拋出一個事件,其餘子系統只須要監聽該事件便可,而沒必要升級直接調用各個子系統
- 登錄成功時須要向消息中間件發送一個消息,那麼[必須保證這個消息發送到了消息中間件],不然依賴這個消息的系統就沒法工做了
互聯網時代的消息中間件
- JMS:Java Message Service->規範->Hornetq,ActiveMQ等產品是這個規範的實現
- 如何解決消息發送一致性
- 消息發送一致性的定義:產生消息的業務動做與消息發送的一致,即若是業務操做成功了,那麼由這個操做產生的消息必定要發送出去,不然就丟失消息了;而另外一方面,若是這個業務行爲沒有發生或者失敗,那麼就不該該把消息發出去.
- JMS消息模型-Queue/Topic_支持XA協議(兩階段提交)->會引入分佈式事務->存在一些限制且成本相對較高
- 一致性方案的正向流程
- (1) 業務處理應用首先把消息發給消息中間件,標記消息的狀態爲待處理.
- (2) 消息中間件收到消息後,把消息存儲在消息存儲中,並不投遞該消息.
- (3)消息中間件返回消息處理的結果,僅是入庫的結果,結果是成功或者失敗.
- (4)業務方收到消息中間件返回的結果並進行處理:
- a) 若是收到的結果是失敗,那麼就放棄業務處理,結束
- b) 若是收到的結果是成功,則進行業務自身的操做
- (5)業務操做完成,把業務操做的結果發送給消息中間件
- (6)消息中間件收到業務操做結果,根據結果進行處理
- a) 若是業務失敗,則刪除消息存儲中的消息,結束
- b)若是業務成功,則更新消息存儲中的消息狀態爲可發送,而且進行調度,進行消息的投遞
- 須要注意各類步驟中可能出現的異常狀況
- 最終一致性方案的補償流程:
- (1)消息中間件詢問狀態爲待處理的消息對應業務操做結果
- (2)應用即消息發佈者對業務操做檢查操做結果
- (3)發送業務處理結果給消息中間件
- 4)消息中間件更新消息狀態,業務成功,消息狀態爲待發送;業務失敗則消息刪除
- 如何解決消息中間件與使用者的強依賴問題
- 把消息中間件所須要的消息表與業務數據表放到同一個業務數據庫->業務操做和寫入消息做爲一個本地事務完成,而後再通知消息中間件有消息能夠發送->解決一致性->也能夠消息中間件定時去輪詢業務數據庫找到須要發送的消息,取出內容後進行發送
- 須要業務本身的數據庫承載消息數據/須要讓消息中間件去訪問業務數據庫/須要業務操做的對象是一個數據庫
- 消息中間件再也不直接與業務數據庫打交道->將業務操做、寫入消息,輪詢消息等所有放到業務應用
- 加一個本地磁盤做爲一個消息存儲
- 消息模型對消息接收的影響
- JMS Queue模型:
- 應用1和應用2發送消息到JMS服務器,這些消息根據到達的順序造成一個隊列->應用3和應用4進行消息的消費;若是Queue裏面的消息被一個應用處理了,那麼鏈接到JMS Queue上的另外一個應用是收不到這個消息的->即鏈接到這個JMS Queue上的應用共同消費了全部的消息->消息從發送端發送出來時不能肯定最終會被哪一個應用消費,可是能夠明確的是隻有一個應用會去消費這條消息->Peer To Peer方式(PTP)
- JMS Topic模型:
- 和Queue模型的最大區別在於消息接收的部分,在該模型中,接收消息的應用3和應用4是能夠獨立收到全部到達Topic的消息的->Pub/Sub方式
- JMS中客戶端鏈接的處理和帶來的限制
- JMS中每一個Connection都有一個惟一的clientId,用於標識鏈接的惟一性
- 應用3和JMS服務器創建了兩個鏈接,應用4和JMS服務器創建了一個鏈接->能夠看到這三個鏈接所接收的消息是徹底不一樣,每一個鏈接收到的消息條數以及收到消息的順序則不是固定的.->另外每一個鏈接都會收到全部發送到Topic的消息.
- 咱們須要什麼樣的消息模型
- 消息發送方和接收方都是集羣/同一個消息的接收方可能有多個集羣進行消息的處理/不一樣集羣對於同一條消息的處理不能相互干擾
- 如8條消息和兩個集羣,每一個集羣剛好有兩臺機器->那麼須要這兩個集羣的機器分別處理掉全部8條消息->不能遺漏也不能重複
- 引入ClusterId,用這個Id來標識不一樣的集羣,而集羣內的各個應用實例的鏈接使用一樣的ClusterId->把Topic模型和Queue模型的特色結合起來使用
- 消息訂閱者訂閱消息的方式
- 做爲消息中間件,提供對於消息的可靠保證是很是重要的事情->一些場景中一些下游系統徹底經過消息中間件進行自身任務的驅動
- 持久訂閱、非持久訂閱
- 非持久訂閱:消息接收者應用啓動時,就創建了訂閱關係->能夠收到消息->若是消息接收者應用結束了,那麼消息訂閱關係也就不存在了->這時的消息是不會爲消息接收者保留的.
- 持久訂閱:消息訂閱關係一旦創建除非應用顯示地取消訂閱關係不然這個訂閱關係將一直存在即便消息接收者應用中止->這個消息也會保留,等待下次應用啓動後再投遞給消息接收者.
- 保證消息可靠性
- 消息從發送端應用到接收端應用,中間有三個階段須要保證可靠,分別是:[消息發送者把消息發送到消息中間件];[消息中間件把消息存入消息存儲];[消息中間件把消息投遞給消息接收者]
- 要保證這三個階段均可靠,才能保證最終消息的可靠
- 消息發送端可靠的保證->注意異對異常的處理->可能出現的問題是在不注意的狀況下吃掉了異常->從而致使錯誤的判斷結果
- 消息存儲的可靠性保證
- 持久存儲部分的代碼徹底自主實現
- 利用現有的存儲系統實現
- 實現基於文件的消息存儲
- 採用數據庫做爲消息存儲
- 基於雙機內存的消息存儲
- 消息中間件自身擴容
- 讓消息的發送者和消息的訂閱者可以感知到有新的消息中間件機器加入到了機器->軟負載中心
- 消息存儲的擴容處理
- 消息投遞的可靠性保證
- 消息接收者在處理消息的過程當中對於異常的處理->千萬不要吃掉異常後確認消息處理成功
- 投遞處理優化:
- 投遞是必定要採用多線程處理
- 單機多訂閱者共享鏈接->消息只發送一次
- 訂閱者視角的消息重複的產生和應對
- 分佈式事務,複雜
- 冪等操做->對於消息接收端->採用一樣的輸入屢次調用處理函數會獲得一樣的結果
- JMS的消息確認方式與消息重複的關係
- AUTOACKNOWLEDGE/CLIENTACKNOWLEDGE/DUPSOKACKNOWLEDGE
- 消息投遞的其餘屬性支持
- 消息優先級
- 訂閱者消息處理順序和分級訂閱
- 自定義屬性
- 局部順序
- 保證順序的消息隊列設計
- 接收端的設計從原來的Push模式變爲了Pull模式
軟負載中心與集中配置管理
- 軟負載中心兩個最基礎的職責
- 聚合地址信息
- 生命週期感知->須要能對服務的上下線自動感知,而且根據這個變化去更新服務地址數據
- 軟負載中心的結構
- 軟負載中心的服務端->負責感知提供服務的機器是否在線,聚合提供者的機器信息並負責把數據傳給使用數據的應用
- 軟負載中心的客戶端
- 服務提供者->把服務器提供者提供服務的具體信息主動傳給服務端->而且隨着提供服務的變化去更新數據
- 服務器使用者->向服務端告知本身所須要的數據並負責去更新數據,還要進行本地的數據緩存
- 軟負載中心三部分重要的數據->聚合數據、訂閱關係、鏈接數據
- 內容聚合功能的設計
- 保證數據正確性
- 高效聚合數據
- 併發下的數據正確性的保證
- 數據更新、刪除的[順序]保證
- 大量數據同時插入、更新時的性能保證
- 根據key進行分線程的處理->保證一樣key的數據是在同一個線程中處理->順序任務隊列
- 解決服務上下線的感知
- 經過客戶端與服務端的鏈接感知
- 長鏈接的心跳或數據的發佈來判斷服務發佈者是否還在線->若是好久沒有心跳或數據的發佈,則斷定爲不在線;那麼就取出這個發佈者發佈的數據->而對於新上線的發佈者,經過鏈接創建和數據發佈就實現了上線的通知
- 當負載中心的自身的負載很高時,可能產生誤判,如軟負載中心壓力很大,處理請求變慢,心跳數據來不及處理->會覺得心跳超時而判斷服務不在線,認爲服務不可用而且把信息通知給服務調用者,這會致使本來可用的服務被下線了
- 另外的問題,若是服務發佈者到軟負載中心的網絡鏈路有問題而服務發佈者到服務使用者的鏈路沒問題,也會形成感知的問題->由於軟負載中心屬於旁路
- 解決:軟負載中心客戶端增長邏輯,當收到軟負載中心通知的應用下線數據時,須要服務調用者進行驗證才能接收這個通知
- 經過對於發佈數據中提供的地址端口進行鏈接的檢查
- 須要服務調用者進行最終確認,由於在系統中進行的實際業務調用通訊是在服務調用者和服務提供者之間
- 軟負載中心的數據分發的特色和設計
- 數據分發與消息訂閱的區別
- 消息中間件須要保證消息不丟失->每條消息都應該送到相關訂閱者->而軟負載中心只須要保證最新數據送到相關的訂閱者->不須要保證每次的數據變化都能讓最終訂閱者感知
- 消息中間件中同一個集羣中的不一樣機器是分享全部消息的,由於該消息只要同一集羣中的一臺機器去處理了就行->而軟負載中心則不一樣,由於其維護的是你們都須要用的服務數據->因此須要把這數據分發給全部的機器
- 提高數據分發性能須要注意的問題
- 數據壓縮->CPU換帶寬
- 全量與增量的選擇->建議剛開始的實現中採用簡單的方式,即傳送全量數據,當全量數據很大時就須要考慮採用增量傳送的方式實現.
- 針對服務化的特性支持
- 軟負載數據分組
- 提供自動感知之外的上下線開關
- 優雅的中止應用
- 咱們應該先從服務列表中去掉這個機器->等待當時正在執行的服務器結束,而後再中止應用->經過指令直接從軟負載中心使機器下線
- 保持應用場景,用於排錯
- 遇到服務的問題時,能夠把出問題的服務留下一臺進行故障定位和場景分析->此時須要把這臺機器從服務列表中拿下來,以避免有新的請求進來形成服務的失敗,這也是須要軟負載中心直接使服務下線的一個場景.
- 維護管理路由規則
- 從單機到集羣
- 數據管理問題/鏈接管理問題
- 數據統一管理
- 數據聚合放在一個地方->軟負載中心集羣,無狀態->對於數據發佈者和訂閱者來講,選擇軟負載中心集羣中的任何一個機器鏈接皆可
- 把軟負載中心集羣中的機器的職責分開,即把聚合數據的任務和推送數據的任務分到專門的機器上處理->將軟負載中心集羣中有一臺機器爲軟負載中心數據聚合,另外一臺機器爲軟負載中心數據推送->發佈者和訂閱者的鏈接是分開管理的->爲了提高性能,在軟負載中心負責數據推送的機器上是能夠對聚合數據作緩存
- 數據對等管理方案
- 將數據分散在各個軟負載中心的節點上而且把本身節點管理的數據分發到其餘節點上,從而保證每一個節點都有整個集羣的所有數據而且這些節點的角色是對等的->使用軟負載中心的數據發佈者和數據訂閱者只須要去鏈接軟負載中心集羣中的任何一臺機器就能夠->軟負載中心集羣內部,各個節點之間會進行數據的同步
- 批量處理同步->合併變化,同步一次
- 若是節點較多,同步量會較大->對集羣內的節點進行指責劃分
- 若是集羣管理的整體數據不少,超過了單機限制->則須要對數據進行分組處理->讓每一個節點管理一部分數據->即用UI規則對數據進行相似分庫分表的操做->則數據訂閱者可能就須要鏈接多個數據分發節點了
- 集中配置管理中心
- 集中配置管理中心結構
- 準備的持久存儲來保存持久數據(Master-Slave)->通常採用關係型數據庫->經過兩個節點的主備來解決持久數據安全的問題.
- 集中配置管理中心集羣這層由多個集中配置管理中心節點組成->對等->均可以提供數據給應用端等->互不依賴
- 集中配置管理中心的單個節點->部署了一個nginx和一個web應用->其中web應用主要負責完成相關的程序邏輯如數據庫的相關操做以及根據ip等的分組操做,即整個應用的邏輯放在了web應用中;單機的本地文件Local File則是爲了容災和提高性能,客戶端進行數據獲取的時候,最後都是從nginx直接獲取本地文件並把數據返回給請求端
- 集中配置管理中心的使用分爲了如下兩部分
- 提供給應用使用的客戶端->主要是業務應用經過客戶端去獲取配置信息和數據,用於數據的讀取
- 爲控制檯或者控制腳本提供管理SDK
- 包括了對數據的讀寫,經過管理SDK能夠進行配置數據的更改
- 客戶端實現和容災策略
- 客戶端經過http協議與集中配置管理中心進行通訊
- 經過輪詢獲取最新數據_普通輪詢
- 改進使用長輪詢,Long Polling->若是沒有數據,長輪詢會等待;若是等待數據,馬上返回;若是一直沒有數據則等到超時後返回,繼續創建鏈接,而普通輪詢就直接返回了->是HTTP普通輪詢和Socket長鏈接方式的折中-
- 容災
- 數據緩存
- 數據快照
- 本地配置
- 文件格式->若是是二進制數據格式,那麼就沒有對應的工具是沒法對配置進行修改->若是客戶端容災退化到一個單機應用就會須要直接修改配置內容和數據->那麼文本格式的限制就很是重要和關鍵了
- 服務端實現和容災策略
- Nginx+Web應用->和邏輯相關的部分在Web應用上實現,Nginx用於請求的處理和最後結果的返回,而供返回的數據的都在本地文件系統中
- 和數據庫的數據同步
- 數據庫策略
- 數據庫在設計時須要支持配置的版本管理,即隨着配置內容的更改,老的版本是須要保留的,爲了方便進行配置變動的對比和回滾->而數據庫自己須要主備進行數據的容災考慮
構建大型網站的其餘要素
- 加速靜態內容訪問速度的CDN
- CDN源站/CDN節點
- 網絡緩存技術
- 幾個關鍵技術
- 全局調度->須要根據用戶地域、接入運營商以及CDN機房的負載狀況去調度
- 緩存技術->提高命中率
- 內容分發
- 帶寬優化
- 大型網站的存儲支持
- 基本上就是在解決存儲和計算的問題
- 關係型數據庫
- 分佈式文件系統
- 圖片、大文本存儲->使用數據庫不合適
- NAS網絡存儲設備(Network Attached Storage),其自己的IO吞吐性能以及擴展性在大型網站中會出現比較明顯不足
- 分佈式文件系統具體產品
- 開源的淘寶的TFS
- 不開源的Google#GFS,Goole File System->GFS Client(負責從Master獲取要操做的文件在ChunkServer中的具體地址,而後直接和ChunkServer通訊,獲取數據或者進行數據的寫入、更新)/GFS Master(維護全部的文件系統元數據、控制整個系統範圍內的一些活動、與ChunkServer之間經過週期性的心跳進行通訊,檢測對方是否在線)/GFS chunkserver(Data Node,文件存儲的地方)
- 主要解決了單機文件存儲容量及安全性的問題,把多臺廉價pC組成一個大的分佈式的看起來像文件系統的集羣
- [HDFS,採用Java的類GFS的實現]
- NoSQL
- No SQL/Not Only SQL
- 基本上處於分佈式文件系統和SQL關係型數據庫之間的系統都被歸爲NOSQL的範疇
- 數據模型
- Key-Value,沒辦法進行高效的範圍查詢
- Ordered Key-Value,Key是有序的->可解決基於Key的範圍查詢的效率問題,不過在這個模型中,Value自己的內容和結構是由應用來負責解析和存儲的->若是在多個應用中去使用則並不直觀也不方便
- BigTable
- Google的結構化數據的分佈式存儲系統->Value是由多個Column Family組成
- Document,Full-Text Search
- 能夠在Value中任意自定義複雜的Scheme/對索引方面的支持
- Graph
- 系統結構
- [HBase]->借鑑Google BigTable的一個Java版本的開源實現
- 存儲到HBase的數據是經過HRegionServer來管理的,每一個HRegionServer管理了多個HRegion,每一個Region管理具體的數據->HMaster是管理全部HRegionServer的節點,是一箇中心控制的結構
- Amazon#Dynamo結構
- 採用了一致性哈希進行管理
- [Cassandra]是一個開源的相似Dynamo的實現
- 緩存系統
- 非持久的存儲,是爲了加速應用對數據的讀取
- Redis和Memcache是兩個使用很普遍的開源緩存系統->Redis已經有了對於集羣的支持,也能夠作單機的應用來使用;而memcache自己仍是一個單機的應用,在使用時->集羣->常見的是採用一致性哈希的方式
- 使用緩存的場景
- 使用緩存來下降對底層存儲的讀壓力,須要注意緩存和數據存儲中數據一致性問題
- 應用 <---> 緩存 <---> 存儲
- 這種方式,應用是不直接操做存儲,存儲由緩存控制;對於緩存來講,須要保證數據寫入緩存後可以存入存儲中,因此緩存自己的邏輯會複雜些,須要有不少操做日誌及故障恢復等
- 另外一種方式,應用直接與緩存和存儲進行交互。通常的作法是應用在寫數據時更新存儲,而後失效緩存數據;而在讀數據時首先讀緩存,若是緩存中沒有數據,那麼再去讀存儲,而且把數據寫入緩存
- 第三種方式,對於全數據緩存比較合適,即當存儲的數據變化時,直接從存儲去同步數據到緩存中,以更新緩存數據
- 另外一個重要場景是對於Web應用的頁面渲染內容的緩存
- 具體的實現技術爲ESI(Edge Side Includes)->經過在返回的頁面中加上特殊的標籤,而後根據標籤的內容去用緩存進行填充的一個過程.
- 處理ESI標籤的具體工做能夠放在Java的應用容器中作,也能夠放在Java應用容器前置的服務器作
- 搜索系統
- 站內搜索->網站的數據量和訪問量很小時,一些數據的查詢能夠直接用數據庫的Like操做來實現->實現效率低->不智能
- 爬蟲問題->根據數據變化來更新索引
- 倒排索引
- 查詢預處理
- 相關度計算
- 數據計算支撐
- 離線計算、實時計算
- 離線計算
- 把業務數據從在線存儲中移動到離線存儲中,而後進行數據處理的過程
- Google MapReduce模型
- Map階段:根據設定的規則把總體數據集映射給不一樣的Worker來處理而且生成各自的處理結果
- Reduce階段:對前面處理過的數據進行聚合,造成最後的結果
- [Hadoop]是MapReduce的一個開源實現.Hadoop使用HDFS進行數據存儲,而[Spark]則提供了基於內存的集羣計算的支持
- 在線計算
- 發佈系統
- 分發應用->須要提供自動高效而且容易操做的機制來把通過測試的程序包分發到線上應用->通常採用Web的操做方式
- 啓動校驗
- 應用重啓啓動後,須要進行校驗從而完成這臺應用服務器上的應用發佈->對應用的校驗一般是應用自身提供一個檢測腳本或者頁面,發佈系統執行這個腳本或者訪問頁面後來判斷返回的結果
- 中止應用時,須要優雅的關閉->須要在關閉應用前把這個應用從負載均衡或者軟負載中心上移除
- 灰度發佈
- 會對新應用進行分批發布,逐步擴大新應用在整個集羣中的比例直至最後所有完成->這裏講的灰度發佈主要是針對新應用在用戶體驗方面徹底感知不到的更新.
- 產品改版Beta
- 應用監控系統
- 可以及時瞭解應用的運行情況並可以進行相應的控制
- 監視和控制量部分
- 數據監視維度
- 系統數據和應用自身的數據->系統數據指的就是當前應用運行的系統環境的信息,如CPU使用率、內存使用狀況、交換分區使用狀況、當前系統負載、IO狀況等;而應用自身的數據,則是不一樣應用有不一樣的數據,通常會是調用次數、成功率、響應時間、異常數量等維度的數據
- 數據記錄方式
- 系統自身的數據已經被記錄到了本地磁盤,應用的數據通常也是存放在應用自身的目錄中,便於採集->也有直接把應用日誌經過網絡發送到採集服務器的狀況,能夠減輕本地寫日誌的壓力
- 對於應用數據的記錄,會考慮用定時統計的方式記錄一些量很大的信息.如對於一個提供服務的應用,在沒有特別需求時,並不直接記錄每次調用的信息,而是會記錄一段時間如5s或者一個間隔時間內的總調用次數、總響應時間這樣寫信息,而對於異常信息則每次都會予以記錄;採用統計的方式是爲了減少記錄的大小以及對本地磁盤的寫入壓力
- 數據採集的方式
- 採集方式有應用服務器主動對同給監控中心以及等待監控中心來拉取兩種方式->前者控制權在應用服務器上,可能出現的問題是應用服務器推送的壓力超過採集的中心服務器的能力,會形成重試等額外開銷而且須要應用服務器上的推送程序控制重試邏輯和當前傳送位置等信息->後者把複雜性都放在中心採集服務器上處理,使得應用服務器中支持數據採集的部分變的簡單
- 展示與告警
- 提供圖表的形式能夠提供Web頁面的展現->經過手機應用來接收報警->比短信方式好
- 控制
- 應用啓動後在運行期對於應用的行爲改變->對於應用的運維,最低的要求是出現問題時能夠經過重啓應用解決,可是咱們仍是須要更加精細化的控制應用-下降和一些切換。降級是咱們遇到大量請求且不能擴容的狀況時所進行的功能限制的行爲->而切換更多的是當依賴的下層系統出現故障而且須要手工進行切換時的一個管理,這些控制通常都是經過開關,參數設置來完成
- 依賴管理系統
- 隨着網站功能增多,應用的個數迅速增長,應用之間的關係也會愈來愈複雜,理清這些依賴關係並可以管理這些依賴會很是重要
- 一個應用在完成某個功能時到底須要依賴哪些外部系統、這些依賴中哪些是必要依賴,強依賴(登錄驗證用戶名和密碼),哪些是有了更好沒有也能夠的依賴,弱依賴(如記錄登錄時間和ip等)
- 動態檢測和靜態檢測->動態檢測的主要檢查方式是模擬被調用系統不可用和響應慢的兩種狀況
- Google#Dapper,A Large-Scale Distributed Systems Tracing Infrastructure->traceId,index->造成一個調用時序圖
- 多機房問題分析
- 同城機房和異地機房
- 同城多個機房中,對於重要的應用系統,會在不止一個機房中部署;而對於數據庫系統,則會把主備放在不一樣機房->儘可能避免沒必要要的跨機房的內部系統調用
- 爲了數據安全,把產生的業務數據都同步到異地的機房->把一些對數據延遲不敏感的系統部署到異地,如只讀系統.
- 系統容量規劃
- 咱們應該知道的信息就是整個系統的容量以及運行時所處的水位->咱們把某個應用系統集羣可以提供的併發能力和當前的壓力比做一個水桶的容量和水位->那麼準確知道各個系統的容量和當前高峯時的水位是一件很重要的事情->由於咱們仍是但願優先經過擴大容量來支持更多的請求而不是首選降級的方案.
- 考慮過去的增加狀況並結合人爲的判斷
- 弄清楚當前系統高峯期的水位
- 弄清楚當前各個系統的容量
- 設置警惕值,高峯水位搞過警惕值就增長容量,保持高峯的水位是低於警惕值的
- 內部私有云
總結:
能夠將一些設計思路、方法等應用到遊戲服務器總體架構設計當中(主要是登錄、支付等http服務).nginx