Spring Cloud(一)—— Eureka讓你的服務(wù)被發(fā)現(xiàn)

Spring Cloud的系列文章考阱,按理來說應(yīng)該從微服務(wù)的介紹開始,我的確是這樣做的粘姜。在開始本文之前鬓照,我還寫了一篇介紹微服務(wù)的文章,然而效果并不滿意孤紧,所以暫且不發(fā)豺裆,網(wǎng)上關(guān)于微服務(wù)概念的介紹很是泛濫,其中不乏優(yōu)質(zhì)文章号显,也無需我在其中濫竽充數(shù)臭猜。我希望在完成整個Spring Cloud系列文章后,再重頭寫這篇引文押蚤,我會在其中針對性的埋下雷點蔑歌,讓讀者能夠從解決問題的角度,來認識微服務(wù)和Spring Cloud提供的構(gòu)建揽碘、部署工具次屠,以求最大之成效。今天聊的是微服務(wù)的服務(wù)發(fā)現(xiàn)工具Eureka钾菊。
我認為在技術(shù)學(xué)習(xí)的過程中帅矗,一個不小的障礙來自我們對技術(shù)名詞預(yù)設(shè)的難度∩诽蹋“面向切面編程”浑此、“服務(wù)發(fā)現(xiàn)”可能就是這樣一類,聽上去挺復(fù)雜滞详,其實也就那么回事兒凛俱。

假設(shè)有這樣一個場景紊馏,我們在豆瓣上瀏覽電影信息,那么服務(wù)端電影基本信息蒲犬,電影豆瓣評分和豆瓣用戶信息由三個服務(wù)分別提供朱监。

簡單的微服務(wù)架構(gòu)

可以想見,無論這個client是客戶端還是其它的service原叮,我們在coding的時候赫编,都需要將這三個服務(wù)的ip和port作為配置項記錄下來,然后在對應(yīng)的請求處調(diào)用》芰ィ現(xiàn)在看來不需要發(fā)現(xiàn)機制擂送,用配置文件管理就可以!
然而隨著項目進行唯欣,用戶量的增加嘹吨,為了保證產(chǎn)品的健壯,我們可能對于一些重要服務(wù)進行分布式部署境氢,比如movie info是咱們網(wǎng)站的主要提供內(nèi)容蟀拷,我們會將同一個movie info application部署到多個實例上,這樣一來萍聊,即便訪問量增加问芬,也可以保證每個實例的負載在可承受的范圍內(nèi),同時脐区,一旦其中一臺服務(wù)器宕機愈诚,整個系統(tǒng)仍然可以正常運轉(zhuǎn),此時的架構(gòu)如下:
對某個服務(wù)進行分布式部署

這個時候會讓程序員為難牛隅,之前在配置文件中,對于movie info application服務(wù)直接用ip1:port1指代酌泰,現(xiàn)在一個服務(wù)由3個ip-port指代媒佣,代碼中如何體現(xiàn)呢?另外我們怎么知道某一時刻應(yīng)該訪問哪一個服務(wù)呢陵刹?當(dāng)然你可以用很tricky的方法默伍,比如將新增的服務(wù)地址一樣寫入配置文件,然后針對每一個請求都以輪詢的方式調(diào)用不同的配置地址衰琐。不難看出這種寫法擴展性差也糊,而且還有一個隱患,如果我們的服務(wù)是在云端羡宙,往往服務(wù)器的ip地址動態(tài)狸剃,因為產(chǎn)品擴容,發(fā)布失敗等原因狗热,所在服務(wù)器的ip地址會發(fā)生改變钞馁,類似以上方式虑省,將ip-port硬編碼在配置文件中,可能會導(dǎo)致不停修改配置文件的尷尬局面僧凰。
基于此我們希望有這樣一個中間件探颈,服務(wù)跑起來的時候,會主動去告訴中間件“我是誰训措,我在哪”伪节,中間件記錄該服務(wù),客戶端在請求的時候绩鸣,只要告訴中間件對應(yīng)服務(wù)的名字架馋,就會獲得該服務(wù)的真實路徑。這就是服務(wù)注冊和服務(wù)發(fā)現(xiàn)的過程:
服務(wù)注冊
服務(wù)發(fā)現(xiàn)

下面認識一下本文的主角——Eureka /ju?ri?k?/
Eureka起初是Netflix(制作過紙牌屋全闷、絕命毒師)因為自身微服務(wù)項目孵化出來的開源產(chǎn)品叉寂,Spring將其融合進了Spring Cloud全家桶,因此你在Spring官網(wǎng)找相關(guān)資源時总珠,實際上它是在Spring Cloud Netflix項目當(dāng)中屏鳍。
在Eureka的官方描述當(dāng)中對其定義和架構(gòu)有所描述:Eureka是CS架構(gòu),其服務(wù)端是一個基于REST(具象狀態(tài)傳輸)的服務(wù)局服,主要用于AWS云中定位服務(wù)钓瞭,以實現(xiàn)中間層服務(wù)器的負載平衡和故障轉(zhuǎn)移;客戶端設(shè)有內(nèi)置的基礎(chǔ)輪詢式的負載均衡器淫奔。所以我們回復(fù)一下之前的實際問題山涡,服務(wù)消費者發(fā)起對movie-info-application的請求,該請求會到達Eureka服務(wù)發(fā)現(xiàn)唆迁,查詢注冊表鸭丛,將所有名叫movie-info-application的服務(wù)地址返回,再由Eureka Client中的負載均衡模塊經(jīng)過計算唐责,確定最終訪問哪一個地址并進行訪問鳞溉。
我們看一下Netflix自建的Eureka高級架構(gòu)

