遷移微服務(wù)框架 SpringCloud 事后總結(jié)

我們這次主要講4件事:
1. 什么是微服務(wù)? 什么是 springcloud?
2. 我們?yōu)槭裁词褂?SpringCloud?
3. 如何使用 SpringCloud? 如何 Quick Start?
4. 遷移過(guò)程中,老代碼使用 springcloud 需要注意哪些坑?

1. 什么是微服務(wù)? 什么是 springcloud?

引用維基百科:

微服務(wù)是一種架構(gòu)風(fēng)格,一個(gè)大型復(fù)雜軟件應(yīng)用由一個(gè)或多個(gè)微服務(wù)組成。系統(tǒng)中的各個(gè)微服務(wù)可被獨(dú)立部署,各個(gè)微服務(wù)之間是松耦合的。每個(gè)微服務(wù)僅關(guān)注于完成一件任務(wù)并很好地完成該任務(wù)。在所有情況下,每個(gè)任務(wù)代表著一個(gè)小的業(yè)務(wù)能力。

圖片
微服務(wù)架構(gòu)的一些通用特性
  1. 通過(guò)服務(wù)實(shí)現(xiàn)應(yīng)用的組件化:微服務(wù)架構(gòu)中將組件定義為可被獨(dú)立替換和升級(jí)的軟件單元,在應(yīng)用架構(gòu)設(shè)計(jì)中通過(guò)將整體應(yīng)用切分成可獨(dú)立部署及升級(jí)的微服務(wù)方式進(jìn)行組件化設(shè)計(jì)。
  2. 圍繞業(yè)務(wù)能力組織服務(wù)(Organizedaround Business Capabilities):微服務(wù)架構(gòu)采取以業(yè)務(wù)能力為出發(fā)點(diǎn)組織服務(wù)的策略,因此微服務(wù)團(tuán)隊(duì)的組織結(jié)構(gòu)必須是跨功能的(如:既管應(yīng)用,也管數(shù)據(jù)庫(kù))、強(qiáng)搭配的DevOps開發(fā)運(yùn)維一體化團(tuán)隊(duì),通常這些團(tuán)隊(duì)不會(huì)太大(如:亞馬遜的“Two pizzateam”- 不超過(guò)12人).
  3. 產(chǎn)品而非項(xiàng)目模式:傳統(tǒng)的應(yīng)用模式是一個(gè)團(tuán)隊(duì)以項(xiàng)目模式開發(fā)完整的應(yīng)用,開發(fā)完成后就交付給運(yùn)維團(tuán)隊(duì)負(fù)責(zé)維護(hù);微服務(wù)架構(gòu)則倡導(dǎo)一個(gè)團(tuán)隊(duì)?wèi)?yīng)該如開發(fā)產(chǎn)品般負(fù)責(zé)一個(gè)“微服務(wù)”完整的生命周期,倡導(dǎo)“誰(shuí)開發(fā),誰(shuí)運(yùn)營(yíng)”的開發(fā)運(yùn)維一體化方法.
  4. 智能端點(diǎn)與管道扁平化:微服務(wù)架構(gòu)主張將組件間通訊的相關(guān)業(yè)務(wù)邏輯/智能放在組件端點(diǎn)側(cè)而非放在通訊組件中,通訊機(jī)制或組件應(yīng)該盡量簡(jiǎn)單及松耦合。RESTful HTTP協(xié)議和僅提供消息路由功能的輕量級(jí)異步機(jī)制是微服務(wù)架構(gòu)中最常用的通訊機(jī)制。
  5. “去中心化”治理:微服務(wù)架構(gòu)則鼓勵(lì)使用合適的工具完成各自的任務(wù),每個(gè)微服務(wù)可以考慮選用最佳工具完成(如不同的編程語(yǔ)言).
  6. “去中心化”數(shù)據(jù)管理:微服務(wù)架構(gòu)倡導(dǎo)采用多樣性持久化的方法,讓每個(gè)微服務(wù)管理其自有數(shù)據(jù)庫(kù),并允許不同微服務(wù)采用不同的數(shù)據(jù)持久化技術(shù).
  7. 基礎(chǔ)設(shè)施自動(dòng)化:云化及自動(dòng)化部署等技術(shù)極大地降低了微服務(wù)構(gòu)建、部署和運(yùn)維的難度,通過(guò)應(yīng)用持續(xù)集成和持續(xù)交付等方法有助于達(dá)到加速推出市場(chǎng)的目的.
  8. 故障處理設(shè)計(jì):微服務(wù)架構(gòu)所帶來(lái)的一個(gè)后果是必須考慮每個(gè)服務(wù)的失敗容錯(cuò)機(jī)制。因此,微服務(wù)非常重視建立架構(gòu)及業(yè)務(wù)相關(guān)指標(biāo)的實(shí)時(shí)監(jiān)控和日志機(jī)制.
  9. 演進(jìn)式的設(shè)計(jì):微服務(wù)應(yīng)用更注重快速更新,因此系統(tǒng)的計(jì)會(huì)隨時(shí)間不斷變化及演進(jìn)。微服務(wù)的設(shè)計(jì)受業(yè)務(wù)功能的生命周期等因素影響。如某應(yīng)用是整體式應(yīng)用,但逐漸朝微應(yīng)用架構(gòu)方向演進(jìn),整體式應(yīng)用仍是核心,但新功能將使用應(yīng)用所提供的API構(gòu)建。再如在某微服務(wù)應(yīng)用中,可替代性模塊化設(shè)計(jì)的基本原則,在實(shí)施后發(fā)現(xiàn)某兩個(gè)微服務(wù)經(jīng)常必須同時(shí)更新,則這很可能意味著應(yīng)將其合并為一個(gè)微服務(wù).

