服務治理: Spring Cloud Eureka

服務治理: Spring Cloud Eureka

1、簡介

Spring cloud eurekaSpring cloud netfilx中的一部分,它基於Netflix Eureka作了二次封裝,主要職責完成Eureka 中的服務治理功能java

本篇主要探討以下:web

  • 服務治理和Eureka簡介
  • 構建服務註冊中心
  • 服務註冊與服務發現
  • Eureka 基礎架構
  • Eureka 的服務治理機制
  • Eureka 的配置

2、 功能概述

服務治理

​ 服務治理能夠是說微服務架構中最爲核心的基礎模塊,它主要用來實現各個微服務實現的自動化註冊與發現。在開始的時候微服務系統的服務可能並很少,咱們須要一些配置來完成服務的調用。spring

  • 服務註冊: 在服務治理框架中,一般會構建一個註冊中心,由各個服務提供者來向註冊中心登記並提供服務,將主機與端口號、版本號、通訊協議等一些附加信息告知註冊中心,註冊中心按照服務名分類組織服務清單。apache

    服務名 位置
    服務A 192.168.1.101:8000, 192.168.1.102:8000
    服務B 192.168.1.103:9000,192.168.1.104:9000,192.168.1.105:9000

    好比咱們有兩個提供服務A 的進程分別位於192.168.1.101:8000, 192.168.1.102:8000 上,另外還有三個提供服務B 的進程分別位於192.168.1.103:9000,192.168.1.104:9000,192.168.1.105:9000 進程上,那麼你向服務中心註冊事後,服務中心就會有一個這樣的服務列表,服務中心向各個註冊的服務發送心跳機制,來檢驗服務是否可用,若不可用就會把服務剔除,來達到故障排除的效果。緩存

  • 服務發現: 因爲在服務治理框架下運做,服務間的調用再也不經過指定的Ip:端口號這種方式來實現 ,而是向服務名發起請求實現。因此,在服務調用方在調用服務提供方接口的時候,並不知道具體服務的位置。所以,服務調用方須要向服務中心獲取服務列表,以實現對具體服務的訪問。網絡

    好比一個服務調用者C想要獲取服務A的ip來完成接口的調用,那麼他首先應該去服務中心發起諮詢你服務的請求,由註冊中心的服務列表將A的位置發送給調用者C,若是按照上面服務A地址的話,那麼調用者C會由兩個服務A的地址來提供服務,當服務C須要調用的時候,便從服務A中的清單中採用輪詢的方式取出一個位置來服務調用,這個過程也被稱爲負載均衡。架構

Netflix Eureka

  • Eureka是Netflix開發的服務發現框架,自己是一個基於REST的服務,主要用於定位運行在AWS域中的中間層服務,以達到負載均衡和中間層服務故障轉移的目的。SpringCloud將它集成在其子項目spring-cloud-netflix中,以實現SpringCloud的服務發現功能。
  • Eureka包含兩個組件: Eureka ServerEureka Client
  • Eureka Server 簡稱Eureka 服務端, 主要提供服務註冊功能,其實也就至關因而註冊中心,和其餘服務註冊中心同樣,提供高可用的配置,同時也支持集羣部署,當集羣中某一個節點發生故障時,那麼Eureka就會進入自我保護模式,它容許故障的節點繼續提供服務的發現與註冊,當故障分片恢復運行時,集羣中的其餘分片會把他們的狀態再同步回來。
  • Eureka Client:簡稱Eureka 客戶端,主要處理服務的註冊與發現。客戶端經過註解和參數配置的方式,Eureka 客戶端向註冊中心註冊自身的服務並週期性的發送心跳機制來更新服務租約。同時,它也能從服務端查詢當前註冊的服務信息並把它們緩存到本地並週期性地刷新服務狀態。

搭建服務註冊中心

