SpringCloud下基于Eureka的服務(wù)注冊(cè)與發(fā)現(xiàn)實(shí)踐
Spring Cloud Eureka作為一個(gè)服務(wù)注冊(cè)與發(fā)現(xiàn)組件,是基于Netflix Eureka的二次封裝,是Spring Cloud的重要組成部分

搭建Eureka注冊(cè)中心
POM依賴
在Eureka中具體包含兩個(gè)組件:Eureka Server、Eureka Client。前者即為注冊(cè)中心;后者則用于完成服務(wù)向注冊(cè)中心的注冊(cè)與發(fā)現(xiàn)。故這里先建立一個(gè)SpringBoot項(xiàng)目,用于作為Eureka Server。其POM中相關(guān)依賴如下所示
<dependencyManagement>
??<dependencies>
????
????<dependency>
??????<groupId>org.springframework.bootgroupId>
??????<artifactId>spring-boot-dependenciesartifactId>
??????<version>2.2.2.RELEASEversion>
??????<type>pomtype>
??????<scope>importscope>
????dependency>
????
????<dependency>
??????<groupId>org.springframework.cloudgroupId>
??????<artifactId>spring-cloud-dependenciesartifactId>
??????<version>Hoxton.SR1version>
??????<type>pomtype>
??????<scope>importscope>
????dependency>
??dependencies>
dependencyManagement>
<dependencies>
??
??<dependency>
????<groupId>org.springframework.cloudgroupId>
????<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
??dependency>
dependencies>
配置文件
為了便于后續(xù)演示,我們?cè)趆osts文件中先添加映射,如下所示