更多微服務(wù)內(nèi)容請(qǐng)移步:IBM developerWorks 中文社區(qū):解析微服務(wù)架構(gòu)

什么是 springcloud?

Spring Cloud是伴隨著微服務(wù)的概念誕生的.基于 SpringBoot.

和 SpringCloud 密切相關(guān)的Netflix是一家什么公司?

Netflix是世界上最大的在線影片租賃提供商,向它的670萬(wàn)名顧客提供超過(guò)85,000部DVD電影的租賃服務(wù),而且能向顧客提供4000多部影片或者電視劇的在線觀看服務(wù)。公司的成功源自于能夠提供超大數(shù)量的DVD,而且能夠讓顧客快速方便的挑選影片,同時(shí)免費(fèi)遞送。Netflix已經(jīng)連續(xù)五次被評(píng)為顧客最滿意的網(wǎng)站.

為什么 Netflix 要做 SpringCould? Netflix 和 SpringCloud 的前世今生.

1、首先,Netflix是一家做視頻的網(wǎng)站,可以這么說(shuō)該網(wǎng)站上的美劇應(yīng)該是最火的。

2、Netflix是一家沒有CTO的公司,正是這樣的組織架構(gòu)能使產(chǎn)品與技術(shù)無(wú)縫的溝通,從而能快速迭代出更優(yōu)秀的產(chǎn)品。在當(dāng)時(shí)軟件敏捷開發(fā)中,Netflix的更新速度不亞于當(dāng)年的微信后臺(tái)變更,雖然微信比Netflix遲發(fā)展,但是當(dāng)年微信的灰度發(fā)布和敏捷開發(fā)應(yīng)該算是業(yè)界最猛的。

3、Netflix由于做視頻的原因,訪問量非常的大,從而促使其技術(shù)快速的發(fā)展在背后支撐著,也正是如此,Netflix開始把整體的系統(tǒng)往微服務(wù)上遷移。

4、Netflix的微服務(wù)做的不是最早的,但是確是最大規(guī)模的在生產(chǎn)級(jí)別微服務(wù)的嘗試。也正是這種大規(guī)模的生產(chǎn)級(jí)別嘗試,在服務(wù)器運(yùn)維上依托AWS云。當(dāng)然AWS云同樣受益于Netflix的大規(guī)模業(yè)務(wù)不斷的壯大。

