1. 程式人生 > >服務發現和註冊和Eureka

服務發現和註冊和Eureka

Spring Cloud和雲端計算沒有關係,只是一個基於Spring Boot的快速構建分散式系統的工具集。

一 Spring Cloud特點

# 約定優於配置

# 開箱即用,快速啟動

# 適用於各種環境,可以部署在PC server或者 雲環境

# 輕量級的元件

# 元件的支援很豐富,功能齊全

# 選型中立

二 服務提供者和服務消費者

三 服務發現和註冊

為什麼需要服務註冊與發現

# 服務重啟或者升級後IP地址變化

# 水平伸縮後服務例項的變化

# 同一個節點執行多個服務

所以需要一種註冊機制,幫助我們去獲取響應。

核心機制:

將例項的資訊註冊到註冊中心

呼叫者通過註冊中心查詢服務

呼叫者獲取服務例項列表

呼叫者通過負載均衡通訊

3.1 基本流程


首先:服務消費者和服務註冊者向服務發現元件註冊

其次:服務消費者要呼叫的時候會從服務發現元件中進行查詢

需求:

# 每一個服務例項都會在啟動的時候通過HTTP/REST或者Thrift等方式釋出遠端API

# 服務端例項的具體數量及位置會發生動態變化

# 虛擬機器與容器通常會被分配動態IP地址

3.2 服務發現元件的功能

# 服務登錄檔: 是一個記錄當前可用服務例項的網路資訊的資料庫,是服務發現機制的核心。服務登錄檔提供查詢API和管理API,使用API 獲得可用的服務例項,使用管理API實現註冊和登出。

# 服務註冊

# 健康檢查

3.3 服務發現的方式

3.3.1 客戶端發現

它的主要特點是客戶端決定服務例項的網路位置,並且對請求進行負載均衡。客戶端查詢服務登錄檔(可用服務例項資料庫),使用負載均衡演算法選擇一個例項,併發出請求。典型代表Eureka或者ZK


客戶端發現模式的優缺點

優點:

不需要很多的網路跳轉

缺點:

客戶端和服務登錄檔耦合

需要為應用程式每一種程式語言、框架等建立客戶端發現邏輯,比如 Netflix Prana就為非JVM客戶端提供一套基於HTTP代理服務發現方案

# 伺服器端發現

向某一服務傳送請求,客戶端會通過向執行位置已知的路由器或者負載均衡器傳送請求。他們會查詢服務登錄檔,並向可用的服務例項轉發該請求。典型代表Consul + Nginx


伺服器端發現模式優缺點:

優點:

客戶端無需實現發現功能,只需要向路由器或者負載均衡器傳送請求即可

缺點:

除非成為雲環境的一部分,否則該路由機制必須作為另一系統元件進行安裝與配置。為實現可用性和一定的接入能力,還需要為其配置一定數量的副本。

相較於客戶端發現,伺服器端發現機制需要更多的網路跳轉。

3.4 服務發現元件Eureka

3.4.1 什麼是Eureka

Eureka是Netflix開發的服務發現框架,本身是一個基於REST的服務,主要用於定位執行在AWS域中的中間層服務,以達到負載均衡和中間層服務故障轉移的目的。Spring Cloud將它整合在其他子專案spring-cloud-netflix中,以實現spring cloud服務發現功能。

3.4.2 Eureka原理

先需要明白AWS幾個概念:

Region: AWS雲服務在全球不同的地方都有資料中心,比如北美、南美和歐洲亞洲等。與此對應,根據地理位置我們把某個地區的基礎設施服務集合稱為一個區域。不同區域之間是相互獨立的。說白了就類似於不同地方的機房。

Available Zone: 基於容災背景提出,簡單而言,就是相同region區域不同的機房

# 首先是服務註冊到Eureka

# 每30s傳送心跳檢測重新進行租約,如果客戶端不能多次更新租約,它將在90s內從伺服器註冊中心移除。

# 註冊資訊和更新會被複制到其他Eureka 節點,來自任何區域的客戶端科可以查詢到註冊中心資訊,每30s發生一次複製來定位他們的服務,並進行遠端呼叫

# 客戶端還可以快取一些服務例項資訊,所以即使Eureka全掛掉,客戶端也是可以定位到服務地址的


3.4.3 搭建Euraka Server

首先:搭建parent專案

