spring cloud之Eureka註冊中心 eureka

1.Eureka註冊中心

1.1.認識Eureka

首先我們來解決第一問題,服務的管理。

問題分析

在案例中,items-service對外提供服務,需要對外暴露自己的地址。而search-consumer(調用者)需要記錄服務提供者的地址。將來地址出現變更,還需要及時更新。這在服務較少的時候並不覺得有什麼,但是在現在日益複雜的互聯網環境,一個項目肯定會拆分出十幾,甚至數十個微服務。此時如果還人爲管理地址,不僅開發困難,將來測試、發佈上線都會非常麻煩,這與DevOps的思想是背道而馳的。

Eureka做什麼?

Eureka就是負責管理、記錄服務提供者的信息。服務調用者無需自己尋找服務,而是把自己的需求告訴Eureka,然後Eureka會把符合你需求的服務告訴你。

同時,服務提供方與Eureka之間通過“心跳”機制進行監控,當某個服務提供方出現問題,Eureka自然會把它從服務列表中剔除。

這就實現了服務的自動註冊、發現、狀態監控。

1.2.原理圖

基本架構:

 

  • Eureka:就是服務註冊中心(可以是一個集羣),對外暴露自己的地址
  • 提供者:啓動後向Eureka註冊自己信息(地址,提供什麼服務)
  • 消費者:向Eureka訂閱服務,Eureka會將對應服務的所有提供者地址列表發送給消費者,並且定期更新
  • 心跳(續約):提供者定期通過http方式向Eureka刷新自己的狀態

1.3.案例監控信息頁

 

1.4.Eureka詳解

接下來我們詳細講解Eureka的原理及配置。

1.4.1.基礎架構

Eureka架構中的三個核心角色:

  • 服務註冊中心

    Eureka的服務端應用,提供服務註冊和發現功能,就是剛剛我們建立的eureka-demo

  • 服務提供者

    提供服務的應用,可以是SpringBoot應用,也可以是其它任意技術實現,只要對外提供的是Rest風格服務即可。

  • 服務消費者

    消費應用從註冊中心獲取服務列表,從而得知每個服務方的信息,知道去哪裏調用服務方。

1.4.2.高可用的Eureka Server

Eureka Server即服務的註冊中心,可以只有一個EurekaServer,也可以是一個集羣,形成高可用的Eureka中心。

服務同步

多個Eureka Server之間也會互相註冊爲服務,當服務提供者註冊到Eureka Server集羣中的某個節點時,該節點會把服務的信息同步給集羣中的每個節點,從而實現數據同步。因此,無論客戶端訪問到Eureka Server集羣中的任意一個節點,都可以獲取到完整的服務列表信息。

動手搭建高可用的EurekaServer

我們假設要搭建3個EurekaServer的集羣,端口分別爲:10086和10087.10088

1)EurekaServer配置:

pom文件:



	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.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.8</java.version>
        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
	</properties>

	<dependencies>
        <!-- Eureka服務端 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
            <!-- SpringCloud依賴,一定要放到dependencyManagement中,起到管理版本的作用即可 -->
			<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>

application 啓動類:

編寫啓動類:

      @SpringBootApplication
@EnableEurekaServer // 聲明這個應用是一個EurekaServer
public class EurekaDemoApplication {

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

 

application,yml文件: 