5、Netflix的微服務(wù)大規(guī)模的應(yīng)用,在技術(shù)上毫無(wú)保留的把一整套微服務(wù)架構(gòu)核心技術(shù)棧開源了出來(lái),叫做Netflix OSS,也正是如此,在技術(shù)上依靠開源社區(qū)的力量不斷的壯大。

6、Spring Cloud是構(gòu)建微服務(wù)的核心,而Spring Cloud是基于Spring Boot來(lái)開發(fā)的。

7、Pivotal在Netflix開源的一整套核心技術(shù)產(chǎn)品線的同時(shí),做了一系列的封裝,就變成了Spring Cloud;雖然Spring Cloud到現(xiàn)在為止不只有Netflix提供的方案可以集成,還有很多方案,但Netflix是最成熟的。

SpringCloud 有哪些牛逼的功能?

SpringCloud 號(hào)稱擁有五虎將(即最常用的五個(gè)功能). 哪五虎將?

  1. Eureka 云端服務(wù)注冊(cè)與發(fā)現(xiàn).
  2. Zuul 動(dòng)態(tài)路由, 服務(wù)網(wǎng)關(guān).
  3. Hystrix 斷路器.容災(zāi)管理工具.
  4. Spring Cloud Config 云端配置中心.
  5. Load Balance 負(fù)載均衡.

還有一些其他的功能, 比如事件總線, 配置管理 API, 輪詢框架, Consul 可與 Docker 無(wú)縫集成, Sleuth 日志收集工具包, Data Flow 大數(shù)據(jù)操作工具, Security 安全工具包, Zookeeper 操作 ZK 的工具包, Stream 數(shù)據(jù)流操作開發(fā)包, 封裝了 Redis, Rabbit, Kafka. Ribbon 負(fù)載均衡, Feign 聲明式的 HTTP 客戶端, Task 提供計(jì)劃任務(wù)管理,任務(wù)調(diào)度框架, Cluster 提供 Leadership 選舉, 類似 Zookeeper 選舉. Starters 為 SpringCloud 提供開箱即用的依賴管理.

2. 我們?yōu)槭裁词褂?SpringCloud?

讓我們先來(lái)看看傳統(tǒng)IT架構(gòu)面臨的一些問題:

  1. 使用傳統(tǒng)的整體式架構(gòu)應(yīng)用開發(fā)系統(tǒng),如CRM、ERP等大型應(yīng)用,隨著新需求的不斷增加,企業(yè)更新和修復(fù)大型整體式應(yīng)用變得越來(lái)越困難;
  2. 隨著移動(dòng)互聯(lián)網(wǎng)的發(fā)展,企業(yè)被迫將其應(yīng)用遷移至現(xiàn)代化UI界面架構(gòu)以便能兼容移動(dòng)設(shè)備,這要求企業(yè)能實(shí)現(xiàn)應(yīng)用功能的快速上線;