<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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">

      <modelVersion>4.0.0</modelVersion>

      <modules>

           <module>microservice-consumer</module>

           <module>microservice-provider</module>

           <module>microservice-discovery-eureka</module>

      </modules>

      <groupId>com.microservice</groupId>

      <artifactId>microservice</artifactId>

      <version>1.0-SNAPSHOT</version>

      <packaging>pom</packaging>

      <parent>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-parent</artifactId>

           <version>1.5.6.RELEASE</version>

      </parent>

      <dependencyManagement>

           <dependencies>

                 <dependency>

                      <groupId>org.springframework.cloud</groupId>

                      <artifactId>spring-cloud-dependencies</artifactId>

                      <version>Dalston.SR3</version>

                      <type>pom</type>

                      <scope>import</scope>

                 </dependency>

           </dependencies>

      </dependencyManagement>

      <dependencies>

           <dependency>

                 <groupId>org.springframework.cloud</groupId>

                 <artifactId>spring-cloud-starter-config</artifactId>

           </dependency>

           <dependency>

                 <groupId>org.springframework.cloud</groupId>

                 <artifactId>spring-cloud-starter-eureka</artifactId>

           </dependency>

           <dependency>

                 <groupId>mysql</groupId>

                 <artifactId>mysql-connector-java</artifactId>

                 <version>5.1.44</version>

           </dependency>

      </dependencies>

      <build>

           <finalName>${project.artifactId}</finalName>

           <plugins>

                 <!--資原始檔拷貝外掛 -->

                 <plugin>

                      <groupId>org.apache.maven.plugins</groupId>

                      <artifactId>maven-resources-plugin</artifactId>

                      <configuration>

                            <encoding>UTF-8</encoding>

                      </configuration>

                 </plugin>

                 <!--java編譯外掛 -->

                 <plugin>

                      <groupId>org.apache.maven.plugins</groupId>

                      <artifactId>maven-compiler-plugin</artifactId>

                      <configuration>

                            <source>1.8</source>

                            <target>1.8</target>

                            <encoding>UTF-8</encoding>

                      </configuration>

                 </plugin>

                 <plugin>

                      <groupId>org.springframework.boot</groupId>

                      <artifactId>spring-boot-maven-plugin</artifactId>

                 </plugin>

           </plugins>

           <pluginManagement>

                 <plugins>

                      <!--配置tomcat外掛 -->

                      <plugin>

                            <groupId>org.apache.tomcat.maven</groupId>

                            <artifactId>tomcat7-maven-plugin</artifactId>

                            <version>2.2</version>

                      </plugin>

                 </plugins>

           </pluginManagement>

      </build>

</project>

其次:搭建Euraka Server

<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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">

      <modelVersion>4.0.0</modelVersion>

      <packaging>war</packaging>

      <parent>

           <artifactId>microservice</artifactId>

           <groupId>com.microservice</groupId>

           <version>1.0-SNAPSHOT</version>

      </parent>

      <artifactId>microservice-discovery-eureka</artifactId>

      <dependencies>

           <dependency>

                 <groupId>org.springframework.cloud</groupId>

                 <artifactId>spring-cloud-starter-eureka-server</artifactId>

           </dependency>

           <dependency>

                 <groupId>org.springframework.boot</groupId>

                 <artifactId>spring-boot-starter-security</artifactId>

           </dependency>

      </dependencies>

</project>

然後:在application.xml中配置application.yml檔案

security:

  basic:

    enabled: true

  user:

    name: user

    password: password

server:

  port: 8761

eureka:

  client:

    register-with-eureka: false

    fetch-registry: false

    service-url:

      defaultZone:http://user:[email protected]:8761/eureka

最後:建立EurekaApplication,並新增@SpringBootApplication

@EnableEurekaServer

public class EurakaApplication{

      public static void main(String[] args) throws Exception {

           SpringApplication.run(EurakaApplication.class, args);

      }

}

就可以啟動Eureka Server了

3.4 將微服務註冊到Eureka上

首先:確認當前maven環境下是否引入了Eureka相關的配置,並且新增如下依賴

           <dependency>

                 <groupId>org.springframework.boot</groupId>

                 <artifactId>spring-boot-starter-actuator</artifactId>

           </dependency>

其次:在微服務啟動類上新增@EnableEurekaClient註解,使得他成為一個Eureka Client,在啟動的 時候就向Eureka Server註冊。

然後:在微服務的應用程式中application.yml配置檔案中,我們不要新增如下配置,因為這表示是伺服器,客戶端我們是需要向Eureka註冊的

eureka:

  client:

    register-with-eureka: false

fetch-registry: false

應該可以的配置有健康檢查和路徑配置

eureka:

  client:

    healthcheck:

      enabled: true

    service-url:

      defaultZone:http://nicky:[email protected]:8761/eureka

最後:在啟動類新增@EnableEurekaClient註解,表示這個類可以作為Eureka 客戶端,啟動之後可以向Eureka註冊

@SpringBootApplication

@EnableEurekaClient

public class UserServiceRunner{

      public static void main(String[] args) throws Exception {

           SpringApplication.run(UserServiceRunner.class, args);

      }

}

3.5 Eureka配置項

eureka.client.allow-redirects:是否允許重定向Eureka客戶端請求到其他或者備份伺服器,預設為fasle

eureka.client.eureka-connection-idle-timeout-seconds:HTTP連線到eureka伺服器可以在關閉之前保持空閒的時間(幾秒鐘)。