Eureka高級架構(gòu)

圖中的三個Eureka Server可以看作是一個Eureka集群(cluster),一個region(圖中的region為us-east-1鼠哥,這是aws里面的概念)會部署一個集群熟菲,每個zone(圖中zone為c、d朴恳、e)中最少部署一個Eureka Server抄罕。服務(wù)提供方(Application Service)和服務(wù)消費方(Application Client)都會集成Eureka Client。就服務(wù)提供方(圖中最左側(cè)的Application Service)而言于颖,每次項目啟動之后呆贿,都會向us-east-1c中的Eureka Server進行注冊,注冊成功后恍飘,不同的Eureka Sever之間會進行注冊表的拷貝榨崩,保證注冊表同步谴垫。服務(wù)提供方在默認情況下會每個30s向Eureka發(fā)一次Renew請求,告訴它自己還活著母蛛,避免自己從注冊表中被踢掉翩剪,也就是心跳檢測。如果Eureka在90s內(nèi)沒有收到某個服務(wù)的renew請求彩郊,則將其從注冊表中除名前弯。如果服務(wù)掛了,或者正常關(guān)閉秫逝,則服務(wù)提供方會發(fā)送cancel請求給Eureka告訴它刪除注冊表記錄恕出。另一方面,當(dāng)服務(wù)消費方需要調(diào)用服務(wù)時违帆,它會向與同一zone的Eureka發(fā)出get registry請求浙巫,獲取注冊表,而后由Eureka Client自帶的負載均衡模塊決定具體的服務(wù)器地址刷后,從而進行真正的服務(wù)請求(make remote call)的畴。

我們可以快速的構(gòu)建三個Eureka項目,其中一個作為Eureka Server尝胆,剩余為Eureka Client丧裁。

實踐步驟:

  • 創(chuàng)建Eureka Server
  • 通過Eureka Client在Server中注冊各個微服務(wù)
  • 通過Eureka Client調(diào)用其它微服務(wù)
  1. 創(chuàng)建Eureka Server
    -> 進入https://start.spring.io/選擇add dependency:Eureka Server
    -> Download
    Spring Initialzr

    -> 配置Eureka Server application.properties

eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defalt-zone=http://${eureka.instance.hostname}:${server.port}/eureka/

-> 在項目入口添加@EnableEurekaServer注解

@SpringBootApplication
@EnableEurekaServer
public class SpringcloudEurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringcloudEurekaServerApplication.class, args);
    }

}

此時我們直接運行項目,然后訪問默認的8080端口含衔,可以看到Eureka控制臺煎娇。可見當(dāng)前沒有任何注冊到Server的服務(wù)(No instances availavle)
Eureka Server Console

如果你留意一下此時的項目日志贪染,你會發(fā)現(xiàn)系統(tǒng)一直在報錯:
錯誤提示

為什么會這樣缓呛!明明我們build的應(yīng)用就是eureka server,它怎么還在獲取all instance registry抑进!其實每個eureka server在默認情況下也是一個eureka client强经,還記得之前提到過,Netflix推薦使用eureka server集群么寺渗,它們相互之間互為eureka client,它們相互注冊兰迫,這樣一來即便一個server掛掉信殊,其它的Server也有它的注冊信息。在我們的demo中汁果,只有一個eureka server涡拘,它不需要跟任何其它的server進行replicate,配置application.properties
//通常情況据德,eureka server端口為8761
server.port=8761

eureka.instance.hostname=localhost
//作為client鳄乏,是否將自己注冊到Eureka Server跷车,默認為true
eureka.client.register-with-eureka=false
//作為client,是否從Eureka Server獲取注冊表信息橱野,默認為true
eureka.client.fetch-registry=false

通過配置告訴eureka server關(guān)閉client身份朽缴,僅作為獨立的server。

2.通過Eureka Client在Server中注冊各個微服務(wù)
在此之前水援,我們創(chuàng)建簡單的3個application密强,它們對應(yīng)的端口分別是本地的8082、8083和8084蜗元。

創(chuàng)建三個service:movie-catalog-service(8082)或渤,movie-info-service(8083),ratings-data-service(8084)奕扣。其中catalog會去調(diào)用info和data取一些數(shù)據(jù)薪鹦,業(yè)務(wù)邏輯不重要,關(guān)鍵在于以服務(wù)發(fā)現(xiàn)的形式調(diào)用惯豆。

  • movie-info-service