我們?yōu)槭裁匆褂?SpringCloud ?
  1. 現(xiàn)有的框架為多個(gè) SpringBoot 的框架, 最明顯的一個(gè)問題就是, 重復(fù)代碼太多, 直接導(dǎo)致的問題就是修改一處, 其余地方都要修改, 代碼難以維護(hù). 比如一個(gè)User 這張表,可能每個(gè) SpringBoot 都需要 User 表的查詢功能, 但是除了 User 模塊, 其余模塊也要?jiǎng)?chuàng)建 User 相關(guān)的類和配置文件,一旦 User 表字段更改, 所有相關(guān)模塊都要更改, 令人恐懼.
    用 SpringCloud 能解決這個(gè)問題嗎?
    能. 只需要 User 開放一個(gè) 關(guān)于 User 表的查詢接口, 其他模塊調(diào)用此接口,就能實(shí)現(xiàn)之前的功能. 避免了大量的重復(fù)代碼. 提高了代碼的可維護(hù)性.

  2. 第二個(gè)問題就是應(yīng)用擴(kuò)展問題, 只要企業(yè)快速發(fā)展, 所有的后端都不可避免的要實(shí)施分布式, 將一個(gè)大的服務(wù)拆分為一個(gè)個(gè)小服務(wù), 保證系統(tǒng)的快速迭代和快速擴(kuò)展. A 應(yīng)用故障不會(huì)導(dǎo)致 B 應(yīng)用也故障. 現(xiàn)有的框架無(wú)法支持橫向擴(kuò)展和快速迭代, 之前的架構(gòu)成為我們的痛點(diǎn)。
    用 SpringCloud 能解決這個(gè)問題嗎?
    SpringCloud 為分布式和微服務(wù)而生, 拆分巨型應(yīng)用, 使得每個(gè)模塊都獨(dú)立, 根據(jù)業(yè)務(wù)拆分服務(wù), 也可根據(jù)業(yè)務(wù)的改變合并服務(wù), SpringCloud 支持集群部署異常簡(jiǎn)單, 且自帶軟負(fù)載均衡, 配合 Zuul 網(wǎng)關(guān)實(shí)現(xiàn)服務(wù)認(rèn)證, 安全過(guò)濾等功能. SpringCloud 自帶的斷路器能夠很好的容災(zāi), 當(dāng)某個(gè)服務(wù)不通時(shí), 不會(huì)影響整個(gè)服務(wù)導(dǎo)致雪崩性的效應(yīng). 并且如果某個(gè)服務(wù)需要迭代, 其余模塊可絲毫不受影響. 更改架構(gòu)后能承受更高的并發(fā)和用戶量。

  3. 第三個(gè)問題是原有的代碼無(wú)法支持分庫(kù)分表,原有的表全部都再一個(gè)庫(kù)種,業(yè)務(wù)高度耦合,難以維護(hù),為了踐行微服務(wù)“去中心化”數(shù)據(jù)管理的理念,每個(gè)服務(wù)管理其自有數(shù)據(jù)庫(kù),我們必須將表根據(jù)業(yè)務(wù)進(jìn)行分割,以應(yīng)對(duì)后期分庫(kù)。

  4. 原有架構(gòu)不清晰,水平擴(kuò)展和快速迭代沒有成熟和現(xiàn)行的技術(shù)方案,使用成熟的 SpringCloud 方案可以減少項(xiàng)目風(fēng)險(xiǎn),提高應(yīng)對(duì)風(fēng)險(xiǎn)的能力和應(yīng)對(duì)業(yè)務(wù)快速變化的要求。

3. 如何使用 SpringCloud? 如何 Quick Start?

快速開始已經(jīng)有很多文章.
[史上最簡(jiǎn)單的 SpringCloud 教程 | 終章]-----SpringCloud 終極入門

4. 老代碼使用 springcloud 需要注意哪些坑?

目前我們重構(gòu)老代碼的主要方向是:
  1. 抽取重復(fù)代碼變成對(duì)外的 Restful 接口供其他模塊調(diào)用.
  2. 根據(jù)數(shù)據(jù)庫(kù)進(jìn)一步合理拆分業(yè)務(wù), 為以后的拆分?jǐn)?shù)據(jù)庫(kù)做鋪墊.
  3. 隔離業(yè)務(wù)和 Restful 接口模塊和實(shí)體類模塊, 以便所有模塊依賴公有代碼.
  4. 避免遞歸調(diào)用和多層調(diào)用, 盡量提供方就是提供方, 消費(fèi)方就是消費(fèi)方. 減少深層次調(diào)用, 方便排錯(cuò).
  5. 模塊與模塊之間的通信和協(xié)作通過(guò)消息隊(duì)列. 減少耦合.
  6. 對(duì)所有模塊依賴的公有代碼進(jìn)行 Scan, 以方便后面的重構(gòu).
遇到了哪些問題呢?
  1. 我們使用了 SpringCloud 的 Feign Http 客戶端, 作為消費(fèi)方. Feign 請(qǐng)求提供方接口時(shí)根據(jù)需要傳參數(shù), 可以使用@RequestParam()注解, 也可以使用@PathVariable路徑傳參. 也可以使用@RequestBody注解傳對(duì)象,關(guān)于這三個(gè)注解的用法,需要注意一下他們的用法和坑點(diǎn).

  • @RequestParam用法
    @RequestParam用法