eureka.client.eureka-server-connect-timeout-seconds:表示連線Eureka伺服器,等待多長時間算超時

eureka.client.eureka-server-port: Eureka Server埠

eureka.client.eureka-server-d-n-s-name:獲取要查詢的DNS名稱以獲得eureka伺服器的列表。

eureka.client.eureka-server-read-timeout-seconds:示在從eureka伺服器讀取資料之前需要等待多長時間(以秒為單位)

eureka.client.eureka-server-total-connections:從eureka客戶端到所有eureka伺服器的所允許連線總數。

eureka.client.eureka-server-total-connections-per-host:設定每一個主機所允許的到Eureka Server連線的數量

eureka.client.fetch-registry: 是否允許客戶端向Eureka 登錄檔獲取資訊,一般伺服器為設定為false,客戶端設定為true

eureka.client.register-with-eureka:是否允許向Eureka Server註冊資訊,預設true,如果是伺服器端,應該設定為false

eureka.client.fetch-remote-regions-registry:逗號分隔的區域列表,用於獲取eureka註冊資訊

eureka.client.g-zip-content:從伺服器端獲取資料是否需要壓縮

eureka.client.prefer-same-zone-eureka: 是否優先使選擇相同Zone的例項,預設為true

eureka.client.registry-fetch-interval-seconds:多長時間從Eureka Server登錄檔獲取一次資料,預設30s

eureka.client.service-url:可用區域對映,列出完全合格的url與eureka伺服器通訊。每個值可以是一個URL,也可以是一個逗號分隔的替代位置列表。

eureka.dashboard.enabled: 是否啟用Eureka首頁,預設為true

eureka.dashboard.path: 預設為/

eureka.instance.appname:在eureka註冊的應用程式的名稱。

eureka.instance.app-group-name:在eureka註冊的應用程式的組名稱

eureka.instance.health-check-url: 健康檢查絕對路徑

eureka.instance.health-check-url-path:健康檢查相對路徑

eureka.instance.hostname:設定主機名

eureka.instance.instance-id:設定註冊例項的id

eureka.instance.lease-expiration-duration-in-seconds:設定多長時間意味著租約到期,預設90

eureka.instance.lease-renewal-interval-in-seconds:表示Eureka客戶端需要傳送心跳到eureka伺服器的頻率(以秒為單位),以表明它仍然存在。指定的期間內如果沒有收到心跳leaseExpirationDurationInSeconds

eureka.instance.metadata-map:可以設定元資料

eureka.instance.prefer-ip-address: 例項名以IP,但是建議hostname,預設為false

四 負載均衡

4.1 Ribbon的介紹和架構

實現負載均衡,我們可以通過伺服器端和客戶端做負載均衡。伺服器端做負載均衡,比如可以使用Nginx。而客戶端做負載均衡,就是客戶端有一個元件,知道有哪些可用的微服務,實現一個負載均衡的演算法。

Ribbon工作流程主要分為兩步:

第一:先選擇Eureka Server,優先選擇在同一個Zone且負載較少的Server;

第二:再根據使用者指定的策略,再從server取到的服務註冊列表中選擇一個地址。其中Ribbon提供了很多種策略,例如輪詢round bin,隨機Random,根據響應時間加權。


4.2 使用Ribbon進行負載均衡

4.2.1 基本用法

要使用Ribbon進行負載均衡,那麼就需要引入對應的依賴,如果已經因如果Eureka的依賴,那麼就不需要再次引入Ribbon的依賴了。

           <dependency>

                 <groupId>org.springframework.cloud</groupId>

                 <artifactId>spring-cloud-starter-ribbon</artifactId>

           </dependency>

然後在啟動類上加上註解@LoadBalanced,則負載均衡生效。

@SpringBootApplication

@EnableEurekaClient

public classMovieServiceRibbonRunner {

      @Bean

      @LoadBalanced

      public RestTemplaterestTemplate(){

           return new RestTemplate();

      }

      public static void main(String[] args) {

           SpringApplication.run(MovieServiceRibbonRunner.class, args);

      }

}

我們可以修改application.yml檔案的instance_id屬性:

eureka:

  client:

    healthcheck:

      enabled: true

    service-url:

      defaultZone:http://nicky:[email protected]:8761/eureka

  instance:

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

在消費者Controller中,訪問虛擬的ip,即我們微服務的名稱

@RestController

public class MovieController {

      @Autowired

      private RestTemplate restTemplate;

      @GetMapping("/movie/{id}")

      public User findById(@PathVariable Long id) {

           returnthis.restTemplate.getForObject("http://microservice-provider-user/user/"+id, User.class);

      }

}

最後啟動兩個服務提供者例項,可以修改埠實現。

4.2.2 通過程式碼自定義配置Ribbon

首先,該類需要加上@Configuration註解,但是注意,如果加上這個註解,他就不能包含在註解@ComponentScan或者@SpringBootApplication所指定包掃描路徑。

@Configuration

public