SpringCloud之服務治理【Eureka】

注冊中心

理解注冊中心:

服務管理,核心是有個服務注冊表,心跳機制動態(tài)維護。

  • Eureka Server: 服務注冊中心,負責服務列表的注冊、維護和查詢等功能;

  • Service Provider: 服務提供方,同時也是一個Eureka Client,啟動的時候向注冊中心上報自己的網(wǎng)絡信息,負責將所提供的服務向Eureka Server進行注冊、續(xù)約和注銷等操作。注冊時所提供的主要數(shù)據(jù)包括服務名、機器ip、端口號、域名等,從而能夠使服務消費方能夠找到;

  • Service Consumer:服務消費方,同時也是一個Eureka Client。 啟動的時候向注冊中心上報自己的網(wǎng)絡信息,拉取provider的相關網(wǎng)絡信息。

Service Provider(服務提供方)和Service Consumer(服務消費方)并不是一個嚴格的概念,往往服務消費方也是一個服務提供方,同時服務提供方也可能會調(diào)用其它服務方所提供的服務。當然在我們進行微服務構(gòu)建時還是需要遵守業(yè)務層級之間的劃分,盡量避免服務之間的循環(huán)依賴。

為什么要用注冊中心【服務治理】:

  • 微服務數(shù)量眾多,要進行遠程調(diào)用就需要知道服務端的ip地址和端口,注冊中心幫助我們管理這些服務的ip和端口。
  • 微服務會實時上報自己的狀態(tài),注冊中心統(tǒng)一管理這些微服務的狀態(tài),將存在問題的服務踢出服務列表,客戶端獲取到可用的服務進行調(diào)用。

主流的注冊中心:

zookeeper、Eureka、consul、etcd 等

Eureka和Zookeeper的比較

CAP原則又稱CAP定理,指的是在一個分布式系統(tǒng)中,一致性(Consistency)、可用性(Availability)、分區(qū)容錯性(Partition tolerance)。CAP 原則指的是,這三個要素最多只能同時實現(xiàn)兩點,不可能三者兼顧。

  • 一致性(C):在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時刻是否同樣的值。(等同于所有節(jié)點訪問同一份最新的數(shù)據(jù)副本)
  • 可用性(A):在集群中一部分節(jié)點故障后,集群整體是否還能響應客戶端的讀寫請求。(對數(shù)據(jù)更新具備高可用性)
  • 分區(qū)容忍性(P):
    一個分布式系統(tǒng)里面,節(jié)點組成的網(wǎng)絡本來應該是連通的。然而可能因為一些故障,使得有些節(jié)點之間不連通了,整個網(wǎng)絡就分成了幾塊區(qū)域。數(shù)據(jù)就散布在了這些不連通的區(qū)域中,這就叫分區(qū)。
    當你一個數(shù)據(jù)項只在一個節(jié)點中保存,那么分區(qū)出現(xiàn)后,和這個節(jié)點不連通的部分就訪問不到這個數(shù)據(jù)了。這時分區(qū)就是無法容忍的。
    提高分區(qū)容忍性的辦法就是一個數(shù)據(jù)項復制到多個節(jié)點上,那么出現(xiàn)分區(qū)之后,這一數(shù)據(jù)項就可能分布到各個區(qū)里。容忍性就提高了。然而,要把數(shù)據(jù)復制到多個節(jié)點,就會帶來一致性的問題,就是多個節(jié)點上面的數(shù)據(jù)可能是不一致的。要保證一致,每次寫操作就都要等待全部節(jié)點寫成功,而這等待又會帶來可用性的問題??偟膩碚f就是,數(shù)據(jù)存在的節(jié)點越多,分區(qū)容忍性越高,但要復制更新的數(shù)據(jù)就越多,一致性就越難保證。為了保證一致性,更新所有節(jié)點數(shù)據(jù)所需要的時間就越長,可用性就會降低。
    但我們可以設想不提供P,也就是不允許出現(xiàn)分區(qū)的情況,是有可能同時提供AC兩個特性的。 實際上這在分布式系統(tǒng)中,并不受控。所以一般認為P是必須提供的,所以AC需要權衡。

Zookeeper保證CP

