Spring Cloud 入門到進階 - 01 搭建Eureka服務環境
博主整理的SpringCloud系列:目錄
一、Eureka介紹
Spring Cloud 集成了 Netflix OSS 多個項目,形成了 spring-cloud-netflix 項目。該項目包含多個子模塊,這些子模塊對集成的 Netflix 旗下的框架進行了封裝,我們這裏將講述其中一個較爲重要的服務管理框架: Eureka
1.1 關於 Eureka
Eureka 提供基於 REST 的服務,在集羣中主要用於服務管理。Eureka 提供了基於 Java 語言 的客戶端組件,客戶端組件實現了負載均衡的功能,爲業務組件的集羣部署創造了條件。使用該框架,可以將業務組件註冊到 Eureka 容器中,這些組件可進行集羣部署,Eureka 主要維護這些服務的列表並自動檢查它們的狀態
1.2 Eureka 架構
一個簡單的 Eureka 集羣,包含一個 Eureka 服務器(註冊中心)、若干個服務提供者、若干個服務調用者。如下圖所示:
有兩個Eureka服務器註冊中心,服務器支持集器部署,每個服務器也可以作爲對方服務器的客戶端進行互相註冊與複製。有三個Eureka客戶端,兩個用於發佈,另一個用與服務調用。不管是服務器還是客戶端,都可以部署多個實例,如此一來,很容易構建高可用的服務集羣。
1.3 Eureka 服務端
對於註冊到服務器端的服務組件,Eureka 服務器並沒有提供後臺的存儲,這些註冊的服務實例被保存在內存的註冊中心,它們通過心跳來保持其最新狀態,這些操作都可以在內存中完成。客戶端存在着相同的機制,同樣在內存中保存了註冊表信息,這樣的機制提升了 Eureka 組件的性能,每次服務的請求都不必經過服務器端的註冊中心。
1.4 Eureka 服務提供者
作爲 Eureka 客戶端存在的服務提供者,主要進行以下工作: 第一,向服務器註冊服務;第二,發送心跳給服務器;第三 向服務器端獲取註冊列表。當客戶端註冊到服務器時,它將會提供一些關於自己的信息給服務器端,例如自己的主機、端口、健康檢測連接等。
1.5 Eureka 服務調用者
對於發佈到 Eureka 服務器的服務,服務調用者可對其進行服務查找與調用,服務調用者也是作爲客戶端存在的,但其職責主要是發現與調用服務。在實際情況中,有可能出現本身既是服務提供者,又是服務調用者的情況,例如在傳統的企業應用三層架構中,服務層會調用數據訪問層的接口進行數據操作,它本身也會提供服務給控制層使用。
二、第一個 Eureka 應用
這裏我們將編寫一個 Hello World 小程序。其中包括一個 Eureka 服務中心、兩個服務提供者、一個服務調用者。
1、Eureka 服務器搭建
先創建一個名稱爲 first-ek-server
的 Maven 項目作爲服務器,在 pom.xml 文件中加入 Spring Cloud 的依賴。
1.1、依賴 pom.xml
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.swotxu</groupId>
<artifactId>first-ek-server</artifactId>
<version>1.0-SNAPSHOT</version>
<name>first-ek-server</name>
<description>This is the service</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 此依賴爲了方便使用,非必須的 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
因爲spring-cloud-starter-eureka-server
會自動引入spring-boot-starter-web
,因此只需要加入該依賴,我們的項目就具有 Web 容器的功能了。
接下來,編寫一個最簡單的啓動類,啓動我們的 Eureka 服務器。
1.2、服務啓動類
package com.swotxu.firstekserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* 第一個 Eureka 服務
*
* @Date: 2020/6/25 20:32
* @Author: swotXu
*/
@SpringBootApplication
@EnableEurekaServer // 聲明這是一個 Eureka 服務器
public class FirstEkServer {
public static void main(String[] args) {
/*
以下兩種寫法都可以,
1、SpringApplication.run(FirstEkServer.class, args)
內部會創建當前對象實例,並調用其run方法,源碼如下:
(new SpringApplication(sources)).run(args);
2、new SpringApplicationBuilder(FirstEkServer.class).run(args);
在創建SpringApplicationBuilder對象的時候,我們查看源碼會發現,它內部
其實依舊是創建了SpringApplication對象,源碼如下:
new SpringApplication(sources);
當調用其run方法時,最終還是委託給了SpringApplication。
由此可見,雖然寫法不同,但實際上殊途同歸。
同時,SpringApplicationBuilder提供給了我們一定的擴展能力,這個咱們後面再說
*/
//SpringApplication.run(FirstEkServer.class, args);
new SpringApplicationBuilder(FirstEkServer.class).run(args);
}
}
由於本例中並沒有配置服務器端口,因此默認端口爲 8080 ,我們將端口配置爲 8761,在 src/main/resources
目錄下創建 application.yml 配置文件,內容如下:
server:
port: 8761
可以看到已經啓動成功,端口爲8761。啓動過程中會出現部分異常信息,暫時不需要進行處理,下面再說。成功啓動後,打開瀏覽器,輸入 http://localho st:8761
,可以看到 Eureka 服務器控制檯,如下圖:
可以看到,服務實例列表爲空。
- 服務器註冊開關
上面提到過,在服務啓動時,會出現如下兩個異常信息:
這是由於在服務器啓動時,服務器會把自己當作一個客戶端,去註冊 Eureka 服務器,並且會到 Eureka 服務器抓取註冊信息,它自己本身只是一個服務器,而不是服務的提供者(客戶端),因此可以修改 application. yml 文件,添加以下兩個配置,即可避免異常信息。java.net.ConnectException: Connection refused: connect com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
1.3、配置 application.yml
server:
port: 8761
eureka:
client:
register-with-eureka: false # 是否將自己的信息註冊到 Eureka 服務器
fetch-registry: false # 是否到 Eureka 服務器抓取註冊信息
1.4、項目完整結構圖 - first-ek-server
2、搭建服務提供者
這裏我們使用 SpringBoot 創建一個名稱爲 first-ek-server-provider
的 Web 工程,並在裏面編寫一個 REST 服務。其中 pom.xml 依賴如下所示。
2.1、依賴 pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.swotxu</groupId>
<artifactId>first-ek-server-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>first-ek-server-provider</name>
<description>This is the service provider</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 由於 eureka-client 並沒有爲我們引入 web 組件,因此需要我們手動引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 此依賴爲了方便使用,非必須的 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2.2、配置 application.yml
在 src/main/resources
目錄下創建 application.yml 配置文件,內容如下:
spring:
application:
name: first-ek-server-provider
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
以上配置中,將應用名稱配置爲 first-ek-server-provider
,該服務將會被註冊到端口爲 8761
的 Eureka 服務器,也就是前面搭建的服務器。另外,還使用了 eureka.instance.hostname
來配置該服務實例的主機名。下面,我們編寫一個 Controller 類,並提供一個簡單的 REST 服務。
2.3、服務提供類
package com.swotxu.firstekserverprovider.web;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @Date: 2020/6/25 20:50
* @Author: swotXu
*/
@RestController
public class FirstController {
@RequestMapping(value = "/user/{userId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public UserInfo findUser(@PathVariable("userId") Integer userId){
UserInfo userInfo = new UserInfo(userId, "swotxu", 18);
return userInfo;
}
}
// ------------------------------------------------
package com.swotxu.firstekserverprovider.web;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Date: 2020/6/25 20:51
* @Author: swotXu
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
private Integer id;
private String username;
private Integer age;
}
2.4、服務啓動類
編寫啓動類,在啓動類中 ,使用了@EnableEurekaClient
註解,聲明該應用是一個 Eureka 客戶端,代碼如下:
package com.swotxu.firstekserverprovider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class FirstEkServerProviderApplication {
public static void main(String[] args) {
// SpringApplication.run(FirstEkServerProviderApplication.class, args);
new SpringApplicationBuilder(FirstEkServerProviderApplication.class).run(args);
}
}
完成後,先運行 first-ek-server
服務器,再運行 first-ek-server-provider
服務提供者,在瀏覽器中訪問 Eureka 服務器控制檯:http://localho st:8761
,可以看到我們的服務提供者已經註冊到服務器了。
2.5、項目完整結構圖 - first-ek-server-provider
接下來,我們編寫服務調用者。
3、搭建服務調用者
服務被註冊、發佈到 Eureka 服務器後,需要有程序去發現它,並且進行調用。此處所說的調用者,是指同樣註冊到 Eureka 的客戶端,來調用其他客戶端發佈的服務。
3.1、依賴 pom.xml
創建一個名稱爲 first-ek-server-invoker
的項目,在 pom.xml 文件中加入依賴如下所示。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.swotxu</groupId>
<artifactId>first-ek-server-invoker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>first-ek-server-invoker</name>
<description>This is the service caller</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<!-- 由於 eureka-client 並沒有爲我們引入 web 組件,因此需要我們手動引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 此依賴爲了方便使用,非必須的 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
因爲spring-cloud-starter-eureka-client
會自動引入spring-cloud-starter-netflix-ribbon
,因此只需要加入該依賴,我們的項目就具有 Ribbon 相關功能了。
3.2、配置 application.yml
在 src/main/resources
目錄下創建 application.yml 配置文件,內容如下:
server:
port: 9000
spring:
application:
name: first-ek-server-invoker
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
在配置文件中,將應用名稱配置爲 first-ek-server-invoker
,這個調用者的訪問端口爲 9000,需要注意的是,這個調用者本身也可以對外提供服務。與提供者一樣,使用 eureka 的配置,將該調用者註冊到 first-ek-server
上面。下面,我們編寫一個控制器,讓調用者對外提供一個測試的服務。
3.3、服務調用類
package com.swotxu.firstekserverinvoker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @Date: 2020/6/27 17:09
* @Author: swotXu
*/
@Slf4j
@Configuration
@RestController
public class lnvokerController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String router(){
RestTemplate restTemplate = getRestTemplate();
// 根據應用名稱調用服務。其中 first-ek-server-provider 是服務提供者配置的服務名(spring.application.name)
String json = restTemplate.getForObject("http://first-ek-server-provider/user/1", String.class);
log.info("result: {}", json);
return json;
}
}
在控制器中,配置了 RestTemplate
的 Bean,RestTemplate
本來是 spring-web
模塊下面的類,主要用來調用 REST 服務。本身並不具備調用分佈式服務的能力,但是 RestTemplate
的 Bean 被 @LoadBalanced
註解修飾後,這個 RestTemplate
實例就具有訪問分佈式服務的能力了。關於該類的機制,我們後面再講。
在控制器中,新建了一個 router 的測試方法,用來對外發布 REST 服務。該方法只起路由作用,實際上是使用 RestTemplate
來調用 first-ek-server-provider
(服務提供者)的服務。
3.4、服務啓動類
接下來編寫啓動類,代碼如下。
package com.swotxu.firstekserverinvoker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class FirstEkServerInvokerApplication {
public static void main(String[] args) {
SpringApplication.run(FirstEkServerInvokerApplication.class, args);
}
}
在啓動類中,使用了 @EnableDiscoveryClient
註解來修改啓動類,該註解使得服務調用者有能力去 Eureka 中發現服務。需要注意的是,@EnableEurekaClient
註解己經包含了 @EnableDiscoveryClient
的功能,也就是說,一個 Eureka 客戶端,本身就具有發現服務的能力。
@EnableEurekaClient
和@EnableDiscoveryClient
區別
共同點:兩者都夠讓註冊中心能夠發現,掃描到該服務。
不同點:@EnableEurekaClient
只適用於Eureka作爲註冊中心,@EnableDiscoveryClient
可以是其他註冊中心。
3.5、項目完整結構圖 - first-ek-server-invoker
4、運行測試
上面三個項目編寫完成後,我們依次啓動:
- 啓動服務器(first-ek-server)
- 啓動服務提供者(first-ek-server-provider)
- 啓動服務調用者(first-ek-server-invoker)
全部啓動完成後,在瀏覽器訪問 http://localhost:8761/
,即可看到註冊的客戶信息:
接下來,我們在瀏覽器中訪問服務調用者發佈的 router 服務:http://localhost:9000/router
,可以看到在瀏覽器中輸出如下信息:
根據輸出可知,實際上調用了服務提供者的 /user/1
服務,第一個 Eureka 應用到此結束,下面對這個應用程序的結構進行簡單描述。
5、程序結構
從上圖可以看到,Eureka 服務爲本例的 first-ek-server,服務提供者爲 first-ek-service-provider,而調用者爲 first-ek-service-invoker ,用戶通過瀏覽器訪問調用者的 9000 端口的 router 服務、 router 服務中查找服務提供者的服務並進行調用 。在本例中,服務調用有點像路由器的角色。
6、源碼下載
項目完整結構圖:
碼雲Gitee倉庫地址:https://gitee.com/swotxu/Spring-Cloud-Study.git
項目路徑:Spring-Cloud-Study/01/springcloud01
爲了能演示 Eureka 高可用特性,下篇,我們將會以本案例爲基礎,搭建一個複雜一點集羣。
Spring Cloud 入門到進階 - 02 Eureka集羣搭建(下)
別忘了點贊關注收藏~