1. 程式人生 > >【親測】SpringCloud之Eureka註冊中心

【親測】SpringCloud之Eureka註冊中心

一.Eureka註冊中心

1.1.認識Eureka

Eureka做什麼?

Eureka就好比是滴滴,負責管理、記錄服務提供者的資訊。服務呼叫者無需自己尋找服務,而是把自己的需求告訴Eureka,然後Eureka會把符合你需求的服務告訴你。

同時,服務提供方與Eureka之間通過“心跳”機制進行監控,當某個服務提供方出現問題,Eureka自然會把它從服務列表中剔除。

這就實現了服務的自動註冊、發現、狀態監控。

1.2.原理圖

基本架構:

在這裡插入圖片描述

  • Eureka:就是服務註冊中心(可以是一個叢集),對外暴露自己的地址
  • 提供者:啟動後向Eureka註冊自己資訊(地址,提供什麼服務)
  • 消費者:向Eureka訂閱服務,Eureka會將對應服務的所有提供者地址列表傳送給消費者,並且定期更新
  • 心跳(續約):提供者定期通過http方式向Eureka重新整理自己的狀態

1.3.入門案例

1.3.1.編寫EurekaServer

接下來我們建立一個專案,啟動一個EurekaServer:
依然使用spring提供的快速搭建工具:
在這裡插入圖片描述
選擇依賴:
在這裡插入圖片描述

完整的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.czxy.demo</groupId> <artifactId>eureka-demo</artifactId> <version>0.0.1-SNAPSHOT</
version
>
<packaging>jar</packaging> <name>eureka-demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!-- SpringCloud版本,是最新的F系列 --> <spring-cloud.version>Finchley.RC1</spring-cloud.version> </properties> <dependencies> <!-- Eureka服務端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <!-- SpringCloud依賴,一定要放到dependencyManagement中,起到管理版本的作用即可 --> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>

編寫啟動類:

@SpringBootApplication
@EnableEurekaServer // 宣告這個應用是一個EurekaServer
public class EurekaDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaDemoApplication.class, args);
	}
}

編寫配置application.yml:

server:
  port: 10086 # 埠
spring:
  application:
    name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
  client:
    register-with-eureka: false # 是否註冊自己的資訊到EurekaServer,預設是true
    fetch-registry: false # 是否拉取其它服務的資訊,預設是true
    service-url: # EurekaServer的地址,現在是自己的地址,如果是叢集,需要加上其它Server的地址。
      defaultZone: http://127.0.0.1:${server.port}/eureka

啟動服務,並訪問:http://127.0.0.1:10086/eureka
在這裡插入圖片描述
在這裡插入圖片描述

1.3.2.將user-service註冊到Eureka

註冊服務,就是在服務上新增Eureka的客戶端依賴,客戶端程式碼會自動把服務註冊到EurekaServer中。

我們在user-service-demo中新增Eureka客戶端依賴:

先新增SpringCloud依賴:

<!-- SpringCloud的依賴 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.RC1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<!-- Spring的倉庫地址 -->
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

然後是Eureka客戶端:

<!-- Eureka客戶端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

在啟動類上開啟Eureka客戶端功能

通過新增@EnableDiscoveryClient來開啟Eureka客戶端功能

@SpringBootApplication
@EnableDiscoveryClient // 開啟EurekaClient功能
public class UserServiceDemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(UserServiceDemoApplication.class, args);
	}
}

編寫配置

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb1?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: user-service # 應用名稱
#    druid:
#      initial-size: 1
#      min-idle: 1
#      max-active: 20
#      test-on-borrow: true
#      stat-view-servlet:
#        allow: true
eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true # 當呼叫getHostname獲取例項的hostname時,返回ip而不是host名稱
    ip-address: 127.0.0.1 # 指定自己的ip資訊,不指定的話會自己尋找

注意:

  • 這裡我們添加了spring.application.name屬性來指定應用名稱,將來會作為應用的id使用。
  • 不用指定register-with-eureka和fetch-registry,因為預設是true

重啟專案,訪問Eureka監控頁面檢視

在這裡插入圖片描述

我們發現user-service服務已經註冊成功了

1.3.3.消費者從Eureka獲取服務

接下來我們修改consumer-demo,嘗試從EurekaServer獲取服務。

方法與消費者類似,只需要在專案中新增EurekaClient依賴,就可以通過服務名稱來獲取資訊了!

1)新增依賴:

