前言
哈嘍大家好,本人最近面試經(jīng)歷有點(diǎn)坎坷,很久沒更新了。但我打開公眾號(hào)發(fā)現(xiàn)粉絲居然還漲了,非常感謝各位一直以來的關(guān)注,接下來會(huì)整理一下最近面試遇到知識(shí)點(diǎn)分享給大家。
Eureka 是什么
Eureka是Netflix開發(fā)的服務(wù)發(fā)現(xiàn)框架,本身是一個(gè)基于REST的服務(wù),主要用于定位運(yùn)行在AWS域中的中間層服務(wù),以達(dá)到負(fù)載均衡和中間層服務(wù)故障轉(zhuǎn)移的目的。SpringCloud將它集成在其子項(xiàng)目spring-cloud-netflix中,以實(shí)現(xiàn)SpringCloud的服務(wù)發(fā)現(xiàn)功能。
架構(gòu)原理

- Register(服務(wù)注冊(cè)):把自己的IP和端口注冊(cè)給Eureka。
- Renew(服務(wù)續(xù)約):發(fā)送心跳包,每30秒發(fā)送一次。告訴Eureka自己還活著。
- Cancel(服務(wù)下線):當(dāng)provider關(guān)閉時(shí)會(huì)向Eureka發(fā)送消息,把自己從服務(wù)列表中刪除。防止consumer調(diào)用到不存在的服務(wù)。
- Get Registry(獲取服務(wù)注冊(cè)列表):獲取其他服務(wù)列表。
- Replicate(集群中數(shù)據(jù)同步):eureka集群中的數(shù)據(jù)復(fù)制與同步。
- Make Remote Call(遠(yuǎn)程調(diào)用):完成服務(wù)的遠(yuǎn)程調(diào)用。
Eureka包含兩個(gè)組件:Eureka Server和Eureka Client。
Eureka Server
Eureka Server提供服務(wù)注冊(cè)服務(wù),各個(gè)節(jié)點(diǎn)啟動(dòng)后,會(huì)在Eureka Server中進(jìn)行注冊(cè),這樣Eureka Server中的服務(wù)注冊(cè)表中將會(huì)存儲(chǔ)所有可用服務(wù)節(jié)點(diǎn)的信息,服務(wù)節(jié)點(diǎn)的信息可以在界面中直觀的看到。
Eureka Server本身也是一個(gè)服務(wù),默認(rèn)情況下會(huì)自動(dòng)注冊(cè)到Eureka注冊(cè)中心。
Eureka Server注冊(cè)中心集群中每個(gè)節(jié)點(diǎn)都是平等的,集群中的所有節(jié)點(diǎn)同時(shí)對(duì)外提供服務(wù)的發(fā)現(xiàn)和注冊(cè)等功能。同時(shí)集群中每個(gè)Eureka Server節(jié)點(diǎn)又是一個(gè)微服務(wù),也就是說,每個(gè)節(jié)點(diǎn)都可以在集群中的其他節(jié)點(diǎn)上注冊(cè)當(dāng)前服務(wù)。又因?yàn)槊總€(gè)節(jié)點(diǎn)都是注冊(cè)中心,所以節(jié)點(diǎn)之間又可以相互注冊(cè)當(dāng)前節(jié)點(diǎn)中已注冊(cè)的服務(wù),并發(fā)現(xiàn)其他節(jié)點(diǎn)中已注冊(cè)的服務(wù)。
如果搭建單機(jī)版的Eureka Server注冊(cè)中心,則需要配置取消Eureka Server的自動(dòng)注冊(cè)邏輯。畢竟當(dāng)前服務(wù)注冊(cè)到當(dāng)前服務(wù)代表的注冊(cè)中心中是一個(gè)說不通的邏輯。
Eureka Server通過Register、Get、Renew等接口提供服務(wù)的注冊(cè)、發(fā)現(xiàn)和心跳檢測(cè)等服務(wù)。
Eureka Server為了避免同時(shí)讀寫內(nèi)存數(shù)據(jù)結(jié)構(gòu)造成的并發(fā)沖突問題,還采用了多級(jí)緩存機(jī)制來進(jìn)一步提升服務(wù)請(qǐng)求的響應(yīng)速度。