@RequestParam配合 GET 請(qǐng)求注解用于標(biāo)注普通類型的參數(shù), 比如8個(gè)基本類型和他們的包裝類或者 String 類型. 也可用使用在 Map 上, 例如fun(@RequestParam ("map")Map<K,V> map), 也可以使用在 Date 類型上, 例如 fun(@RequestParam("date") Date date),注意, 只要有參數(shù), 且參數(shù)是基本類型或者是包裝類型和 String 類型, 必須使用該注解配合 GET 請(qǐng)求. 否則肯定報(bào)錯(cuò).


  • @PathVariable() 用法
    @PathVariable()

該注解用于配合路徑中的占位符使用, 注意: 該注解必須在子類上也寫上注解, 也就是說(shuō), 服務(wù)提供方的 API 接口 如果使用了該注解, 那么實(shí)現(xiàn)該接口的子類必須在參數(shù)前加上此注解, 這三個(gè)注解都不支持繼承,因?yàn)镾pring 是不能識(shí)別該注解應(yīng)該作用于哪個(gè)參數(shù), 如果不寫, 就會(huì)得到 Null 值, 導(dǎo)致錯(cuò)誤. 那么 @RequestParam 需要寫嗎? 答案是不需要寫的, 因?yàn)?code>@RequestParam是 url 傳參, Spring 支持將參數(shù)名稱映射到參數(shù)上給定的參數(shù)上. 因此, 使用該注解時(shí)需要注意的是: 必須在子類中加入該注解. 還有, 該注解不能傳類似版本號(hào)的數(shù)字(如:1.2.1), 會(huì)導(dǎo)致 http 解析時(shí)去掉最后的小數(shù)點(diǎn). 建議使用 URL中的www.google.com?name=tom 傳參.


@RequestBody用法

@RequestBody用法

如果參數(shù)很多, 難道我們要一個(gè)一個(gè)參數(shù)寫上去嗎? 不需要, 我們可以使用@RequestBody 注解配合 POST 請(qǐng)求, 將多個(gè)參數(shù)封裝成一個(gè)對(duì)象, 當(dāng)然 @RerquestParam 注解配合 GET 加上 Map 參數(shù)也可以, 但是不建議使用 Map 作為參數(shù), Map 會(huì)出現(xiàn)很多問題, 我們稍后再講. 回到我們的@RequestBody 注解的用法上面來(lái), 注意: 這個(gè)注解也必須在子類上聲明, 否則參數(shù)無(wú)法映射. 因?yàn)?Spring 映射時(shí)找的是子類, 而該注解的作用是: 解析Body 里的內(nèi)容變成 JSON, 然后映射到參數(shù)中, 如果不寫, Spring 將不知道如何映射. 因?yàn)?Spirng 也是支持用流獲取參數(shù)的. 并且該注解也是 Swagger 文檔框架的基礎(chǔ). 注意, 一個(gè)方法中只能有一個(gè)@RequestBody注解. 注意: 該注解要求 Http 消息頭中包含: Content-Type:Application/json.


總結(jié)一下上面關(guān)于三個(gè)注解的內(nèi)容.

1. 實(shí)現(xiàn)類除@RequestParam(如果參數(shù)是map則需要加) 不需要加注解,其余都要加注解;
2. map 可以用get傳也可以用post傳;
3. get 請(qǐng)求不能有一個(gè)或多個(gè)復(fù)雜對(duì)象(頂多用一個(gè)map代替多個(gè)參數(shù));
4. post 請(qǐng)求有且只能有一個(gè)復(fù)雜對(duì)象,必須使用@RequestBody 注解;
5. set 類型使用post 請(qǐng)求 + @RequestBody 注解;
6. 不能使用數(shù)組,包裝類型也不行,可用List代替:get請(qǐng)求+@RequestParam, Post請(qǐng)求中的
   @RequestBody,jackSon工具無(wú)法解析數(shù)組,因?yàn)閿?shù)組沒有類型信息.而get請(qǐng)求框架會(huì)自動(dòng)參數(shù)隱射;
