服務發現和註冊和Eureka

一 Spring Cloud特色java

# 約定優於配置mysql

# 開箱即用,快速啓動web

# 適用於各類環境,能夠部署在PC server或者 雲環境算法

# 輕量級的組件spring

# 組件的支持很豐富,功能齊全sql

# 選型中立數據庫

 

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

 

三 服務發現和註冊編程

爲何須要服務註冊與發現緩存

# 服務重啓或者升級後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 開源的服務發現組件, Spring Cloud 將其集成在 Spring Cloud Netflix 中,實現服務的註冊和發現。Eureka 主要包含兩個組件: Eureka Server 和 Eureka Client。 二者的做用以下:

  • Eureka Server 提供服務發現的功能, 各個微服務會將本身的信息註冊到Eureka Server。
  • Eureka Client 即服務提供者,將其信息註冊到Eureka Server上面。
  • 微服務會週期性(默認30秒)地向Eureka Server 發送心跳以維持本身的註冊狀態,若是Eureka Server 在必定時間(默認90秒)沒有接受到某個微服務實例的心跳,Eureka Server 將會註銷該實例。
  • 默認狀況下,Eureka Server 同時也是 Client, 多個Eureka Server 實例之間能夠經過複製的方法, 來實現服務註冊表數據的同步。
  • Eureka Client 會緩存服務註冊表中的信息,因此 Eureka Client 無須每次調用微服務都要先查詢Eureka Server,能有效地緩解Eureka Server的壓力,並且即便全部的Eureka Server節點都宕掉,Client 依然能夠根據緩存中信息找到服務提供者

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:password@localhost: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:123abcABC@localhost: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:123abcABC@localhost: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 classRibbonTestConfiguration {

      @Autowired

      IClientConfig config;

     

      @Bean

      public IRuleribbonRule(IClientConfig config){

           return new RandomRule();

      }

}

其次:在啓動類上加上@RibbonClient註解,指定名稱和configuration文件類

@SpringBootApplication

@EnableEurekaClient

@RibbonClient(name="microservice-provider-user",configuration=RibbonTestConfiguration.class)

public classMovieServiceRunner {

      @Bean

      @LoadBalanced

      public RestTemplaterestTemplate(){

           return new RestTemplate();

      }

 

      public static void main(String[] args) {

           SpringApplication.run(MovieServiceRunner.class, args);

      }

}

 

4.2.3 經過配置文件自定義Ribbon

即咱們能夠經過yml或者properties配置文件,進行配置,而後使用咱們配置的選項。

注意:這裏有些優先級的順序問題:

配置文件定義的優先級大於經過使用Java代碼@RibbonClient的優先級大於使用spring默認的優先級

支持如下屬性:

NFLoadBalancerClassName:應該實現 ILoadBalancer

NFLoadBalancerRuleClassName:應該實現 IRule

NFLoadBalancerPingClassName:應該實現IPing

NIWSServerListClassName:應該實現 ServerList

NIWSServerListFilterClassName:應該實現 ServerListFilter

 

application.yml中配置以下:

microservice-provider-user:

  ribbon:

    NFLoadBalancerRuleClassName:com.netflix.loadbalancer.RandomRule

 

五 Feign (聲明式的REST Client)

5.1 簡介及基礎使用

Feign是一個聲明式的web服務客戶端,它使得寫web服務客戶端更加容易。使用Feign建立一個接口並對其進行註解。它具備可插拔的註解支持,包括Feign本身的註解以及jax-rs註釋。Feign還支持可插拔的的編碼器和解碼器。Spring Cloud增長了對Spring MVC註解的支持,並使用了在Spring Web中默認使用的相同的HttpMessageConverters。Spring Cloud集成了Ribbon和靈感,在使用時提供了負載均衡的http客戶端。

 

加入Feign的依賴到Maven

<dependency>

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

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

</dependency>

而後建立接口,而且添加註解@EnableFeignClients

@SpringBootApplication

@EnableEurekaClient

@EnableFeignClients

public classMovieServiceRunner {

      public static void main(String[] args) {

           SpringApplication.run(MovieServiceRunner.class, args);

      }

}

 

寫真正的接口去調用其餘微服務,以供本應用調用

建立接口:

首先在接口加上註解@FeignClient,指定須要調用微服務名字

其次:若是啓動的時候,提示Http Method不正確咱們須要使用之前的老的註解,即:

@RequestMapping(method= RequestMethod.GET, value = "/user/{id}"),而不能使用:

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

 

而後:若是提示PathVariableannotation was empty on param 0,那麼咱們就須要:

 

還有就是:微服務提供方和消費者兩邊的HTTP 方法必須最好一致,若是一邊是GET 請求,另一邊是POST請求,就會報錯。並且若是傳遞的是一個負載對象,即便指定的是GET請求,也會做爲POST請求,通常作法就是不傳遞對象,而是傳遞單個參數

好比@ReqquestParam(「name」) String name,@ReqquestParam(「age」) int age之類的

 

public UserfindById(@PathVariable("id") Long id); 而不是:

public UserfindById(@PathVariable Long id); 就能夠

這至關因而Feign的坑吧

@FeignClient("microservice-provider-user")

public interface UserFeignClient {

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