Spring Cloud Eureka 是採用SpringBoot 進行項目的快速搭建的,若是不太瞭解SpringBoot的話,能夠了解一下SpringBoot 入門實例。app

  • 首先建立SpringBoot工程,命名爲Eureka-server,也就是Eureka服務端,建立完成後在pom.xml文件中增長以下maven依賴,完整的文件以下:
<?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.eureka.server</groupId>
    <artifactId>eureka-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-server</name>
    <description>Demo project for Spring Boot</description>

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


    <properties>
        <java.version>1.8</java.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-eureka-server</artifactId>
        </dependency>

    </dependencies>

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

</project>
  • 在SpringBoot啓動類,也就是@SpringBootApplication修飾的主方法中加入以下註解@EnableEurekaServer
@EnableEurekaServer
    @SpringBootApplication
    public class EurekaServerApplication {

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

    }

加入這個註解也就標識着這是一個Eureka的服務端,能夠啓動服務了,可是啓動服務會報錯,由於你沒有添加註冊中心的相關配置。負載均衡

  • application.properties文件中加入以下內容
server.port=8000

eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

server.port 就表明着註冊中心的端口號

eureka.client.service-url.defaultZone :eureka客戶端默認服務url

eureka.client.register-with-eureka : 表示註冊中心是否向其餘註冊中心註冊本身,單節點註冊中心不須要,設置爲false

eureka.client.fetch-registry: 表示註冊中心是否主動去檢索服務,並不須要檢索服務,設置爲false

其餘配置:

# 項目contextPath,通常在正式發佈版本中,咱們不配置
# 避免加上更目錄:Cannot execute request on any known server
# 加上根目錄也須要在註冊地址上加入根
 server.context-path=/eureka81
# 錯誤頁,指定發生錯誤時,跳轉的URL。請查看BasicErrorController源碼便知
 server.error.path=/error
# 經過spring.application.name屬性,咱們能夠指定微服務的名稱後續在調用的時候只須要使用該名稱就能夠進行服務的訪問。
 spring.application.name=eureka-server
# eureka是默認使用hostname進行註冊,可經過一下項自動獲取註冊服務IP或者直接經過eureka.instance.ip-address指定IP
# eureka.instance.prefer-ip-address=true
# SpringBoot 在啓動的時候會讀配置文件,會把prefer-ip-address 默認轉換爲preferIpAddress駝峯命名
eureka.instance.preferIpAddress=true

# 設爲false,關閉自我保護
eureka.server.enable-self-preservation=false
# 清理間隔(單位毫秒,默認是60*1000
eureka.server.eviction-interval-timer-in-ms=6000

# 開啓健康檢查(須要spring-boot-starter-actuator依賴)
eureka.client.healthcheck.enabled=false
# 續約更新時間間隔(默認30秒)
eureka.instance.lease-renewal-interval-in-seconds=10    
# 續約到期時間(默認90秒)
eureka.instance.lease-expiration-duration-in-seconds=30

沒有加入 eureka.instance.preferIpAddress=true 以前,默認本地爲註冊中心

加入 eureka.instance.preferIpAddress=true 以後,圈出來的ip即爲eureka.client.service-url.defaultZone指定的 ip。

在完成了上述配置以後,應用程序啓動並訪問http://localhost:1111/ 能夠看到以下圖所示的信息版,其中Instances curently registered with Eureka 是空的,代表尚未任何服務提供者提供服務。

註冊服務提供者

在完成了上述搭建以後,接下來咱們嘗試將一個既有的SpringBoot應用加入Eureka服務治理體系去。

使用上一小節的快速入門工程進行改造,將其做爲一個微服務應用向服務註冊中心發佈註冊本身

pom.xml配置以下:

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

    <properties>
        <java.version>1.8</java.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-eureka</artifactId>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Brixton.SR5</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>
  • 配置完pom.xml,咱們須要在啓動類上加入@EnableDiscoverClient註解,用於開啓eureka-client客戶端
  • application.properties中加入以下內容