先新增SpringCloud依賴:

<!-- SpringCloud的依賴 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.RC1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<!-- Spring的倉庫地址 -->
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

然後是Eureka客戶端:

<!-- Eureka客戶端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2)在啟動類開啟Eureka客戶端

@SpringBootApplication
@EnableDiscoveryClient // 開啟Eureka客戶端
public class UserConsumerDemoApplication {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    }
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerDemoApplication.class, args);
    }
}

3)修改配置:

server:
  port: 8080
spring:
  application:
    name: consumer # 應用名稱
eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true # 當其它服務獲取地址時提供ip而不是hostname
    ip-address: 127.0.0.1 # 指定自己的ip資訊,不指定的話會自己尋找

4)修改程式碼,用DiscoveryClient類的方法,根據服務名稱,獲取服務例項:

@Service
public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;// Eureka客戶端,可以獲取到服務例項資訊

    public List<User> queryUserByIds(List<Long> ids) {
        List<User> users = new ArrayList<>();
        // String baseUrl = "http://localhost:8081/user/";
        // 根據服務名稱,獲取服務例項
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
        // 因為只有一個UserService,因此我們直接get(0)獲取
        ServiceInstance instance = instances.get(0);
        // 獲取ip和埠資訊
        String baseUrl = "http://"+instance.getHost() + ":" + instance.getPort()+"/user/";
        ids.forEach(id -> {
            // 我們測試多次查詢,
            users.add(this.restTemplate.getForObject(baseUrl + id, User.class));
            // 每次間隔500毫秒
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        return users;
    }
}

5)Debug跟蹤執行:

在這裡插入圖片描述

生成的URL:
在這裡插入圖片描述
訪問結果:
在這裡插入圖片描述

1.4.Eureka詳解

接下來我們詳細講解Eureka的原理及配置。

1.4.1.基礎架構

Eureka架構中的三個核心角色:

  • 服務註冊中心

    Eureka的服務端應用,提供服務註冊和發現功能,就是剛剛我們建立的eureka-demo

  • 服務提供者

    提供服務的應用,可以是SpringBoot應用,也可以是其它任意技術實現,只要對外提供的是Rest風格服務即可。本例中就是我們實現的user-service-demo

  • 服務消費者

    消費應用從註冊中心獲取服務列表,從而得知每個服務方的資訊,知道去哪裡呼叫服務方。本例中就是我們實現的consumer-demo

1.4.2.高可用的Eureka Server

Eureka Server即服務的註冊中心,在剛才的案例中,我們只有一個EurekaServer,事實上EurekaServer也可以是一個叢集,形成高可用的Eureka中心。

服務同步

多個Eureka Server之間也會互相註冊為服務,當服務提供者註冊到Eureka Server叢集中的某個節點時,該節點會把服務的資訊同步給叢集中的每個節點,從而實現資料同步。因此,無論客戶端訪問到Eureka Server叢集中的任意一個節點,都可以獲取到完整的服務列表資訊。

動手搭建高可用的EurekaServer

我們假設要搭建兩條EurekaServer的叢集,埠分別為:10086和10087

1)我們修改原來的EurekaServer配置:

server:
  port: 10086 # 埠
spring:
  application:
    name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
  client:
    service-url: # 配置其他Eureka服務的地址,而不是自己,比如10087
      defaultZone: http://127.0.0.1:10087/eureka

所謂的高可用註冊中心,其實就是把EurekaServer自己也作為一個服務進行註冊,這樣多個EurekaServer之間就能互相發現對方,從而形成叢集。因此我們做了以下修改:

  • 刪除了register-with-eureka=false和fetch-registry=false兩個配置。因為預設值是true,這樣就會吧自己註冊到註冊中心了。
  • 把service-url的值改成了另外一臺EurekaServer的地址,而不是自己

2)另外一臺配置恰好相反:

server:
  port: 10087 # 埠
spring:
  application:
    name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
  client:
    service-url: # 配置其他Eureka服務的地址,而不是自己,比如10087
      defaultZone: http://127.0.0.1:10086/eureka

注意:idea中一個應用不能啟動兩次,我們需要重新配置一個啟動器:

在這裡插入圖片描述

然後啟動即可。

3)啟動測試:

在這裡插入圖片描述

4)客戶端註冊服務到叢集

因為EurekaServer不止一個,因此註冊服務的時候,service-url引數需要變化:

