通常而言中間件和框架的區別是,中間件是獨立運行的用於處理某項專門業務的CS程序,會有配套的客戶端和服務端,框架雖然也是處理某個專門業務的可是它不是獨立程序,是寄宿在宿主程序進程內的一套類庫。
git
圖上綠色部分表明了框架,紅色部分表明了管理系統,紫色部分表明了中間件。本文會着重介紹管理系統和中間件部分。github
比較知名的分佈式配置服務和管理系統有攜程的github.com/ctripcorp/a…(上圖)以及github.com/knightliao/…。對於比較大型的互聯網項目來講,由於業務繁雜,需求多變,每每各類系統都會有大量的配置,覆蓋幾個方面:算法
· 針對系統內部技術層面的各類配置,各類池的大小、 隊列的大小、日誌級別、各類路徑、批次大小、處理間隔、重試次數、超時時間等。數據庫
· 針對業務運營層面的各類配置,活動的週期獎勵、黑白名單、彈窗、廣告位等。後端
· 針對運維和發佈層面的配置,灰度名單、註冊中心地址、數據庫地址、緩存地址、MQ地址等。緩存
由於一些基礎組件好比SOA框架和發佈系統也會用到配置,這個時候就會可能會有雞生蛋的問題,這裏我比較建議把配置系統做爲最最底層的系統,其它服務均可以依賴配置系統。通常而言配置管理除了實現最基本的Key-Value的配置讀取和配置以外,還會有下面的一些特性和功能:安全
· 高性能。配置服務的壓力會是很是嚇人的,在一次服務調用中可能就會有幾十次的配置調用,若是服務的總體QPS在500那麼配置服務的壓力可能在1萬的QPS,這樣的QPS不走緩存基本是不可能的。好在即便是1萬甚至是5萬的QPS也不算一個很誇張的沒法解決的壓力。服務器
· 高可用。如今各類開源的配置服務是所謂的分佈式配置服務,由可擴展的配置服務集羣來承擔負載均衡和高可用功能,配置服務一旦掛了可能會讓系統癱瘓。你可能會說配置服務通常本地會有緩存,會有本地的配置文件做爲後備,會有默認值,可是由於配置是運營運維在實時修改的,若是某個業務的配置沒有使用最新的配置走的是錯誤的默認值的話,系統會處於徹底混亂的狀態,因此配置服務的穩定性過重要了。網絡
· 樹形的配置體系。若是隻是把全部配置堆在一個列表裏,加上項目和分類的話,當配置多達幾千項的時候仍是會有點多。能夠支持樹形的層級配置,不拘泥於項目和分類這兩個條件。項目下能夠有模塊,模塊下能夠有分類,分類下能夠有小類,根據本身的需求動態構建配置樹。架構
· 好用的客戶端。好比能夠和SpringBoot以及@Value註解結合起來,非侵入整合配置系統,無需任何代碼的改動。
· 毫秒級粒度的修改實時生效。可使用長鏈接推的方式實現,也能夠實現緩存失效的方式實現。
· 配置的分層隔離。包括按照環境、集羣和項目來提供多套配置相互獨立不影響,包括能夠以層級的方式作配置繼承。
· 配置的權限控制。不一樣類型、環境、集羣、項目的配置具備不一樣的管理權限,好比脫敏只讀、只讀、讀寫、導出。
· 配置的版本管理。配置的每一次修改都是一個版本,能夠爲單獨的配置或項目進行直接版本回滾。
· 豐富的Value形式。配置的Value若是要保存列表的話,保存一個JSON閱讀和修改都不方便,能夠直接提供List方式的Value,在後臺能夠單獨增刪改裏面的一項。在好比黑名單的引用上這種方式比較高效,不然更新一個名單每次都要修改整個黑名單。這個功能能夠和Redis結合在一塊兒進行實現。Value除了支持字符串能夠是JSON和XML形式,系統能夠對格式進行格式化,對格式進行校驗。Value也能夠是非字符串類型的各類數字格式,系統也會根據類型進行校驗。
· 豐富的配置發佈生效形式。好比能夠天然生效、當即生效以及定時生效。定時生效的功能適合於在某個時間點須要開啓某個配置,好比用於面向用戶的推送、活動業務。還有支持灰度自動發佈,以必定的時間間隔來對集羣裏的實例進行發佈,避免人工去按期逐一發布單臺的麻煩。
· 審覈審計功能。配置的修改能夠由管理員進行審覈(也就是修改和發佈的權限支持分離),避免配置錯誤修改。全部配置的修改記錄能夠查詢到誰何時由於什麼緣由修改了什麼配置,過後能夠審計審查。
· 配置生效跟蹤和使用率跟蹤。能夠看到每個配置項如今哪些客戶端在使用,生效的值的版本是哪一個。經過這個功能還能夠排查如今系統中過去一段時間從沒有用過的配置,刪除無用的配置。
· 動態配置。在API設計的時候咱們引入上下文的概念,經過傳入一個Map字典做爲上下文,好比某個配置按照不一樣的用戶類型、城市須要有不一樣的值,這個邏輯咱們能夠不須要在代碼裏面手工編寫,直接經過在後臺配置上下文的匹配策略來動態讀取到不一樣的配置值。
· 本地快照。對配置進行快照本地保存,在出現故障沒法鏈接服務端的時候使用本地的配置。
這裏能夠看到要實現一個功能完善的配置系統工做量仍是至關大的,一個優秀的功能強大的配置系統能夠節省不少開發的工做量,由於可配置部分的功能基本就是由配置系統直接實現了,無需在數據庫中在搞大量的XXConfig表(不誇張的說,不少業務系統40%的工做量在這個上面,不但須要作這些配置表還須要配以配置後臺)。
微服務的建設中實現遠程調用只是實現了20%的工做量(可是確實知足了80%的需求)。服務管理治理這塊有大量的工做要作。這也就是實現本身RPC框架的好處,這是第一步,有了這第一步讓數據流過咱們本身的框架之後咱們接能夠作更多的事情,好比:
· 調用鏈跟蹤。可否記錄整個調用的狀況,而且查看這個調用鏈。下面一節會再說一下這點。
· 註冊管理。查看服務的註冊狀況,服務手動上線下線,集羣切換,壓力分配干預。
· 配置管理。配置服務端客戶端線程池和隊列的配置,超時配置等等。固然,這個也能夠在配置系統中進行。
· 運維層面的管理。查看和管理方法熔斷,進行併發限流配置,服務權限黑白名單配置,安全方面的配置(信息加密,日誌脫敏等)。
· Service Store的概念。服務發佈須要知足必定要求,有文檔(好比能夠經過註解方式在代碼註釋裏提供),有信息(開發負責人、運維負責人,服務類型,提供的能力),知足要求後就能夠以相似於蘋果App Store發佈程序的方式發佈服務,這樣咱們就能夠在統一的平臺上查看服務的維護信息和文檔。
· 版本控制調用統計。對服務進行灰度升級,按版本路由,不一樣版本調用分析等等。相似於一些應用統計平臺提供的功能(友盟、TalkingData)。
這裏我想說的理念是,服務能調用通是第一步,隨着服務數量變多,部署方式的複雜化,依賴關係複雜化,版本的迭代,API的變動,開發人員和架構師其實急需有一套地圖可以對服務能力的全貌進行總體的瞭解,運維也須要有系統能對服務進行觀察和調配。服務治理的部分徹底能夠以iOS那套(開發符合時候須要符合標準+發佈的時候須要有流程)方式來運做。
開源的實現有github.com/dianping/ca…以及github.com/naver/pinpo…(上圖)等等。對於微服務比較多的(主流程涉及8+微服務)系統,若是沒有服務的全鏈路調用跟蹤那麼排查故障以及性能問題就會很困難了。通常完善的全鏈路監控體系不只僅覆蓋微服務,並且功能也會更豐富,實現下面的功能:
· 以Log、Agent、Proxy或整合進框架的方式實現,儘量少的侵入的狀況下實現數據的收集。並且確保數據的收集不會影響到主業務,收集服務端宕機的狀況下業務不影響。
· 調用跟蹤。涉及到服務調用、緩存調用、數據庫調用,MQ調用,不只僅能夠以樹的形式呈現每次調用的類型、耗時、結果,還能夠呈現完整的根,也就是對於網站請求呈現出請求的完整信息,對於Job任務呈現出Job的信息。
· JVM的信息(好比對於Java)。呈現每個進程JVM層次的GC、Threads、Memory、CPU的使用狀況。能夠進行遠程Stack的查看和Heap的快照(沒有進程的內存信息,不少時候基於服務器層面粗粒度的資源使用狀況的監控,基本不可能分析出根本緣由),而且能夠設定策略進行按期的快照。虛擬機的信息查看和調用跟蹤甚至能夠經過快照進行關聯,在出現問題的時候可以瞭解當時虛擬機的狀態對於排查問題是很是有好處的。
· 依賴關係一覽。有的時候咱們作架構方案,第一步就是梳理模塊和服務之間的依賴關係,只有這樣咱們才能肯定影響範圍重構範圍,對於微服務作的比較複雜的項目來講,每一個人可能只是關注本身服務的上下游,對於上游的上游和下游的下游徹底不清楚,致使公司也沒有人能夠說的清楚架構的全貌。這個時候咱們有全鏈路跟蹤系統的話,能夠對經過分析過去的調用來繪製出一張依賴關係的架構圖。這個圖若是對QPS作一些熱點的話,還能夠幫助咱們作一些運維層面的容量規劃。
· 高級分析建議。好比在全鏈路壓測後定位分析瓶頸所在。定時分析全部組件的執行性能,得出性能衰退的趨勢,提前進行問題預警。分析JVM的線程和GC狀況,輔助定位High CPU和Memory Leak的問題。退一萬步說,即便沒有這樣的自動化的高級分析,有了調用跟蹤的圖和組件依賴關係圖,至少在出問題的時候咱們人能分析出來咋回事。
· Dashboard。非必須,只要數據收集足夠全面,如以前文章所示,咱們能夠用Grafana來進行各類個性化的圖表配置。
開源的實現有C實現的github.com/Qihoo360/At…以及Go實現的github.com/flike/kings… 等。數據訪問中間件是獨立部署的數據庫的透明代理,自己須要是以集羣方式支持高可用,背後還須要對接多套數據庫做爲一個集羣,通常而言會提供以下的功能:
· 最經常使用的功能就是讀寫分離。也包括負載均衡和故障轉移的功能,自動在多個從庫作負載均衡,經過可用性探測,在主庫出現故障的時候配合數據庫的高可用和複製作主庫的切換。
· 隨着數據量的增多須要分片功能。分片也就是Sharding,把數據按照必定的維度均勻分散到不一樣的表,而後把表分佈在多個物理數據庫中,實現壓力的分散。這裏寫入的Sharding通常而言沒有太多的差別,可是讀取方面由於涉及到歸併彙總的過程,若是要實現複雜功能的話仍是比較麻煩的。因爲分片的維度每每可能有多個,這方面能夠採用多寫多個維度的底層表來實現也能夠採用維度索引表方式來實現。
· 其它一些運維方面的功能。好比客戶端權限控制,黑白名單,限流,超時熔斷,和調用鏈搭配起來的調用跟蹤,全量操做的審計搜索,數據遷移輔助等等。
更高級的話能夠實現SQL的優化功能。隨時進行SQL的Profiler,而後達到必定閾值後提供索引優化建議。相似github.com/Meituan-Dia…。
· 其它。極少的代理實現了分佈式事務的功能(XA)。還能夠實現代理層面的分佈式悲觀鎖的功能。其實細想一下,SQL由於並非直接扔到數據庫執行,這裏的可能性就太多了,想幹啥均可以。
實現上通常須要作下面幾件事情:
· 有一個高性能的網絡模型,通常基於高性能的網絡框架實現,畢竟Proxy的網絡方面的性能不能成爲瓶頸。
· 有一個MySQL協議的解析器,開源實現不少,拿過來直接用便可。
· 有一個SQL語法的解析器,Sharding以及讀寫分離免不了須要解析SQL,通常流程爲SQL解析、查詢優化、SQL路由、SQL重寫,在把SQL提交到多臺數據庫執行後進行結果歸併。
· Proxy自己最好是無狀態節點,以集羣方式實現高可用。
這些功能除了Proxy方式的實現還有和數據訪問標準結合起來的實現,好比改寫JDBC的框架方式實現,兩種實現方式各有優缺點。框架方式的實現不侷限於數據庫類型,性能略高,Proxy方式的實現支持任意的語言更透明,功能也能夠作的更強大一些。最近還出現了邊車Sidecard方式實現的理念,相似於ServiceMesh的概念,網上有一些資料,可是這種方式到目前爲止還沒看到成熟的實現。
相似於數據庫的Proxy,這裏是以緩存服務做爲後端,提供一些集羣化的功能。好比以Redis爲後端的開源的實現有github.com/CodisLabs/c…以及餓了麼的github.com/eleme/corvu… 等等。其實不採用Proxy方式作,開發一個緩存客戶端在框架層面作也是徹底能夠的,可是以前也說了這兩種方式各有優劣。代理方式的話更透明,若是有Java、Python、Go都須要連接Redis,咱們無需開發多套客戶端了。通常實現下面的功能:
· 分佈式。這是最基本的,經過各類算法把Key分散到各個節點,提供必定的容量規劃和容量報警功能。
· 高可用。配合Redis的一些高可用方案實現必定程度的高可用。
· 運維方面的功能。好比客戶端權限控制,黑白名單,限流,超時熔斷,全量操做的審計搜索,數據遷移輔助等等。
· 跟蹤和問題分析。配合全鏈路監控實現一體化的緩存訪問跟蹤。以及更智能的分析使用的狀況,結合緩存的命中率,Value的大小,壓力平衡性提供一些優化建議和報警,儘早發現問題,緩存的崩盤每每是有前兆的。
· 完善的管理後臺,能夠一覽集羣的用量、性能,以及作容量規劃和遷移方案。
若是Redis集羣特別大的話的確是有一套的本身的Proxy體系會更方便,小型項目通常用不到。
以前有提升過,Job是我認爲的互聯網架構體系中三馬車的三分之一,扮演了重要的角色。開源實現有elasticjob.io/。Job的管理的實現有兩種方式,一種是相似於框架的方式,也就是Job的進程是一直啓動着的,由框架在合適的時候調用方法去執行。一種是相似於外部服務的方式,也就是Job的進程是按須要在合適的機器啓動的。在本文一開始的圖中,我畫了一個任務調度的中間件,對於後一種方式的實現,咱們須要有一套中間件或獨立的服務來複雜Job進程的拉起。整個過程以下:
· 找一些機器加入集羣做爲咱們的底層服務器資源。
· Job編譯後打包部署到統一的地方。Job能夠是各個語言實現的,這沒有關係。能夠是裸程序,也可使用Docker來實現。
· 在容許Job前咱們須要對資源進行分配,估算一下Job大概須要怎麼樣的資源,而後根據執行頻次統一計算得出一個合適的資源分配。
· 由中間件根據每個Job的時間配置在合適的時候把進程(或Docker)拉起執行,執行前根據當前的狀況計算分配一個合適的機器,完成後釋放資源,下一次執行不必定在同一臺機器執行。
這樣的中間件是更底層的一套服務,通常而言任務框架會提供以下的功能:
· 分佈式。Job不會受限於單機,能夠由集羣來提供運行支持,能夠隨着壓力的上升進行集羣擴容,任何一臺機器的宕機不會成爲問題。若是咱們採用中間件方式的話,這個功能由底層的中間件來支持了。
· API層面提供豐富的Job執行方式。好比任務式的Job,拉數據和處理分開的Job。拉數據和處理分開的話,咱們能夠對數據的處理進行分片執行,實現相似Map-Reduce的效果。
· 執行依賴。咱們能夠配置Job的依賴關係實現自動化的Job執行流程分析。業務只管實現拆散的業務Job,Job的編排經過規則由框架分析出來。
· 整合到全鏈路監控體系的監控跟蹤。
· 豐富的管理後臺,提供統一的執行時間、數據取量配置,提供Job執行狀態和依賴分析一覽,查看執行歷史,運行、暫停、中止Job等等管理功能。
發佈管理其實和開發沒有太大的關聯,可是我以爲這也是整個體系閉環中的一個環節。發佈管理可使用Jenkins等開源實現,在後期可能仍是須要有本身的發佈系統。能夠基於Jenkins再包一層,也能夠如最開始的圖所示,直接基於通用的任務調度中間件實現底層的部署。通常而言,發佈管理有下面的功能:
· 豐富的任務類型和插件,支持各類語言程序的構建和發佈。有最基本的發佈、回滾、重啓、中止功能。
· 支持項目的依賴關係設置,實現自動化的依賴路徑上的程序自動發佈。
· 一些運維層面的控制。好比和CMDB結合作權限控制,作發佈窗口控制。
· 用於集羣的發佈流程。好比能夠一覽集羣的分組,設置自動的灰度發佈方案。
· 適合本身公司的發佈流程。好比在流程控制上,咱們是Dev環境到QA到Stage到Live。其中,QA環境通過QA的確認後能夠進入Stage環境,通過開發主管的確認後能夠到Stage環境,通過產品經理的確認後能夠進入Live環境進行發佈。在發佈系統上咱們能夠結合OA作好這個流程的控制。
· 在構建的時候,集成單元測試,集成編碼規範檢查等等,在後臺能夠方便的看到每一次發佈的代碼變動,測試執行狀況以及代碼規範違例。
Jenkins等系統在對於1和2作的比較好,對於和公司層面其它系統的結合無能力爲,每每處於這個緣由咱們須要在Jenkins之上包裝出來本身的發佈系統。
總結一下,之因此標題說不斷耕耘的基礎中間件,是指中間件也好框架也好,每每也須要一個小團隊來獨立維護,並且功能是不斷迭代增長,這套體系若是結合的好,就不只僅是實現功能這個最基本的標準了,而是:
· 運維自動化API化和AI化的很重要的構成。把控是由於咱們掌握了數據流,數據都是從咱們的中間件穿越過去到達底層的服務、數據庫、緩存,有了把控就有了自動化的可能,有了智能監控一體化報警的可能。
· 也由於數據流的通過,經過對數據進行分析,咱們能夠給到開發不少建議,咱們能夠在這上面作不少標準。這些事情均可以由框架架構團隊默默去作,不須要業務研發的配合。
· 由於底層數據源的屏蔽,加上服務框架一塊兒,咱們實現的是業務系統被框架包圍而不是業務系統在使用框架和中間件這麼一個形態,那麼對於公司層面的一些大型架構改造,好比多活架構,咱們能夠實現業務系統的改造最小。數據+服務+流程都已經被中間件所包圍和感知,業務系統只是在實現業務功能而已,咱們能夠在業務系統無感知的狀況下對數據作動態路由,對服務作動態調用,對流程作動態控制。以下圖,是否是有點Mesh的意思?
本文不少地方基於思考和YY,開源組件要實現這個理念須要有大量的修改和整合,不少大公司內部都必定程度作了這些事情,可是也由於框架的各類粘連依賴沒法完全開源,這塊工做要作好須要大量的時間精力,真的須要不斷耕耘和沉澱才能發展出適合本身公司技術棧的各類中間件和管理系統體系。