天天看點

SpringBoot Cloud eureka 注冊中心

SpringBoot Cloud是什麼

Spring Cloud是一個分布式的整體解決方案。 Spring Cloud 為開發者提供了在

分布式系統(配置管理,服務發現,熔斷,路由,微代理,控制總線,一次性token,全局瑣, leader選舉,分布式session,叢集狀态)中快速建構的工具,

使用Spring Cloud的開發者可以快速的啟動服務或建構應用、同時能夠快速和雲平台資源進行對接。

SpringCloud分布式開發五大常用元件

• 服務發現——Netflix Eureka (發現了 英 [,jʊ(ə)'riːkə] )

• 客服端負載均衡——Netflix Ribbon (緞帶  英 ['rɪbən] )

• 斷路器——Netflix Hystrix (斷路器)

• 服務網關——Netflix Zuul (路由網關)

• 分布式配置——Spring Cloud Config (配置)

SpringBoot Cloud eureka 注冊中心

https://www.processon.com/diagraming/5cef1a48e4b05d5b38bdb090

特别強調: springboot和springcloud如果版本不相容會報異常java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder.<init>([Ljava/lang/Class;)V

springcloud中eureka搭建

建一個空項目springboot-07-cloud,内有三個子子產品

  1. eureka-server (注冊中心)
  2. cloud-provider (服務提供者)
  3. cloud-consumer  (消費者)

三個子子產品檔案結構如下

springboot-07-cloud base分支中三個子子產品如下

springboot-07-cloud中三個子子產品 eureka-server (注冊中心) cloud-provider (服務提供者) cloud-consumer  (消費者)
 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>1.5.21.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Edgware.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!--  starter-netflix-eureka-server自動依賴starter-actuator   -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>      
<?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>1.5.21.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>cloud-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud-provider</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Edgware.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>      
<?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>1.5.21.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>cloud-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud-consumer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Edgware.SR6</spring-cloud.version>
    </properties>

    <dependencies>
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>      
application.yml差別
server:
  port: 8761
eureka:
  instance:
    hostname: eureka-server # eureka執行個體主機名
  client:
    register-with-eureka: false # 不把自己注冊到eureka上, 因為自己本來就是注冊中心
    fetch-registry: false # 不從eureka上擷取服務的注冊資訊
    service-url:
      defaultZone: http://localhost:8761/eureka/
      #defaultZone 是注冊中心服務注冊的位址      

預設eureka服務注冊中心也會将自己作為用戶端來嘗試注冊它自己,是以我們需要禁用它的用戶端注冊行為,将register-with-eureka置為false

若未禁用eureka服務注冊中心的用戶端注冊行為,需提供service-url注冊中心位址

server:
  port: 8001
spring:
  application:
    name: provider
eureka:
  instance:
    prefer-ip-address: true # 注冊服務時使用服務的ip位址
  client:
    register-with-eureka: true # 把自己注冊到eureka上
    fetch-registry: true # 從eureka上擷取服務的注冊資訊
    service-url:
      defaultZone: http://localhost:8761/eureka/
      #defaultZone 是注冊中心服務注冊的位址      
server:
  port: 8002
spring:
  application:
    name: consumer
eureka:
  instance:
    prefer-ip-address: true # 注冊服務時使用服務的ip位址
  client:
    register-with-eureka: true # 把自己注冊到eureka上
    fetch-registry: true # 從eureka上擷取服務的注冊資訊
    service-url:
      defaultZone: http://localhost:8761/eureka/
      #defaultZone 是注冊中心服務注冊的位址      

特别注意: springboot和springcloud的版本有對應要求, 本樣例用的是

springboot <version>1.5.21.RELEASE</version> 對應 springbloud <spring-cloud.version>Edgware.SR6</spring-cloud.version>

在實際開發過程中,我們需要更詳細的版本對應:以倫敦地鐵站命名

spring-boot-starter-parent spring-cloud-dependencies
版本号 釋出日期 版本号 釋出日期
1.5.2.RELEASE 2017年3月 穩定版 Dalston.RC1 2017年未知月
1.5.9.RELEASE 2017年11月 穩定版 Edgware.RELEASE 2017年11月 穩定版
1.5.16.RELEASE Edgware.SR5
1.5.20.RELEASE Edgware.SR5
2.0.2.RELEASE 2018年5月 Finchley.BUILD-SNAPSHOT 2018年未知月
2.0.6.RELEASE Finchley.SR2
2.1.4.RELEASE Greenwich.SR1
2.2.2.RELEASE Hoxton.SR1 2019年
 待更新...

eureka-server (注冊中心)

EurekaServerApplication.java

注冊中心啟動入口 , 注意需要@EnableEurekaServer注解支援

package com.example.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * 使用注冊中心步驟
 * 1. application.yml配置
 * 2. @EnableEurekaServer注解添加
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

}      

cloud-provider (服務提供者)

CloudProviderApplication.java

 生産者啟動類, 不需要額外注解, 因為做了注冊到eureka上的配置, 就可以直接把@RestController注解的所有接口自動注冊到eureka注冊中心 , 供消費者調用.

package com.example.cloud.provider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CloudProviderApplication {

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

}      

CloudProviderController.java

package com.example.cloud.provider.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CloudProviderController {
    static int number;

    @GetMapping("/buyTicket")
    public String buyTicker(){
        return "ticker "+ ++number;
    }
}      

cloud-consumer (消費者)

CloudConsumerApplication.java

消費者啟動入口 , 注意需要開啟@EnableDiscoveryClient注解

package com.example.cloud.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * cloud 用戶端調用時需要引入 @EnableDiscoveryClient 注解
 */
@SpringBootApplication
@EnableDiscoveryClient
public class CloudConsumerApplication {

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

}      

MyConfiguration.java

需要把RestTemplate類組裝到springboot容器中, 專門用于調用eureka上發現的服務

package com.example.cloud.consumer.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class MyConfiguration {

    @LoadBalanced//負載均衡
    @Bean//需要把RestTemplate裝配進來,專門用于調用Eureka上發現的服務
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}      

CloudConsumberController.java

使用 http://localhost:8002/buyTicket 通路

package com.example.cloud.consumer.controller;

import com.netflix.discovery.converters.Auto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class CloudConsumberController {

    @Autowired
    RestTemplate restTemplate;

    //http://localhost:8002/buyTicket
    @GetMapping("/buyTicket")
    public String buyTicker(){
        String retData = restTemplate.getForObject("http://PROVIDER/buyTicket",String.class);//
        retData = "成功購買" + retData;
        return retData;
    }

}      

 通路

 通路springcloud的eureka-server 注冊中心 http://localhost:8761/

通路cloud-consumer消費者提供的controller接口  http://localhost:8002/buyTicket 後,浏覽器顯示如下

成功購買ticker 1      

注意Status一欄顯示的是服務執行個體名, 預設取名規則為${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}   ,  最後的寫法意為如果spring.application.instance_id沒有定義,則取server.port, 如果已定義了則舍掉server.port取spring.application.instance_id

遇見異常

 Error creating bean with name 'gsonBuilder' defined in class path resource 

異常詳情如下:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gsonBuilder' defined in class path resource [org/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.class]:Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.google.gson.GsonBuilder]: Factory method 'gsonBuilder' threw exception; nested exception is java.lang.BootstrapMethodError: java.lang.NoSuchMethodError: com.google.gson.GsonBuilder.setLenient()Lcom/google/gson/GsonBuilder

那是因為整個項目沒有引入parent依賴 , 添加如下<parent>即可

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>      

Eureka服務心跳檢測(健康檢測機制)

1.Eureka服務端

在某一些時候注冊在Eureka的服務已經挂掉了,但是服務卻還留在Eureka的服務清單的情況。

Eureka服務端的配置application.yml:

server:
  port: 9501

eureka:
  instance:
    hostname: 127.0.0.1
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    # 關閉自我保護機制
    enable-self-preservation: false
    # 每隔10s掃描服務清單,移除失效服務
    eviction-interval-timer-in-ms: 10000      

預設情況下,如果Eureka Server在一定時間内(預設90秒)沒有接收到某個微服務執行個體的心跳,Eureka Server将會移除該執行個體。但是當網絡分區故障發生時,微服務與Eureka Server之間無法正常通信,而微服務本身是正常運作的,此時不應該移除這個微服務,是以引入了自我保護機制。

自我保護模式正是一種針對網絡異常波動的安全保護措施,使用自我保護模式能使Eureka叢集更加的健壯、穩定的運作。

自我保護機制的工作機制是如果在15分鐘内超過85%的用戶端節點都沒有正常的心跳,那麼Eureka就認為用戶端與注冊中心出現了網絡故障,Eureka Server自動進入自我保護機制,此時會出現以下幾種情況:

1、Eureka Server不再從注冊清單中移除因為長時間沒收到心跳而應該過期的服務。

2、Eureka Server仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其它節點上,保證目前節點依然可用。

3、當網絡穩定時,目前Eureka Server新的注冊資訊會被同步到其它節點中。

是以Eureka Server可以很好的應對因網絡故障導緻部分節點失聯的情況,而不會像ZK那樣如果有一半不可用的情況會導緻整個叢集不可用而變成癱瘓。

# 該配置可以移除這種自我保護機制,防止失效的服務也被一直通路 (Spring Cloud預設該配置是 true)
eureka.server.enable-self-preservation: false

# 該配置可以修改檢查失效服務的時間,每隔10s檢查失效服務,并移除清單 (Spring Cloud預設該配置是 60s)
eureka.server.eviction-interval-timer-in-ms: 10      

2.Eureka用戶端

Eureka用戶端的配置application.yml:

eureka:
  instance:
    # 每隔10s發送一次心跳
    lease-renewal-interval-in-seconds: 10
    # 告知服務端30秒還未收到心跳的話,就将該服務移除清單
    lease-expiration-duration-in-seconds: 30
  client:
    serviceUrl:
      defaultZone: http://localhost:9501/eureka/

server:
  port: 9502
spring:
  application:
    name: service-hi      
# 該配置訓示eureka用戶端需要向eureka伺服器發送心跳的頻率  (Spring Cloud預設該配置是 30s)
eureka.instance.lease-renewal-interval-in-seconds: 10

# 該配置訓示eureka伺服器在接收到最後一個心跳之後等待的時間,然後才能從清單中删除此執行個體 (Spring Cloud預設該配置是 90s)
eureka.instance.lease-expiration-duration-in-seconds: 30      

SpringCloud:Eureka的健康檢測機制==>https://blog.csdn.net/akaks0/article/details/79512680

遺留問題

defaultZone 需要添加/eureka字尾? 是的預設就這樣.

我的git項目位址

https://gitee.com/KingBoBo/springboot-07-cloud  base分支

 入門篇

Eureka 服務的注冊和發現==>https://www.cnblogs.com/fangwu/p/8975990.html

進階篇

SpringBoot SpringCloud叢集==>https://www.cnblogs.com/whatlonelytear/p/10894161.html