配置文件application.yml如下所示。這里我們不需要注冊(cè)中心對(duì)自身進(jìn)行注冊(cè),故register-with-eureka、fetch-registry設(shè)為false
server:
????port:?7001
spring:
????application:
????????name:?eureka?server
eureka:
????instance:
????????hostname:?eureka7001.com??#?Eureka?Server的實(shí)例名稱
????client:
????????#?是否將自己注冊(cè)到Eureka?Server,?默認(rèn)為true
????????register-with-eureka:?false
????????#?是否從Eureka?Server獲取注冊(cè)信息,?默認(rèn)為true
????????fetch-registry:?false
????????service-url:
????????????#?單機(jī)版Eureka?Server:?指向自己
????????????defaultZone:?http://localhost:7001/eureka/
Java實(shí)現(xiàn)
作為Eureka Server而言,Java代碼比較簡(jiǎn)單。我們只需提供一個(gè)SpringBoot啟動(dòng)類即可。值得一提的是,需要在該啟動(dòng)類上添加 @EnableEurekaServer 注解
@SpringBootApplication
@EnableEurekaServer
public?class?EurekaServerApplication?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(EurekaServerApplication.class,?args);
????}
}
搭建服務(wù)提供者
POM依賴
同樣再次建立一個(gè)SpringBoot項(xiàng)目Payment用于對(duì)外提供服務(wù)。由于該服務(wù)需要注冊(cè)到Eureka中,故需要向其中引入Eureka Client依賴。如下所示
<dependencyManagement>
??<dependencies>
????
????<dependency>
??????<groupId>org.springframework.bootgroupId>
??????<artifactId>spring-boot-dependenciesartifactId>
??????<version>2.2.2.RELEASEversion>
??????<type>pomtype>
??????<scope>importscope>
????dependency>
????
????<dependency>
??????<groupId>org.springframework.cloudgroupId>
??????<artifactId>spring-cloud-dependenciesartifactId>
??????<version>Hoxton.SR1version>
??????<type>pomtype>
??????<scope>importscope>
????dependency>
??dependencies>
dependencyManagement>
<dependencies>
??
??<dependency>
??????<groupId>org.springframework.cloudgroupId>
??????<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
??dependency>
dependencies>
配置文件
在配置文件中,我們定義了payment服務(wù)的兩個(gè)實(shí)例,分別使用8001、8002端口。在啟動(dòng)時(shí)中通過設(shè)置不同的spring.profiles.active配置,即可啟動(dòng)兩個(gè)使用不同端口的payment服務(wù)
#?通用配置
spring:
????application:
????????name:?payment
????profiles:
????????active:?payment1
eureka:
????client:
????????#?是否將自己注冊(cè)到Eureka?Server,?默認(rèn)為true
????????register-with-eureka:?true
????????#?是否從Eureka?Server獲取注冊(cè)信息,?默認(rèn)為true
????????#?單機(jī)版Eureka?Server無所謂,集群版Eureka?Server下必須設(shè)置為true才能配合Ribbon使用負(fù)載均衡
????????fetch-registry:?true
????????service-url:
????????????#?單機(jī)版?Eureka?Server
????????????defaultZone:?http://localhost:7001/eureka
---
#?payment服務(wù)實(shí)例1配置
spring:
????profiles:?payment1
server:
????port:?8001
eureka:
????instance:
????????instance-id:?payment-8001
---
#?payment服務(wù)實(shí)例2配置
spring:
????profiles:?payment2
server:
????port:?8002
eureka:
????instance:
????????instance-id:?payment-8002
Java實(shí)現(xiàn)
為便于測(cè)試,我們提供一個(gè)Controller,如下所示
@RestController
@RequestMapping("pay")
public?class?PaymentController?{
????@Value("${server.port}")
????private?String?serverPort;
????@GetMapping("/getServerPort")
????public?String?getServerPort()?{
????????String?msg?=?"[Payment?Service]:?Port:?"?+?serverPort;
????????return?msg;
????}
????@GetMapping("/hello")
????public?String?hello(@RequestParam?String?name)?{
????????String?msg?=?"[Payment?Service-"+?serverPort?+"]:?"?+?name;
????????return?msg;
????}
}
同時(shí)在啟動(dòng)類上添加 @EnableEurekaClient 注解
@SpringBootApplication
@EnableEurekaClient
public?class?PaymentApplication?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(PaymentApplication.class,?args);
????}
}
搭建服務(wù)消費(fèi)者
POM依賴
類似地,建立SpringBoot項(xiàng)目Order作為服務(wù)消費(fèi)者。同理該項(xiàng)目也需要引入Eureka Client依賴。如下所示
<dependencyManagement>
??<dependencies>
????
????<dependency>
??????<groupId>org.springframework.bootgroupId>
??????<artifactId>spring-boot-dependenciesartifactId>
??????<version>2.2.2.RELEASEversion>
??????<type>pomtype>
??????<scope>importscope>
????dependency>
????
????<dependency>
??????<groupId>org.springframework.cloudgroupId>
??????<artifactId>spring-cloud-dependenciesartifactId>
??????<version>Hoxton.SR1version>
??????<type>pomtype>
??????<scope>importscope>
????dependency>
??dependencies>
dependencyManagement>
<dependencies>
??
??<dependency>
??????<groupId>org.springframework.cloudgroupId>
??????<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
??dependency>
dependencies>
配置文件
一般地,微服務(wù)中一個(gè)服務(wù)既是提供者也是消費(fèi)者,故我們也將該服務(wù)注冊(cè)到Eureka中。配置如下
server:
????port:?80
spring:
????application:
????????name:?order
eureka:
????client:
????????#?是否將自己注冊(cè)到Eureka?Server,?默認(rèn)為true
????????register-with-eureka:?true
????????#?是否從Eureka?Server獲取注冊(cè)信息,?默認(rèn)為true
????????fetch-registry:?true
????????service-url:
????????????#?單機(jī)版?Eureka?Server
????????????defaultZone:?http://localhost:7001/eureka
????instance:
????????instance-id:?order-80
Java實(shí)現(xiàn)
首先聲明創(chuàng)建RestTemplate實(shí)例。一個(gè)是普通的RestTemplate實(shí)例,一個(gè)則是添加了 @LoadBalanced 注解的RestTemplate實(shí)例。前者可進(jìn)行基于IP、Port服務(wù)調(diào)用;后者由于添加了 @LoadBalanced 注解,其一方面可進(jìn)行基于服務(wù)名的服務(wù)調(diào)用,另一方面支持負(fù)載均衡
@Configuration
public?class?RestTemplateConfig?{
????/**
?????*?普通的restTemplate實(shí)例,?可利用IP、Port調(diào)用服務(wù)
?????*?@return
?????*/
????@Bean
????public?RestTemplate?restTemplate1()?{
????????return?new?RestTemplate();
????}
????/**
?????*?@LoadBalanced?注解作用:?
?????*???1.?基于服務(wù)名調(diào)用的restTemplate實(shí)例
?????*???2.?支持負(fù)載均衡
?????*?@return
?????*/
????@Bean
????@LoadBalanced
????public?RestTemplate?restTemplate2()?{
????????return?new?RestTemplate();
????}
}
然后通過Controller調(diào)用Payment服務(wù)接口。下面展現(xiàn)了 傳統(tǒng)地基于IP、Port信息 和 基于服務(wù)名 兩種形式的服務(wù)調(diào)用
@RestController
@RequestMapping("order")
public?class?OrderController?{
????@Qualifier("restTemplate1")
????@Autowired
????private?RestTemplate?restTemplate1;
????@Qualifier("restTemplate2")
????@Autowired
????private?RestTemplate?restTemplate2;
????//?使用?固定IP、Port
????private?static?final?String?PAYMENT_URL_1?=?"http://localhost:8001";
????//?使用?注冊(cè)到Eureka中的?服務(wù)名稱
????private?static?final?String?PAYMENT_URL_2?=?"http://PAYMENT";
????@GetMapping("/test1")
????public?String?test1(@RequestParam?String?name)?{
????????String?msg?=?restTemplate1.getForObject(PAYMENT_URL_1?+"/pay/hello?name={1}",?String.class,?name);
????????String?result?=?"[Order?Service?#test1]:?"?+?msg;
????????return?result;
????}
????@GetMapping("/test2")
????public?String?test2(@RequestParam?String?name)?{
????????String?msg?=?restTemplate2.getForObject(PAYMENT_URL_2?+"/pay/hello?name={1}",?String.class,?name);
????????String?result?=?"[Order?Service?#test2]:?"?+?msg;
????????return?result;
????}
}
最后,在啟動(dòng)類上添加 @EnableEurekaClient 注解
@SpringBootApplication
@EnableEurekaClient
public?class?OrderApplication?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(OrderApplication.class,?args);
????}
}
測(cè)試
啟動(dòng)Eureka Server、Payment、Order三個(gè)服務(wù)。其中,Payment服務(wù)啟動(dòng)了兩個(gè)實(shí)例,如下所示