      spring:
  profiles:
    active: peer1
---
server:
  port: 10086 # 端口
spring:
  application:
    name: register # 應用名稱,會在Eureka中顯示
  profiles: peer1
clusterOtherPort:
  - 10086
  - 10087
  - 10088
eureka:
  instance:
    hostname: register.slave1.com #hosts中自己配置域名
    appname: registerApp
#    appname: ${spring.application.name}
  client:
#    register-with-eureka: false #默認true
    #    fetch-registry: false  #默認true
    service-url: # EurekaServer的地址,單註冊中心時是自己的地址,如果是集羣,需要加上其它Server的地址。
      defaultZone: http://register.slave1.com:${clusterOtherPort[0]}/eureka,http://register.slave2.com:${clusterOtherPort[1]}/eureka,http://register.slave3.com:${clusterOtherPort[2]}/eureka
---
server:
  port: 10087 # 端口
spring:
  application:
    name: register # 應用名稱,會在Eureka中顯示
  profiles: peer2
clusterOtherPort:
  - 10086
  - 10087
  - 10088
eureka:
  instance:
    hostname: register.slave2.com #hosts中自己配置域名
    appname: registerApp
#    appname: ${spring.application.name}
  client:
#    register-with-eureka: false
    #    fetch-registry: false
    service-url: # EurekaServer的地址,單註冊中心時是自己的地址,如果是集羣,需要加上其它Server的地址。
      defaultZone: http://register.slave1.com:${clusterOtherPort[0]}/eureka,http://register.slave2.com:${clusterOtherPort[1]}/eureka,http://register.slave3.com:${clusterOtherPort[2]}/eureka
---
server:
  port: 10088 # 端口
spring:
  application:
    name: register # 應用名稱,會在Eureka中顯示
  profiles: peer3
clusterOtherPort:
  - 10086
  - 10087
  - 10088
eureka:
  instance:
    hostname: register.slave3.com #hosts中自己配置域名
    appname: registerApp
  #    appname: ${spring.application.name}
  client:
#    register-with-eureka: false
#    fetch-registry: false
    service-url: # EurekaServer的地址,單註冊中心時是自己的地址,如果是集羣,需要加上其它Server的地址。
      defaultZone: http://register.slave1.com:${clusterOtherPort[0]}/eureka,http://register.slave2.com:${clusterOtherPort[1]}/eureka,http://register.slave3.com:${clusterOtherPort[2]}/eureka


    

所謂的高可用註冊中心,其實就是把EurekaServer自己也作爲一個服務進行註冊,這樣多個EurekaServer之間就能互相發現對方,從而形成集羣。因此我們做了以下修改:

  • 刪除了register-with-eureka=false和fetch-registry=false兩個配置。因爲默認值是true,這樣就會吧自己註冊到註冊中心了。
  • 把service-url的值改成了另外一臺EurekaServer的地址,而自己的可寫可不寫

2)另外2臺配置:因爲我使用了profiles配置,因此只在啓動時分別在program arguments修改參數即可--server.port=10088 --spring.profiles.active=peer3和--server.port=10088 --spring.profiles.active=peer3, 

1.4.3.服務提供者

pom文件依賴:

<dependencies>
        <!-- Eureka客戶端 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
            <!-- SpringCloud依賴,一定要放到dependencyManagement中,起到管理版本的作用即可 -->
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Greenwich.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

 

在啓動類上開啓Eureka客戶端功能

通過添加@EnableDiscoveryClient來開啓Eureka客戶端功能

      @SpringBootApplication
@EnableDiscoveryClient // 開啓EurekaClient功能
public class UserServiceDemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(UserServiceDemoApplication.class, args);
	}
}

服務提供者要向EurekaServer註冊服務,並且完成服務續約等工作。

  port: 8080
spring:
  application:
    name: provider # 應用名稱
eureka:
  instance:
    prefer-ip-address: true
    ip-address: 192.168.0.103 #自己的主機ip或單機時迴環地址127.0.0.1
  client:
    service-url: # EurekaServer地址,多個地址以','隔開
      defaultZone: http://register.slave1.com:10086/eureka,http://register.slave3.com:10088/eureka

服務註冊

服務提供者在啓動時,會檢測配置屬性中的:eureka.client.register-with-erueka=true參數是否正確,事實上默認就是true。如果值確實爲true,則會向EurekaServer發起一個Rest請求,並攜帶自己的元數據信息,Eureka Server會把這些信息保存到一個雙層MetaDataMap結構中。第一層Map的Key就是服務名稱,第二層Map的key是服務的實例id。

服務續約

在註冊服務完成以後,服務提供者會維持一個心跳(定時向EurekaServer發起Rest請求),告訴EurekaServer:“我還活着”。這個我們稱爲服務的續約(renew);

有兩個重要參數可以修改服務續約的行爲:

      eureka:
  instance:
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30

    
  • lease-renewal-interval-in-seconds:服務續約(renew)的間隔,默認爲30秒
  • lease-expiration-duration-in-seconds:服務失效時間,默認值90秒

也就是說,默認情況下每個30秒服務會向註冊中心發送一次心跳,證明自己還活着。如果超過90秒沒有發送心跳,EurekaServer就會認爲該服務宕機,會從服務列表中移除,這兩個值在生產環境不要修改,默認即可。