當向注冊中心查詢服務列表時,我們可以容忍注冊中心返回的是幾分鐘以前的注冊信息,但不能接受服務直接down掉不可用。也就是說,服務注冊功能對可用性的要求要高于一致性。但是zk會出現(xiàn)這樣一種情況,當master節(jié)點因為網(wǎng)絡故障與其他節(jié)點失去聯(lián)系時,剩余節(jié)點會重新進行l(wèi)eader選舉。問題在于,選舉leader的時間太長,30 ~ 120s, 且選舉期間整個zk集群都是不可用的,這就導致在選舉期間注冊服務癱瘓。
在云部署的環(huán)境下,因網(wǎng)絡問題使得zk集群失去master節(jié)點是較大概率會發(fā)生的事,雖然服務能夠最終恢復,但是漫長的選舉時間導致的注冊長期不可用是不能容忍的。

Eureka保證AP

Eureka看明白了這一點,因此在設計時就優(yōu)先保證可用性。Eureka各個節(jié)點都是平等的,幾個節(jié)點掛掉不會影響正常節(jié)點的工作,剩余的節(jié)點依然可以提供注冊和查詢服務。而Eureka的客戶端在向某個Eureka注冊或時如果發(fā)現(xiàn)連接失敗,則會自動切換至其它節(jié)點,只要有一臺Eureka還在,就能保證注冊服務可用(保證可用性),只不過查到的信息可能不是最新的(不保證強一致性)。
除此之外,Eureka還有一種自我保護機制,如果在15分鐘內(nèi)超過85%的節(jié)點都沒有正常的心跳,那么Eureka就認為客戶端與注冊中心出現(xiàn)了網(wǎng)絡故障,此時會出現(xiàn)以下幾種情況:
1. Eureka不再從注冊列表中移除因為長時間沒收到心跳而應該過期的服務
2. Eureka仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其它節(jié)點上(即保證當前節(jié)點依然可用)
3. 當網(wǎng)絡穩(wěn)定時,當前實例新的注冊信息會被同步到其它節(jié)點中

因此, Eureka可以很好的應對因網(wǎng)絡故障導致部分節(jié)點失去聯(lián)系的情況,而不會像zookeeper那樣使整個注冊服務癱瘓。

對比

  1. Eureka不持久化,緩存,Zookeeper持久化,對于注冊中心沒必要持久化,我們只關心當前瞬時的服務狀態(tài)
  2. Eureka通過增量更新注冊信息,Zookeeper通過Watch事件監(jiān)控變化,對于服務注冊變化的過程,我們不關心,只關心瞬時狀態(tài)
  3. Eureka提供客戶端緩存,Zookeeper無客戶端緩存,在網(wǎng)絡隔離注冊中心訪問不了的情況下,寧可返回某服務5分鐘之前在哪幾個服務器上可用的信息,也不能因為暫時的網(wǎng)絡故障而找不到可用的服務器

總結(jié)

Eureka作為單純的服務注冊中心來說要比zookeeper更加“專業(yè)”,因為注冊服務更重要的是可用性,我們可以接受短期內(nèi)達不到一致性的狀況。
Zookeeper:CP設計,保證了一致性,集群搭建的時候,某個節(jié)點失效,則會進行選舉行的leader,或者半數(shù)以上節(jié)點不可用,則無法提供服務,因此可用性沒法滿足
Eureka:AP原則,無主從節(jié)點,一個節(jié)點掛了,自動切換其他節(jié)點可以使用,去中心化
所以Eureka適合作為服務注冊發(fā)現(xiàn)中心,Zookeeper適合更廣泛的分布式協(xié)調(diào)服務
如果要求可用性,則Eureka,如電商系統(tǒng);如果要求一致性,則選擇zookeeper,如金融行業(yè)

Eureka

Eureka介紹

Spring Cloud Eureka 是對Netflix公司的Eureka的二次封裝,它實現(xiàn)了服務治理的功能,Spring Cloud Eureka提供服務端與客戶端,服務端即是Eureka服務注冊中心,客戶端完成微服務向Eureka服務的注冊與發(fā)現(xiàn)。下圖顯示了Eureka Server與Eureka Client的關系:


Eureka架構(gòu)

Eurka 工作流程