打開 http://localhost:7001 ,進(jìn)入Eureka Server的Web頁面,可以看到Payment、Order服務(wù)都已經(jīng)被成功注冊(cè)到Eureka當(dāng)中。其中Payment服務(wù)下存在兩個(gè)實(shí)例

現(xiàn)在我們通過Order服務(wù)來調(diào)用Payment服務(wù)的接口,測(cè)試結(jié)果如下,符合預(yù)期。與此同時(shí),紅框結(jié)果同時(shí)表明實(shí)現(xiàn)了負(fù)載均衡

Eureka集群環(huán)境搭建也比較簡(jiǎn)單,只是相關(guān)的配置文件發(fā)生變化。這里就Server、Client側(cè)分別說明
Eureka Server配置
在Eureka集群環(huán)境中,各實(shí)例的 eureka.client.service-url.defaultZone 不再是指向自己,而是指向集群下的其它實(shí)例。下面即是一個(gè)包含三實(shí)例的Eureka集群配置,如下所示
#?通用配置
spring:
????application:
????????name:?eureka?server
????profiles:
????????active:?eurekaServer1
---
#?Eureka?Server服務(wù)實(shí)例1配置
spring:
????profiles:?eurekaServer1
server:
????port:?7001
eureka:
????instance:
????????hostname:?eureka7001.com??#?Eureka?Server的實(shí)例名稱
????client:
????????#?是否將自己注冊(cè)到Eureka?Server,?默認(rèn)為true
????????register-with-eureka:?true
????????#?是否從Eureka?Server獲取注冊(cè)信息,?默認(rèn)為true
????????fetch-registry:?true
????????service-url:
????????????#?集群版?Eureka?Server:?指向集群中的其它Eureka?Server
????????????defaultZone:?http://eureka7002.com:7002/eureka/,?http://eureka7003.com:7003/eureka/
---
#?Eureka?Server服務(wù)實(shí)例2配置
spring:
????profiles:?eurekaServer2
server:
????port:?7002
eureka:
????instance:
????????hostname:?eureka7002.com??#?Eureka?Server的實(shí)例名稱
????client:
????????#?是否將自己注冊(cè)到Eureka?Server,?默認(rèn)為true
????????register-with-eureka:?true
????????#?是否從Eureka?Server獲取注冊(cè)信息,?默認(rèn)為true
????????fetch-registry:?true
????????service-url:
????????????#?集群版?Eureka?Server:?指向集群中的其它Eureka?Server
????????????defaultZone:?http://eureka7001.com:7001/eureka/,?http://eureka7003.com:7003/eureka/
????
---
#?Eureka?Server服務(wù)實(shí)例3配置
spring:
????profiles:?eurekaServer3
server:
????port:?7003
eureka:
????instance:
????????hostname:?eureka7003.com??#?Eureka?Server的實(shí)例名稱
????client:
????????#?是否將自己注冊(cè)到Eureka?Server,?默認(rèn)為true
????????register-with-eureka:?true
????????#?是否從Eureka?Server獲取注冊(cè)信息,?默認(rèn)為true
????????fetch-registry:?true
????????service-url:
????????????#?集群版?Eureka?Server:?指向集群中的其它Eureka?Server
????????????defaultZone:?http://eureka7001.com:7001/eureka/,?http://eureka7002.com:7002/eureka/
啟動(dòng)該集群環(huán)境后,進(jìn)入任一一個(gè)Eureka實(shí)例的Web頁面,即可看到整個(gè)集群環(huán)境已經(jīng)建立完畢。如下所示