# 這個名字就是Eureka註冊中新的實例名稱
    spring.application.name=server-provider
    # 向註冊中心註冊本身
    eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
  • 配置完上面兩個以後,在package文件夾下新建HelloController類,具體代碼以下
@RestController
  public class HelloController {

      private final Logger log = LoggerFactory.getLogger(HelloController.class);

      @Resource
      private DiscoveryClient discoveryClient;

      @RequestMapping(value = "hello", method = RequestMethod.GET)
      public String hello(){
          ServiceInstance instance = discoveryClient.getLocalServiceInstance();
          log.info("instance.host = " + instance.getHost() + " instance.service = " +  instance.getServiceId()
                  + " instance.port = " + instance.getPort());
          return "Hello World";
      }
  }
  • 啓動服務提供者,啓動完成後,會出現以下表示啓動成功。

  • 訪問http://localhost:1111/ ,主頁上顯示eureka-provider註冊到了註冊中心

此處的Status 中的內容也就包括上面配置的spring.application.name=server-provider

  • 在主頁訪問 http://localhost:8080/hello ,發現頁面上 輸出了Hello World,控制檯打印出來了

    c.s.provider.controller.HelloController  : instance.host = macliu instance.service = server-provider instance.port = 8080

    注意事項

  1. 上面註冊到註冊中心的圖,你會發現這樣一行紅色的文字

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

這是Eureka的一種自我保護機制,Eureka Server在運行期間,會統計心跳失敗的比例在15分鐘以內是否低於85%,若是出現低於的狀況(在單機調試的時候很容易知足,實際在生產環境上一般是因爲網絡不穩定致使),Eureka Server會將當前的實例註冊信息保護起來,同時提示這個警告。

Eureka server和client之間每隔30秒會進行一次心跳通訊,告訴server,client還活着

  1. 把上面的server-provider服務中止以後,會出現以下狀態

這個表示server-provider 已經標記爲下線,也就是 DOWN 狀態,再次從新上線後,發現Status又變爲了UP狀態。

  1. 把上面的配置文件中自我保護功能關閉後,出現以下狀態

高可用配置中心

在微服務架構這樣的分佈式環境中,須要充分考慮到發生故障的狀況,因此在生產環境中必須對各個組件進行高可用部署,對於微服務是如此,對於註冊中心也同樣。

Eureka Server的設計就充分考慮了高可用問題,在Eureka的服務治理體系中,全部的節點既是服務提供方,也是服務的消費者,服務註冊中心也不例外,不一樣的註冊中心在向其餘註冊中心提供節點列表的時候,也在向其餘註冊中心獲取節點列表。

高可用的配置中心就是向其餘註冊中心註冊本身,同時把服務列表提供給其餘註冊中心,從而達到註冊中心列表同步,達到高可用的效果。經過下面兩個配置來實現

eureka.client.register-with-eureka=true
    eureka.client.fetch-registry=true

下面就在單節點的基礎之上建立一下高可用的配置中心(雙節點註冊中心)

  • 首先,建立兩個配置文件,分別是application-peer1.propertiesapplication-peer2.properties,內容分別以下

    • application-peer1.properties

      spring.application.name=eureka-server
      server.port=1111
      
      eureka.instance.hostname=peer1
      eureka.client.register-with-eureka=true
      eureka.client.fetch-registry=true
      
      eureka.client.service-url.defaultZone=http://peer2:1112/eureka/
    • application-peer2.properties

      spring.application.name=eureka-server
      server.port=1112
      
      eureka.instance.hostname=peer2
      eureka.client.register-with-eureka=true
      eureka.client.fetch-registry=true
      
      eureka.client.service-url.defaultZone=http://peer1:1111/eureka/
  • 在本地修改配置文件/etc/hosts ,Windows下面是C:\Winows\System32\drivers\etc\hosts。

    添加以下內容

    127.0.0.1 peerl 
    127.0.0.1 peer2

    以下:

  • 首先在idea 或者eclipse 使用mvn clean 和 mvn install命令,會直接打包,這裏注意,必定要在pom.xml中配置以下,不然使用java -jar會報沒有主清單屬性的錯誤。

    <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
    </build>
  • 打包完成後,切換到eureka-server項目,再切換到target目錄下,此時有mvn install 的jar包,使用java -jar eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1java -jar eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 兩個命令,啓動兩個例程。起來事後分別訪問 http://localhost:peer1/eureka/ 和 http://localhost:peer2/eureka/ 主頁,發現對應的註冊中心分別註冊進去了,並且分片也處於可用分片狀態。