application.properties

//設(shè)置該應(yīng)用的名稱池磁,該名稱會展示在Eureka控制臺中,也是作為該服務(wù)在注冊表中的key值(value為ip:port)
spring.application.name=movie-info-service 
server.port=8083

MovieResource.java

@RestController
@RequestMapping("/movies")
public class MovieResource {

    @RequestMapping("/{movieId}")
    public Movie getMovieInfo(@PathVariable("movieId") String movieId) {
        //定義一個Movie.java這里不贅述
        return new Movie("1", "東邪西毒", "很好的一步電影");
    }
}
  • ratings-data-service
    application.properties
spring.application.name=ratings-data-service
server.port=8083

RatingsResource.java

@RestController
@RequestMapping("/ratingsdata")
public class RatingsResource {
    @RequestMapping("/user/{userId}")
    public UserRating getUserRatings(@PathVariable("userId") String userId) {
        UserRating userRating = new UserRating();
        userRating.setUserId(userId);
        //為了方便hard code循帐。UserRating.java不贅述
        userRating.setRating = 80框仔;
        return userRating;
    }
}
  • movie-catalog-service
    application.properties
spring.application.name=movie-catalog-service
server.port=8082

MovieCatalogServiceApplication.java

@SpringBootApplication
@EnableDiscoveryClient

public class MovieCatalogServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(MovieCatalogServiceApplication.class, args);
    }

    @LoadBalanced //加上該注解后,restTemplate才會將application-name作為key拄养,去注冊中心查找离斩,否則application-name會被當(dāng)作域名而無法訪問。
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

CatalogResource.java

@RestController
@RequestMapping("/catalog")
public class CatalogResource {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/{userId}")
    public List<CatalogItem> getCatalog(@PathVariable("userId") String userId) {
        //直接指定application name
        UserRating userRating = restTemplate.getForObject("http://RATINGS-DATA-SERVICE/ratingsdata/user/" + userId, UserRating.class);

        return userRating.getRatings().stream()
                .map(rating -> {
                    Movie movie = restTemplate.getForObject("http://MOVIE-INFO-SERVICE/movies/" + rating.getMovieId(), Movie.class);
                    return new CatalogItem(movie.getName(), movie.getDescription(), rating.getRating());
                })
                .collect(Collectors.toList());
    }
}

幾個model的為代碼:CatalogItem Movie Rating UserRating

public class CatalogItem {
    private String name;
    private String desc;
    private int rating;
}
public class Movie {
    private String movieId;
    private String name;
    private String description;
}
public class Rating {
    private String movieId;
    private int rating;
}
public class UserRating {

    private String userId;
    private int rating;
}

整個過程如下圖所示:


服務(wù)發(fā)現(xiàn)流程.png

這里需要說明的是如果同一個服務(wù)被分布式的存儲在多個服務(wù)器上瘪匿,舉個例子movie-info-application擠在192.168.0.113:8083上存在跛梗,也在192.168.0.124:8083存在,那么根據(jù)key在服務(wù)注冊服務(wù)器返回得到的是包含113和114的地址列表棋弥,這個列表返回給movie-catalog-service核偿,由ribbon進行負載均衡,從中選則一個ip作為請求發(fā)送的ip顽染。
以上是Eureka的基本介紹漾岳,下一節(jié)繼續(xù)Spring Cloud——斷路器!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粉寞,一起剝皮案震驚了整個濱河市尼荆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唧垦,老刑警劉巖捅儒,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡巧还,警方通過查閱死者的電腦和手機鞭莽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來麸祷,“玉大人澎怒,你說我怎么就攤上這事∫》妫” “怎么了丹拯?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荸恕。 經(jīng)常有香客問我乖酬,道長,這世上最難降的妖魔是什么融求? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任咬像,我火速辦了婚禮,結(jié)果婚禮上生宛,老公的妹妹穿的比我還像新娘县昂。我一直安慰自己,他們只是感情好陷舅,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布倒彰。 她就那樣靜靜地躺著,像睡著了一般莱睁。 火紅的嫁衣襯著肌膚如雪待讳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天仰剿,我揣著相機與錄音创淡,去河邊找鬼。 笑死南吮,一個胖子當(dāng)著我的面吹牛琳彩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播部凑,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼露乏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涂邀?” 一聲冷哼從身側(cè)響起施无,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎必孤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡敷搪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年兴想,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赡勘。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嫂便,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闸与,到底是詐尸還是另有隱情毙替,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布践樱,位于F島的核電站厂画,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拷邢。R本人自食惡果不足惜袱院,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞭稼。 院中可真熱鬧忽洛,春花似錦、人聲如沸环肘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悔雹。三九已至复哆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荠商,已是汗流浹背寂恬。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莱没,地道東北人初肉。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像饰躲,于是被迫代替她去往敵國和親牙咏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容