Spring Cloud(二):Eureka

服務發現概述

  • 服務發現機制是為了解決硬網絡編碼問題,服務消費者使用這種機制獲取服務提供者網絡信息,當微服務網絡地址發生變更(例如IP或端口),會重新注冊到服務發現組件,而服務消費者就無須人工修改網絡地址了。
  • 服務提供者、服務消費者、服務發現之間的關系如下:
    • 微服務在啟動時,將自己的網絡地址等信息注入到服務發現組件中,服務發現組件會存儲這些信息
    • 服務消費者可以從服務發現組件查詢服務提供者的網絡地址,并使用該地址調用服務提供者的接口
    • 各個微服務與服務發現組件使用一定的機制(心跳)通信,服務組件如長時間無法與某微服務實例通信,就會注銷該實例
  • 服務發現組件功能:
    • 服務注冊表:是服務發現組件的核心,用來記錄微服務信息,如名稱、IP、端口等,提供查詢和管理API,注冊和注銷
    • 服務注冊與服務發現:服務注冊是指服務在啟動時將自己信息發送到服務注冊組件上,服務發現是指查詢可用微服務列表及網絡地址的機制
    • 服務檢查:服務發現組件檢測已注冊的服務,若某一個服務長時間無法訪問則移除該實例

Eureka

  • Eureka 是開源的服務發現組件,本身是一個基于 REST 的服務,包含 Server 和 Client 兩部分
Eureka 架構
  • Region:表示服務系統中的地理位置,Spring Cloud默認使用的是 us-east-1
  • Availbility Zone:可理解為機房,Region理解為跨機房的 Eureka 集群
  • Application Service:服務提供者
  • Application Client:服務消費者
  • Make Remote Call:如調用 RESTful API
  • us-east-1c、us-east-1d、us-east-1e都是zone,都屬于 us-east-1 這個region
  • Eureka Server:提供服務發現能力,各個微服務啟動時,會向 Eureka Server 注冊自己的信息,Eureka Server 會保存這些信息
  • Eureka Client:是一個 Java 客戶端,用于簡化與Eureka Server的交互
  • 微服務啟動后,會周期性(默認30秒)的與 Eureka Server 發送心跳信息
  • 如果 Eureka Server 在一定時間內沒接收到心跳信息則注銷該實例(默認90秒)
  • 多個 Eureka Server 之間通過復制方式,來實現服務注冊表中數據的同步
  • Eureka Client 會緩存注冊表中信息,微服務無需每次請求都查詢 Eureka Server,降低了壓力

快速入門

編寫 Eureka Server (Demo源碼 eureka)

  • 引入 Eureka Server 庫
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
  • 添加 @EnableEurekaServer注解聲明這是一個 Eureka Server
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}
  • 添加配置
server:
  port: 8761                  #配置端口
eureka:
  client:
    register-with-eureka: false   #是否向服務端注冊自己,它本身就是Eureka Server,所以為false
    fetch-registry: false         #表示是否從Eureka Server獲取信息,因為這是一個單節點,不需要同步其它Eureka Server的數據,所以為 false
    service-url:
      defaultZone: http://localhost:8761/eureka/    #設置 Eureka Client 與 Eureka Server 同步的地址,注冊、查詢服務都要使用該地址,多個地址可用逗號分隔

將微服務注冊到 Eureka Server

  • 引入 Eureka Client 庫
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.1.5.RELEASE</version>
</dependency>
  • 添加 @EnableDiscoverClient 注解聲明這是一個微服務
@SpringBootApplication
@EnableEurekaClient
public class FlimUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(FlimUserApplication.class, args);
    }
}
  • 添加配置
spring:
  application:
    name: flim-user   #注冊到Eureka的名字
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/    #設置EurekaServer的服務地址
  instance:
    prefer-ip-address: true   #true代表將自己的IP注冊到EurekaServer上,false代表注冊主機名到EurekaServer
image

Eureka高可用

  • 單節點 Eureka Server 并不適合生產環境,Eureka Client 會定時連接 Eureka Server,獲取服務注冊表中的信息并緩存在本地,微服務消費遠程 API 時總是使用本地緩存中的數據。因此即使 Eureka Server 發生故障也不會影響到服務之間的調用,但如果 Eureka Server 故障時,某些微服務也出現了不可用的情況,Eureka Client 中的緩存若不被更新,則可能會影響到微服務之間的調用
  • 因此在生產環境中,通常會部署一個高可用的 Eureka Server 集群,Eureka Server 可以通過運行多個實例并相互注冊的方式實現高可用部署,Eureka Server 之間也會彼此增量的同步信息,從而確保所有節點數據一致,節點之間相互注冊也是 Eureka Server 的默認行為

構建雙結點的 Eureka Server 集群

  • 修改 Eureka Server 配置文件,添加兩個 Eureka 環境