7. Date 類型 使用post + @RequestBody或者,使用 get + @RequestParam;
8. 注解必須搭配使用,GetMapping + @RequestParam, @PostMapping + @RequestBody;

  1. 還有一個(gè)坑點(diǎn), 就是上面說(shuō)的 Map 參數(shù), 因?yàn)槲覀冎饕貥?gòu)的是關(guān)于 Mapper 方法, 也就是數(shù)據(jù)庫(kù)操作, 而這之前為了方便傳參數(shù), 使用了大量的 Map 作為參數(shù)的方法, 并且 Value 的泛型為 Object 類型, 這個(gè)時(shí)候問題就出現(xiàn)了, 如果之前 Map 中有個(gè)鍵值對(duì)是data=new Date(), 在 xml 配置文件中則是create_date <= #{date}, 原本這樣寫是沒有任何問題的. 但是一旦把 Map 作為網(wǎng)絡(luò)調(diào)用的參數(shù), 當(dāng)你把 Date 類型傳過(guò)去的時(shí)候, 而 Value 的泛型又是 Objece, 那么那邊接受到的就是 Long 類型的時(shí)間戳, 在 xml 文件中就會(huì)出現(xiàn)create_date <= 1213244343432, 很明顯, 就會(huì)溢出報(bào)錯(cuò). 所以, 我們后期都將做了特殊處理, 類似下面這樣:
  public void function(Map<String, Object> map){
    Long date = (Long)map.get("date");
    // 將Long 類型轉(zhuǎn)換成 Date 類型
    map.put("date", new Date(date));
  }

所以, 以后遇到需要傳遞 Date 類型的時(shí)候, 盡量不要使用 Map, 如果使用 Map, Value 的泛型也盡量不要寫 Object.

3 . 還有一個(gè)問題就是分頁(yè)插件的問題, 我們使用的 pageHelper 的開源分頁(yè)插件, 原理是在當(dāng)前線程中放入一個(gè) ThreadLocal 的 Page 對(duì)象, 當(dāng)調(diào)用動(dòng)態(tài)代理了 mybatis 的方法時(shí), 經(jīng)過(guò)代理對(duì)象 invoke 方法時(shí)會(huì)觸發(fā)繼承了 Mybatis 攔截器的分頁(yè)插件, 分頁(yè)插件會(huì)從 ThreadLocal 中取出分頁(yè)對(duì)象, 進(jìn)行分頁(yè). 執(zhí)行結(jié)束后會(huì)刪除該 Page 對(duì)象.
那么我們發(fā)生一個(gè)什么故障呢? 原有的分頁(yè)是這樣寫的:

  public List<User> getPageUser(User user){
    // 該方法進(jìn)行分頁(yè), 構(gòu)造一個(gè) Page 對(duì)象, 將 Page 對(duì)象作為 ThreadLocal 
    // 放入當(dāng)前線程
    PageHelper.start(page, rows);
    // 執(zhí)行查詢 SQL
    userMapper.select(user);
  }

這段代碼之前寫是沒有任何問題的, 但是, 由于 UserMapper 被抽取出來(lái)成為公共部分代碼. 所以這個(gè)地方被重構(gòu)成:

  public List<User> getPageUser(User user){
    // 該方法進(jìn)行分頁(yè), 構(gòu)造一個(gè) Page 對(duì)象, 將 Page 對(duì)象作為 ThreadLocal 
    // 放入當(dāng)前線程
    PageHelper.start(page, rows);
    // 注意: 這里變成了網(wǎng)絡(luò)調(diào)用, 執(zhí)行 SQL 的線程已經(jīng)在另一個(gè)虛擬機(jī)中.
    userClient.select(user);
  }

