Spring Cloud Eureka

                       Spring Cloud基礎教程Eureka

 Spring Cloud簡介

Spring Cloud是一個基於Spring Boot實現的雲應用開發工具,它爲基於JVM的雲應用開發中涉及的配置管理、服務發現、斷路器、智能路由、微代理、控制總線、全局鎖、決策競選、分佈式會話和集羣狀態管理等操作提供了一種簡單的開發方式。

Spring Cloud包含了多個子項目(針對分佈式系統中涉及的多個不同開源產品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud0 CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等項目。

微服務架構

微服務架構”在這幾年非常的火熱,以至於關於微服務架構相關的開源產品被反覆的提及(比如:netflix、dubbo),Spring Cloud也因Spring社區的強大知名度和影響力也被廣大架構師與開發者備受關注。

那麼什麼是“微服務架構”呢?簡單的說,微服務架構就是將一個完整的應用從數據存儲開始垂直拆分成多個不同的服務,每個服務都能獨立部署、獨立維護、獨立擴展,服務與服務間通過諸如RESTful API的方式互相調用。

對於“微服務架構”,大家在互聯網可以搜索到很多相關的介紹和研究文章來進行學習和了解。也可以閱讀始祖Martin Fowler的《Microservices》,本文不做更多的介紹和描述。 

注意:本文是建立在SpringBoot基礎進行代碼演示.

服務治理

在簡單介紹了Spring Cloud和微服務架構之後,下面迴歸本文的主旨內容,如何使用Spring Cloud來實現服務治理。

由於Spring Cloud爲服務治理做了一層抽象接口,所以在Spring Cloud應用中可以支持多種不同的服務治理框架,比如:Netflix Eureka、Consul、Zookeeper。在Spring Cloud服務治理抽象層的作用下,我們可以無縫地切換服務治理實現,並且不影響任何其他的服務註冊、服務發現、服務調用等邏輯。

所以,下面我們通過介紹兩種服務治理的實現來體會Spring Cloud這一層抽象所帶來的好處。

Spring Cloud Eureka【Dalston版】

首先,我們來嘗試使用Spring Cloud Eureka來實現服務治理。

Spring Cloud Eureka是Spring Cloud Netflix項目下的服務治理模塊。而Spring Cloud Netflix項目是Spring Cloud的子項目之一,主要內容是對Netflix公司一系列開源產品的包裝,它爲Spring Boot應用提供了自配置的Netflix OSS整合。通過一些簡單的註解,開發者就可以快速的在應用中配置一下常用模塊並構建龐大的分佈式系統。它主要提供的模塊包括:服務發現(Eureka),斷路器(Hystrix),智能路由(Zuul),客戶端負載均衡(Ribbon)等。

下面,就來具體看看如何使用Spring Cloud Eureka實現服務治理。

github地址:[email protected]:13849141963/spring-cloud.git

創建“服務註冊中心”

創建一個基礎的Spring Boot工程,命名爲springcloud-eureka,並在pom.xml中引入需要的依賴內容:

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

    <dependencies>
        <!--Eureka服務註冊中心-->
          <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>

        <!--生產環境下給Eureka註冊中心添加安全的用戶認證-->
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>-->

        <!-- 熱部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.RC1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

在默認設置下,該服務註冊中心也會將自己作爲客戶端來嘗試註冊它自己,所以我們需要禁用它的客戶端註冊行爲,只需要在application.yml配置文件中增加如下信息:

spring:
  application:
    #應用名稱
    name: Eureka-application
    #Eureka註冊中心訪問權限  使用時在pom.xml文件中添加spring-boot-starter-security的座標
  #security:
    #user:
     # name: admin
     # password: admin
    
eureka:
  server:
    enable-self-preservation: false #關閉自我保護模式(默認爲打開)
  instance:
    #服務註冊中心實例的主機名
    hostname: 10.0.45.103
  client:
    #是否向服務註冊中心註冊自己
    register-with-eureka: false
    #是否檢索服務
    fetch-registry: false
    serviceUrl:
      ##服務註冊中心的配置內容,指定服務註冊中心的位置
      #defaultZone: http://admin:admin@${eureka.instance.hostname}:${server.port}/eureka/  # 安全的註冊地址
     defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    registry-fetch-interval-seconds: 30
    #開啓客戶端存活狀態監測

server:
  #服務註冊中心端口號
  port: 8088

#熱部署
devtools:
  restart:
    #不加在這些配置文件
    exclude: static/**,templates/**
    enabled: true

1、eureka.client.register-with-eureka: 對於同一個進程,既可以作爲註冊中心的服務端,也可以作爲註冊中心的客戶端,在本例中,註冊中心提供單一的註冊服務功能,不提供其他接口服務能力,所以設置爲false,最終不作爲服務實例註冊在註冊中心中供其他服務調用。 
2、eureka.server.enable-self-preservation
是否開啓自我保護模式,默認爲true。

默認情況下,如果Eureka Server在一定時間內沒有接收到某個微服務實例的心跳,Eureka Server將會註銷該實例(默認90秒)。但是當網絡分區故障發生時,微服務與Eureka Server之間無法正常通信,以上行爲可能變得非常危險了——因爲微服務本身其實是健康的,此時本不應該註銷這個微服務。

Eureka通過“自我保護模式”來解決這個問題——當Eureka Server節點在短時間內丟失過多客戶端時(可能發生了網絡分區故障),那麼這個節點就會進入自我保護模式。一旦進入該模式,Eureka Server就會保護服務註冊表中的信息,不再刪除服務註冊表中的數據(也就是不會註銷任何微服務)。當網絡故障恢復後,該Eureka Server節點會自動退出自我保護模式。

綜上,自我保護模式是一種應對網絡異常的安全保護措施。它的架構哲學是寧可同時保留所有微服務(健康的微服務和不健康的微服務都會保留),也不盲目註銷任何健康的微服務。使用自我保護模式,可以讓Eureka集羣更加的健壯、穩定。

當我們在本地調試基於Eureka的程序時,基本上都會碰到這樣一個問題,在服務註冊中心的信息面板中出現類似的紅色警告信息:

實際上,該警告就是觸發了Eureka Server的自我保護機制。之前我們介紹過,服務註冊到Eureka Server之後,會維護一個心跳連接,告訴Eureka Server自己還活着。Eureka Server在運行期間,會統計心跳失敗的比例在15分鐘之內是否低於85%,如果出現低於的情況(在單機調試的時候很容易滿足,實際在生產環境中通常是網絡不穩定導致),Eureka Server會將當前的實例註冊信息保護起來,讓這些實例不會過期,儘可能的保護這些註冊信息。在這段保護 期間內實例出現問題,那麼客戶端很容易拿到實際已經不存在的服務實例,會出現調用失敗的情況,所以客戶端必須要有容錯機制,比如可以使用請求重試、斷路器等機制。 
由於本地調試很容易觸發註冊中心的保護機制,這會使得註冊中心維護的服務實例不那麼準確。所以,我們在本地進行開發的時候,可以使eureka.server.enable-self-preservation=false參數關閉保護機制,以確保註冊中心可以將不可用的實例正確剔除。
3、eureka.server.eviction-interval-timer-in-ms
eureka server清理無效節點的時間間隔,默認60000毫秒,即60秒。服務下線存在兩種情況,一種是客戶端主要發送“服務下線”的請求,另外一種是通過定時任務掃描,每隔固定的時間清理無效的節點信息。清理無效節點的策略由客戶端決定,客戶端節點會設置相應的上報時間和最大超時時間(最大超時時間必須超過上報時間,推介設置爲3倍關係),eureka server至上一次收到client的心跳之後,等待下一次心跳的超時時間,在這個時間內若沒收到下一次心跳,則將移除該instance。
 

spring cloud服務發現註解之@EnableDiscoveryClient與@EnableEurekaClient的作用以及各自的區別

通過@EnableEurekaServer註解啓動一個Eureka服務註冊中心提供給其他應用進行對話。這一步非常的簡單,只需要在一個普通的Spring Boot應用中添加這個註解就能開啓此功能,比如下面的例子:啓動

package com.zy.cn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer//該註解表示使用Eureka服務註冊中心
@SpringBootApplication
public class SpringcloudEurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringcloudEurekaApplication.class, args);
    }
}

了與後續要進行註冊的服務區分,這裏將服務註冊中心的端口通過server.port屬性設置爲1001。啓動工程後,訪問:http://10.0.45.103:8088/,可以看到下面的頁面,其中還沒有發現任何服務。

創建“服務提供方”

下面我們創建提供服務的客戶端,並向服務註冊中心註冊自己。本文我們主要介紹服務的註冊與發現,所以我們不妨在服務提供方中嘗試着提供一個接口來獲取當前所有的服務信息。

首先,創建一個基本的Spring Boot應用。命名爲springcloud-provider,在pom.xml中,加入如下配置:

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
       
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.26</version>
        </dependency>

      
        <!-- Spring boot 熱部署 : 此熱部署會遇到 java.lang.ClassCastException 異常 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!--aop切面  做接口的安全校驗-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.44</version>
        </dependency>

        <!--在Eureka 註冊中心註冊服務-->
          <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </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>

其次,實現請求處理接口,接口內容

package com.zy.cn.controller;
import com.zy.cn.entity.User;
import com.zy.cn.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/***
 * 操作用戶的接口
 */
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    //@GetMapping("/queryAll") 等於下面的註解
    @RequestMapping(value = "/queryAll",method = RequestMethod.GET)
    public Object queryAll(){
        List<User> users = userService.queryAll();
        return users;
    }

    @RequestMapping(value = "/insert",method = RequestMethod.POST)
    public Object insert(@RequestBody User user){
        userService.inseret(user);
        return "OK";
    }

    @RequestMapping(value = "/find/{id}",method = RequestMethod.GET)
    public Object find(@PathVariable("id") Integer id){
        User user = userService.find(id);
        return user;
    }


}

