天天看點

服務注冊中心Eureka - Spring Cloud系列(一)

本文章基于spring-boot-starter-parent 2.0.6RELEASE,spring-cloud-dependencies Finchley.SR2。

Eureka是什麼

Eureka 是 Netflix的子子產品之一, 用于實作服務注冊與發現,是微服務架構中最為核心和基礎的子產品。Eureka有兩個元件:一個是EurekaServer,用于定位服務以實作中間層伺服器的負載均衡和故障轉移;另個一是EurekaClient(內建在我們的微服務中),用于與Server進行互動,并可以通過服務辨別符去擷取服務。 Spring Cloud 基于 Netflix Eureka做了二次封裝,主要負責完成微服務架構中的服務治理功能。

Eureka基礎架構

三個核心要素:

  • 服務注冊中心: Eureka提供的服務端,提供服務注冊于發現功能
  • 服務提供者: 提供服務的應用,可以是spring boot應用,也可以是其他技術平台且遵循Eureka通信機制的應用。
  • 服務消費者: 消費者應用從服務注冊中心擷取服務清單,進而使消費者可以知道去何處調用其所需要的服務。

下圖是來自Eureka官方的基于叢集配置的架構圖:

服務注冊中心Eureka - Spring Cloud系列(一)
  • 處于不同節點的Eureka Server通過Replicate進行資料同步;
  • Application Service為服務提供者,向Eureka Server注冊、服務同步、服務續約等;
  • Application Client為服務消費者,可以向Eureka Server擷取服務執行個體清單、服務調用、服務續約、服務下線等;
  • Make Remote Call完成一次服務調用。

服務治理機制:

服務啟動後向Eureka注冊,Eureka Server會将注冊資訊向其他Eureka Server進行同步,當服務消費者要調用服務提供者,則向服務注冊中心擷取服務提供者位址,然後會将服務提供者位址緩存在本地,下次再調用時,則直接從本地緩存中取,完成一次調用。

當服務注冊中心Eureka Server檢測到服務提供者因為當機、網絡原因不可用時,則在服務注冊中心将服務置為DOWN狀态,并把目前服務提供者狀态向訂閱者釋出,訂閱過的服務消費者更新本地緩存。

服務提供者在啟動後,周期性(預設30秒)向Eureka Server發送心跳,以證明目前服務是可用狀态。Eureka Server在一定的時間(預設90秒)未收到用戶端的心跳,則認為服務當機,登出該執行個體。

Eureka使用

下面用一個使用者擷取訂單的例子,來說明Eureka的使用:

Eureka服務注冊中心

Dependency:

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

application.ym:

server:
  port: 8081 #預設值8080

eureka:
  server:
    enable-self-preservation: false #關閉自我保護機制
    eviction-interval-timer-in-ms: 4000 #設定清理間隔(機關:毫秒 預設是60*1000)
  instance:
    hostname: localhost
  client:
    register-with-eureka: false #不把自己作為一個用戶端注冊到自己身上
    fetch-registry: false #不從服務端擷取注冊資訊(因為在這裡自己就是服務端,而且已經禁用自己注 冊了)
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
           

在spring boot啟動項目上加上注解:@EnableEurekaServer

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

浏覽器通路http://localhsot:8081,看到下圖則說明服務注冊中心啟動成功

服務注冊中心Eureka - Spring Cloud系列(一)

服務提供者(Order)

Dependency:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
           

application.yml:

server:
  port: 6000
  
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8081/eureka     #Eureka服務端提供的注冊位址
  instance:
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #此執行個體注冊到eureka服務端的唯一的執行個體ID
    prefer-ip-address: true #是否顯示IP位址
spring:
  application:
    name: order-microservice #此執行個體注冊到eureka服務端的name
           

OrderController:

@RestController
public class OrderController {
    @RequestMapping("/getOrder.do")
    public Map<String,Object> getOrder() {
        Map<String, Object> map = new HashMap();
        map.put("key","order");
        return map;
    }
}
           

啟動類加上@EnableEurekaClient注解:

@SpringBootApplication
@EnableEurekaClient
public class AppOrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(AppOrderApplication.class);
    }
}
           

服務消費者(User)

Dependency:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
           

application.yml:

server:
  port: 5000

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8081/eureka     #Eureka服務端提供的注冊位址
  instance:
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #此執行個體注冊到eureka服務端的唯一的執行個體ID
    prefer-ip-address: true #是否顯示IP位址
spring:
  application:
    name: user-microservice #此執行個體注冊到eureka服務端的name

           

AppConfig:

@Configuration
public class AppConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
           

UserController:

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

    @RequestMapping("/getOrder.do")
    public Object getOrder() {
        return restTemplate.getForObject("http://localhost:6000/getOrder.do",Object.class);
    }
}
           

啟動類上加上@EnableEurekaClient注解:

@SpringBootApplication
@EnableEurekaClient
public class AppUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(AppUserApplication.class);
    }
}
           

用戶端與服務端啟動後,通路Eurek注冊中心即可看到服務執行個體已注冊:

服務注冊中心Eureka - Spring Cloud系列(一)

浏覽器通路http://localhost:5000/getOrder.do ,結果頁面如下,說明服務調用成功。

服務注冊中心Eureka - Spring Cloud系列(一)

Eureka叢集