可以看到, userMapper 變成了userClient, 原有的 SQL 變成了網(wǎng)絡(luò)調(diào)用. 那么這個(gè)時(shí)候會(huì)發(fā)生什么事情呢? PageHelper.start(page, rows); 放入線程的 Page 對(duì)象將不會(huì)被消費(fèi), 也不會(huì)被刪除, 因?yàn)楫?dāng)前線程根本不會(huì)執(zhí)行 Mybatis 的方法, 更不會(huì)進(jìn)入攔截器, 而容器使用的是線程池, 當(dāng)該方法結(jié)束后, 攜帶著Page 對(duì)象的線程會(huì)隨機(jī)分配一個(gè)任務(wù)執(zhí)行 SQL, 如果該 SQL 不支持分頁(yè), 但是分頁(yè)插件發(fā)現(xiàn)該線程中含有 Page 對(duì)象, 就會(huì)強(qiáng)行分頁(yè), 導(dǎo)致錯(cuò)誤, 而這個(gè)錯(cuò)誤很難排查. 所以, 當(dāng)使用PageHelper.start(page, rows);方法后, 在本任務(wù)執(zhí)行結(jié)束之前一定要在本虛擬機(jī)中跟一條 SQL , 也就是跟一個(gè)Mybatis 的方法. 否則將會(huì)影響其他線程中 SQL 的執(zhí)行. 導(dǎo)致報(bào)錯(cuò).

  1. 還有就是, 重構(gòu)后, 實(shí)體類的全限定名都變化了, 如果使用緩存,或者是任何依賴序列化的中間件, 都會(huì)因?yàn)轭惷粚?duì)導(dǎo)致無(wú)法反序列化為新實(shí)體類而報(bào)錯(cuò).
    所以應(yīng)該在保證安全的情況下, 將緩存清空.

  2. 關(guān)于斷路器的使用, 斷路器的作用是, 如果 Feign 調(diào)用失敗, 并且重試多次失?。ㄎ业臏y(cè)試是5秒之內(nèi)連續(xù)10次失敗之后), 就會(huì)觸發(fā)斷路器, 也就是我們重寫的方法, 剛開始, 我們直接在斷路器中返回了默認(rèn)值, 比如如果是對(duì)象就返回 null, 如果是容器就返回空, 如果是基本類型就返回默認(rèn)值, 但是, 仔細(xì)一想不對(duì), 如果業(yè)務(wù)代碼將 null 和默認(rèn)值作為邏輯判斷怎么辦, 實(shí)際上, 網(wǎng)絡(luò)調(diào)用失敗返回 null 和調(diào)用成功返回 null 是不同的. 因此, 我們將觸發(fā)斷路器方法中的內(nèi)容改為了拋出異常. 避免影響老代碼的邏輯, 可以在新代碼的使用中, 針對(duì)斷路器如何返回值做出新的約定. 防止斷路器和業(yè)務(wù)返回內(nèi)容混淆. 斷路器什么時(shí)候會(huì)恢復(fù)調(diào)用呢?答案是5秒之后斷路器會(huì)變成半開閉的狀態(tài),如果有服務(wù)請(qǐng)求,就會(huì)嘗試調(diào)用一次,如果成功則關(guān)閉斷路器,如果失敗,則開啟斷路器。5秒之后就又變成了半開閉的狀態(tài)。

  3. 還有一個(gè)坑點(diǎn)就是: 如何傳遞Header, , 我們需要的網(wǎng)絡(luò)調(diào)用中傳遞 Header , 而我們現(xiàn)有的 Header都是存放在 ThreadLocal 中的, 難道我們要寫一個(gè) Feign 的配置類, 并在每個(gè)配置類上加上一個(gè)@Header的注解? 或者我們要將 Header 放在參數(shù)中傳遞嗎? 答案是不必的. 我們只需要攔截 Http Request , 在 Http 請(qǐng)求前加入我們需要的 Header. 而這就引出了第七個(gè)問題。

  1. SpringCloud Feign 和斷路器為了服務(wù)的高可用和應(yīng)用健壯性, 提供了二種隔離策略線程池隔離信號(hào)量隔離,

