本文但願從技術角度來探討下微服務,所以,不會過多地談及如何根據業務進行微服務劃分,更可能是介紹微服務的相關技術,微服務的業務劃分方法可參考「領域驅動設計「相關方法論。html
複雜的單體架構會有如下的挑戰:前端
(1)項目啓動初期,須要尋找一個能儘可能涵蓋全部需求的開發語言,技術選型難度高;java
(2)工程龐大,組件、中間件繁多,編譯時間長;開發環境複雜,須要安裝大量的輔助軟件,環境準備時間長;python
(3)團隊無效溝通多,溝通成本高;web
(4)部署環境依賴大,某個組件的問題可能致使整個系統沒法運行;ajax
(5)新功能添加或者bug修復的時候,會影響現有功能,引起新的(未知)問題,添加單元測試難度大;docker
(6)版本回滾顆粒度大,靈活性差。數據庫
以上幾點都是實際項目中遇到的問題,若是你也遇到了一樣的問題,那麼服務化是較好的解決方案。json
服務化解耦後:後端
(1)微服務能夠根據自身業務特徵選擇合適的開發語言或數據庫;
(2)微服務的開發者只須要安裝該服務相關的輔助軟件;
(3)溝通多集中在微服務團隊中,與周邊(或公共)微服務有交集時才產生相應的溝通;
(4)部署環境依賴小,某個微服務部署失敗僅影響該微服務(或周邊幾個微服務);
(5)功能調整,若是接口沒有調整,基本不會影響其它微服務,添加單元測試、接口測試難度低,自動化(迴歸)測試覆蓋率高;
(6)版本回歸最小單位爲某個微服務,顆粒度小,可更好地實現藍綠部署、A/B測試、灰度(金絲雀)發佈。
容器(docker)具備輕量、環境依賴低、啓動速度快等特色;
虛擬化技術(openstack)負責IaaS層(存儲、計算、網絡)資源的調度;
容器治理平臺(Kubernetes、docker swarm)配合資源監控對容器進行靈活調度;
以上3種技術極大地提升了微服務的橫向(彈性)伸縮以及高可用的能力,使微服務具有更好的高併發處理能力。
配合DevOps,CI/CD等工具及技術提高了團隊快速響應、持續交付的能力。
我認爲,團隊應該基於產品或項目實際狀況選擇合適的微服務程度。
我認爲,當前使用先後端分離的開發模式仍是十分有好處的,關於先後端分離的描述,可參考我以前的《淺談開發模式及架構發展》。
Web A/B/C/...是幾個純前端項目,能夠根據實際狀況在不一樣項目中使用Angularjs、Vuejs或Reactjs等框架進行開發;
API X/Y/Z/...是幾個API項目,供Web或者App調用,能夠根據實際狀況使用.Net Core、Java或python等語言進行開發;
也能夠根據帶寬或性能須要,讓Web或API啓動多份示例。
基本交互:
瀏覽器通過網關從服務端獲取網站的html及js(橙色箭頭);
Web經過url或ajax通過網關訪問服務端API,App經過類Http Client方式通過網關訪問服務端API(灰色箭頭);
API X/Y/Z/...註冊到服務中心(藍色箭頭);
Web A/B/C/...、API X/Y/Z/...從配置中心讀取各自的配置(紫色箭頭);
API X經過服務中心調用API Z(綠色箭頭)。
所以,微服務的三個基礎組成部分分別是服務註冊發現,配置管理以及網關。
我認爲最簡單的服務註冊發現是直接經過IP端口進行訪問,這種方式適用於單個實例的服務,但若是API Y是多個實例,那麼須要藉助相似虛擬IP(VIP)等技術。
API Y實例1/2/.../n啓動時,會把本身的信息註冊到服務中心(自上報);API X須要調用API Y,會先從服務中心中獲取API Y服務實例的IP端口列表;而後根據特定的策略(隨機,網絡狀況,權重等)篩選出一個實例進行調用,負載均衡是在客戶端(調用方)實現的。
這種方式的典型表明是Spring Cloud Eureka,若是服務中心down掉了,那麼會影響整個系統,所以,要保證服務中心的高可用;另外,須要有特定的jdk/sdk和服務中心進行交互,如Java的FeignClient(集成了ribbon實現服務的負載均衡),steeltoe的DiscoveryHttpClientHandler(隨機選擇實現服務的負載均衡),有必定的語言侵入性。
API Y實例1/2/.../n部署啓動時,治理平臺會給它們分配IP端口,並記錄在服務中心;API X須要調用API Y,會基於dns,經過API Y的服務名或集羣 IP(Cluster IP,相似於Virtual IP)加端口進行訪問。負載均衡由治理平臺負責,是在服務端(平臺)實現的。
這種方式的典型表明是docker swarm以及Kubernetes,服務註冊發現的高可用由平臺保證,由於基於dns,普通的http客戶端就能夠進行Api訪問,如java的restTemplate或C#的HttpClient,無語言侵入性,但負載均衡的靈活性比中間件的方式稍微低一些。
最簡單的配置管理就是平時經常使用的配置管理,如java的application.properties、.net的web.config、.net core的appsettings.json等,基本是和應用程序一塊兒,可以兼容多個環境(開發、測試、生產)。
但當咱們的程序須要啓用多份的時候,這種簡單的配置管理方式遇到了挑戰,配置的更新須要手動更新各個實例的配置文件,繁瑣且容易出錯(遺漏、修改錯誤或環境依賴)。
這也是微服務中面臨的一個主要挑戰。
這種方式的典型表明是Spring Cloud Config Server。
API X、Y...會經過Url訪問配置中心,經過心跳(2s)來確認配置中心的健康以及檢測配置內容的更新。
其中,application.yaml用於保存各個微服務的公共配置,{服務名}.yaml用於保存微服務的私有配置。
和Eureka同樣,使用者須要本身保證Config Server的高可用,不然,配置中心down掉的話,整個系統的配置信息就會亂套;另外,也須要有特定的jdk/sdk和配置中心進行交互;配置文件的格式基本也限制於yaml格式。
這種方式的典型表明是Kubernetes ConfigMap。
部署、升級、增長API X、Y...實例時,Kubernetes會按照設置,把對應的配置文件放置到容器(docker)指定的位置,也能夠是環境變量。
配置中心的高可用由治理平臺保證,微服務不須要使用特定的jdk/sdk和配置中心交互,只須要解析本地路徑的某些文件,文件格式能夠根據須要選擇(json,xml,yaml,properties)。
微服務公共配置與私有配置也能夠實現,但須要語言支持,好比.net core,詳細的能夠參考我以前的文章《你可能不知道的.Net Core Configuration》。
網關做爲微服務的統一出口,通常須要完成如下任務:反向代理,跨域處理,負載均衡,流量控制,緩存,日誌,公共功能(如認證)等,經常使用的網關中間件有Nginx,Spring Cloud Zuul,Kong,Ocelot等。
或許有人會問,像公共功能(如認證)這些,在過濾器(filter)裏作就行了啊,爲何要在網關作,沒看出什麼優點。I think it is a good call.
確實,像認證這些功能的確能夠在過濾器裏作,可是,若是過濾器須要升級,那麼每一個微服務都要進行升級;另一種狀況是,若是微服務是使用不一樣語言編寫的,那麼還須要提供多個版本的filter;更爲惡劣的多是該語言不支持filter,或者像單點登陸這些公共模塊沒有提供該語言的jdk或sdk;還有一種比較特殊的狀況是,可能在不一樣的環境系統須要有不一樣的認證機制,如對接第三方的認證系統。使用網關就能比較好的解決以上問題。
既然能夠經過部署一個網關,讓全部請求都通過它來實現一些公共的功能,那麼有沒有可能使微服務的請求通過一個特定的「層」,來實現一些特定的功能(如調用鏈、熔斷,服務調用認證,請求限制等)呢?答案是確定的。
我認爲Kubernetes其中一個強大的設計是,它的最小單位是pod,而不是容器(container);一個pod裏面能夠有多個容器,並且它們能夠共享網絡,共享存儲。
能夠經過在pod裏面部署一個業務容器,同時也部署一個小型的sidecar容器,讓請求到達業務容器以前,先通過sidecar容器(起到了filter的做用),在sidecar容器中實現調用鏈、熔斷,服務調用認證,請求限制等功能,這樣就能夠經過基於部署的方式解決語言限制的問題。
目前能夠選擇Istio或Linkerd來實現上述效果。
我認爲,從架構的層面來看微服務架構,應該是這樣的:
擴展性:下降複雜系統的耦合度、溝通成本以及系統複雜度,需求快速響應;
伸縮性:能夠經過增長資源的方式來快速應對海量併發(僅僅是併發層面,大數據量仍是須要根據業務進行分片或分割);
穩定性:微服務治理平臺,PaaS平臺保證了系統的高可用性,能夠下降業務的中斷時間;
安全性:和傳統架構的要求差異不大,可是因爲網關和網格(Service Mesh)的存在,使得安全處理,APM等的實現更加簡單。
另外,我認爲微服務能夠經過部署的方式來實現功能或模塊的複用,必定程度上代替了過往經過jdk/sdk來實現共用的方式;使得開發更加靈活,也使得開發能夠更加關注於業務,而非各類邊邊角角的公共(輪子)功能。