      public User findById(@PathVariable Long id);

}

 

而後在其餘類中就能夠調用了:

@RestController

public class MovieController {

 

      @Autowired

      private UserFeignClient userFeignClient;

 

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

      public User findById(@PathVariable Long id) {

 

           return userFeignClient.findById(id);

      }

}

 

5.2 覆寫Feign的配置

Spring Cloud容許你徹底控制Feign 客戶端經過聲明一些額外的註解,即@FeignClient(name = "stores",configuration = FooConfiguration.class)

 

通常來講,咱們不須要使用在自定義的FooConfiguration上使用@Configuration組件,若是要使用咱們須要把它從包含@ComponentScan或者@SpringBootApplication註解所掃描的包中排除掉。和Ribbon相似。

 

假設咱們如今定義了一個Configuration1這個類,使用feign默認的契約,而不是使用SpringMvcContract,即:

@Configuration

public class Configuration1 {

      @Bean

      public ContractfeignContract() {

 

           return newfeign.Contract.Default();

      }

}

而後在Feign的接口處,指定@FeignClient的configuration,重寫配置,使用剛纔定義的Configuration1,即:

@FeignClient(name="microservice-provider-user",configuration=Configuration1.class)

public interface UserFeignClient {

 

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

      public User findById(@PathVariable("id") Long id);

}

可是在這兒咱們依然用的是SpringMVC的註解,GetMapping,這裏雖然沒有報錯,可是啓動的時候就會報錯了。好比java.lang.IllegalStateException: MethodfindById not annotated with HTTP method type (ex. GET, POST)

因此,接口處咱們應該替換爲Feign本身的註解。

@FeignClient(name="microservice-provider-user",configuration=Configuration1.class)

public interface UserFeignClient {

 

      @RequestLine("GET /user/{id}")

      public User findById(@Param("id") Long id);

}

 

 

Feign支持請求響應GZIP壓縮:

feign.compression.request.enabled=true

feign.compression.response.enabled=true

 

5.3 feign的日誌

Feign的默認日誌級別是DEBUG 級別。

@Bean

Logger.Level feignLoggerLevel(){

      return Logger.Level.FULL;

}

 

六 常見問題解決方案

6.1 Eureka環境以及cloud配置

Eureka能夠運行AWS環境和非AWS環境上,Eureka也能夠以測試環境和生產環境運行。

若是須要運行在AWS環境上,則須要經過-Deureka.datacenter=cloud指定運行在AWS上,在yml中咱們能夠經過eureka.datacenter:cloud指定。

eureka.datacenter:cloud

若是須要運行在測試或者生產環境,咱們須要經過-Deureka.environment來指定。若是指定測試環境eureka.environment: test, 若是運行在生產環境,則指定eureka.environment:product

 

6.2 自我保護提示

 

6.3 Eureka註冊服務慢的問題如何解決

做爲實例還涉及到與註冊中心的週期性心跳,默認持續時間爲30秒(經過serviceUrl)。在實例、服務器、客戶端都在本地緩存中具備相同的元數據以前,服務不可用於客戶端發現(因此可能須要3次心跳)。你可使用eureka.instance.leaseRenewalIntervalInSeconds配置,這將加快客戶端鏈接到其餘服務的過程。

在生產中,最好堅持使用默認值,由於在服務器內部有一些計算,他們對續約作出假設。

 

6.4如何解決Eureka Server不踢出已關停的節點的問題

服務器端:

# 關閉自我保護

eureka.server.enable-self-preservation:false

# 縮小清理間隔(單位毫秒,默認是60*1000)

eureka.server.eviction-interval-timer-in-ms:  5000

 

客戶端:

開啓健康檢查(須要spring-boot-starter-actuator依賴)

eureka.client.healthcheck.enabled= true    

租期更新時間間隔(默認30秒)

eureka.instance.lease-renewal-interval-in-seconds=10

租期到期時間(默認90秒)

eureka.instance.lease-expiration-duration-in-seconds=30

 

6.5 Eureka HA配置(假設三個節點)

6.5.1 在hosts文件中加入以下配置

127.0.0.1  peer1

127.0.0.1  peer2

127.0.0.1  peer3

 

6.5.2 在application.yml中加入如下配置

---

spring:

  profiles: peer1

  application:

    name: EUREKA-HA

server:

  port: 8761

eureka:

  instance:

    hostname: peer1

  client:

    serviceUrl:

      defaultZone:http://peer2:8762/eureka/,http://peer3:8763/eureka/

---

spring:

  profiles: peer2

  application:

    name: EUREKA-HA

server:

  port: 8762

eureka:

  instance:

    hostname: peer2

  client:

    serviceUrl:

      defaultZone:http://peer1:8761/eureka/,http://peer3:8763/eureka/

---

spring:

  profiles: peer3

  application:

    name: EUREKA-HA

server:

  port: 8763

eureka:

  instance:

    hostname: peer3

  client:

    serviceUrl:

      defaultZone:http://peer1:8761/eureka/,http://peer2:8762/eureka/

————————————————版權聲明:本文爲CSDN博主「happy19870612」的原創文章。本文收藏主要用於學,感謝happy19870612提供

相關文章
相關標籤/搜索