服務發現和註冊和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