JAVA微服務拆分架構(業(yè)務,規(guī)劃,設計,實現)
一、微服務演變過程版本1.0
幾年前,小明和小皮一起創(chuàng)業(yè)做網上超市。小明負責程序開發(fā),小皮負責其他事宜。當時互聯網還不發(fā)達,網上超市還是藍海。只要功能實現了就能隨便賺錢。所以他們的需求很簡單,只需要一個網站掛在公網,用戶能夠在這個網站上瀏覽商品、購買商品;另外還需一個管理后臺,可以管理商品、用戶、以及訂單數據。
我們整理一下功能清單:
- 網站
-
- 用戶注冊、登錄功能
- 商品展示
- 下單
- 管理后臺
-
- 用戶管理
- 商品管理
- 訂單管理
由于需求簡單,小明左手右手一個慢動作,網站就做好了。管理后臺出于安全考慮,不和網站做在一起,小明右手左手慢動作重播,管理網站也做好了。總體架構圖如下:
小明揮一揮手,找了家云服務部署上去,網站就上線了。上線后好評如潮,深受各類肥宅喜愛。小明小皮美滋滋地開始躺著收錢。
版本2.0
好景不長,沒過幾天,各類網上超市緊跟著拔地而起,對小明小皮造成了強烈的沖擊。
在競爭的壓力下,小明小皮決定開展一些營銷手段:
- 開展促銷活動。比如元旦全場打折,春節(jié)買二送一,情人節(jié)狗糧優(yōu)惠券等等。
- 拓展渠道,新增移動端營銷。除了網站外,還需要開發(fā)移動端App,微信小程序等。
- 精準營銷。利用歷史數據對用戶進行分析,提供個性化服務。
這些活動都需要程序開發(fā)的支持。小明拉了同學小紅加入團隊。小紅負責數據分析以及移動端相關開發(fā)。小明負責促銷活動相關功能的開發(fā)。
因為開發(fā)任務比較緊迫,小明小紅沒有好好規(guī)劃整個系統(tǒng)的架構,隨便拍了拍腦袋,決定把促銷管理和數據分析放在管理后臺里,微信和移動端APP另外搭建。通宵了幾天后,新功能和新應用基本完工。這時架構圖如下:
這一階段存在很多不合理的地方:
- 網站和移動端應用有很多相同業(yè)務邏輯的重復代碼。
- 數據有時候通過數據庫共享,有時候通過接口調用傳輸。接口調用關系雜亂。
- 單個應用為了給其他應用提供接口,漸漸地越改越大,包含了很多本來就不屬于它的邏輯。應用邊界模糊,功能歸屬混亂。
- 管理后臺在一開始的設計中保障級別較低。加入數據分析和促銷管理相關功能后出現性能瓶頸,影響了其他應用。
- 數據庫表結構被多個應用依賴,無法重構和優(yōu)化。
- 所有應用都在一個數據庫上操作,數據庫出現性能瓶頸。特別是數據分析跑起來的時候,數據庫性能急劇下降。
- 開發(fā)、測試、部署、維護愈發(fā)困難。即使只改動一個小功能,也需要整個應用一起發(fā)布。有時候發(fā)布會不小心帶上了一些未經測試的代碼,或者修改了一個功能后,另一個意想不到的地方出錯了。為了減輕發(fā)布可能產生的問題的影響和線上業(yè)務停頓的影響,所有應用都要在凌晨三四點執(zhí)行發(fā)布。發(fā)布后為了驗證應用正常運行,還得盯到第二天白天的用戶高峰期……
- 團隊出現推諉扯皮現象。關于一些公用的功能應該建設在哪個應用上的問題常常要爭論很久,最后要么干脆各做各的,或者隨便放個地方但是都不維護。
盡管有著諸多問題,但也不能否認這一階段的成果:快速地根據業(yè)務變化建設了系統(tǒng)。不過緊迫且繁重的任務容易使人陷入局部、短淺的思維方式,從而做出妥協式的決策。在這種架構中,每個人都只關注在自己的一畝三分地,缺乏全局的、長遠的設計。長此以往,系統(tǒng)建設將會越來越困難,甚至陷入不斷推翻、重建的循環(huán)。
版本3.0
幸好小明和小紅是有追求有理想的好青年。意識到問題后,小明和小紅從瑣碎的業(yè)務需求中騰出了一部分精力,開始梳理整體架構,針對問題準備著手改造。
要做改造,首先你需要有足夠的精力和資源。如果你的需求方(業(yè)務人員、項目經理、上司等)很強勢地一心追求需求進度,以致于你無法挪出額外的精力和資源的話,那么你可能無法做任何事……
在編程的世界中,最重要的便是抽象能力。微服務改造的過程實際上也是個抽象的過程。小明和小紅整理了網上超市的業(yè)務邏輯,抽象出公用的業(yè)務能力,做成幾個公共服務:
- 用戶服務
- 商品服務
- 促銷服務
- 訂單服務
- 數據分析服務
各個應用后臺只需從這些服務獲取所需的數據,從而刪去了大量冗余的代碼,就剩個輕薄的控制層和前端。這一階段的架構如下:
這個階段只是將服務分開了,數據庫依然是共用的,所以一些煙囪式系統(tǒng)的缺點仍然存在:
- 數據庫成為性能瓶頸,并且有單點故障的風險。
- 數據管理趨向混亂。即使一開始有良好的模塊化設計,隨著時間推移,總會有一個服務直接從數據庫取另一個服務的數據的現象。
- 數據庫表結構可能被多個服務依賴,牽一發(fā)而動全身,很難調整。
如果一直保持共用數據庫的模式,則整個架構會越來越僵化,失去了微服務架構的意義。因此小明和小紅一鼓作氣,把數據庫也拆分了。所有持久化層相互隔離,由各個服務自己負責。另外,為了提高系統(tǒng)的實時性,加入了消息隊列機制。架構如下:
完全拆分后各個服務可以采用異構的技術。比如數據分析服務可以使用數據倉庫作為持久化層,以便于高效地做一些統(tǒng)計計算;商品服務和促銷服務訪問頻率比較大,因此加入了緩存機制等。
還有一種抽象出公共邏輯的方法是把這些公共邏輯做成公共的框架庫。這種方法可以減少服務調用的性能損耗。但是這種方法的管理成本非常高昂,很難保證所有應用版本的一致性。 數據庫拆分也有一些問題和挑戰(zhàn):比如說跨庫級聯的需求,通過服務查詢數據顆粒度的粗細問題等。但是這些問題可以通過合理的設計來解決。總體來說,數據庫拆分是一個利大于弊的。
微服務架構還有一個技術外的好處,它使整個系統(tǒng)的分工更加明確,責任更加清晰,每個人專心負責為其他人提供更好的服務。在單體應用的時代,公共的業(yè)務功能經常沒有明確的歸屬。最后要么各做各的,每個人都重新實現了一遍;要么是隨機一個人(一般是能力比較強或者比較熱心的人)做到他負責的應用里面。在后者的情況下,這個人在負責自己應用之外,還要額外負責給別人提供這些公共的功能——而這個功能本來是無人負責的,僅僅因為他能力較強/比較熱心,就莫名地背鍋(這種情況還被美其名曰能者多勞)。結果最后大家都不愿意提供公共的功能。長此以往,團隊里的人漸漸變得各自為政,不再關心全局的架構設計。
問題:
如果沒有注冊中心,URL異常麻煩; 大量配置,遠程訪問很麻煩; 大量配置,配置沒有統(tǒng)一管理; 服務的熔斷降級; 鏈路追蹤; 需要一些服務進行支撐;版本4.0
網關可以做,統(tǒng)一的鑒權,權限控制。
二、微服務拆分劃分原則(面試:考核業(yè)務劃分的想法)
我們拆分微服務的時候需要按照某些原則進行拆分.
- 基于業(yè)務邏輯
-
- 將系統(tǒng)中的業(yè)務按照職責范圍進行識別,職責相同的劃分為一個單獨的服務。
- 基于穩(wěn)定性
-
- 將系統(tǒng)中的業(yè)務模塊按照穩(wěn)定性進行排序。穩(wěn)定的、不經常修改的劃分一塊;將不穩(wěn)定的,經常修改的劃分為一個獨立服務。比如日志服務、監(jiān)控服務都是相對穩(wěn)定的服務,可以歸到一起。
- 基于可靠性
-
- 同樣,將系統(tǒng)中的業(yè)務模塊按照可靠性進行排序。對可靠性要求比較高的核心模塊歸在一起,對可靠性要求不高的非核心模塊歸在一塊。
- 這種拆分的高明可以很好的規(guī)避因為一顆老鼠屎壞了一鍋粥的單體弊端,同時將來要做高可用方案也能很好的節(jié)省機器或帶寬的成本。
- 基于高性能
-
- 同上,將系統(tǒng)中的業(yè)務模塊按照對性能的要求進行優(yōu)先級排序。把對性能要求較高的模塊獨立成一個服務,對性能要求不高的放在一起。比如全文搜索,商品查詢和分類,秒殺就屬于高性能的核心模塊。
基于上訴的拆分原則,我們可以針對騾窩窩項目進行微服務的拆分:
- 游記服務
-
- 目的地管理
- 旅游攻略
- 旅游日記
- 評論服務
-
- 旅游功能評論
- 旅游日記評論
- 景點評論
- 旅行社評論
- 用戶服務
-
- 用戶個人中心
- 用戶積分相關
- 黑名單/白名單
- 粉絲關注
- 消息服務
-
- 短信通知
- 郵件通知
- 站內信
- 搜索服務
-
- 攻略搜索
- 游記搜搜
- 用戶搜索
- 景點搜索
假如我們按照業(yè)務來劃分,根據粒度大小,可能存在以下兩種:
- 第一種分為商品、交易、用戶3個服務;
- 第二種分為商品、訂單、支付、物流、買家、賣家6個服務。
3 VS 6,這該怎么辦?
如果你的團隊只有9個人,那么分成3個是合理的,如果有18個人,那么6個服務是合理的。這里引入團隊成員進行協助拆分。
在拆分遇到爭議的時候,一般情況下我們增加一項拆分條件,雖然不是充要條件,但至少我們的答案會更加接近真理。
除了業(yè)務可能存在爭議,其他的劃分也會有爭議,比如一個獨立的服務到底需要多少人員的配置?
為什么說是三個人分配一個服務(當然,成員主要是后端人員)?
- 假設是1個人,請個假、生個病都不行。一個人會遇到單點的問題,所以不合理。
- 假設是2個人,終于有備份了,但是抽離一個后,剩下1個壓力還是很大,不合理。
- 假設是3個人,抽離一個還有2個在。而且數字3是個穩(wěn)定而神奇數字,用得好事半功倍。特別是遇到技術討論,3個人相對周全,如果是2個可能會各持己見,帶有自我的偏見和盲區(qū)。
那么這個3是不是就是穩(wěn)定的數量呢?
假設你做的是邊開飛機邊換引擎的重寫工作,那么前期3個人都可能捉襟見肘。但是到了服務后期,你可能1個就夠了。
微服務實踐項目結構圖
基礎模塊搭建01.parent模塊
管理統(tǒng)一依賴;
依賴:
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
1.8
Greenwich.SR1
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
02.注冊中心Euraka
提供服務的注冊和發(fā)現功能;
依賴:eureka-server
org.springframework.cloud
spring-cloud-starter.NETflix-eureka-server
配置application.yml
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/euraka
fetch-registry: false
register-with-eureka: false
03.配置中心Config-Server
依賴
eureka-client:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
config-server:
org.springframework.cloud
spring-cloud-config-server
配置application.yml
server:
port: 9100
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri:
username:
password:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
04.網關Zuul
依賴
eureka-client:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
netflix-zuul:
org.springframework.cloud
spring-cloud-starter-netflix-zuul
config-client:
org.springframework.cloud
spring-cloud-config-client
配置
bootstrap.yml:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: zuul-server
cloud:
config:
discovery:
enabled: true
service-id: config-server
label: master
name: zuul-server
遠程托管平臺的文件:zuul-server.yml:
port: 9000
ribbon:
ConnectTimeout: 3000
ReadTimeout: 60000
zuul:
sensitive-headers:
forceOriginalQueryStringEncoding: true #強制采用原始請求的編碼格式,即不對Get請求參數做編解碼
ignored-patterns: /*-server/** #忽略匹配這個格式的路徑
05.公共模塊common
依賴
lombok:
org.projectlombok
lombok
web:
org.springframework.boot
spring-boot-starter-web
06.服務接口父模塊provider-api
07.服務父模塊provider-server
前端項目
本來由前端人員寫好的前端項目;
創(chuàng)建frontend-website組件,和任何項目沒有關系,引入依賴:
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
org.springframework.boot
spring-boot-starter-web
創(chuàng)建啟動類。
啟動發(fā)出注冊請求,發(fā)現訪問的是8080端口,微服務的項目,不再是直接訪問到某個Tomcat服務器了。而是需要通過網關:
微服務結構,請求發(fā)到 zuul 網關,當zuul接受到請求后,首先會由前置過濾器進行處理,然后在由路由過濾器具體把請求轉發(fā)到后端應用,然后在執(zhí)行后置過濾器把執(zhí)行結果返回到請求方。
修改static/js/vue/common.js中的url訪問的端口;
微服務架構發(fā)送請求到返回數據的執(zhí)行流程:
聚合服務website-server結構&依賴關系處理
在provider-server模塊中添加依賴,因為下面三個依賴是其模塊下所有的server模塊都要用到的,所以公共所有的依賴在方法父項目中添加,子項目可以繼承到:
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-config-client
配置文件
bootstrap.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: website-server
cloud:
config:
discovery:
enabled: true
service-id: config-server
label: master
name: website-server
碼云的website-server.yml
server:
port: 8082
定義路由規(guī)則
碼云的zuul-server.yml
zuul:
routes:
member-server-route:
path: /website/**
service-id: website-server
加啟動類啟動測試:
跨域問題:直接在網關位置就可以處理跨域的問題:
跨域原因:域名、端口、ip不一樣的時候會產生跨域問題。默認不允許跨域的原因:對被訪問的域外資源來說,服務器壓力變大了,假設不信任的惡意攻擊可能導致訪問量過大宕機。所有需要對信任的域名做配置。預檢請求會有一個緩存,除了第一次之外其他的都是一次請求。詳情見單獨的文檔;
在zuul-server中設置跨域:
解決跨域的問題:設置的位置,在zuul-server中的啟動類中添加設置:
// 解決跨域問題
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
// 允許cookies跨域
config.setAllowCredentials(true);
// #允許向該服務器提交請求的URI,*表示全部允許,在SpringMVC中,如果設成*,會自動轉成當前請求頭中的Origin
config.addAllowedOrigin("*");
// #允許訪問的頭信息,*表示全部
config.addAllowedHeader("*");
// 預檢請求的緩存時間(秒),即在這個時間段里,對于相同的跨域請求不會再預檢了
config.setMaxAge(18000L);
// 允許提交請求的方法,*表示全部允許
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
// 允許Get的請求方法
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
測試發(fā)現跨域的錯誤信息已經沒有了。接下來寫controller來處理我們的請求;
響應類&統(tǒng)一異常:
controller的設計:返回類型需要確定
返回值:以前單體項目返回的是Object;現在是微服務項目,我們希望有一個狀態(tài)碼,成功帶上數據,錯誤帶上錯誤信息:
返回類型應該是什么樣的?那么應該怎么定義字段:
所有的返回對象都是Result對象,具體的數據封裝在result對象的data中;
在controller中,會調用到各種業(yè)務層的業(yè)務方法:業(yè)務層中必須考慮到執(zhí)行過程中出現異常的情況,需要回滾:
設計的錯誤狀態(tài)碼和錯誤信息是一一對應的,應該是常量(枚舉類或者自定義類來封裝他們)
自定義異常:來解決業(yè)務方法執(zhí)行過程中出現的異常,即拋出的異常里要包含了剛剛封裝了錯誤狀態(tài)碼和錯誤信息的對象,在throw的時候返回給調用者,由調用者(controller)來處理。
業(yè)務層的業(yè)務方法中寫操作等,必須要保持一致性(事務),當出現異常的時候需要將異常throw出去。
聚合服務中,需要對有可能出現的各種異常進行統(tǒng)一的處理,AOP思想。出現異常,將封裝了狀態(tài)碼和異常信息的result返回給前臺,前臺才能夠接受到具體的錯誤和錯誤信息。
實現:異常的處理基本的類是所有的微服務都需要有的,所以放到common中
CodeMsg
* 封裝狀態(tài)碼和信息
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CodeMsg implements Serializable {
private Integer code;
private String msg;
Result
* 返回前臺的數據類型
* @param
@Setter
@Getter
public class Result implements Serializable {
public static final int SUCCESS_CODE = 200;//成功碼.
public static final String SUCCESS_MESSAGE = "操作成功";//成功信息.
public static final int ERROR_CODE = 500000;//錯誤碼.
public static final String ERROR_MESSAGE = "系統(tǒng)異常";//錯誤信息.
private int code;
private String msg;
private T data;
public Result(){}
private Result(int code, String msg, T data){
this.code = code;
this.msg = msg;
this.data = data;
public static Result success(T data){
return new Result(SUCCESS_CODE,SUCCESS_MESSAGE,data);
public static Result success(String msg,T data){
return new Result(SUCCESS_CODE,msg,data);
public static Result error(CodeMsg codeMsg){
return new Result(codeMsg.getCode(),codeMsg.getMsg(),null);
public static Result defaultError(){
return new Result(ERROR_CODE,ERROR_MESSAGE,null);
public boolean hasError(){
//狀態(tài)嗎!=200 說明有錯誤.
return this.code!=SUCCESS_CODE;
BusinessException
* 自定義異常
@Setter
@Getter
public class BusinessException extends RuntimeException {
private CodeMsg codeMsg;
public BusinessException(CodeMsg codeMsg){
this.codeMsg = codeMsg;
CommonExceptionAdvice
* 公共的sdvice,不貼 @ControllerAdvice 注解,因為它是讓其他服務來繼承的基類
public class CommonExceptionAdvice {
@ExceptionHandler(Exception.class)
@ResponseBody
public Result hanlderDefaultException(Exception e){
e.getMessage();
return Result.defaultError();
會員服務:結構&依賴關系處理模塊provider-api中添加依賴:
因為該模塊下的所有的api都要提供給其他的服務來調用,所以需要在api的父項目中添加這三個依賴:
cn.wolfcode.xloud.luowowo
common
1.0.0
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
模塊member-api:暴露會員服務接口
用戶的信息使用mongodb存儲的,所以要引入mongodb的依賴:
org.springframework.boot
spring-boot-starter-data-mongodb
true
UserInfo
@Setter
@Getter
@Document("userInfo")@ToString
public class UserInfo implements Serializable {
public static final int GENDER_SECRET = 0; //保密
public static final int GENDER_MALE = 1; //男
public static final int GENDER_FEMALE = 2; //女
public static final int STATE_NORMAL = 0; //正常
public static final int STATE_DISABLE = 1; //凍結
@Id
protected String id;
private String nickname; //昵稱
private String phone; //手機
private String email; //郵箱
private String password; //密碼
private int gender = GENDER_SECRET; //性別
private int level = 0; //用戶級別
private String city; //所在城市
private String headImgUrl; //頭像
private String info; //個性簽名
private int state = STATE_NORMAL; //狀態(tài)
模塊member-server:會員服務,api負責暴露接口,這里負責具體的業(yè)務
添加依賴:
member-api依賴、redis依賴、fastjson依賴
cn.wolfcode.xloud.luowowo
menber-api
1.0.0
org.springframework.boot
spring-boot-starter-data-redis
com.alibaba
fastjson
1.2.47
bootstrap.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: member-server
cloud:
config:
discovery:
enabled: true
service-id: config-server
label: master
name: member-server,redis
member-server.yml
server:
port: 8080
spring:
data:
mongodb:
uri: mongodb://192.168.20.130:27017/member
創(chuàng)建微服務對應的數據庫,拆分以前單體項目的表:
![image-2020051621441662
在模塊menber-server中準備repository接口,來訪問mongodb數據庫的接口;
@Repository
public interface UserInfoRepository extends MongoRepository {
* 通過號碼查詢用戶信息
* @param phone
* @return
UserInfo findByPhone(String phone);
* 通過號碼和莫密碼查詢用戶信息
* @param username
* @param password
* @return
UserInfo getByPhoneAndPassword(String username, String password);
服務之間調用:
聚合服務需要調用會員服務中的方法。需要在menber-api中用feign負載均衡將menber-server的業(yè)務方法暴露出來一個接口;
// 類似controller
@FeignClient(name = "member-server") //負載訪問的服務
public interface MemberFeignApi {
@RequestMapping("/checkPhone") //映射方法
Result checkPhone(@RequestParam("phone") String phone); //映射參數
返回類型是
Result
的原因:
因為,基礎服務也有可能出現異常,直接返會封裝了錯誤狀態(tài)碼和錯誤信息的Result 回去,聚合服務可以直接通過result中boolean值來區(qū)分對異常的處理情況;
在member-server中,需要對接口做具體的實現了:
需要在server中對異常做統(tǒng)一的處理:
每個服務的異常有不相同的,比如注冊的錯和登錄的錯就不一樣,怎么體現各個服務之間的異常差異呢?可能會有其他的異常比如 連接數據庫超時的異常。自己實現具體的CoreMsg:
* member-server服務中返回的異常結果:封裝code和msg
public class MemberServerCodeMsg extends CodeMsg {
//用父類的構造器來完成初始化操作
public MemberServerCodeMsg(Integer code, String msg){
super(code, msg);
public static final MemberServerCodeMsg DEFAULT_ERROR =
new MemberServerCodeMsg(500100, "會員服務繁忙!");
public static final MemberServerCodeMsg PHONE_EXIST_ERROR =
new MemberServerCodeMsg(500101, "手機號碼已存在!");
異常的advice增強類,繼承增強基類:
* 需要在該增強類中處理member-server中特有的異常
@ControllerAdvice
public class MemberServerExceptionAdvice extends CommonExceptionAdvice {
* 處理普通的Exception
* @param ex
* @return
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handlerDefault(Exception ex){
ex.printStackTrace();
return Result.error(MemberServerCodeMsg.DEFAULT_ERROR); //返回自己的默認異常
那具體拋出哪一個server中的異常就看在呢一個server中出現異常了;
熔斷降級:
既然可能在某個服務中會出現異常,那我們就不能讓出現異常的時候使服務出現宕機而影響其它服務的正常運行。所以應該要想到對服務做熔斷降級的處理;
* 對服務可能出現異常做熔斷降級處理
@Component
public class UserInfoFeignHystrix implements UserInfoFeignApi{
@Override
public Result checkPhone(String phone) {
//熔斷降級之后,應該做的有些處理
return null;
回退類和方法完成之后,在api接口的注解上加上:
注意:feign默認是沒有開啟Hystrix的,需要添加配置進行開啟:直接放到碼云中的website-server的配置文件中
# hystrix默認是關閉的,需要手動開啟一下,否則一直是超時的
feign:
hystrix:
enabled: true
到此,member-server的模塊就完成了,當服務出現熔斷會調用到降級方法;
注入服務接口對象,完成遠程調用:
注入需要引入menber-api的依賴:
返回的數據應該是什么樣的,怎么解決出現異常的情況:
實現自己服務的返回CodeMsg:
public class WebsiteServerCodeMsg extends CodeMsg {
public WebsiteServerCodeMsg(Integer code, String msg) {
super(code, msg);
public static final WebsiteServerCodeMsg MEMBER_SERVER_ERROR =
new WebsiteServerCodeMsg(500201, "會員服務繁忙!");
public static final WebsiteServerCodeMsg DEFAULT_ERROR =
new WebsiteServerCodeMsg(500200, "聚合服務繁忙!");
controller
@RestController
@RequestMapping("/userRegister")
public class UserRegisterController {
@Autowired
private UserInfoFeignApi userInfoFeignApi;
@RequestMapping("/checkPhone")
public Result checkPhone(String phone){
//檢查號碼
Result result = userInfoFeignApi.checkPhone(phone);
//result返回的情況有3種,需要區(qū)分開
if (result == null){
//返回null。說明member-server服務宕機了,對應的CodeMsg應該重新設計
return Result.error(WebsiteServerCodeMsg.MEMBER_SERVER_ERROR);
//到這里,表示遠程調用成功,遠程的服務返回的是一個result,直接返回即可
//返回參數
return result;
還需要在啟動類上加注解:@EnableFeignClients,spring才能掃描到API接口,才能創(chuàng)建代理對象,代理對象才能發(fā)起服務器之間的遠程調用。
website-server對異常的統(tǒng)一處理:
* 對website-server的異常統(tǒng)一處理
@ControllerAdvice
public class WebsiteServerExceptionAdvice extends CommonExceptionAdvice{
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handlerDefaultException(Exception ex){
ex.printStackTrace();
return Result.error(WebsiteServerCodeMsg.DEFAULT_ERROR);
啟動測試:
兩處錯誤:
最終:
微服務架構逐漸成熟,如何做到相對獨立,成了當前重點考慮的問題。
我將分割了以下基礎模塊:
user-service 用戶認證服務
shopping-cart-service 購物車服務
info-version-service 信息版本服務
user-edit-service 用戶編輯服務
order-service 訂單服務
seller-info-service 店鋪信息
discount-Service 折扣 促銷服務
inventory-service 庫存服務
account-service 賬戶會員服務
payment-service 支付組件
promotion-service 促銷服務Base
Search-service 搜索服務
concern-Service 互動服務
tracks-service 足跡
Cms Search-service CMS搜索服務
Seller-service 商戶認證服務
product-Mgr-service 商品管理服務
Order-mgr-service 訂單服務
Catalog-mgr-service 產品目錄
Seller-edit-service 商戶編輯服務
sendout-service 發(fā)貨服務/推送
Grant-service 權限服務
employee-service 雇員認證服務
employee-mgr-service 雇員服務
Promotion-mgr-Service 促銷管理
static-service 靜態(tài)化服務
product-Mgr-service 商品管理服務
Order-mgr-service 訂單服務
Catalog-mgr-service 產品目錄
dict-service 字典管理服務
Cms-service CMS服務
Seller-edit-service 商戶編輯服務