1、在拉取注冊(cè)表的時(shí)候:
首先從ReadOnlyCacheMap里查緩存的注冊(cè)表。
若沒有,就找ReadWriteCacheMap里緩存的注冊(cè)表。
如果還沒有,就從內(nèi)存中獲取實(shí)際的注冊(cè)表數(shù)據(jù)。
2、在注冊(cè)表發(fā)生變更的時(shí)候:
會(huì)在內(nèi)存中更新變更的注冊(cè)表數(shù)據(jù),同時(shí)過期掉ReadWriteCacheMap。
此過程不會(huì)影響ReadOnlyCacheMap提供人家查詢注冊(cè)表。
一段時(shí)間內(nèi)(默認(rèn)30秒),各服務(wù)拉取注冊(cè)表會(huì)直接讀ReadOnlyCacheMap
30秒過后,Eureka Server的后臺(tái)線程發(fā)現(xiàn)ReadWriteCacheMap已經(jīng)清空了,也會(huì)清空ReadOnlyCacheMap中的緩存
下次有服務(wù)拉取注冊(cè)表,又會(huì)從內(nèi)存中獲取最新的數(shù)據(jù)了,同時(shí)填充各個(gè)緩存。
多級(jí)緩存機(jī)制的優(yōu)點(diǎn)是什么?
盡可能保證了內(nèi)存注冊(cè)表數(shù)據(jù)不會(huì)出現(xiàn)頻繁的讀寫沖突問題。
并且進(jìn)一步保證對(duì)Eureka Server的大量請(qǐng)求,都是快速從純內(nèi)存走,性能極高。
Eureka Client
Eureka Client是一個(gè)java客戶端,用于簡(jiǎn)化與Eureka Server的交互,客戶端同時(shí)也具備一個(gè)內(nèi)置的、使用輪詢(round-robin)負(fù)載算法的負(fù)載均衡器。
在應(yīng)用啟動(dòng)后,將會(huì)向Eureka Server發(fā)送心跳,默認(rèn)周期為30秒,如果Eureka Server在多個(gè)心跳周期內(nèi)沒有接收到某個(gè)節(jié)點(diǎn)的心跳,Eureka Server將會(huì)從服務(wù)注冊(cè)表中把這個(gè)服務(wù)節(jié)點(diǎn)移除(默認(rèn)90秒)。
Eureka Client分為兩個(gè)角色,分別是:Application Service(Service Provider)和Application Client(Service Consumer)
通過定時(shí)任務(wù)30秒拉取一次注冊(cè)表,30秒發(fā)起一次心跳
單機(jī)版
注冊(cè)中心依賴
pom.xml
<!-- spring cloud Eureka Server 啟動(dòng)器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
注冊(cè)中心啟動(dòng)類
EurekaServerApplication.java
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
注冊(cè)中心配置
application.yml
server: # 服務(wù)端口
port: 9090
spring:
application: # 應(yīng)用名字,eureka 會(huì)根據(jù)它作為服務(wù)id
name: spring-cloud-eureka-server
eureka:
instance:
hostname: localhost
client:
service-url: # eureka server 的地址, 咱們單實(shí)例模式就寫自己好了
defaultZone: http://localhost:9090/eureka
register-with-eureka: false # 不向eureka server 注冊(cè)自己
fetch-registry: false # 不向eureka server 獲取服務(wù)列表
服務(wù)提供者依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
服務(wù)提供者依賴
@SpringBootApplication
@EnableDiscoveryClient
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
服務(wù)提供者配置
application.yml
server:
port: 7070
spring:
application:
name: spring-cloud-order-service-provider
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9090/eureka
fetch-registry: true
register-with-eureka: true
服務(wù)提供者與服務(wù)消費(fèi)者配置依賴類似,只是消費(fèi)者需要restemplate進(jìn)行消費(fèi),而服務(wù)提供者需要暴露Controller接口給消費(fèi)者調(diào)用。
集群版
1、修改host文件
127.0.0.1 EurekaServerA
127.0.0.1 EurekaServerB
2、修改application.yml
這里是2個(gè)Eureka Server組成的集群,然后u1 的往u2 上面注冊(cè),u2往u1上面注冊(cè)
spring:
application:
name: spring-cloud-eureka-server
---
spring:
profiles: u1
eureka:
instance:
hostname: EurekaServerA
client:
service-url:
defaultZone: http://EurekaServerB:9091/eureka
register-with-eureka: true
fetch-registry: true
server:
port: 9090
---
spring:
profiles: u2
eureka:
instance:
hostname: EurekaServerB
client:
service-url:
defaultZone: http://EurekaServerA:9090/eureka
register-with-eureka: true
fetch-registry: true
server:
port: 9091
3、修改服務(wù)提供者或消費(fèi)者application.yml
server:
port: 7070
spring:
application:
name: spring-cloud-order-service-provider
eureka:
client:
service-url:
defaultZone: http://EurekaServerA:9090/eureka,http://EurekaServerB:9091/eureka
fetch-registry: true
register-with-eureka: true
instance:
prefer-ip-address: true # 使用ip注冊(cè)
#自定義實(shí)例顯示格式,添加版本號(hào)
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
服務(wù)保護(hù)
服務(wù)保護(hù)模式(自我保護(hù)模式)
一般情況下,微服務(wù)在Eureka上注冊(cè)后,會(huì)每30秒發(fā)送心跳包,Eureka通過心跳來判斷服務(wù)時(shí)候健康,同時(shí)會(huì)定期刪除超過90秒沒有發(fā)送心跳服務(wù)。
導(dǎo)致Eureka Server接收不到心跳包的可能:一是微服務(wù)自身的原因,二是微服務(wù)與Eureka之間的網(wǎng)絡(luò)故障。通常微服務(wù)的自身的故障只會(huì)導(dǎo)致個(gè)別服務(wù)出現(xiàn)故障,一般不會(huì)出現(xiàn)大面積故障,而網(wǎng)絡(luò)故障通常會(huì)導(dǎo)致Eureka Server在短時(shí)間內(nèi)無法收到大批心跳。慮到這個(gè)區(qū)別,Eureka設(shè)置了一個(gè)閥值,當(dāng)判斷掛掉的服務(wù)的數(shù)量超過閥值時(shí),Eureka Server認(rèn)為很大程度上出現(xiàn)了網(wǎng)絡(luò)故障,將不再刪除心跳過期的服務(wù)。
那么這個(gè)閥值是多少呢?Eureka Server在運(yùn)行期間,會(huì)統(tǒng)計(jì)心跳失敗的比例在15分鐘內(nèi)是否低于85%,如果低于85%,Eureka Server則任務(wù)是網(wǎng)絡(luò)故障,不會(huì)刪除心跳過期服務(wù)。
這種服務(wù)保護(hù)算法叫做Eureka Server的服務(wù)保護(hù)模式。
這種不刪除的,90秒沒有心跳的服務(wù),稱為無效服務(wù),但是還是保存在服務(wù)列表中。如果Consumer到注冊(cè)中心發(fā)現(xiàn)服務(wù),則Eureka Server會(huì)將所有好的數(shù)據(jù)(有效服務(wù)數(shù)據(jù))和壞的數(shù)據(jù)(無效服務(wù)數(shù)據(jù))都返回給Consumer。
關(guān)閉服務(wù)保護(hù)模式
# 關(guān)閉自我保護(hù):true為開啟自我保護(hù),false為關(guān)閉自我保護(hù)
eureka.server.enableSelfPreservation=false
# 清理間隔(單位:毫秒,默認(rèn)是60*1000),當(dāng)服務(wù)心跳失效后多久,刪除服務(wù)。
eureka.server.eviction.interval-timer-in-ms=60000
優(yōu)雅關(guān)閉服務(wù)
在Spring Cloud中,可以通過HTTP請(qǐng)求的方式,通知Eureka Client優(yōu)雅停服,這個(gè)請(qǐng)求一旦發(fā)送到Eureka Client,那么Eureka Client會(huì)發(fā)送一個(gè)shutdown請(qǐng)求到Eureka Server,Eureka Server接收到這個(gè)shutdown請(qǐng)求后,會(huì)在服務(wù)列表中標(biāo)記這個(gè)服務(wù)的狀態(tài)為down,同時(shí)Eureka Client應(yīng)用自動(dòng)關(guān)閉。這個(gè)過程就是優(yōu)雅停服。
如果使用了優(yōu)雅停服,則不需要再關(guān)閉Eureka Server的服務(wù)保護(hù)模式。
1、POM依賴:
優(yōu)雅停服是通過Eureka Client發(fā)起的,所以需要在Eureka Client中增加新的依賴,這個(gè)依賴是autuator組件,添加下述依賴即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
2、修改全局配置文件:
Eureka Client默認(rèn)不開啟優(yōu)雅停服功能,需要在全局配置文件中新增如下內(nèi)容:
# 啟用shutdown,優(yōu)雅停服功能
endpoints.shutdown.enabled=true
# 禁用密碼驗(yàn)證
endpoints.shutdown.sensitive=false
3、發(fā)起shutdown請(qǐng)求:
必須通過POST請(qǐng)求向Eureka Client發(fā)起一個(gè)shutdown請(qǐng)求。請(qǐng)求路徑為:http://ip:port/shutdown。可以通過任意技術(shù)實(shí)現(xiàn),如:HTTPClient、form表單,AJAX等。
建議使用優(yōu)雅停服方式來關(guān)閉Application Service/Application Client服務(wù)。
源碼剖析
Eureka Server

Eureka Client

思考
文章到此結(jié)束,那么以下問題是否有答案了呢?
- 1、為什么要用注冊(cè)中心,用Nginx不行?
- 2、Eureka的client和server是如何工作的,服務(wù)注冊(cè),服務(wù)發(fā)現(xiàn)是怎么做到的?
- 3、Eureka集群是如何工作的,一致性能夠保證?
參考文章
https://blog.csdn.net/zhuyanlin09/article/details/89598245
https://github.com/Netflix/eureka/wiki
http://www.itdecent.cn/p/56155d2bde6b
https://cloud.tencent.com/developer/article/1033336