到如今爲止,咱們已經讓兩個註冊中心分別註冊各自的服務了,還記得上面還有一個server-provider服務嗎?咱們也讓server-provider分別註冊到這兩個註冊中心。

server-provider中修改對應的配置文件

eureka.client.service-url.defaultZone=http://peer1:1111/eureka, http://peer2:1112/eureka/

啓動程序,發現http://localhost:1111/ 和 http://localhost:1112/ 中都註冊了server-provider服務

訪問http://localhost:8080/hello,你會發現頁面上顯示出來hello world,斷開其中的任意一個註冊中心,hello world也可以顯示出來。也就是說,server-provider 分別對兩個註冊中心分別註冊了各自的服務,由兩個註冊中心以輪詢的方式提供服務。斷開其中一個註冊中心,還有另一個註冊中心能夠提供服務,這也是Eureka 高可用的體現。

注意事項

  1. 若是application-peer1.propertiesapplication-peer2.properties中的eureka.instance.hostname與 本地hosts文件中的名稱不一致的話,那麼註冊中心啓動後,會使分片處於不可用的狀態, spring.application.name 表示的是實例的名稱,也就是以下的地方

  1. server-provider註冊進來的時候,高可用配置的註冊中心會以輪詢的方式提供服務,每次提供服務是哪一個註冊中心是不可預知的。

  2. 如咱們不想使用主機名來定義註冊中心的地址,也可使用IP地址的形式, 可是須要在配置文件中增長配置參數eureka.instance.prefer-ip-address=true, 該值默認爲false。

服務發現與消費

經過上述的內容介紹與實踐,咱們已經搭建起來微服務架構中的核心組件— 服務註冊中心(包括單節點模式和高可用模式)。並用server-provider註冊雙節點,在頁面上發起一個url請求時,註冊中心找到server-provider,並有兩個節點以輪詢的方式提供服務。

下面就來構建一個消費者,它主要完成兩個目標:發現服務消費服務。其中,服務發現的任務由Eureka客戶端完成,消費服務的任務由Ribbon來完成。

先來認識一下什麼是Ribbon:Ribbon是客戶端負載均衡器,可讓您對HTTP和TCP客戶端的行爲進行控制。 Feign已經使用了Ribbon,若是你使用了@FeignClient,那麼Ribbon也適用。

Ribbon能夠在經過客戶端中配置的ribbonServerList服務端列表去輪詢訪問以達到負載均衡的效果。當ribbonEureka聯合使用時,Ribbon的服務實例清單RibbonServerList會被DiscoveryEnabledNIWSServerList重寫,擴展成從Eureka註冊中心中獲取服務端列表。同時它也會用NIWSDiscoveryPing來取代Ping,它將職責委託給Eureka來肯定服務端是否啓動,咱們目前不細緻探討Ribbon的細節問題。

