【編者的話】隨着公司業務量的飛速發展,平臺面臨的挑戰已經遠遠大於業務,需求量不斷增長,技術人員數量增長,面臨的複雜度也大大增長。在這個背景下,平臺的技術架構也完成了從傳統的單體應用到微服務化的演進。html
這是平臺最開始的狀況,當時流量小,爲了節約成本,並將全部應用都打包放到一個應用裏面,採用的架構爲 .NET SQL Server:前端
表示層:位於最外層(最上層),最接近用戶。用於顯示數據和接收用戶輸入的數 據,爲用戶提供一種交互式操做的界面,平臺所使用的是基於.NET的Web形式。
業務邏輯層(Business Logic Layer):無疑是系統架構中體現核心價值的部分。它的關注點主要集中在業務規則的制定、業務流程的實現等與業務需求有關的系統設計,也便是說它是與系統所應對的領域(Domain)邏輯有關,不少時候,也將業務邏輯層稱爲領域層。 業務邏輯層在體系架構中的位置很關鍵,它處於數據訪問層與表示層中間,起到了數據交換中承上啓下的做用。因爲層是一種弱耦合結構,層與層之間的依賴是向下的,底層對於上層而言是「無知」的,改變上層的設計對於其調用的底層而言沒有任何影響。若是在分層設計時,遵循了面向接口設計的思想,那麼這種向下的依賴也應該是一種弱依賴關係。對於數據訪問層而言,它是調用者;對於表示層而言,它倒是被調用者。
數據訪問層:有時候也稱爲是持久層,其功能主要是負責數據庫的訪問,能夠訪問數據庫系統、二進制文件、文本文檔或是XML文檔,平臺在這個階段使用的是 hibernate.net sqlserver。
第一代架構看似很簡單,卻支撐了平臺的早期業務發展,知足了網站用戶訪問量在幾萬規模的處理需求。可是當用戶訪問量呈現大規模增加,問題就暴露出來了:nginx
爲了解決第一代架構面臨的問題,團隊制定了以下的策略,並造成了第二代應用架構(垂直應用架構)。git
能夠看到第二代架構解決應用級別的水平擴展擴展,通過優化後,該架構支撐了幾十萬用戶的訪問需求,在這一階段有部分應用已經使用 Java 完成了 MVC 架構的重寫。固然也存在一些問題。github
爲了解決第一代與第二代架構存在的問題,咱們對平臺進行了梳理優化。根據平臺業務須要以及對第一二代架構的總結,咱們肯定了第三代架構的核心需求:算法
並以此爲基礎進行了平臺的第三代架構的重構工做。sql
看第三代架構裏面的組成,主要分爲八個部分:docker
> 康威定律:任何組織在設計一套系統時,所交付的設計方案在結構上都與該組織的溝通結構保持一致。
數據庫
在實踐第三代架構時,咱們對團隊組織作了幾個調整:後端
再談談整個團隊的交付流程與開發模式,若是沒有預先定義好,則很難讓微服務架構發揮出真正的價值,下面咱們先來看看微服務架構的交付流程。
使用微服務架構開發應用程序,咱們其實是針對一個個微服務進行設計、開發、測試、部署,由於每一個服務之間是沒有彼此依賴的,大概的交付流程就像上圖這樣。
設計階段:
架構組將產品功能拆分爲若干微服務,爲每一個微服務設計 API 接口(例如 REST API),須要給出 API 文檔,包括 API 的名稱、版本、請求參數、響應結果、錯誤代碼等信息。
在開發階段,開發工程師去實現 API 接口,也包括完成 API 的單元測試工做,在此期間,前端工程師會並行開發 Web UI 部分,可根據 API 文檔造出一些假數據(咱們稱爲「mock 數據」),這樣一來,前端工程師就沒必要等待後端 API 所有開發完畢,才能開始本身的工做了,實現了先後端並行開發。
測試階段:
這一階段過程全自動化過程,開發人員提交代碼到代碼服務器,代碼服務器觸發持續集成構建、測試,若是測試經過則會自動經過Ansible腳本推送到模擬環境;在實踐中對於線上環境則是先要走審覈流程,經過以後才能推送到生產環境。提升工做效率,而且控制了部分可能由於測試不充分而致使的線上不穩定。
在以上交付流程中,開發、測試、部署這三個階段可能都會涉及到對代碼行爲的控制,咱們還須要制定相關開發模式,以確保多人可以良好地協做。
實踐"絞殺者模式":
因爲第三代架構跨度較大,而且面臨了沒法修改的.net遺留系統,咱們採用絞殺者模式,在遺留系統外面增長新的Proxy代理微服務,而且在LB控制upstream的方式,而不是直接修改原有系統,逐步的實現對老系統的替換。
開發規範:
經驗代表,咱們須要善用代碼版本控制系統,我曾經遇到一個開發團隊,因爲分支沒有規範,最後一個小版本上線合代碼竟然化了幾個小時,最後開發人員本身都不知道合到哪一個分支。拿 GitLab 來講,它很好地支持了多分支代碼版本,咱們須要利用這個特性來提升開發效率,上圖就是咱們目前的分支管理規範。
最穩定的代碼放在 master 分支上,咱們不要直接在 master 分支上提交代碼,只能在該分支上進行代碼合併操做,例如將其它分支的代碼合併到 Master 分支上。
咱們平常開發中的代碼須要從 master 分支拉一條 develop 分支出來,該分支全部人都能訪問,但通常狀況下,咱們也不會直接在該分支上提交代碼,代碼一樣是從其它分支合併到 develop 分支上去。
當咱們須要開發某個特性時,須要從 develop 分支拉出一條 feature 分支,例如 feature-1 與 feature-2,在這些分支上並行地開發具體特性。
當特性開發完畢後,咱們決定須要發佈某個版本了,此時須要從 develop 分支上拉出一條 release 分支,例如 release-1.0.0,並將須要發佈的特性從相關 feature 分支一同合併到 release 分支上,隨後將針對 release 分支推送到測試環境,測試工程師在該分支上作功能測試,開發工程師在該分支上修改 bug。待測試工程師沒法找到任何 bug 時,咱們可將該 release 分支部署到預發環境,再次驗證之後,均無任何 bug,此時可將 release 分支部署到生產環境。待上線完成後,將 release 分支上的代碼同時合併到 develop 分支與 master 分支,並在 master 分支上打一個 tag,例如 v1.0.0。
當生產環境發現 bug 時,咱們須要從對應的 tag 上(例如 v1.0.0)拉出一條 hotfix 分支(例如 hotfix-1.0.1),並在該分支上作 bug 修復。待 bug 徹底修復後,需將 hotfix 分支上的代碼同時合併到 develop 分支與 master 分支。
對於版本號咱們也有要求,格式爲:x.y.z,其中,x 用於有重大重構時纔會升級,y 用於有新的特性發布時纔會升級,z 用於修改了某個 bug 後纔會升級。針對每一個微服務,咱們都須要嚴格按照以上開發模式來執行。
咱們已經對微服務團隊的架構、交付流程、開發模式進行了描述,下面咱們聊聊概括一下微服務開發體系。
Martin Flower的定義:
In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies
簡單的說,微服務是軟件系統架構上的一個設計風格,它倡導將一個本來獨立的系統分紅多個小型服務,這些小型服務都在各自獨立的進程中運行,服務之間經過基於 HTTP 的 RESTful 輕量級 API 進行通訊協做。被拆分的每一個微服務圍繞系統中的某項或一些耦合度較高的業務進行構建,而且每一個服務都維護着自身的數據存儲、業務開發、自動化測試案例以及獨立部署機制。因爲有了輕量級通訊機制,這些微服務間可使用不通的語言來編寫。
微服務到底拆分到一個多大的粒度,更多時候是須要在粒度與團隊之間找到一個平衡點,微服務越小,微服務獨立性帶來的好處就越多。可是管理大量微服務也會越複雜。基本上拆分須要遵循如下幾個原則:
爲了搭建好微服務架構,技術選型是一個很是重要的階段,只有選擇合適的"演員",才能把這臺戲演好。
咱們使用 Spring Cloud 做爲微服務開發框架,Spring Boot 擁有嵌入式 Tomcat,可直接運行一個 jar 包來發布微服務,此外它還提供了一系列「開箱即用」的插件,例如:配置中心,服務註冊與發現,熔斷器,路由,代理,控制總線,一次性令牌,全局鎖,leader選舉,分佈式 會話,集羣狀態等,可大量提升咱們的開發效率。
工程結構規範
上圖是咱們實踐中每一個服務應該具備的項目組成結構。
其中:
API 網關實踐
API 網關做爲後端全部微服務和 API 的訪問入口, 對微服務和 API 進行審計,流控, 監控,計費等。經常使用的 API 網關解決方案有:
在以上的方案裏:
Consul 做爲狀態存儲或者說配置中心(主要使用 Consul 的 KV 存儲功能);Nginx 做爲 API 網關, 根據 Consul 中 Upstreams 的相關配置,動態分發流量到配置的 Upstreams 結點;
Nginx 根據配置項, 鏈接到 Consul 集羣;
啓動的 API 或者微服務實例, 經過手工/命令行/發佈部署平臺, 將實例信息註冊/寫入 Consul;
Nginx 獲取到相應的 Upstreams 信息更新, 則動態變動 Nginx 內部的 Upstreams 分發配置,從而將流量路由和分發到對應的 API 和微服務實例結點;
將以上註冊和發現邏輯經過腳本或者統一的發佈部署平臺固化後,就能夠實現透明的服務訪問和擴展。
鏈路監控實踐
咱們發現,之前在單應用下的日誌監控很簡單,在微服務架構下卻成爲了一個大問題,若是沒法跟蹤業務流,沒法定位問題,咱們將耗費大量的時間來查找和定位問題,在複雜的微服務交互關係中,咱們就會很是被動,此時分佈式鏈路監控應運而生,其核心就是調用鏈。經過一個全局的ID將分佈在各個服務節點上的同一次請求串聯起來,還原原有的調用關係、追蹤系統問題、分析調用數據、統計系統指標。
分佈式鏈路跟蹤最先見於 2010 年 Google 發表的一篇論文《 Dapper》。
那麼咱們先來看一下什麼是調用鏈,調用鏈其實就是將一次分佈式請求還原成調用鏈路。顯式的在後端查看一次分佈式請求的調用狀況,好比各個節點上的耗時、請求具體打到了哪臺機器上、每一個服務節點的請求狀態,等等。它能反映出一次請求中經歷了多少個服務以及服務層級等信息(好比你的系統 A 調用 B,B 調用 C,那麼此次請求的層級就是 3),若是你發現有些請求層級大於 10,那這個服務頗有可能須要優化了。
常見的解決方案有:
GitHub 地址:GitHub - naver/pinpoint: Pinpoint is an open source APM (Application Performance Management) tool for large-scale distributed systems written in Java.對 APM 有興趣的朋友都應該看看這個開源項目,這個是一個韓國團隊開源出來的,經過 JavaAgent 的機制來作字節碼代碼植入(探針),實現加入 traceid 和抓取性能數據的目的。 NewRelic、OneAPM 之類的工具在 Java 平臺上的性能分析也是相似的機制。
官網:OpenZipkin · A distributed tracing system這個是 Twitter 開源出來的,也是參考 Dapper 的體系來作的。
GitHub 地址:GitHub - openzipkin/zipkin: Zipkin is a distributed tracing system
Brave 的 GitHub 地址: https://github.com/openzipkin/brave這個組件經過實現一系列的 Java 攔截器,來作到對 http/servlet 請求、數據庫訪問的調用過程跟蹤。而後經過在 Spring 之類的配置文件里加入這些攔截器,完成對 Java 應用的性能數據採集。
GitHub 地址:GitHub - dianping/cat: Central Application Tracking這個是大衆點評開源出來的,實現的功能也仍是蠻豐富的,國內也有一些公司在用了。不過 CAT 實現跟蹤的手段,是要在代碼裏硬編碼寫一些「埋點」,也就是侵入式的。
前面三個工具裏面,若是不想重複造輪子,我推薦的順序依次是Pinpoint—>Zipkin—>CAT。緣由很簡單,就是這三個工具對於程序源代碼和配置文件的侵入性,是依次遞增的。
咱們的解決方案:
針對於微服務,咱們在 Spring Cloud 基礎上,對微服務架構進行了擴展,基於 Google Dapper 的概念,設計了一套基於微服務架構的分佈式跟蹤系統(WeAPM)。
如上圖所示,咱們能夠經過服務名、時間、日誌類型、方法名、異常級別、接口耗時等參數查詢響應的日誌。在獲得的TrackID能夠查詢到該請求的整個鏈路日誌,爲重現問題、分析日誌提供了極大方便。
斷路器實踐
在微服務架構中,咱們將系統拆分紅了一個個的微服務,這樣就有可能由於網絡緣由或是依賴服務自身問題出現調用故障或延遲,而這些問題會直接致使調用方的對外服務也出現延遲,若此時調用方的請求不斷增長,最後就會出現因等待出現故障的依賴方響應而造成任務積壓,最終致使自身服務的癱瘓。爲了解決這樣的問題,所以產生了斷路器模式。
咱們在實踐中使用了 Hystrix 來實現斷路器的功能。Hystrix 是 Netflix 開源的微服務框架套件之一,該框架目標在於經過控制那些訪問遠程系統、服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力。Hystrix 具有擁有回退機制和斷路器功能的線程和信號隔離,請求緩存和請求打包,以及監控和配置等功能。
斷路器的使用流程以下:
啓用斷路器
@SpringBootApplication @EnableCircuitBreaker public class Application { public static void main(String[] args) { SpringApplication.run(DVoiceWebApplication.class, args); } }
代用使用方式
@Component public class StoreIntegration { @HystrixCommand(fallbackMethod = "defaultStores") public Object getStores(Map<String, Object> parameters) { //do stuff that might fail } public Object defaultStores(Map<String, Object> parameters) { return /* something useful */; } }
配置文件
資源控制實踐
聊到資源控制,估計不少小夥伴會聯繫到 Docker,Docker 確實是一個實現資源控制很不錯的解決方案,咱們前期作調研時也對是否使用 Docker 進行了評審,可是最終選擇放棄,而使用 Linux 的 libcgroup 腳本控制,緣由以下:
爲何要有 group?
Linux 系統中常常有個需求就是但願能限制某個或者某些進程的分配資源。也就是能完成一組容器的概念,在這個容器中,有分配好的特定比例的 CPU 時間,IO 時間,可用內存大小等。
因而就出現了 cgroup 的概念,cgroup 就是 controller group,最初由 Google 的工程師提出,後來被整合進 Linux 內核中,Docker 也是基於此來實現。
libcgroup 使用流程:
安裝
yum install libcgroup
啓動服務
service cgconfig start
配置文件模板(以 memory 爲例):
cat /etc/cgconfig.conf
看到 memory 子系統是掛載在目錄 /sys/fs/cgroup/memory 下,進入這個目錄建立一個文件夾,就建立了一個 control group 了。
mkdir test echo "服務進程號">> tasks(tasks 是 test 目錄下的一個文件)
這樣就將當前這個終端進程加入到了內存限制的 cgroup 中了。
總結一下,本文從咱們微服務實踐的背景聊起,介紹了微服務實踐的工做方式,技術選型,以及相關的一些微服務技術。包括:API 網關、註冊中心、斷路器等。相信這些技術會給你們在實踐中帶來一些新的思路。
原文連接:從 Spring Cloud 開始,聊聊微服務架構的實踐之路