Eureka Server的設計一開始就考慮了高可用問題,在Eureka的服務治理設計中,所有節點即是服務提供方,也是服務消費方,服務注冊中心也是。在上面的單節點服務注冊中心的配置檔案中,我們設定了下面兩個配置:

register-with-eureka: false #不把自己作為一個用戶端注冊到自己身上
 fetch-registry: false #不從服務端擷取注冊資訊(因為在這裡自己就是服務端,而且已
           

Eureka Server的高可用就是将自己作為服務向其他服務注冊中心注冊自己。

下面進行執行個體示範,在單台機器上模拟叢集環境:

1.修改/etc/host檔案,mac上路徑為/etc/hosts,Windows系統路徑

C:\Window\System32\drivers\etc\hosts

服務注冊中心Eureka - Spring Cloud系列(一)

2.簡單起見,就不複制兩個工程了,直接通過配置三個profile來啟動三個Eureka Server

spring:
  application:
    name: eureka-server

---
spring:
  profiles: peer1
server:
  port: 8081

eureka:
  server:
    enable-self-preservation: false #關閉自我保護機制
    eviction-interval-timer-in-ms: 4000 #設定清理間隔(機關:毫秒 預設是60*1000)
  instance:
    hostname: peer1

  client:
    serviceUrl:
      defaultZone: http://peer2:8082/eureka,http://peer3:8083/eureka

---
spring:
  profiles: peer2
server:
  port: 8082

eureka:
  server:
    enable-self-preservation: false #關閉自我保護機制
    eviction-interval-timer-in-ms: 4000 #設定清理間隔(機關:毫秒 預設是60*1000)
  instance:
    hostname: peer2

  client:
    serviceUrl:
      defaultZone: http://peer1:8081/eureka,http://peer3:8083/eureka

---
spring:
  profiles: peer3
server:
  port: 8083

eureka:
  server:
    enable-self-preservation: false #關閉自我保護機制
    eviction-interval-timer-in-ms: 4000 #設定清理間隔(機關:毫秒 預設是60*1000)
  instance:
    hostname: peer3

  client:
    serviceUrl:
      defaultZone: http://peer1:8081/eureka,http://peer2:8082/eureka

           

.yml

檔案中

---

辨別檔案分割.

3. 啟動項目

打成jar包啟動方式:

java -jar eurka-server-0.0.1.jar --spring.profiles.active=peer1
java -jar eurka-server-0.0.1.jar --spring.profiles.active=peer2
java -jar eurka-server-0.0.1.jar --spring.profiles.active=peer3
           

這裡使用IDEA啟動:

服務注冊中心Eureka - Spring Cloud系列(一)

三個配置依次填入peer1、peer2、peer3

4.通路Eureka服務注冊中心

http://peer1:8081 http://peer2:8082 http://peer3:8083 即可看到叢集搭建成功。

服務注冊中心Eureka - Spring Cloud系列(一)
服務注冊中心Eureka - Spring Cloud系列(一)

Eureka的自保護機制

服務注冊到Eureka Server之後,會維護一個心跳連接配接,告訴Eureka Server自己還活着。Eureka Server在運作期間,會統計心跳失敗的比例在15分鐘之内是否低于85%,如果出現低于的情況,Eureka Server會認為用戶端與注冊中心出現了網絡故障,會将目前的執行個體注冊資訊保護起來,讓這些執行個體不會過期,盡可能保護這些注冊資訊,此時會出現以下情況:

  • Eureka 不再從注冊清單中移除因為長時間沒有收到心跳而過期的服務。
  • Eureka 仍然能夠接收新服務的注冊和查詢請求,但是不會被同步到其它節點上(即保證目前節點可用)
  • 當網絡穩定後,目前執行個體新的注冊資訊會被同步到其它節點中

另外,在保護期間如果執行個體出現問題,Eureka Server則會出現可用性問題,部分請求會失敗。是以用戶端也要配套容錯機制,比如可以使用請求重試、斷路器等機制。

本地開發的時候,可以使用

eureka.server.enable-self-preservation=false

參數來關閉保護機制,以確定注冊中心可以将不可用的執行個體正确剔除。

Eureka與Zookeeper的差別

不知道CAP定理的同學請先去了解CAP定理知識。

Zookeeper在設計的時候遵循的是CP原則,即保證一緻性和分區容錯性,Zookeeper會出現這樣一種情況,當master節點因為網絡故障與其他節點失去聯系時剩餘節點會重新進行leader選舉,問題在于,選舉leader的時間太長:30~120s,且選舉 間整個Zookeeper叢集是不可用的,這就導緻在選舉期間注冊服務處于癱瘓狀态,在雲部署的環境下,因網絡環境使Zookeeper叢集失去master節點是較大機率發生的事情,雖然服務能夠最終恢複,但是漫長的選舉時間導緻長期的服務注冊不可用是不能容忍的。

Eureka在設計的時候遵循的是AP原則,即可用性和分區容錯性。Eureka各個節點(服務)是平等的, 沒有主從之分,幾個節點 down掉不會影響正常工作,剩餘的節點(服務) 依然可以提供注冊與查詢服務,而Eureka的用戶端在向某個 Eureka注冊或發現連接配接失敗,則會自動切換到其他節點,也就是說,隻要有一台Eureka還在,就能注冊可用(保證可用性), 隻不過查詢到的資訊不是最新的(不保證強一緻)。

------------本文結束感謝您的閱讀------------

繼續閱讀