spring:
  application:
    name: eureka
---
spring:
  profiles: eureka1
eureka:
  instance:
    hostname: eureka1
  client:
    service-url:
      default-zone: http://localhost:8762/eureka
---
spring:
  profiles: eureka2
eureka:
  instance:
    hostname: eureka2
  client:
    service-url:
      default-zone: http://localhost:8761/eureka
  • 執行以下命令啟動兩個 Eureka Server 實例,可看到兩個實例之間已同步(注意:先后運行實例后,要等待一會才會同步成功)
java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active = eureka1 --server.port=8761
java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active = eureka2 --server.port=8762
Eureka 高可用

將應用注冊到 Eureka Server 集群上

  • 只需配置多個 Eureka Server 地址,就可以將其注冊到Eureka Server集群了
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
  • 即使只配置一個 Eureka Server 集群中的某個節點,也能注冊到集群上,因為多個Eureka Server之間數據會同步

Eureka Server 安全

為 Eureka Server 添加用戶認證

  • 前面例子中 Eureka Server 允許匿名訪問,可以構建一個需要登錄才能訪問的 Eureka Server,這需要添加 Spring Security 庫
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • 添加以下配置
spring:
    security:
      user:
        name: user
        password: 123456
  • 訪問 http:localhost:8761/ 時會提示身份驗證對話框

將微服務注冊到需要認證的 Eureka Server

  • 修改配置文件,屬性上加上用戶名與密碼即可
eureka:
  client:
    serviceUrl:
      defaultZone: http://user:123456@localhost:8761/eureka/
  • 對于更復雜的需求可以創建一個類型為 DiscoveryClientOptionalArgs 的 @Bean,并向其中注入 ClientFilter

Eureka 元數據

  • Eureka 元數據有兩種,標準元數據和自定義元數據
    • 標準元數據:指主機名、IP地址、端口號和健康檢查信息等,這些信息都會被發現服務注冊表中,用于服務之間調用
    • 自定義元數據:可以用 eureka.instance.metadata-map 配置,這些元數據可以在遠程客戶端中訪問,但一般不會改變遠程客戶端行為,除非客戶端知道該元數據的含義

自定義元數據-服務提供者

  • 修改配置文件,使用 eureka.instance.metadata-map 添加自定義元數據
eureka:
  client:
    serviceUrl:
      defaultZone: http://user:123456@localhost:8761/eureka/
  instance:
    prefer-ip-address: true
    metadata-map:
      my-metadata: 自定義的元數據

自定義元數據-服務消費者

  • 修改電影微服務 MovieController 獲取自定義元數據
@RestController
public class MovieController {
    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/user-instance")
    public List<ServiceInstance> showInfo(){
        return this.discoveryClient.getInstances("flim-user");      //獲取Flim-User微服務實例的信息
    }
}
[
    {
        "host": "192.168.43.43",
        "port": 8080,
        "metadata": {
            "management.port": "8080",
            "my-metadata": "自定義的元數據"
        },
        "secure": false,
        "serviceId": "FLIM-USER",
        "uri": "http://192.168.43.43:8080",
        "instanceInfo": {
            "instanceId": "linyuandembp:flim-user",
            "app": "FLIM-USER",
            "appGroupName": null,
            "ipAddr": "192.168.43.43",
            "sid": "na",
            "homePageUrl": "http://192.168.43.43:8080/",
            "statusPageUrl": "http://192.168.43.43:8080/info",
            "healthCheckUrl": "http://192.168.43.43:8080/health",
            "secureHealthCheckUrl": null,
            "vipAddress": "flim-user",
            "secureVipAddress": "flim-user",
            "countryId": 1,
            "dataCenterInfo": {
                "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                "name": "MyOwn"
            },
            "hostName": "192.168.43.43",
            "status": "UP",
            "leaseInfo": {
                "renewalIntervalInSecs": 30,
                "durationInSecs": 90,
                "registrationTimestamp": 1513418181530,
                "lastRenewalTimestamp": 1513418181530,
                "evictionTimestamp": 0,
                "serviceUpTimestamp": 1513418181530
            },
            "isCoordinatingDiscoveryServer": false,
            "metadata": {
                "management.port": "8080",
                "my-metadata": "自定義的元數據"
            },
            "lastUpdatedTimestamp": 1513418181530,
            "lastDirtyTimestamp": 1513418181485,
            "actionType": "ADDED",
            "asgName": null,
            "overriddenStatus": "UNKNOWN"
        }
    }
]

Eureka Server 的 REST 端點

  • Eureka Server 提供了一些 REST 端點,非 JVM 的微服務可以使用這些 REST 端點操作 Eureka,從而實現服務注冊與發現,其中:
    • appID:應用程序的名稱
    • instanceID:與實例相關聯的唯一ID