1、Eureka Server 啟動成功,等待服務端注冊。在啟動過程中如果配置了集群,集群之間定時通過 Replicate 同步注冊表,每個 Eureka Server 都存在獨立完整的服務注冊表信息
2、Eureka Client 啟動時根據(jù)配置的 Eureka Server 地址去注冊中心注冊服務
3、Eureka Client 會每 30s 向 Eureka Server 發(fā)送一次心跳請求,證明客戶端服務正常
4、當 Eureka Server 90s 內(nèi)沒有收到 Eureka Client 的心跳,注冊中心則認為該節(jié)點失效,會注銷該實例
5、單位時間內(nèi) Eureka Server 統(tǒng)計到有大量的 Eureka Client 沒有上送心跳,則認為可能為網(wǎng)絡異常,進入自我保護機制,不再剔除沒有上送心跳的客戶端
6、當 Eureka Client 心跳請求恢復正常之后,Eureka Server 自動退出自我保護模式
7、Eureka Client 定時全量或者增量從注冊中心獲取服務注冊表,并且將獲取到的信息緩存到本地
8、服務調(diào)用時,Eureka Client 會先從本地緩存找尋調(diào)取的服務。如果獲取不到,先從注冊中心刷新注冊表,再同步到本地緩存
9、Eureka Client 獲取到目標服務器信息,發(fā)起服務調(diào)用
10、Eureka Client 程序關閉時向 Eureka Server 發(fā)送取消請求,Eureka Server 將實例從注冊表中刪除

在Eureka中,其核心的概念主要有如下幾個:

服務注冊(Register)
??當Eureka客戶端向Eureka服務注冊器注冊時,它提供自身的元數(shù)據(jù),比如IP地址、端口等信息。
服務續(xù)約(Renew)
??在服務續(xù)約中,Eureka客戶端會每隔30秒發(fā)送一次心跳來進行服務續(xù)約。通過續(xù)約來告知Eureka服務器該客戶端仍然存在,希望服務器不要剔除自己。
服務下線(Cancel)
??Eureka客戶端在程序關閉時向Eureka服務器發(fā)送取消請求。發(fā)送請求后,該客戶端實例信息將從服務器的實例注冊列表中刪除。
服務剔除(Eviction)
??在默認的情況下,當Eureka客戶端連續(xù)90秒沒有像Eureka服務器發(fā)送服務續(xù)約的心跳(Heartbeat),Eureka服務器就會將該服務實例從服務注冊列表中刪除,即剔除該服務。
獲取服務注冊列表信息(Fetch Registries)
??Eureka客戶端從Eureka服務器獲取服務注冊列表信息,并將其緩存到本地??蛻舳藭褂迷撔畔⒉檎移渌眨瑥亩M行遠程調(diào)用。該注冊列表信息定期(每隔30秒)更新一次。每次返回的注冊列表信息可能與Eureka客戶端的緩存信息有所不同,Eureka客戶端會自動處理兩者之間的差異。

Eureka搭建

工程結(jié)構(gòu)如下:


Hello Spring Cloud工程結(jié)構(gòu)

1、搭建微服務Parent工程

先構(gòu)建一個parent工程,該工程僅用來定義一個pom文件,后續(xù)工程的pom文件的皆繼承該pom。在該pom中我們將定義各工程所共同使用的第三方依賴及相應版本定義,比如我們接下來的各工程中對Spring Cloud的依賴等。這樣我們就可以統(tǒng)一對第三方依賴及基礎信息定義進行管理,后續(xù)當我們需要升級第三方依賴時,只需要修改一個地方就可以了。

注意:Spring Cloud的版本與Spring Boot的版本一定要對應上,不然會編譯不通過。
parent pom文件中的內(nèi)容如下:

<?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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    <groupId>twostepsfromjava.cloud</groupId>
    <artifactId>twostepsfromjava-cloud-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>    
</project>

2、搭建服務治理服務器——Eureka服務器

  1. 編寫的pom.xml文件要繼承上面的Parent項目,增加spring-cloud-starter-netflix-eureka-server依賴包,并把artifactId定義為:service-discovery。

注意:

  • 這里我們直接繼承parent項目中的pom,所以只需要聲明我們需要的新增的spring-cloud-starter-eureka-server依賴即可。
  • 很多老的博客或者書中,用的是spring-cloud-starter-eureka-server依賴包,是廢棄的。特別對于Spring Boot2.0以上的版本是無法使用的。
<?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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>twostepsfromjava.cloud</groupId>
        <artifactId>twostepsfromjava-cloud-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../parent</relativePath>
    </parent>

    <artifactId>service-discovery</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
    </dependencies>    