我們在完成了服務內容的實現之後,再繼續對application.yml做一些配置工作,具體如下:端口爲8081

server:
  port: 8081
  servlet:
    context-path: /springcloud-provider
  tomcat:
    uri-encoding: UTF-8
#mybatis相關配置信息
mybatis:
  mapper-locations: com/zy/cn/mapper/*.xml
  type-aliases-package: com.zy.cn.entity


spring:
  application:
    #應用名稱
    name: springcloud-provider
    #mysql相關配置信息
  datasource:
    url: jdbc:mysql://10.0.45.103/mysql-test?characterEncoding=utf8&serverTimezone=GMT%2B8
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    #http編碼格式
  http:
    encoding:
      enabled: true
      charset: UTF-8
      force: true

#熱部署
devtools:
  restart:
    exclude: static/**,templates/**
    enabled: true
  jpa:
    database: MYSQL
    show-sql: true
#在Eureka註冊中心進行註冊服務
eureka:
  client:
    service-url:
      #註冊地址
      defaultZone: http://10.0.45.103:8088/eureka

1、eureka.instance.lease-renewal-interval-in-seconds
lease-renewal-interval-in-seconds,表示eureka client發送心跳給server端的頻率。如果在leaseExpirationDurationInSeconds後,server端沒有收到client的心跳,則將摘除該instance。除此之外,如果該instance實現了HealthCheckCallback,並決定讓自己unavailable的話,則該instance也不會接收到流量,默認30秒。

2、eureka.instance.lease-expiration-duration-in-seconds
lease-expiration-duration-in-seconds,表示eureka server至上一次收到client的心跳之後,等待下一次心跳的超時時間,在這個時間內若沒收到下一次心跳,則將移除該instance,該值默認爲90秒。

3、eureka.client.registry-fetch-interval-seconds
表示eureka client間隔多久去拉取服務註冊信息,默認爲30秒,對於api-gateway,如果要迅速獲取服務註冊狀態,可以縮小該值,比如5秒。

最後在應用主類中通過加上@EnableEurekaClient註解,該註解能激活Eureka中的EurekaClient實現,這樣才能實現Controller中對服務信息的輸出。

package com.zy.cn;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@MapperScan("com.zy.cn.dao")
@EnableEurekaClient
@SpringBootApplication
public class SpringcloudProviderApplication  {
	public static void main(String[] args) {
		SpringApplication.run(SpringcloudProviderApplication.class, args);
	}
}

通過spring.application.name屬性,我們可以指定微服務的名稱後續在調用的時候只需要使用該名稱就可以進行服務的訪問。eureka.client.serviceUrl.defaultZone屬性對應服務註冊中心的配置內容,指定服務註冊中心的位置。爲了在本機上測試區分服務提供方和服務註冊中心,使用server.port屬性設置不同的端口。

啓動該工程後,再次訪問:http://10.0.45.103:8081/。可以如下圖內容,我們定義的服務被成功註冊了。

 當然,我們也可以通過直接訪問springcloud-provider服務提供的/queryAll接口來獲取當前的服務清單,只需要訪問:http://10.0.45.103:8081/springcloud-provider/queryAll,我們可以得到如下輸出返回:

 

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