REST 端點集合
  • 使用 REST 請求向 Eureka Server 注冊微服務時,需要 POST 一個請求體( JSON 或 XML 格式,請求題略長,此處不展示

Eureka 自我保護模式

  • Eureka Server 的自我保護模式,最直觀的是首頁輸出的警告
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.
  • 默認情況下,如果 EurekaServer 在一定時間內沒接收到某個微服務的心跳,則會注銷該實例,但當網絡發生故障,微服務無法與 EurekaServer 正常通信時,該行為就非常危險了,Eureka 通過“自我保護”來解決這個問題,當 Eureka Server 節點短時間內丟失過多客戶端時,就會進入自我保護模式,一旦進入該模式,Eureka Server 就會保護服務注冊表中的信息,不再刪除服務注冊表中的信息(不注銷任何微服務),當網絡恢復后,Eureka Server 退出自我保護模式

多網環境下的IP選擇

  • 對于多網卡用戶,如某臺服務器有三塊網卡,但只有 eth1 可以被訪問,可以通過配置文件來實現
  • 使用 spring.cloud.inetutils.ignored-interfaces 屬性忽略指定名稱的網卡
spring:
  cloud:
    inetutils:
      ignored-interfaces:
        - docker0     #忽略docker0網卡
        - veth.*      #忽略所有veth開頭的網卡
  • 使用 spring.cloud.inetutils.preferredNetworks 屬性指定使用的網絡地址
spring:
  cloud:
    inetutils:
      preferred-networks:
        - 192.168
        - 10.0      

Eureka 健康檢查

  • 在 Status 欄有個 UP 表示應用程序狀態正常,還有其它值如:DOWN、OUT_OF_SERVICE、UNKNOWN等,但只有 UP 狀態的才會被服務請求,Eureka Server 與 Eureka Client 之間使用心跳機制確定 Eureka Client 狀態,正常情況下為 UP
  • 該機制不能反應應用程序狀態,如微服務與 Eureka Server 心跳正常,可該服務數據源發生問題,無法正常工作
  • Spring Boot Actuator 提供了 /health 端點,該端點可展示應用程序健康信息

Eureka 常見問題

Eureka 注冊服務慢

  • 默認情況下,服務注冊到 Eureka Server 的過程較慢,在開發測試時往往希望能快速一點,從而提升工作效率
  • 服務的注冊涉及到周期性心跳,默認 30 秒一次,只有當實例、服務器端和客戶端的本地緩存中的元數據都相同時,服務才能被發現(所以可能需要 3 次心跳)
  • 可以使用參數 eureka.instance.leaseRenewalintervalInSeconds 修改時間間隔,單位為秒,從而加快客戶端連接到服務的過程
    -生產環境最好使用默認值,因為服務器內部有一些計算

已停止的微服務節點注銷慢或不注銷

  • 開發環境下,我們希望能迅速有效的注銷已停止的微服務實例,但 Eureka Server 清理無效節點周期(默認 90 秒),以及自我保護等原因,可能回遇到微服務注銷慢甚至不注銷
  • 解決方法如下:
    • Eureka Server 端:
      • 關閉自我保護,并配置清理無效節點的間隔
      eureka.server.enable-self-preservation = false  #關閉自我保護
      eureka.server.eviction-interval-timer-in-ms = 60000 #清理間隔,單位毫秒
      
    • Eureka Client 端:
      • 開啟健康檢查,并設置更新時間和到期時間
      eureka.client.healthcheck.enabled = true  #開啟健康檢查
      eureka.instance.lease-renewal-interval-in-seconds = 30000  #更新時間,單位毫秒,默認30秒
      eureka.instance.lease-expiration-duration-in-seconds = 90000  #到期時間,單位毫秒,默認90秒
      

如何自定義微服務的 InstanceID

  • InstanceID 用于唯一標識到 Eureka Server 上的微服務實例,Eureka Server 首頁可以直觀的看到各個微服務的 InstanceID
  • 在 Spring Cloud 中,服務的 InstanceID 默認值是
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
  • 也可以通過下面配置修改 InstanceID 格式
eureka:
    instance:
        instance-id: ${spring.cloud.client.ipAddress}:${server.port}

Eureka 的UNKNOWN 問題

  • 注冊信息 UNKNOWN 有兩種情況,一種是應用名稱 UNKNOWN,一種是應用狀態 UNKNOWN
  • 應用名稱 UNKNOWN 可能是因為未配置 spring.application.name 或 eureka.instance.appname 屬性
  • 應用狀態 UNKNOWN 是微服務不正常情況,該問題一般由健康檢查導致,eureka.client.healthcheck.enabled=true 必須設置在 application.yml 中,而不是 bootstrap.yml 中
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容