注:這個(gè)使用文章根據(jù)閱讀 《spring cloud 微服務(wù)實(shí)戰(zhàn)》在加上自己的一點(diǎn)理解天通。
如果想學(xué)習(xí)springCloud的話披摄,要熟悉SpringBoot。
Spring Cloud 是一個(gè)基于SpringBoot實(shí)現(xiàn)的微服務(wù)開發(fā)工具间护。它為微服務(wù)架構(gòu)中涉及的配置管理雳攘,服務(wù)治理,斷路器任柜,智能路由卒废,微代理,控制總線宙地,全局鎖摔认,決策競(jìng)選,分布式會(huì)話和集群狀態(tài)管理等操作提供了一種簡(jiǎn)單的開發(fā)方式宅粥。(做為分布式框架参袱,和Dubbo相比,SpringCloud更像是一個(gè)全家桶)
-
Eureka
Eureka服務(wù)端,我們也成為服務(wù)注冊(cè)中心抹蚀,它支持高可用的配置剿牺。相當(dāng)于Dubbo框架中對(duì)Zookeeper的作用
Eureka客戶端,主要處理服務(wù)中的注冊(cè)與發(fā)現(xiàn)环壤。
下邊看一個(gè)eureka服務(wù)端的單機(jī)例子晒来。
主類
/**
* 通過@EnableEurekaServer注解啟動(dòng)一個(gè)服務(wù)注冊(cè)中心提供給其他應(yīng)用進(jìn)行對(duì)話
*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServiceApplication.class, args);
}
}
配置文件
#端口號(hào)
server.port=1111
#名稱
eureka.instance.hostname=localhost
#不向注冊(cè)中心注冊(cè)自己
eureka.client.register-with-eureka=false
#不需要檢索服務(wù)
eureka.client.fetch-registry=false
#注冊(cè)地址
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
- 高可用注冊(cè)中心
在微服務(wù)架構(gòu)這樣的分布式環(huán)境中,我們需要充分考慮發(fā)生故障的情況郑现,所以在生產(chǎn)環(huán)境中必須對(duì)各個(gè)組件進(jìn)行高可用部署湃崩,對(duì)于微服務(wù)如此,對(duì)于注冊(cè)中心也如此接箫。
Eureka Service 的設(shè)計(jì)一開始就考慮到了高可用問題攒读,在Eureka的服務(wù)治理設(shè)計(jì)中,所有的節(jié)點(diǎn)既是服務(wù)提供方辛友,也是服務(wù)消費(fèi)方薄扁,服務(wù)注冊(cè)中心也不例外。
Eureka Service的高可用實(shí)際上就是自己作為服務(wù)向其他注冊(cè)眾生心注冊(cè)自己瞎领,這樣就可以形成一組互相注冊(cè)的服務(wù)中心泌辫。
下邊看一個(gè)雙節(jié)點(diǎn)服務(wù)注冊(cè)中心集群
配置文件
application-peer1.properties
spring.application.name=eureka-service
server.port=1111
eureka.instance.hostname=peer1
eureka.client.service-url.defaultZone=http://127.0.0.1:1112/eureka/
application-peer2.properties
spring.application.name=eureka-service
server.port=1112
eureka.instance.hostname=peer2
eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka/
啟動(dòng)的時(shí)候 随夸,利用spring profiles九默。
java -jar xxx.jar --spring.profiles.active=peer1
java -jar xxx.jar --spring.profiles.active=peer2
此時(shí)訪問,http://localhost:1111 可以看到1112的分片宾毒,訪問http://localhost:1112 可以看到1111的分片驼修。
- 提供者
主類
/**
* 在主類上增加@EnableDiscoveryClient注解,激活Eureka中的
* DiscoveryClient實(shí)現(xiàn)诈铛。讓該應(yīng)用注冊(cè)為Eureka客戶端乙各。
*/
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderServiceApplication.class, args);
}
}
controller
@RestController
public class UserController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private DiscoveryClient client;
@RequestMapping("/user")
public String index(){
ServiceInstance instance = client.getLocalServiceInstance();
logger.info("/user,host:"+instance.getHost()+",service_id:"+instance.getServiceId());
return "this is userService";
}
}
注冊(cè)到上邊Eureka集群中的配置文件
spring.application.name=user-service
#注冊(cè)到多個(gè)eureka服務(wù)器
eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka/,http://127.0.0.1:1112/eureka/
- 消費(fèi)者
主類
/**
* 在主類上增加@EnableDiscoveryClient注解,激活Eureka中的
* DiscoveryClient實(shí)現(xiàn)幢竹。讓該應(yīng)用注冊(cè)為Eureka客戶端耳峦。
* 同時(shí),在該主類中創(chuàng)建RestTemplate的SpringBean實(shí)例焕毫,
* 并通過@LoadBalanced注解開啟客戶端負(fù)載均衡
*/
@EnableDiscoveryClient
@SpringBootApplication
public class RibbonConsumerApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
}
controller
@RestController
public class ConsumerController {
private final Logger logger = Logger.getLogger(this.getClass());
/**
* 通過RestTemplate來實(shí)現(xiàn)對(duì)user-service的接口調(diào)用蹲坷。
*/
@Autowired
RestTemplate restTemplate;
@RequestMapping("/rpc-user")
public String helloConsumer(){
logger.info("遠(yuǎn)程調(diào)用開始");
return restTemplate.getForEntity("http://user-service/user",String.class).getBody();
}
}
配置文件
server.port=3333
spring.application.name=user-consumer
eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka/,http://127.0.0.1:1112/eureka/
此時(shí)訪問 http://localhost:1111 可以看到消費(fèi)者和提供者的信息。
訪問http://localhost:3333/rpc-user 也可以通過服務(wù)中心調(diào)用到提供者那里邑飒。
看圖
下邊看一下關(guān)于服務(wù)提供者循签,服務(wù)消費(fèi)者,服務(wù)注冊(cè)中心的一些概念疙咸,更好的理解上邊這幅圖
-
服務(wù)提供者
服務(wù)注冊(cè):“服務(wù)提供者”在啟動(dòng)的時(shí)候回通過發(fā)送REST請(qǐng)求的方式將自己注冊(cè)到Eureka Server上县匠,同時(shí)帶上了自身服務(wù)的一些元數(shù)據(jù),Eureka Server接收 到這個(gè)REST請(qǐng)求之后,將元數(shù)據(jù)信息存儲(chǔ)在一個(gè)雙層結(jié)構(gòu)的Map中乞旦,第一層Map的Key是服務(wù)名贼穆,第二層的Key是具體服務(wù)的實(shí)例名。
在服務(wù)注冊(cè)之時(shí)兰粉,要確認(rèn)一下eureka.client.register-with-eureka=ture參數(shù)是否正確扮惦,默認(rèn)為true,若為false將不會(huì)啟動(dòng)注冊(cè)操作亲桦。
服務(wù)同步:如上圖所示崖蜜,這里的兩個(gè)服務(wù)提供者分別注冊(cè)到了兩個(gè)不同的服務(wù)注冊(cè)中心上,也就是說客峭,他們的信息分別被兩個(gè)服務(wù)注冊(cè)中心維護(hù)豫领,此時(shí),由于服務(wù)注冊(cè)中心之間因互相注冊(cè)為服務(wù)舔琅,當(dāng)服務(wù)提供者發(fā)送注冊(cè)請(qǐng)求到一個(gè)服務(wù)注冊(cè)中心時(shí)等恐,他會(huì)將該請(qǐng)求轉(zhuǎn)發(fā)給集群中相連的其他注冊(cè)中心,從而實(shí)現(xiàn)注冊(cè)中心之間的服務(wù)同步备蚓。通過服務(wù)同步课蔬,兩個(gè)服務(wù)提供者的服務(wù)信息就可以通過這兩個(gè)服務(wù)注冊(cè)中心中的任意一臺(tái)獲取到。
服務(wù)續(xù)約:在注冊(cè)完服務(wù)之后郊尝,服務(wù)提供者會(huì)維護(hù)一個(gè)心跳來通知Eureka Service二跋,以防止Eureka Service的“剔除任務(wù)”將該服務(wù)實(shí)例從服務(wù)列表中排除出去,我們稱該操作為服務(wù)續(xù)約流昏。
關(guān)于服務(wù)續(xù)約的兩個(gè)配置
#該參數(shù)用于定義服務(wù)續(xù)約任務(wù)的調(diào)用時(shí)間間隔
eureka.instance.lease-renewal-interval-in-seconds=30
#該參數(shù)用于定義服務(wù)失效的時(shí)間
eureka.instance.lease-expiration-duration-in-seconds=90
-
服務(wù)消費(fèi)者
獲取服務(wù):當(dāng)啟動(dòng)服務(wù)消費(fèi)者的時(shí)候扎即,它會(huì)發(fā)送一個(gè)REST請(qǐng)求給服務(wù)注冊(cè)中心,來獲取上面注冊(cè)的服務(wù)清單况凉。為了性能考慮谚鄙。Eureka Server 會(huì)維護(hù)一份只讀的服務(wù)清單來返回給客戶端,同時(shí)刁绒,該緩存清單每隔30s更新一次闷营。
#獲取服務(wù)是服務(wù)消費(fèi)者的基礎(chǔ),所以必須確保eureka.client.fetch-registry=true .默認(rèn)為true
eureka.client.fetch-registry=true
#若希望修改緩存清單的更新時(shí)間知市,可以通過該參數(shù)修改傻盟,默認(rèn)為30s
eureka.client.registry-fetch-interval-seconds=30
服務(wù)調(diào)用:服務(wù)消費(fèi)者在獲取服務(wù)清單后,通過服務(wù)名可以獲得具體提供服務(wù)的實(shí)例名和該實(shí)例的元數(shù)據(jù)信息初狰,因?yàn)橛羞@些服務(wù)實(shí)例的詳細(xì)信息莫杈,所以客戶端可以根據(jù)自己的需要決定具體調(diào)用哪個(gè)實(shí)例,在Ribbon中會(huì)默認(rèn)采用輪詢的方式進(jìn)行調(diào)用奢入,從而實(shí)現(xiàn)客戶端的負(fù)載均衡筝闹。
服務(wù)下線:在系統(tǒng)運(yùn)行過程中必然會(huì)面臨關(guān)閉或重啟的某個(gè)實(shí)例情況媳叨,在服務(wù)關(guān)閉期間,我們自然不希望客戶端會(huì)繼續(xù)調(diào)用關(guān)閉了的實(shí)例关顷,所以在客戶端程序中糊秆,當(dāng)服務(wù)實(shí)例進(jìn)行正常關(guān)閉操作時(shí),它會(huì)觸發(fā)一個(gè)服務(wù)下線的REST請(qǐng)求給EurekaServer议双,告訴服務(wù)中心痘番,我要下線了。服務(wù)端在收到請(qǐng)求后平痰,將該服務(wù)狀態(tài)置為下線汞舱,并把該下線時(shí)間傳播出去。
-
服務(wù)注冊(cè)中心
失效剔除:有些時(shí)候宗雇,我們的服務(wù)實(shí)例并一定會(huì)正常下線昂芜,可能由于內(nèi)存溢出,網(wǎng)絡(luò)故障等原因使得服務(wù)不能正常工作赔蒲,而服務(wù)注冊(cè)中心并未收到“服務(wù)下線”的請(qǐng)求泌神。為了從服務(wù)列表中將這些無法提供服務(wù)的實(shí)例剔除,Eureka Server在啟動(dòng)的時(shí)候會(huì)創(chuàng)建一個(gè)定時(shí)任務(wù)舞虱,默認(rèn)每隔一段時(shí)間(默認(rèn)60s)將當(dāng)前清單中超過(默認(rèn)90s)沒有續(xù)約的服務(wù)剔除出去欢际。
自我保護(hù):當(dāng)我們?cè)诒镜卣{(diào)試基于Eureka的程序時(shí),基本上都會(huì)碰到一個(gè)問題矾兜,在服務(wù)注冊(cè)中心的信息面板中出現(xiàn)類似下面的紅色警告信息
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.
RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
實(shí)際上损趋,該警告就是出發(fā)了EurekaServer的自我保護(hù)機(jī)制,之前我們介紹過焕刮,服務(wù)注冊(cè)到EurekaServer之后舶沿,會(huì)維護(hù)一個(gè)心跳鏈接墙杯,告訴EurekaServer自己還活著配并,EurekaServer在運(yùn)行期間,會(huì)統(tǒng)計(jì)心跳失敗的比例在15分鐘之內(nèi)是否低于85%高镐,如果出現(xiàn)這種情況溉旋,EurekaServer會(huì)講當(dāng)前實(shí)例注冊(cè)保護(hù)起來,讓這些實(shí)例不會(huì)過期嫉髓。盡可能保護(hù)這些注冊(cè)信息观腊,但是,在這段保護(hù)期間內(nèi)實(shí)例若出現(xiàn)問題算行,那么客戶端很容易拿到實(shí)際已經(jīng)不存在的服務(wù)實(shí)例梧油,會(huì)出現(xiàn)調(diào)用失敗的情況,所以客戶端必須有容錯(cuò)機(jī)制州邢,比如可以使用請(qǐng)求重試儡陨,斷路器等機(jī)制。
由于本地調(diào)試很容易觸發(fā)注冊(cè)中心的保護(hù)機(jī)制。這會(huì)使得注冊(cè)中心維護(hù)的服務(wù)實(shí)例不那么準(zhǔn)確骗村。所以嫌褪,我們?cè)诒镜剡M(jìn)行開發(fā)的時(shí)候,可以使用下邊的參數(shù)關(guān)閉保護(hù)機(jī)制胚股,以確保注冊(cè)中心可以將不可用的實(shí)例正確剔除笼痛。
#關(guān)閉自我保護(hù)機(jī)制
eureka.server.enable-self-preservation=false