</project>

關于Spring Cloud的命名:由于Spring Cloud是諸多子項目集合的綜合項目,原則上由其子項目維護自己的發(fā)布版本號,也就是我們常用的版本號,如:1.2.3.RELEASE、1.1.4.RELEASE等。因此Spring Cloud為了避免版本號與其子項目的版本號混淆,所以沒有采用版本號的方式,而是采用命名的方式。這些版本名稱采用了倫敦地鐵站的名字,根據(jù)字母表的順序來對應版本時間順序。比如,最早的Release版本名稱為Angel,第二個Release版本的名稱為Brixton,以此類推……。而我們在本系列文章所使用的版本名稱為:Dalston.SR1,也就是最新版本。后續(xù)版本名稱根據(jù)項目中公布的分別為: EdgwareFinchley。
另,Dalston.SR1中的SRservice releases的簡寫,而1則是該版本名稱中的第1個版本。
具體關于Spring Cloud版本的命名說明可以參考這里.

  1. 修改Spring Boot的應用引導類,在應用引導類中增加服務治理服務器的注解。
    通過@EnableEurekaServer注解,Spring Boot在啟動應用的時候就會自動構(gòu)建一個默認的服務治理服務器。
@EnableEurekaServer//標識這是一個Eureka服務
@SpringBootApplication
public class GovernCenterApplication {
    public static void main(String[] args) {
        SpringApplication.run(GovernCenterApplication.class, args);
    }
}
  1. 增加Eureka相應的配置:
server.port=8260

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
  • registerWithEureka:
    用來控制當Spring Boot啟動服務完成后是否將該服務注冊到服務治理服務器上。因為該服務本身就是服務治理服務器,而且未構(gòu)建服務治理集群,所以設置為false,表示不注冊。
    被其它服務調(diào)用時設置為true,表示需向Eureka注冊。
  • fetchRegistry
    應用啟動后不需要從服務治理服務器上同步已注冊的服務注冊列表數(shù)據(jù)到本地。需要從Eureka中查找要調(diào)用的目標服務時需要設置為true。
  • serviceUrl.defaultZone
    配置上報Eureka服務地址高可用狀態(tài)配置對方的地址,單機狀態(tài)配置自己
  • enable-self-preservation
    自保護設置。
    Eureka Server有一種自我保護模式,當微服務不再向Eureka Server上報狀態(tài),Eureka Server會從服務列表將此服務刪除,如果出現(xiàn)網(wǎng)絡異常情況(微服務正常),此時Eureka server進入自保護模式,不再將微服務從服務列表刪除。
    在開發(fā)階段建議關閉自保護模式
  • eviction-interval-timer-in-ms:
    清理失效結(jié)點的間隔,在這個時間段內(nèi)如果沒有收到該結(jié)點的上報則將結(jié)點從服務列表中剔除。
  1. 啟動Eureka Server
    接下來你可以在你的IDE中啟動該服務。當然你也可以將該服務器打包成一個Fat Jar,然后通過java -jar的命令啟動,如:
java -jar service-discovery-1.0.0-SNAPSHOT.jar

說明: 如果需要打包成一個Fat Jar你需要修改pom.xml中的配置,增加如下內(nèi)容:

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

增加一個Spring Boot打包插件。這樣編譯出來的Jar包就可以通過上述命令直接運行。

啟動成功后訪問http://localhost:50101/可以查看Eureka的控制臺。

image.png

上圖紅色提示信息:
THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OFNETWORK/OTHER PROBLEMS.
自我保護模式被關閉。在網(wǎng)絡或其他問題的情況下可能不會保護實例失效。

搭建服務提供者——注冊服務

Eureka服務器我們已經(jīng)編寫好了,接下來我們就可以編寫一個Eureka的客戶端了。這個客戶端可能是一個服務提供者,也可能是一個服務消費者,甚至兩者都是。
我們先編寫一個簡單的Eureka Client,該客戶端提供一個簡單的服務,就是調(diào)用/hello服務端點(EndPoint)時返回一個字符串Hello, Spring Cloud!

1. 編寫pom.xml文件

同樣,我們繼承自parent項目的pom.xml,這里將artifactId定義為:service-hello,也就是提供Hello服務。
pom文件增加spring-cloud-starter-netflix-eureka-client依賴包,使用戶微服務具有服務注冊功能。

    <?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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>twostepsfromjava.cloud</groupId>
        <artifactId>twostepsfromjava-cloud-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../parent</relativePath>
    </parent>

    <artifactId>service-hello</artifactId>

    <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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