但是在開發時,這個值有點太長了,經常我們關掉一個服務,會發現Eureka依然認爲服務在活着。所以我們在開發階段可以適當調小。

      eureka:
  instance:
    lease-expiration-duration-in-seconds: 10 # 10秒即過期
    lease-renewal-interval-in-seconds: 5 # 5秒一次心跳

    

實例id

先來看一下服務狀態信息:

在Eureka監控頁面,查看服務註冊信息:

 

在status一列中,顯示以下信息:

  • UP(1):代表現在是啓動了1個示例,沒有集羣
  • manage.ayh.com:provider:8080:是示例的名稱
  • (instance-id),
    • 由於爲了測試在hosts文件中配置了域名manage.ayh.com爲第一個非迴環地址所以顯示他
    • 默認格式是:${hostname} + ${spring.application.name} + ${server.port}
    • instance-id是區分同一服務的不同實例的唯一標準,因此不能重複。

我們可以通過instance-id屬性來修改它的構成:

      eureka:
  instance:
    instance-id: ${spring.application.name}:${server.port}
    



    

1.4.4.服務消費者

pom依賴:

<dependencies>
        <!-- Eureka客戶端 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
            <!-- SpringCloud依賴,一定要放到dependencyManagement中,起到管理版本的作用即可 -->
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Greenwich.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>


 

 

2)在啓動類開啓Eureka客戶端  (用來消費的服務必須加上@EnableDiscoveryClient // 開啓Eureka客戶端)

      @SpringBootApplication
@EnableDiscoveryClient // 開啓Eureka客戶端
public class UserConsumerDemoApplication {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    }
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerDemoApplication.class, args);
    }
}

application,yml文件:

server:
  port: 8081
spring:
  application:
    name: consumer # 應用名稱
eureka:
  instance:
    prefer-ip-address: true # 當其它服務獲取地址時提供ip而不是hostname
    ip-address: 192.168.0.103 # 指定自己的ip信息或()單機時迴環地址127.0.0.1,不指定的話會自己尋找
    instance-id: ${spring.application.name}:${server.port}
  client:
    registry-fetch-interval-seconds: 5 # 每隔5秒會重新獲取服務列表並更新數據
    service-url: # EurekaServer地址,多個地址以','隔開
      defaultZone: http://register.slave1.com:10086/eureka,http://register.slave3.com:10088/eureka
    

  

獲取服務列表

當服務消費者啓動是,會檢測eureka.client.fetch-registry=true參數的值,如果爲true,則會從Eureka Server服務的列表只讀備份,然後緩存在本地。並且每隔30秒會重新獲取並更新數據。我們可以通過下面的參數來修改:

      eureka:
  client:
    registry-fetch-interval-seconds: 5

    

生產環境中,我們不需要修改這個值。

但是爲了開發環境下,能夠快速得到服務的最新狀態,我們可以將其設置小一點。

1.4.5.失效剔除和自我保護

失效剔除

有些時候,我們的服務提供方並不一定會正常下線,可能因爲內存溢出、網絡故障等原因導致服務無法正常工作。Eureka Server需要將這樣的服務剔除出服務列表。因此它會開啓一個定時任務,每隔60秒對所有失效的服務(超過90秒未響應)進行剔除。

可以通過eureka.server.eviction-interval-timer-in-ms參數對其進行修改,單位是毫秒,生成環境不要修改。

這個會對我們開發帶來極大的不變,你對服務重啓,隔了60秒Eureka才反應過來。開發階段可以適當調整,比如10S

自我保護

我們關停一個服務,就會在Eureka面板看到一條警告:

 

這是觸發了Eureka的自我保護機制。當一個服務未按時進行心跳續約時,Eureka會統計最近15分鐘心跳失敗的服務實例的比例是否超過了85%。在生產環境下,因爲網絡延遲等原因,心跳失敗實例的比例很有可能超標,但是此時就把服務剔除列表並不妥當,因爲服務可能沒有宕機。Eureka就會把當前實例的註冊信息保護起來,不予剔除。生產環境下這很有效,保證了大多數服務依然可用。

但是這給我們的開發帶來了麻煩, 因此開發階段我們都會關閉自我保護模式:

      eureka:
  server:
    enable-self-preservation: false # 關閉自我保護模式(缺省爲打開)
    eviction-interval-timer-in-ms: 1000 # 掃描失效服務的間隔時間(缺省爲60*1000ms)

 

負載均衡Robbin (待續)

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