簡(jiǎn)單說(shuō)一下什么是線程池隔離, SpringCloud將服務(wù)調(diào)用的主線程和 Feign 調(diào)用的Http請(qǐng)求分開存放在不同的線程池, 為什么要這么做呢?試想一下如果不分開存放, 當(dāng)主線程調(diào)用時(shí)超時(shí),那么 tomcat 就會(huì)阻塞大量線程,影響整個(gè)服務(wù),但,如果使用線程池隔離, Feign 請(qǐng)求會(huì)開辟新的線程,就算超時(shí)也不影響整體應(yīng)用的,更不影響tomcat 線程池中的線程,保證了容器的安全。

再說(shuō)一下什么是信號(hào)量隔離, 信號(hào)量就是系統(tǒng)設(shè)置的并發(fā)請(qǐng)求數(shù),如果設(shè)置的信號(hào)量為10, 那么該服務(wù)接口的請(qǐng)求并發(fā)數(shù)就為10, 超過(guò)10就進(jìn)行服務(wù)降級(jí),忽略請(qǐng)求。信號(hào)量隔離是使用tomcat中的主線程進(jìn)行服務(wù)請(qǐng)求,因此,不能算是隔離,只能算是限流,限制請(qǐng)求的數(shù)量,即使目標(biāo)服務(wù)不通,也不會(huì)拖垮整個(gè) tomcat 中的服務(wù)。如果請(qǐng)求的服務(wù)速度很快,并發(fā)很高,并且提供方服務(wù)穩(wěn)定。那么使用信號(hào)量是合算的。因?yàn)椴粫?huì)有線程的上下文切換。否則使用線程池隔離比較合算。

那么對(duì)于我們來(lái)說(shuō),隔離策略帶給我們什么影響呢? 第6點(diǎn)的時(shí)候我們說(shuō),我們需要將Header傳遞, 而Header放在ThreadLocal 中,所以,線程池隔離的策略難以無(wú)縫支持ThreadLocal 中的Header。 除非特殊配置或者寫在參數(shù)中,但這需要修改大量代碼,我們想使用切面的方式將Header 透明的傳遞。信號(hào)量隔離策略和適合我們,既能保證服務(wù)的穩(wěn)定性,也能保證服務(wù)中Header的傳遞。所以我們選擇了信號(hào)量隔離。

  1. 由于分布式調(diào)用出錯(cuò)調(diào)試較為復(fù)雜, 因此, 以前返回客戶端只是服務(wù)端錯(cuò)誤, 現(xiàn)在使用@ExceptionHandler(HystrixRuntimeException.class) 注解, 細(xì)化每個(gè)異常, 用以返回不同的錯(cuò)誤信息, 方便排錯(cuò).

  2. 測(cè)試用例在之前貌似不怎么重要, 因?yàn)橐粋€(gè) debug 可以一路調(diào)通, 但分布式的可能需要跨越多層調(diào)用, 因此, 單元測(cè)試就顯得很重要, 能將錯(cuò)誤慢慢分割. 從而更加快速精準(zhǔn)的定位異常原因, 因此, 重構(gòu)后增加了大量的測(cè)試用例, 用以排錯(cuò).

  3. 統(tǒng)一配置類, 使用@ComponentScans()注解掃描一個(gè)共同的配置模塊, 方便重構(gòu)和配置.否則, 大量的配置類將不同統(tǒng)一配置, 我們將疲于奔命.

  4. SpringCloud Feign 客戶端第一次遠(yuǎn)程調(diào)用可能會(huì)失敗, 原因是由于Spring 是懶加載的, 調(diào)用Feign需要加載很多類,需要一些時(shí)間, 而Feign 默認(rèn)超時(shí)一秒就會(huì)認(rèn)為服務(wù)調(diào)用失敗, 拋出異常。 因此我們測(cè)試時(shí)只需要關(guān)注第二次調(diào)用即可。



總結(jié):至此,我們知道了什么?

1. 什么是微服務(wù)? 什么是 springcloud?
2. 我們?yōu)槭裁词褂?SpringCloud?
3. 如何使用 SpringCloud? 如何 Quick Start?
4. 遷移過(guò)程中,老代碼使用 springcloud 需要注意哪些坑?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容