微服務基礎內容:
在微服務中,首先學習了微服務的橫向拆分與縱向拆分,縱向拆分指按照功能拆分模塊,橫向拆分指將高復用的模塊單獨拆分,使縱向拆分的模塊去調用這部分內容。
學習了基本拆分后,需要知道微服務之間的關系是多對多的消費者生成者模型,每個模塊互為消費者與生產者,為了微服務之間傳遞的可靠性,需要用到nacos組件。nacos組件的主要充當一個中介——注冊中心,nacos與生產者間通過心跳協議維護一張服務表單,當消費者需要生產者的調用時,nacos會給消費者提供相應的生產者表單,這個表單維護了多個完成消費者服務的端口號,消費者根據負載均衡選擇一個端口號進行建立連接,從而調用生產者。
學習了基本拆分與nacos后,拿到了具體的生產者的端口號,引入了openfeign組件,這個組件實際上就是http轉發工具,因為不同微服務之間是相互隔離的,擁有自己的數據庫與服務器,所以不同微服務之間的調用需要采用網絡請求,openfeign的作用就是這個。通過端口號以及對應的openfeign轉發,從而構建不同微服務之間的連接與調用。
學習了上述內容之后,開始思考如何進行鑒權。還是由于微服務的隔離性,不同服務之間如何拿到鑒權信息?回顧單體架構的鑒權方式,使用的是springmvc攔截器,通過在攔截器中獲取http.request.header中的授權信息后,在后端將授權信息比對,從而將需要的權力信息(比如userInfo)存儲在ThreadLocal中,然后在線程執行期間就可以隨時在鑒權頁面拿到想要的內容。
在微服務架構中采用網關來實現上述操作,不僅如此,網關還可以解決前后端端口號不一致的矛盾。前端只會訪問網關,網關來負責轉發給不同的微服務。
以鑒權舉例,網關通過攔截器拿到userInfo,存儲在threadLocal中,當本次調用的微服務要調用下一個微服務,并且需要userInfo時,會使用openfeign工具,將userInfo存儲在這個網絡請求的請求頭中,轉發的請求仍然再一次訪問網關,網管攔截器拿到userInfo,提取出放入該微服務執行線程的threadlocal中。從而實現,多個微服務之間的數據傳遞。
開始:
上述內容都是微服務的基本內容,現在需要考慮微服務的一些弊端。
一、首先微服務顯然會出現雪崩的場景,當某個微服務A宕機后,后續調用該微服務B調用A時也會卡在請求A的過程中,后續又有C調用B,C也卡住(繼續向后疊加獎池)從而造成A/B/C的資源一直在請求中無法釋放,最終造成服務器資源耗盡而崩潰。
sentinel組件就是用于解決這個問題的,sentinel組件也是alibaba旗下的,與nacos配合服用。
sentinel的操控在控制臺中,只需要導包與配置,方便快捷,在管理面板添加相應的配置項后,甚至可以免服務部署來更新對應的參數。說一說sentinel的作用,sentinel的作用主要分為限流與熔斷。
1.限流包含限制訪問請求數以及為微服務分配的線程數,限制請求數就是限定QPS,這個很好理解。
2.限制線程數為每個微服務分配固定的線程數,請求到了微服務,首先去線程池中取線程,如果沒有線程,那么就會返回報錯。
3.針對返回報錯導致用戶體驗不好的弊端,在openfeign中又定義了一個錯誤處理,這個可以理解為單體架構中的異常處理,代碼案例如下:
@Slf4j
public class CartToItemFallback implements FallbackFactory<ItemClient>{@Overridepublic ItemClient create(Throwable cause) {return new ItemClient() {@Overridepublic List<ItemDTO> queryItemById(Collection<Long> ids) {log.error("查詢商品失敗",cause);return CollUtils.emptyList();}@Overridepublic void deductStock(List<OrderDetailDTO> items) {log.error("扣減庫存失敗",cause);throw new RuntimeException(cause);}};}
}
這個內容會被對應的ItemClient生成注解并標記。需要理解的是,我們進行fallback處理的是微服務調用之間的問題,而宕機的微服務不是通過fallback進行處理的。當微服務A調用宕機微服務B時,A這個時候因為限流沒有拿到B的資源,這時直接fallback返回,不會出現報錯,A中的其余項目繼續處理即可。
4.sentinel中除了限流還有熔斷機制,限流只是為了防止未出現故障的微服務A調用已經宕機的微服務B而造成整個系統崩潰,但是A還是會去調用B,只不過A沒有調用成功而已,這個請求的過程仍然在耗費資源。
而使用熔斷機制后,能夠直接不讓A發送請求。熔斷機制會監控一段時間或一定次數內的調用失敗率(或錯誤次數),比如在sentinel中就有TTL,當連接響應超過設置的TTL以及持續多少秒后(達到閾值),就將熔斷器置為 Open 狀態,此時所有對微服務 B 的調用都會被短路快速失敗,不再真正發送請求。經過一個休眠時長后,熔斷器進入 Half-Open,允許少量請求恢復探測;如果恢復良好,則關閉熔斷器,否則繼續保持 Open。
二、在學習了sentinel后知道了如何處理單個微服務的崩潰問題,繼續學習微服務中的事務問題。
微服務的事務問題和單體架構的事務問題很相似,舉個例子:
支付操作:對用戶扣款,將訂單狀態修改為已支付。
這個例子中,用戶服務會去調用訂單服務,如果用戶扣款服務正常執行,但是執行訂單狀態修改時出現了問題,從而導致數據庫不一致。
這個解決的方案就是使用seata組件,來對事務進行控制。
想要進行事務,簡單分析就是讓多個微服務進行提交的同步或者回滾的同步,就如同數據庫中的提交與回滾類似,只不過在這里的事務不完全保證原子性(如AT方法)
seata組件支持兩種方式來進行事務控制,分別是XA和AT:
XA方式:
XA方式是最早的傳統方式,方式比較粗暴,首先RM注冊分支事務到TC(也就是告訴TC我是一個多服務時間,需要XA管理),然后RM去執行對應的sql操作,但是此時不進行提交,RM 向 TC 發送服務運行結果,TC比對這個時間所有RM的執行結果,進行統一的提交和回滾。
顯然這個方式很好的保證了多個微服務的sql操作同步性,但是弊端也比較明顯,這種方式即使對應的微服務已經完成工作并且發送給TC其成功狀態,仍然還需要等待其余RM執行完畢,造成資源的浪費。優點是不會出現數據庫的不一致,保證結果的完全可靠。
TA方式:
TA方式是現在國內用的比較多的一種方式,TA方式不同點在于 所有RM注冊后,會對當前的環境生成相應的快照undo-log,說白了就是備份當前數據庫環境。
然后每個RM分別執行自己的,執行完后自行提交,提交由RM自行完成,提交完后向TC報告執行狀態。
當所有RM進行報告后,TC會來驗收,如果有RM出現故障,就會回滾到最初的快照狀態。
TA方式優勢很明顯,每個RM結束后就會立刻釋放資源,不會造成資源的浪費。
缺點也比較明顯,TA方式會導致短暫的數據不一致(在RM提交后到TC檢查到錯誤并回滾之前)