2. 在application.yml配置
server.port=2100

spring.application.name=SERVICE-HELLO

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

這里spring.application.name必須要設置,服務消費者將通過該名稱調(diào)用所提供的服務。 eureka.client.service-url也必須設置,表示我們要向那些Eureka服務器進行服務注冊,這里可以聲明多個Eureka服務器,具體我們將在后面關于Eureka高可用相關章節(jié)中進行詳細說明。

3. 在啟動類上添加注解

引導類中增加@EnableDiscoveryClient,表示這是一個Eureka客戶端,通過該注解,在Spring Boot啟動完畢之后,就會根據(jù)配置中的信息嘗試與服務治理服務器進行連接,連接成功之后進行服務注冊或服務注冊信息的同步。

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

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

}

說明: 這里與service-discovery的唯一區(qū)別就是啟動類上注解變成了@EnableDiscoveryClient,聲明這是一個Eureka Client。

4. 編寫一個簡單的API服務
@RestController
public class HelloEndpoint {
    protected Logger logger = LoggerFactory.getLogger(HelloEndpoint.class);

    @Autowired
    private EurekaInstanceConfig eurekaInstanceConfig;
    @Value("${server.port}")
    private int serverPort = 0;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        this.logger.info("/hello, instanceId:{}, host:{}", eurekaInstanceConfig.getInstanceId(), eurekaInstanceConfig.getHostName(false));
        return "Hello, Spring Cloud! My port is " + String.valueOf(serverPort);
    }
}

該服務僅提供一個/hello服務端點,調(diào)用該服務后將返回一個字符串Hello, Spring Cloud!。

5. 啟動服務器

同樣啟動該服務器。啟動成功后,我們將在控制臺上看到這么一句日志:

[DiscoveryClient-InstanceInfoReplicator-0] INFO  c.netflix.discovery.DiscoveryClient - DiscoveryClient_SERVICE-HELLO/192.168.0.105:SERVICE-HELLO:2100 - registration status: 204

這時候我們回到瀏覽器,刷新http://localhost:8260,將會看到如下界面:

Eureka Client UI

注意:一個服務實例注冊到Eureka服務器時需要30s才能夠在控制臺中查看該服務。這是因為,Eureka要求服務提供者必須發(fā)送3次心跳才認為該服務實例是ok的。

構(gòu)建服務消費者

上面一個簡單的Eureka服務器和客戶端就已經(jīng)構(gòu)建完畢,接下來構(gòu)建一個服務消費者,該服務消費者將調(diào)用SERVICE-HELLO所提供的服務。

1. 編寫pom.xml文件

同樣,我們繼承自parent項目的pom.xml,這里將artifactId定義為:consumer-hello,也就是Hello服務消費者。

<?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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>twostepsfromjava.cloud</groupId>
        <artifactId>twostepsfromjava-cloud-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../parent</relativePath>
    </parent>

    <artifactId>consumer-hello</artifactId>
    
    <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>    
</project>

這里需要注意的是我們除了依賴spring-cloud-starter-eureka,還依賴了Spring Cloud中的另外一個子項目spring-cloud-starter-ribbon,該子項目提供客戶端負載均衡功能,可以自動從Eureka服務器中獲取服務提供者的地址列表,從而能夠發(fā)起相應的調(diào)用。這個后面我們將詳細進行說明,這里先引入進來就可以了。

2. 編寫啟動類

@EnableDiscoveryClient
@SpringBootApplication
public class Application {
    //@Bean
    //@LoadBalanced
    //RestTemplate restTemplate() {
     //   return new RestTemplate();
    //}
    @Bean
    @LoadBalanced
    RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build ();
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • Service-Hello一樣在啟動類上注解了@EnableDiscoveryClient,說明這也是一個Eureka Client。
  • SpringBoot版本大于1.4 Spring Boot不在自動定義RestTemplate,而是定義了一個RestTemplateBuilder。自動掃描注入RestTempate Bean。
  • @LoadBalanced是必須要加的

3. 編寫服務調(diào)用

@RestController
public class HelloController {
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
    }
}

該服務調(diào)用時一個標準的controller,hello()方法將通過restTemplate調(diào)用SERVICE-HELLO/hello服務并返回。