eureka:
  client:
    service-url: # EurekaServer地址,多個地址以','隔開
      defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka

1.4.3.服務提供者

服務提供者要向EurekaServer註冊服務,並且完成服務續約等工作。

服務註冊

服務提供者在啟動時,會檢測配置屬性中的:eureka.client.register-with-erueka=true引數是否正確,事實上預設就是true。如果值確實為true,則會向EurekaServer發起一個Rest請求,並攜帶自己的元資料資訊,Eureka Server會把這些資訊儲存到一個雙層Map結構中。第一層Map的Key就是服務名稱,第二層Map的key是服務的例項id。

服務續約

在註冊服務完成以後,服務提供者會維持一個心跳(定時向EurekaServer發起Rest請求),告訴EurekaServer:“我還活著”。這個我們稱為服務的續約(renew);

有兩個重要引數可以修改服務續約的行為:

eureka:
  instance:
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30
  • lease-renewal-interval-in-seconds:服務續約(renew)的間隔,預設為30秒
  • lease-expiration-duration-in-seconds:服務失效時間,預設值90秒

也就是說,預設情況下每個30秒服務會向註冊中心傳送一次心跳,證明自己還活著。如果超過90秒沒有傳送心跳,EurekaServer就會認為該服務宕機,會從服務列表中移除,這兩個值在生產環境不要修改,預設即可。

但是在開發時,這個值有點太長了,經常我們關掉一個服務,會發現Eureka依然認為服務在活著。所以我們在開發階段可以適當調小。

eureka:
  instance:
    lease-expiration-duration-in-seconds: 10 # 10秒即過期
    lease-renewal-interval-in-seconds: 5 # 5秒一次心跳

例項id

先來看一下服務狀態資訊:

在Eureka監控頁面,檢視服務註冊資訊:
在這裡插入圖片描述

在status一列中,顯示以下資訊:

  • UP(1):代表現在是啟動了1個示例,沒有叢集
  • DESKTOP-2MVEC12:user-service:8081:是示例的名稱(instance-id),
    • 預設格式是:${hostname} + ${spring.application.name} + ${server.port}
    • instance-id是區分同一服務的不同例項的唯一標準,因此不能重複。

我們可以通過instance-id屬性來修改它的構成:

eureka:
  instance:
    instance-id: ${spring.application.name}:${server.port}

重啟服務再試試看:

在這裡插入圖片描述

1.4.4.服務消費者

獲取服務列表

當服務消費者啟動是,會檢測eureka.client.fetch-registry=true引數的值,如果為true,則會從Eureka Server服務的列表只讀備份,然後快取在本地。並且每隔30秒會重新獲取並更新資料。我們可以通過下面的引數來修改:

eureka:
  client:
    registry-fetch-interval-seconds: 5

生產環境中,我們不需要修改這個值。

但是為了開發環境下,能夠快速得到服務的最新狀態,我們可以將其設定小一點。

1.4.5.失效剔除和自我保護

失效剔除

有些時候,我們的服務提供方並不一定會正常下線,可能因為記憶體溢位、網路故障等原因導致服務無法正常工作。Eureka Server需要將這樣的服務剔除出服務列表。因此它會開啟一個定時任務,每隔60秒對所有失效的服務(超過90秒未響應)進行剔除。

可以通過eureka.server.eviction-interval-timer-in-ms引數對其進行修改,單位是毫秒,生成環境不要修改。

這個會對我們開發帶來極大的不便,你對服務重啟,隔了60秒Eureka才反應過來。開發階段可以適當調整,比如10S

自我保護

我們關停一個服務,就會在Eureka面板看到一條警告:

在這裡插入圖片描述

這是觸發了Eureka的自我保護機制。當一個服務未按時進行心跳續約時,Eureka會統計最近15分鐘心跳失敗的服務例項的比例是否超過了85%。在生產環境下,因為網路延遲等原因,心跳失敗例項的比例很有可能超標,但是此時就把服務剔除列表並不妥當,因為服務可能沒有宕機。Eureka就會把當前例項的註冊資訊保護起來,不予剔除。生產環境下這很有效,保證了大多數服務依然可用。

但是這給我們的開發帶來了麻煩, 因此開發階段我們都會關閉自我保護模式:

eureka:
  server:
    enable-self-preservation: false # 關閉自我保護模式(預設為開啟)
    eviction-interval-timer-in-ms: 1000 # 掃描失效服務的間隔時間(預設為60*1000ms)