01.Spring Cloud Eureka服務註冊與發現
Spring Cloud Eureka
Spring Cloud Eureka
Spring Cloud Eureka是對Netflix Eureka的二次封裝。
- Eureka服務端
Eureka就是註冊中心,同時它也是一個客戶端——Eureka server(Eureka服務端)同是也是Eureka Client(Eureka客戶端)。 - Eureka客戶端
提供服務,向註冊中心註冊自服務,定時傳送心跳給註冊中心以更新當前服務的可用狀態。也可以從註冊中心查詢註冊的服務資訊。
1.構建服務註冊中心
新建Maven 工程,由於Spring Cloud應用是基於Spring Boot構建的,所以先匯入SpringBoot依賴:
<parent>
<groupId>org.springframework.boot</ groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</ artifactId>
</dependency>
</dependencies>
設定Spring Milestones倉庫:
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
在**src/main下新建Spring boot啟動類:
@SpringBootApplication
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class);
}
}
新增spring cloud依賴:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
補充:
關於Spring Boot和Spring Cloud版本對應關係參照Spring Cloud官網:https://spring.io/projects/spring-cloud#learn
新增eureka-server依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
在啟動類上新增**@EnableEurekaServer**註解:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApp {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApp.class);
}
}
完整依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>cloud01-eureka-server</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RC1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
啟動專案:
如果有如下報錯
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'eurekaRegistration' defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$EurekaClientConfiguration.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$EurekaClientConfiguration; factoryMethodName=eurekaRegistration; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$EurekaClientConfiguration.class]] for bean 'eurekaRegistration': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration; factoryMethodName=eurekaRegistration; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration.class]] bound.
檢查依賴是否導錯了:
是:
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
不是:
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
如果有如下報錯:
com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187) ~[jersey-apache-client4-1.19.1.jar:1.19.1]
.....省略.....
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) ~[na:1.8.0_181]
.....省略.....
2018-12-17 14:22:32.324 WARN 16608 --- [freshExecutor-0] c.n.d.s.t.d.RetryableEurekaHttpClient : Request execution failed with message: java.net.ConnectException: Connection refused: connect
2018-12-17 14:22:32.324 ERROR 16608 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_UNKNOWN/localhost:8088 - was unable to refresh its cache! status = Cannot execute request on any known server
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
.....省略.....
是因為eureka-server服務端同是也是一個客戶端,也需要註冊到註冊中心去,這裡沒有指定註冊中心的位置(ip、埠等),所以會如上所示的錯誤!
解決辦法,在application.yml(application.properties)中配置:
# 開啟調示
debug: true
# 設定註冊中心埠
server:
port: 8088
eureka:
# 設定當前例項的資訊,一個eureka-server或者client都是一個例項
instance:
# 設定主機名
hostname: localhost
# 註冊中心eureka-server本身也是一個客戶端 client,所以配置client連線server的屬性
client:
service-url:
# 設定註冊中心的地址,${}用來讀取配置檔案中的屬性的值
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
啟動專案,訪問http://localhost:8088,即可開啟eureka server的管理介面:
UNKNOWN既是註冊的eureka client名,也就是註冊的專案的名字,可以通過spring:
來修改:
application.name=EUREKA_SERVER
# 開啟調示
debug: true
# 設定註冊中心埠
server:
port: 8088
eureka:
# 設定當前例項的資訊,一個eureka-server或者client都是一個例項
instance:
# 設定主機名
hostname: localhost
# 註冊中心eureka-server本身也是一個客戶端 client,所以配置client連線server的屬性
client:
service-url:
# 設定註冊中心的地址,${}用來讀取配置檔案中的屬性的值
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# ----------------------------------------------
# 指定例項名
spring:
application:
name: EUREKA_SERVER
重啟專案,並訪問即可看到更改。
當然,如果當前專案作為註冊中心存在,可以不配置向註冊中心註冊自己,也可以解決上面的錯誤:
# 開啟調示
debug: true
# 設定註冊中心埠
server:
port: 8088
eureka:
# 設定當前例項的資訊,一個eureka-server或者client都是一個例項
instance:
# 設定主機名
hostname: localhost
# 註冊中心eureka-server本身也是一個客戶端 client,所以配置client連線server的屬性
client:
# 不向註冊中心註冊自己
register-with-eureka: false
# 不去發現服務(查詢服務),註冊中心提供服務註冊,不需要發現服務
fetch-registry: false
# 指定例項名
spring:
application:
name: EUREKA_SERVER
2.提供服務,註冊服務
依賴
同樣的方式新建一個boot專案,在匯入eureka-server依賴的地方改成如下依賴:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
啟動類
新建啟動類,並在啟動類上標記**@EnableDiscoveryClient**註解:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApp {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApp.class);
}
}
在application.properties(application.yml)中配置資訊:
# 設定當前專案埠
server:
port: 8089
# 設定註冊中心地址,當前client要往註冊中心註冊
eureka:
client:
service-url:
defaultZone: http://localhost:8088/eureka
# 設定當前專案的名字
spring:
application:
name: EUREKA_CLIENT
啟動當前專案(確保eureka-server專案已啟動),訪問eureka-server地址:http://localhost:8088,可以看到當前專案已註冊到註冊中心了:
獲取服務
新建一個controller,注入DiscoveryClient(org.springframework.cloud.client.discovery.DiscoveryClient),從DiscoveryClient中獲取服務資訊:
@RestController
public class HelloController {
@Autowired
private DiscoveryClient client;
private final Logger log = LoggerFactory.getLogger(this.getClass());
@GetMapping("/hello")
public String hello() {
// 獲取服務
List<ServiceInstance> eureka_client = client.getInstances("EUREKA_CLIENT");
String description = client.description();
log.info(description);
for (ServiceInstance si : eureka_client) {
// 獲取每個服務對應的資訊
String host = si.getHost();
int port = si.getPort();
String serviceId = si.getServiceId();
log.info("ServiceId是:" + serviceId + " -> " + host + port);
}
return "hello eureka";
}
}
訪問/hello地址,檢視控制檯列印日誌:
ServiceId是:EUREKA_CLIENT -> localhost8089
3.配置註冊中心高可用
如果系統中只有一個註冊中心,那麼這個註冊中心如果宕機了,可能會引起整個系統的癱瘓。系統的正常執行需要有高可用的註冊中心作為支撐。
這裡以maven多模組做專案演示:
新建一個maven工程作為父工程,修改packaging為pom(如果新建工程中有src/…,刪除),並修改pom檔案,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cloud</groupId>
<artifactId>cloud01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!--引入boot專案依賴管理-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<!--引入cloud專案依賴管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RC1</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<!--一定要引入Spring Milestones,否則這個版本的依賴下載不下來,因為使用的是預覽版-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
在父工程下新建兩個模組,分別為cloud-eureka-server01、cloud-eureka-server02,每個工程都引入相同的依賴,並配置啟動類,啟動類上標註**@EnableEurekaServer**註解如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
對兩個專案的application.properties(application.yml)分別配置:
# 這是cloud-eureka-server01的配置
# 指定專案埠(這個專案作為註冊中心,即註冊中心埠)
server:
port: 8091
# 指定當前client註冊到的註冊中心的地址
eureka:
client:
service-url:
defaultZone: http://192.168.1.35:8092/eureka/
# 設定專案名
spring:
application:
name: eureka_server_01
# 這是cloud-eureka-server02的配置
# 指定專案埠(這個專案作為註冊中心,即註冊中心埠)
server:
port: 8092
# 指定當前client註冊到的註冊中心的地址
eureka:
client:
service-url:
defaultZone: http://192.168.1.35:8091/eureka/
# 設定專案名
spring:
application:
name: eureka_server_02
啟動專案,可以發現兩個專案互相包含。
註冊服務到註冊中心叢集
新建一個maven專案cloud-eureka-client01,匯入如下依賴:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
新建啟動類,並在啟動類上標註@EnableDiscoveryClient,並配置application.xml(application.yml):
server:
port: 9091
eureka:
instance:
prefer-ip-address: true
ip-address: 192.168.1.35
client:
service-url:
defaultZone: http://192.168.35:8091/eureka/,http://192.168.1.35:8092/eureka/
spring:
application:
name: eureka_client01
啟動專案,訪問註冊中心地址:
3.服務的發現與消費
分別建立四個工程,如下圖所示:
其中server01,server02作為註冊中心,client01,client02作為客戶端。分別配置好各自的關係,為client01提供一個/hello請求:
@RestController
public class HelloController {
@GetMapping("/hello")
public String getHello() {
return "Hello World!";
}
}
在client02中呼叫這個服務:
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaClient02App {
@Bean
@LoadBalanced // 開啟客戶端負載均衡
public RestTemplate restTempalte(