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服務器註冊中心,服務器支持集器部署,每個服務器也可以作爲對方服務器的客戶端進行互相註冊與複製。有三個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 服務器控制檯,如下圖:
在這裏插入圖片描述
可以看到,服務實例列表爲空。

  • 服務器註冊開關
    上面提到過,在服務啓動時,會出現如下兩個異常信息:
    java.net.ConnectException: Connection refused: connect
    com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
    
    這是由於在服務器啓動時,服務器會把自己當作一個客戶端,去註冊 Eureka 服務器,並且會到 Eureka 服務器抓取註冊信息,它自己本身只是一個服務器,而不是服務的提供者(客戶端),因此可以修改 application. yml 文件,添加以下兩個配置,即可避免異常信息。
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、運行測試

上面三個項目編寫完成後,我們依次啓動:

  1. 啓動服務器(first-ek-server)
  2. 啓動服務提供者(first-ek-server-provider)
  3. 啓動服務調用者(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集羣搭建(下)

別忘了點贊關注收藏~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章