Eureka Client配置
Eureka Client的配置就比較簡(jiǎn)單,我們以payment服務(wù)為例。只需將 eureka.client.service-url.defaultZone 配置項(xiàng)指向Eureka集群的各實(shí)例即可,如下所示
#?通用配置
spring:
????application:
????????name:?payment
????profiles:
????????active:?payment1
????
eureka:
????client:
????????#?是否將自己注冊(cè)到Eureka?Server,?默認(rèn)為true
????????register-with-eureka:?true
????????#?是否從Eureka?Server獲取注冊(cè)信息,?默認(rèn)為true
????????#?單節(jié)點(diǎn)無所謂,集群必須設(shè)置為true才能配合Ribbon使用負(fù)載均衡
????????fetch-registry:?true
????????service-url:
????????????#?集群版Eureka?Server
????????????defaultZone:?http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
Note- IDEA下啟動(dòng)某個(gè)服務(wù)的多實(shí)例,需要先勾選 Allow parallel 配置

在配置Eureka Server時(shí),各實(shí)例的eureka.instance.hostname配置項(xiàng)不允許重復(fù)。所以對(duì)于單機(jī)版Eureka Server而言,該項(xiàng)是可以直接使用localhost。但為了便于驗(yàn)證集群環(huán)境,上文我們?cè)趆osts中添加了多條指向localhost的映射
Eureka中的服務(wù)名稱默認(rèn)為應(yīng)用名稱大寫
- Spring微服務(wù)實(shí)戰(zhàn) John Carnell著