4. 編寫配置文件

server.port=8080
spring.application.name=consumer-hello
eureka.client.service-url.defaultZone=http://localhost:8260/eureka

5. 啟動服務器

啟動成功后,同樣我們將在控制臺上看到這么一句日志:

[DiscoveryClient-InstanceInfoReplicator-0] INFO  c.netflix.discovery.DiscoveryClient - DiscoveryClient_CONSUMER-HELLO/192.168.0.105:consumer-hello:8080 - registration status: 204

然后我們回到瀏覽器,刷新http://localhost:8260,將會看到如下界面:

image.png

說明我們的兩個服務都已經(jīng)在Eureka服務器上注冊成功。

驗證服務調(diào)用

在瀏覽器中,我們輸入http://localhost:8080/hello,也就是該服務所定義的端口server.port=8080,將會看到如下界面:

image.png

同時在Service-Hello的控制臺中會打印下面一句日志:

[http-nio-2100-exec-1] INFO  i.t.c.s.hello.api.HelloEndpoint - /hello, instanceId:cd826dembp.lan:SERVICE-HELLO:2100, host:192.168.0.105

獲取Eureka中的注冊信息

通過URL的方式獲取服務的注冊信息

這種方式比較簡單,比如說我們現(xiàn)在有一個本地的Eureka服務器,其端口號為8761,當我們想獲取其中所注冊的服務信息時,我們只需要訪問下面的地址:

http://localhost:8761/eureka/apps

其展示的結(jié)果為:


image.png

當我們想訪問其中的product-service服務時,此時我們應該訪問下面的地址:

http://localhost:8761/eureka/apps/product-service

其展示的結(jié)果為:


image.png
使用DiscoveryClient獲取服務的注冊信息

盡管說通過Eureka提供的管理界面和HTTP端點可以獲取服務的詳細信息,但是這還遠遠不夠。在現(xiàn)實應用中,很多時候我們希望通過代碼在運行時能夠?qū)崟r獲取注冊中心的服務列表。
在Eureka的客戶端,我們通過Eureka提供的DiscoveryClient工具類就可以獲取Eureka中注冊的服務信息。
EurekaController

@RestController
@RequestMapping("/eurekaCentre")
public class EurekaController {

    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 獲取注冊在Eureka中的服務名稱
     * @return
     */
    @GetMapping("/getEurekaServices")
    public List<String> getEurekaServices(){
        List<String> services = new ArrayList<>();
        List<String> serviceNames = discoveryClient.getServices();
        for(String serviceName : serviceNames){
            List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
            for(ServiceInstance serviceInstance : serviceInstances){
                services.add(String.format("%s:%s",serviceName,serviceInstance.getUri()));
            }
        }
        return services;
    }

}

application.yml

# 服務名稱
spring:
  application:
    name: product-service
# 服務端口號
server:
  port: 8081
eureka:
  instance:
    # 指明使用IP而不是服務名稱來注冊自身服務。因為Eureka默認是使用域名進行服務注冊和解析
    prefer-ip-address: true
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

運行
??在瀏覽器中輸入下列地址:

http://127.0.0.1:8081/eurekaCentre/getEurekaServices

其返回的服務列表如下:

[
"eureka-server:http://peer1:8761",
"eureka-server:http://peer2:8762",
"product-service:http://192.168.23.1:8081",
"product-service:http://192.168.23.1:8082",
"account-service:http://192.168.23.1:8084",
"order-service:http://192.168.23.1:8083"
]



搭建服務消費者——獲取服務01

image.png

image.png

必須將eureka.client.fetch-registry的值設置為true,表示應用啟動成功之后需要從服務治理服務器中獲取已注冊的服務列表到本地。


image.png

SpringBoot版本大于1.4 Spring Boot不在自動定義RestTemplate,而是定義了一個RestTemplateBuilder。所以創(chuàng)建RestTempateConfig自動掃描注入RestTempate Bean。
image.png

@LoadBalanced是必須要加的,不然會提示下面的錯誤


image.png

Controller代碼示例
image.png

搭建服務消費者——獲取服務02

<?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.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-cloud-consumer</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.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.7</java.version>
        <spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</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>

然后更新Maven依賴,然后在入口文件添加@EnableDiscoveryClient注解和@EnableFeignClients注解,聲明這個一個服務消費者。