下面經過一個簡單的實例,看看Eureka的服務治理體系下如何實現服務的發現與消費。

  • 首先,先作一些準備工做,啓動以前實現的服務註冊中心eureka-server以及server-provider服務,爲了實現ribbon的負載均衡功能,咱們經過java -jar命令行的方式來啓動兩個不一樣端口的server-provider
    • 啓動一個eureka-server便可
    • 使用java -jar service-provider-0.0.1-SNAPSHOT.jar --server.port=8081java -jar service-provider-0.0.1-SNAPSHOT.jar --server.port=8082 來啓動兩個server-provider 進程
  • 啓動完成後,可見註冊中心註冊了兩個server-provider 實例

  • 新建立一個SpringBoot 工程,命名爲ribbon-consumer,相較於以前pom.xml,咱們新增了spring-cloud-starter-ribbon
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>


    <properties>
        <java.version>1.8</java.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-eureka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Brixton.SR5</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>
  • 建立完pom.xml,在ribbon-consumer 啓動類加上@EnableDiscoveryClient註解,讓該註解註冊爲Eureka客戶端,以得到服務發現的能力,同時,建立RestTemplate對象,加上@LoadBalance註解開啓負載均衡。
@EnableDiscoveryClient
  @SpringBootApplication
  public class RibbonConsumerApplication {

      @Bean
      @LoadBalanced
      RestTemplate restTemplate(){
          return new RestTemplate();
      }

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

  }
  • 在src目錄下新建一個RibbonController類,注入@RestTemplate,構造一個方法來調用server-provider中的/hello 方法。代碼以下
@RestController
    public class RibbonController {

        @Autowired
        RestTemplate restTemplate;

        @RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET)
        public String helloConsumer(){
            return restTemplate.getForEntity("http://server-provider/hello",String.class).getBody();
        }
    }

在helloConsumer方法上面採用Restful 風格的編碼方式,這個方法遠程調用了server-provider中的hello方法,在這裏不像是http://ip:端口號這種書寫方式,而是直接採用 服務名/方法名來直接調用方法,由於你不知道具體的方法在哪一個ip的機器上,須要由Eureka進行調用,這樣消費者就不用關心由誰提供了服務,只要提供了服務便可,這也是面向對象方法的一種體現,同時也能很好的解耦。

  • application.properties 配置以下
spring.application.name=ribbon-consumer
server.port=9000

eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

由於ribbon-consumer須要由客戶端來主動調用方法,因此須要提供實例名稱,端口號,並在註冊中心註冊ribbon-consumer服務

  • 啓動服務,訪問Eureka主頁發現Ribbon-consumer的服務也註冊進來了。

INFO 29397 --- [nio-9000-exec-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client server-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=server-provider,current list of Servers=[macliu:8082, macliu:8081],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2;   Active connections count: 0;    Circuit breaker tripped count: 0;   Active connections per server: 0.0;]
},Server stats: [[Server:macliu:8082;   Zone:defaultZone;   Total Requests:0;   Successive connection failure:0;    Total blackout seconds:0;   Last connection made:Thu Jan 01 08:00:00 CST 1970;  First connection made: Thu Jan 01 08:00:00 CST 1970;    Active Connections:0;   total failure count in last (1000) msecs:0; average resp time:0.0;  90 percentile resp time:0.0;    95 percentile resp time:0.0;    min resp time:0.0;  max resp time:0.0;  stddev resp time:0.0]
, [Server:macliu:8081;  Zone:defaultZone;   Total Requests:0;   Successive connection failure:0;    Total blackout seconds:0;   Last connection made:Thu Jan 01 08:00:00 CST 1970;  First connection made: Thu Jan 01 08:00:00 CST 1970;    Active Connections:0;   total failure count in last (1000) msecs:0; average resp time:0.0;  90 percentile resp time:0.0;    95 percentile resp time:0.0;    min resp time:0.0;  max resp time:0.0;  stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@3dc2c2eb

再嘗試刷新幾回url,由於咱們實在後臺啓動的程序,在終端會看到以下的信息

INFO 28929 --- [nio-8082-exec-3] c.s.provider.controller.HelloController  : instance.host = macliuinstance.service = server-providerinstance.port = 8082

由於開了兩個終端,一個是8081端口,一個是8082端口,多刷新幾回頁面後,會發現終端在循環輸出上面的信息,來判斷使用ribbon 實現了負載均衡。

歡迎關注Java建設者:一塊兒學習交流

相關文章
相關標籤/搜索