package com.example.consumer;

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

@SpringBootApplication
//啟用服務注冊與發(fā)現(xiàn)
@EnableDiscoveryClient
//啟用feign進行遠程調(diào)用
@EnableFeignClients
public class SpringCloudConsumerApplication {

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

配置application.properties文件

#指定微服務的名稱后續(xù)在調(diào)用的時候只需要使用該名稱就可以進行服務的訪問
spring.application.name=spring-cloud-consumer
#配置端口號
server.port=9002
#服務注冊中心的配置內(nèi)容,指定服務注冊中心的Url
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/

編寫feign調(diào)用實現(xiàn)接口

package com.example.consumer.remote;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name= "spring-cloud-producer")
public interface HelloRemote {
    
    //restful api 調(diào)用
    @GetMapping("/hello/{name}")
    public String hello(@PathVariable("name") String name);

    //傳統(tǒng)api調(diào)用
    //@GetMapping(value = "/hello")
    //public String hello(@RequestParam(value = "name") String name);
}

web層調(diào)用遠程服務

package com.example.consumer.controller;

import com.example.consumer.remote.HelloRemote;
import com.example.consumer.remote.HiRemote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConsumerController {

    @Autowired
    HelloRemote helloRemote;

    @GetMapping("/hello/{name}")
    public String index(@PathVariable("name") String name) {
        return helloRemote.hello(name);;
    }
}

高可用環(huán)境搭建

Eureka Server 高可用環(huán)境需要部署兩個Eureka server,它們互相向?qū)Ψ阶浴H绻诒緳C啟動兩個Eureka需要注意兩個Eureka Server的端口要設置不一樣,這里我們部署一個Eureka Server工程,將端口可配置,制作兩個Eureka Server啟動腳本,啟動不同的端口,如下圖:

image.png
  • 在實際使用時Eureka Server至少部署兩臺服務器,實現(xiàn)高可用。
  • 兩臺Eureka Server互相注冊。
  • 微服務需要連接兩臺Eureka Server注冊,當其中一臺Eureka死掉也不會影響服務的注冊與發(fā)現(xiàn)。
  • 微服務會定時向Eureka server發(fā)送心跳,報告自己的狀態(tài)。
  • 微服務從注冊中心獲取服務地址以RESTful方式發(fā)起遠程調(diào)用

配置如下:
1、端口可配置

server:
  port: ${PORT:50101} #服務端口

2、Eureka服務端的交互地址可配置

eureka:
  client:
    registerWithEureka: true #服務注冊,是否將自己注冊到Eureka服務中
    fetchRegistry: true #服務發(fā)現(xiàn),是否從Eureka中獲取注冊信息
    serviceUrl: #Eureka客戶端與Eureka服務端的交互地址,高可用狀態(tài)配置對方的地址,單機狀態(tài)配置自己(如果不配置則默認本機8761端口)
      defaultZone: ${EUREKA_SERVER:http://eureka02:50102/eureka/}

3、配置hostname
Eureka 組成高可用,兩個Eureka互相向?qū)Ψ阶裕@里需要通過域名或主機名訪問,這里我們設置兩個Eureka服務的主機名分別為 eureka01、eureka02。
完整的eureka配置如下:

eureka:
  client:
    registerWithEureka: true #服務注冊,是否將自己注冊到Eureka服務中
    fetchRegistry: true #服務發(fā)現(xiàn),是否從Eureka中獲取注冊信息
    serviceUrl: #Eureka客戶端與Eureka服務端的交互地址,高可用狀態(tài)配置對方的地址,單機狀態(tài)配置自己(如果不配置則默認本機8761端口)
      defaultZone: ${EUREKA_SERVER:http://eureka02:50102/eureka/}
  server:
    enable‐self‐preservation: false #是否開啟自我保護模式
    eviction‐interval‐timer‐in‐ms: 60000 #服務注冊表清理間隔(單位毫秒,默認是60*1000)
  instance:
    hostname: ${EUREKA_DOMAIN:eureka01}

4、在IDEA中制作啟動腳本
啟動1:


image.png

啟動2:


image.png

在host文件配置
127.0.0.1 eureka01
127.0.0.1 eureka02

運行兩個啟動腳本,分別瀏覽:
http://localhost:50101/
http://localhost:50102/
Eureka主畫面如下

image.png

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容