一、Eureka簡介
Eureka是Netflix開發的服務發現框架,本身是一個基於REST的服務,主要用於定位運行在AWS域中的中間層服務,以達到負載均衡和中間層服務故障轉移的目的。SpringCloud將它集成在其子項目spring-cloud-netflix中,以實現SpringCloud的服務發現功能。
1、Eureka組件
Eureka包含兩個組件:Eureka Server和Eureka Client。
1.1 Eureka Server
Eureka Server提供服務注冊服務,各個節點啟動后,會在Eureka Server中進行注冊,這樣Eureka Server中的服務注冊表中將會存儲所有可用服務節點的信息,服務節點的信息可以在界面中直觀的看到。
Eureka Server本身也是一個服務,默認情況下會自動注冊到Eureka注冊中心。
如果搭建單機版的Eureka Server注冊中心,則需要配置取消Eureka Server的自動注冊邏輯。畢竟當前服務注冊到當前服務代表的注冊中心中是一個說不通的邏輯。
Eureka Server通過Register、Get、Renew等接口提供服務的注冊、發現和心跳檢測等服務。
2.1 Eureka Client
Eureka Client是一個java客戶端,用於簡化與Eureka Server的交互,客戶端同時也具備一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。在應用啟動后,將會向Eureka Server發送心跳,默認周期為30秒,如果Eureka Server在多個心跳周期內沒有接收到某個節點的心跳,Eureka Server將會從服務注冊表中把這個服務節點移除(默認90秒)。
Eureka Client分為兩個角色,分別是:Application Service(Service Provider)和Application Client(Service Consumer)
2.1.1 Application Service
服務提供方,是注冊到Eureka Server中的服務。
2.1.2 Application Client
服務消費方,通過Eureka Server發現服務,並消費。
在這里,Application Service和Application Client不是絕對上的定義,因為Provider在提供服務的同時,也可以消費其他Provider提供的服務;Consumer在消費服務的同時,也可以提供對外服務。
2、Eureka Server架構原理簡介
Register(服務注冊):把自己的IP和端口注冊給Eureka。
Renew(服務續約):發送心跳包,每30秒發送一次。告訴Eureka自己還活着。
Cancel(服務下線):當provider關閉時會向Eureka發送消息,把自己從服務列表中刪除。防止consumer調用到不存在的服務。
Get Registry(獲取服務注冊列表):獲取其他服務列表。
Replicate(集群中數據同步):eureka集群中的數據復制與同步。
Make Remote Call(遠程調用):完成服務的遠程調用。
Eureka Server
Eureka Server既是一個注冊中心,同時也是一個服務。那么搭建Eureka Server的方式和以往搭建Dubbo注冊中心ZooKeeper的方式必然不同,那么首先搭建一個單機版的Eureka Server注冊中心。
二、搭建單機版Eureka Server
Eureka已經被Spring Cloud繼承在其子項目spring-cloud-netflix中,搭建Eureka Server的方式還是非常簡單的。只需要通過一個獨立的maven工程即可搭建Eureka Server。pom依賴如下:
<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.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.16.RELEASE</version> </parent> <groupId>com.bjsxt</groupId> <artifactId>spring-cloud-eureka-server-single</artifactId> <version>1.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Edgware.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- spring cloud 默認配置啟動器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- spring cloud Eureka Server 啟動器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
全局配置文件:
而Eureka Server本身也是一個服務,同時又是一個注冊中心。在Spring Cloud中,啟動的微服務會自動的搜索注冊中心並注冊服務,那么在單機版Eureka Server環境中,當前服務注冊到當前服務中,明顯是不合適的。所以搭建Eureka Server單機版時,需要提供特殊的全局配置,避免回路注冊邏輯。
同理,Eureka Server服務在注冊中心中發現服務列表邏輯也是不必要的。畢竟注冊中心是一個中立的服務管理平台,如果是單機版Eureka Server環境中,Eureka Server服務再去發現服務列表,明顯也是不必要的。也需要通過全局配置,避免回路發現邏輯。
# 設置spring應用命名,可以自定義,非必要
spring.application.name=eureka-server
# 設置Eureka Server WEB控制台端口,自定義
server.port=8761
#是否將自己注冊到Eureka-Server中,默認的為true
eureka.client.registerWithEureka=false
#是否從Eureka-Server中獲取服務注冊信息,默認為true
eureka.client.fetchRegistry=false
啟動類配置:
啟動Eureka Server注冊中心,和普通的SpringBoot應用的啟動沒有太大的區別。只需要在啟動類上增加@EnableEurekaServer注解,來開啟Eureka Server服務即可。
注意:此處@SpringCloudApplication注解定義啟動類。@SpringCloudApplication注解定義啟動類涉及到hystrix相關內容。
@EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
訪問Eureka Server WEB控制台:通過IP和端口,使用瀏覽器訪問即可查看Eureka Server中的信息。本例中訪問地址為:http://localhost:8761/
三、搭建集群版Eureka Server
注冊中心作為微服務架構中的核心功能,其重要性不言而喻。所以單機版的Eureka Server在可靠性上並不符合現在的互聯網開發環境。集群版的Eureka Server才是商業開發中的選擇。
Eureka Server注冊中心的集群和Dubbo的ZooKeeper注冊中心集群在結構上有很大的不同。
ZooKeeper注冊中心集群搭建后,集群中各節點呈現主從關系,集群中只有主節點對外提供服務的注冊和發現功能,從節點相當於備份節點,只有主節點宕機時,從節點會選舉出一個新的主節點,繼續提供服務的注冊和發現功能。
而Eureka Server注冊中心集群中每個節點都是平等的,集群中的所有節點同時對外提供服務的發現和注冊等功能。同時集群中每個Eureka Server節點又是一個微服務,也就是說,每個節點都可以在集群中的其他節點上注冊當前服務。又因為每個節點都是注冊中心,所以節點之間又可以相互注冊當前節點中已注冊的服務,並發現其他節點中已注冊的服務。所以Eureka Server注冊中心集群版在搭建過程中有很多的方式,找到一個最合適最可靠的搭建方式才能稱為一個稱職的程序員。
集群版Eureka Server可以通過Spring Boot多環境配置方式快速搭建。只要創建一個合適的Eureka Server工程,通過多個全局配置即可完成快速搭建。
本案例中搭建一個雙節點的Eureka Server集群。
Linux版本為: CentOS 6.5
JDK版本為: 1.8
POM依賴
和單機版Eureka Server相同。
全局配置文件
本例中的兩個節點分別會搭建在兩個Linux系統中,為這兩個Linux系統分別定義域名為eurekaserver1和eurekaserver2。
在集群搭建過程中,全局配置文件的定義非常重要。其中euraka.client.serviceUrl.defaultZone屬性是用於配置集群中其他節點的。如果有多個節點,使用逗號','分隔。
有部分程序員只配置某一個集群節點信息,通過集群節點間的注冊通訊實現節點的全面發現。這種配置形式是不推薦的。因為Eureka Server在服務管理上,會根據連帶責任來維護服務列表,如果某集群節點宕機,那么通過這個節點注冊過來的服務都會連帶刪除。
#eurekaserver1配置 spring.application.name=eureka-server server.port=8761 # 設置eureka實例名稱,建議與配置文件的變量相同,必須和Linux系統域名相同 eureka.instance.hostname=eurekaserver1 # 設置服務注冊中心地址,指向另一個注冊中心,使用域名作為訪問路徑 eureka.client.serviceUrl.defaultZone=http://eurekaserver2:8761/eureka/ #eurekaserver2配置 spring.application.name=eureka-server server.port=8761 eureka.instance.hostname=eurekaserver2 eureka.client.serviceUrl.defaultZone=http://eurekaserver1:8761/eureka/
打包工程形成jar文件
使用run -> maven install即可實現打包過程。打包后的jar文件保存在工程中的target目錄中,並上傳打包后的jar文件到Linux系統。
設置Linux主機域名
修改/etc/hosts文件,設置主機域名。將主機域名和IP進行綁定。新增內容如下:兩個Linux系統修改內容相同。
192.168.2.115 eurekaserver1 192.168.2.116 eurekaserver2
使用命令啟動Eureka Server
可以在Linux終端中,通過java命令來啟動Eureka Server。在啟動的時候,可以通過啟動參數來設置有效的配置環境。具體命令如下:
java -jar -Dspring.profiles.active=eurekaserver1 spring-cloud-eureka-server-cluster-1.0.jar
其中-Dspring.profiles.active啟動參數,用於定義本次啟動的Eureka Server應用的有效全局配置命名,也就是全局配置文件的后綴。SpringBOOT在啟動的時候,會根據啟動參數來決定讀取的有效全局配置文件是哪一個。
也可以定義一個shell文件來簡化操作。具體shell內容如下:
#!/bin/bash cd `dirname $0` CUR_SHELL_DIR=`pwd` CUR_SHELL_NAME=`basename ${BASH_SOURCE}` JAR_NAME="項目jar包名稱" JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME #JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m" JAVA_MEM_OPTS="" SPRING_PROFILES_ACTIV="-Dspring.profiles.active=配置文件變量名稱" #SPRING_PROFILES_ACTIV="" LOG_DIR=$CUR_SHELL_DIR/logs LOG_PATH=$LOG_DIR/${JAR_NAME%..log echo_help() { echo -e "syntax: sh $CUR_SHELL_NAME start|stop" } if [ -z $1 ];then echo_help exit 1 fi if [ ! -d "$LOG_DIR" ];then mkdir "$LOG_DIR" fi if [ ! -f "$LOG_PATH" ];then touch "$LOG_DIR" fi if [ "$1" == "start" ];then # check server PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'` if [ -n "$PIDS" ]; then echo -e "ERROR: The $JAR_NAME already started and the PID is ${PIDS}." exit 1 fi echo "Starting the $JAR_NAME..." # start nohup java $JAVA_MEM_OPTS -jar $SPRING_PROFILES_ACTIV $JAR_PATH >> $LOG_PATH 2>&1 & COUNT=0 while [ $COUNT -lt 1 ]; do sleep 1 COUNT=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l` if [ $COUNT -gt 0 ]; then break fi done PIDS=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'` echo "${JAR_NAME} Started and the PID is ${PIDS}." echo "You can check the log file in ${LOG_PATH} for details." elif [ "$1" == "stop" ];then PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'` if [ -z "$PIDS" ]; then echo "ERROR:The $JAR_NAME does not started!" exit 1 fi echo -e "Stopping the $JAR_NAME..." for PID in $PIDS; do kill $PID > /dev/null 2>&1 done COUNT=0 while [ $COUNT -lt 1 ]; do sleep 1 COUNT=1 for PID in $PIDS ; do PID_EXIST=`ps --no-heading -p $PID` if [ -n "$PID_EXIST" ]; then COUNT=0 break fi done done echo -e "${JAR_NAME} Stopped and the PID is ${PIDS}." else echo_help exit 1 fi
設置好shell啟動腳本后,需要提供可執行權限:shell腳本文件名自己修改:chmod 755 xxx.sh
腳本使用方式為:
#啟動Eureka Server ./xxx.sh start #關閉Eureka Server ./xxx.sh stop
四、Eureka Server安全認證
Eureka Server作為Spring Cloud中的服務注冊中心,如果可以任意訪問的話,那么其安全性太低。所以Spring Cloud中也有為Eureka Server提供安全認證的方式。可以使用spring-boot-starter-security組件來為Eureka Server增加安全認證。
POM依賴:
<!-- spring boot security安全認證啟動器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
修改全局配置文件,在全局配置文件中,開啟基於http basic的安全認證。
# eurekaserver1配置
spring.application.name=eureka-server
server.port=8761
eureka.instance.hostname=eurekaserver1
# 使用http basic安全認證語法,在集群通信中增加認證信息。 http://用戶名:密碼@地址:端口/eureka/
eureka.client.serviceUrl.defaultZone=http://test:123456@eurekaserver2:8761/eureka/
# 開啟基於http basic的安全認證
security.basic.enabled=true
# 設置安全認證用戶名
security.user.name=test
# 設置安全認證密碼
security.user.password=123456
# eurekaserver2配置
spring.application.name=eureka-server
server.port=8761
eureka.instance.hostname=eurekaserver2
eureka.client.serviceUrl.defaultZone=http://test:123456@eurekaserver1:8761/eureka/
security.basic.enabled=true
security.user.name=test
security.user.password=123456
五、CAP定理
CAP原則又稱CAP定理,指的是在一個分布式系統中,Consistency(數據一致性)、 Availability(服務可用性)、Partition tolerance(分區容錯性),三者不可兼得。CAP由Eric Brewer在2000年PODC會議上提出。該猜想在提出兩年后被證明成立,成為我們熟知的CAP定理。
分布式系統CAP定理 | |
數據一致性 (Consistency) |
數據一致性(Consistency) 也叫做數據原子性系統在執行某項操作后仍然處於一致的狀態。在分布式系統中,更新操作執行成功后所有的用戶都應該讀到最新的值,這樣的系統被認為是具有強一致性的。等同於所有節點訪問同一份最新的數據副本。 優點: 數據一致,沒有數據錯誤可能。 缺點: 相對效率降低。 |
服務可用性 (Availablity) |
每一個操作總是能夠在一定的時間內返回結果,這里需要注意的是"一定時間內"和"返回結果"。一定時間內指的是,在可以容忍的范圍內返回結果,結果可以是成功或者是失敗。 |
分區容錯性 (Partition-torlerance) |
在網絡分區的情況下,被分隔的節點仍能正常對外提供服務(分布式集群,數據被分布存儲在不同的服務器上,無論什么情況,服務器都能正常被訪問) |
定律:任何分布式系統只可同時滿足二點,沒法三者兼顧。 | |
CA,放棄P | 如果想避免分區容錯性問題的發生,一種做法是將所有的數據(與事務相關的)/服務都放在一台機器上。雖然無法100%保證系統不會出錯,但不會碰到由分區帶來的負面效果。當然這個選擇會嚴重的影響系統的擴展性。 |
CP,放棄A | 相對於放棄"分區容錯性"來說,其反面就是放棄可用性。一旦遇到分區容錯故障,那么受到影響的服務需要等待一定時間,因此在等待時間內系統無法對外提供服務。 |
AP,放棄C | 這里所說的放棄一致性,並不是完全放棄數據一致性,而是放棄數據的強一致性,而保留數據的最終一致性。以網絡購物為例,對只剩下一件庫存的商品,如果同時接受了兩個訂單,那么較晚的訂單將被告知商品告罄。 |
Eureka和ZooKeeper的特性
六、Eureka Client
在Spring Cloud中,開發Eureka Client組件還是非常方便的。我們在開發過程中,不需要像Dubbo那樣關注服務的角色。無論是Provider還是Consumer都是一個微服務客戶端,只是在編碼層面上,服務消費者代碼比較麻煩。
1、Application Service服務提供者開發
POM依賴:
如果Eureka 開啟了security安全校驗機制,那么Eureka Client在開發的時候,依賴的jar包需要額外依賴一個actuator插件。那么可以修改Eureka Client工程中的啟動器,把spring-cloud-starter-eureka改為spring-cloud-starter-eureka-server。Eureka-server啟動器中包含HTTP Basic安全認證相關的jar包,實際上spring-cloud-starter-eureka-server啟動器包含啟動器spring-cloud-starter-eureka中的所有的包。
<!-- spring cloud Eureka Client 啟動器,因為Eureka Server開啟了安全校驗, 所有需要依賴更大范圍的jar包資源spring-cloud-starter-eureka-server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <!-- actuator 組件是Spring Boot的監控組件,actuator一旦應用,在啟動的時候,會發布一系列的URL服務。包含一個shutdown服務,代表優雅關閉 當Spring Boot 應用中的actuator組件接收到shutdown請求的時候,會觸發優雅關閉。 如果當前應用中有Eureka Client的集成,則會觸發Eureka Client向Eureka Server發起一個shutdown優雅停服的請求 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> </dependency>
全局配置:
在配置Eureka Server信息時,建議將Eureka Server集群中的所有節點依次配置,Eureka Client在注冊服務的時候,會根據節點列表依次訪問Eureka Server集群節點,只要注冊成功,后續Eureka Server節點不再訪問注冊。雖然Eureka Server集群各節點可以相互發現服務,但是Eureka Server集群中每個節點對服務的管理都使用連帶責任,及從某Eureka Server節點發現服務A,如果這個Eureka Server節點宕機,則A服務同時從服務列表中刪除。
# 定義SpringBoot應用的名稱,建議必須提供。在SpringCloud中,對服務的最大粒度的管理是使用應用命名的 # 最好是一個應用一個名稱,在Consumer角色開發的時候,比較容易查找Provider
spring.application.name=eureka-application-service
server.port=8081
# 配置Eureka Server的地址信息,如果是Eureka Server集群,多個節點使用逗號','分割。 # 如果開啟了安全認證,使用HTTP Bacic格式提供用戶名和密碼。 # 如果Eureka Server是一個集群,那么配置Eureka Server節點信息的時候,建議將所有的Eureka Server節點信息都配置上 # 實際上,只配置一個Eureka Server節點其實就可以了,但是,Eureka Server對服務的管理有連帶責任。如果只配置一個Eureka Server節點,那么會導致級聯刪除的風險,可能導致服務不可靠 # 如果配置了多個Eureka Server節點,Eureka不會將當期的服務同時注冊到所有Eureka Server節點上 # 從第一個配置的Eureka Server節點開始注冊,如果注冊成功,后續的Eureka Server節點不再重復注冊 # 每30秒,Eureka Client發送一個心跳到Eureka Server上,如果心跳沒有反饋,則從已配置的Eureka Server節點列表的下一個服務節點繼續注冊。 # 這樣做可以保證服務的可靠性,降低服務連帶責任導致的服務不可靠。 # 如果多個Eureka Client需要注冊,建議Eureka Server的服務列表順序是隨機排列的。 # 如:有Eureka Server s1,s2,s3,有Eureka Client c1,c2,c3。 # 那么在c1上配置的Eureka Server列表建議是s1,s2,s3,在c2上配置的是s2,s3,s1,在c3上配置的是s3,s1,s2,這樣可以更好的利用Eureka Server集群的特性。 # 因為Eureka Server和Eureka Client對心跳的監測都是3*間隔時間的,所以會有服務列表數據的不同步可能。 # 所以在CAP原則上,Eureka Server是保證AP原則,放棄C原則的。
eureka.client.serviceUrl.defaultZone=http://eurekaserver1:111111@eurekaserver1:8761/eureka/,http://eurekaserver2:222222@eurekaserver2:8761/eureka/
security.basic.enabled=true
# 啟用shutdown,優雅停服功能,配置actuator的優雅關閉 # actuator 組件監聽shutdown請求地址的時候,要求請求的method必須是POST # shutdown的請求地址是使用:@PostMapping或@RequestMapping(method=RequestMethod.POST)
endpoints.shutdown.enabled=true
# 禁用密碼驗證
endpoints.shutdown.sensitive=false
建議:如果有多個服務功能需要注冊,那么在設置Eureka Server信息的時候,推薦異序排列。如:現在有3個工程A、B、C需要注冊服務到Eureka Server集群中,集群節點有三個,分別是e1、e2、e3,那么在工程中推薦配置為,A工程配置-e1,e2,e3,B工程配置e2,e3,e1,C工程配置e3,e1,e2。這樣可以更好的利用Eureka Server集群的特性。
啟動類:
需要在啟動類上新增注解@EnableEurekaClient,代表當前應用開啟Eureka客戶端,應用啟動后,會自動將服務注冊到Eureka Server中。
/** * Eureka Client啟動類 * 是使用SpringBoot啟動類實現啟動的 * @EnableEurekaClient 注解,就是用於通知SpringBoot應用,當前應用是一個Irk客戶端 * 需要做一下服務的注冊。 * 使用全局配置文件application.properties配置Eureka Server的相關信息 * 如:Eureka Server的地址,個數,是否需要安全認證等。 */ @EnableEurekaClient @SpringBootApplication public class EurekaApplicationServiceApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplicationServiceApplication.class, args); } }
對外接口:
Eureka Client的Application Server對外需要暴露接口方法,接口定義如Rest請求定義方式:
@Controller public class TestApplicationServiceController { @RequestMapping(value="/test") @ResponseBody public List<Map<String, Object>> test(){ List<Map<String, Object>> result = new ArrayList<>(); for(int i = 0; i < 3; i++){ Map<String, Object> data = new HashMap<>(); data.put("id", i+1); data.put("name", "test name " + i); data.put("age", 20+i); result.add(data); } return result; } }
因此服務也可以作為url請求直接調用(Security Basic安全認證導致返回結果為XML類型,而不是JSON類型):
2、Application Client服務消費者開發
在Spring Cloud中,服務消費方代碼的開發確實比較麻煩,並不像Dubbo那么直接注入服務接口代理對象,通過代理對象方法直接訪問遠程服務。在Spring Cloud中,微服務的提供是通過REST風格提供的,也就是服務的調用是基於HTTP協議的,所以在服務調用上比較麻煩,具體詳見案例代碼。
POM依賴:
同Application Service工程。
全局配置:
因為都是本地啟動,需要修改服務端口。推薦修改spring應用命名。在Eureka Server中,對服務的管理是基於spring應用名稱的,所以不同的服務推薦使用不同的應用名稱。
spring.application.name=eureka-application-client server.port=8080 # 點對點直連是不發現服務,不是不注冊服務。 # 任何Eureka Client都必須注冊。如果沒有配置Eureka Server節點列表,則注冊失敗。Eureka client無法正常啟動。 eureka.client.serviceUrl.defaultZone=http://eurekaserver1:111111@eurekaserver1:8761/eureka/,http://eurekaserver2:222222@eurekaserver2:8761/eureka/ # 設置負載均衡策略 eureka-application-service為調用的服務的名稱 # 沒有配置全部服務的負載均衡策略的方式。因為不是每個服務都可以使用相同負載均衡策略的。 # 如:搜索服務和注冊服務就不能使用相同的負載均衡策略。 eureka-application-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule # 關閉ribbon訪問注冊中心Eureka Server發現服務 ribbon.eureka.enabled=false # 配置服務列表,其中eureka-application-service代表要訪問的服務的應用名,如果有多個服務結點組成集群,多個節點的配置信息使用逗號','分隔。 # 配置服務列表,需要配置要調用的服務的名字和服務所在的位置。 # 服務的名字,就是Application Service中配置的spring.application.name。 # 服務的位置,就是服務的所在ip和端口。 # 如果服務位置有多個,也就是服務集群,那么使用逗號','分割多個服務列表信息。 eureka-application-service.ribbon.listOfServers=localhost:8083
啟動類:
同Applicaiton Service一致。
請求調用類:
/** * 在這里開發Eureka Client中的Application Client角色。就是consumer服務的消費者。 * 服務消費者需要在注冊中心中發現服務列表的。且同時將自己注冊到注冊中心的服務列表中。(參考如下截圖) * * consumer在消費provider的時候,是通過LoadBalancer來實現的。 * LoadBalancer簡介 : 是Eureka client內置的一個負載均衡器。復雜在發現的服務列表中選擇服務應用,獲取服務的IP和端口。 * 實現服務的遠程調用。 * * application client代碼開發相比較dubbo的consumer開發麻煩很多。 * */ @RestController public class TestApplicationClientController { /** * ribbon負載均衡器,其中記錄了從Eureka Server中獲取的所有服務信息。 * 這些服務的信息是IP和端口等。應用名稱,域名,主機名等信息。 */ @Autowired private LoadBalancerClient loadBalancerClient; /** * 通過HTTP協議,發起遠程服務調用,實現一個遠程的服務消費。 * @return */ @GetMapping public List<Map<String, Object>> test() { // 通過spring應用命名,獲取服務實例ServiceInstance對象 // ServiceInstance 封裝了服務的基本信息,如 IP,端口 /* * 在Eureka中,對所有注冊到Eureka Server中的服務都稱為一個service instance服務實例。 * 一個服務實例,就是一個有效的,可用的,provider單體實例或集群實例。 * 每個service instance都和spring application name對應。 * 可以通過spring application name查詢service instance */ ServiceInstance si = this.loadBalancerClient.choose("eureka-application-service"); // 拼接訪問服務的URL StringBuilder sb = new StringBuilder(); // http://localhost:8081/test sb.append("http://").append(si.getHost()) .append(":").append(si.getPort()).append("/test"); System.out.println("本次訪問的service是: " + sb.toString()); // SpringMVC RestTemplate,用於快速發起REST請求的模板對象。 /* * RestTemplate是SpringMVC提供的一個用於發起REST請求的模板對象。 * 基於HTTP協議發起請求的。 * 發起請求的方式是exchange。需要的參數是: URL, 請求方式, 請求頭, 響應類型,【URL rest參數】。 */ RestTemplate rt = new RestTemplate(); /* * 創建一個響應類型模板。 * 就是REST請求的響應體中的數據類型。 * ParameterizedTypeReference - 代表REST請求的響應體中的數據類型。 */ ParameterizedTypeReference<List<Map<String, Object>>> type = new ParameterizedTypeReference<List<Map<String, Object>>>() { }; /* * ResponseEntity:封裝了返回值信息,相當於是HTTP Response中的響應體。 * 發起REST請求。 */ ResponseEntity<List<Map<String, Object>>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); /* * ResponseEntity.getBody() - 就是獲取響應體中的java對象或返回數據結果。 */ List<Map<String, Object>> result = response.getBody(); return result; } }
LoadBanlancerClient中包含了所有的服務注冊信息,如下圖示例:
七、服務保護
1 服務保護模式
服務保護模式(自我保護模式):一般情況下,微服務在Eureka上注冊后,會每30秒發送心跳包,Eureka通過心跳來判斷服務時候健康,同時會定期刪除超過90秒沒有發送心跳服務。
導致Eureka Server接收不到心跳包的可能:一是微服務自身的原因,二是微服務與Eureka之間的網絡故障。通常微服務的自身的故障只會導致個別服務出現故障,一般不會出現大面積故障,而網絡故障通常會導致Eureka Server在短時間內無法收到大批心跳。慮到這個區別,Eureka設置了一個閥值,當判斷掛掉的服務的數量超過閥值時,Eureka Server認為很大程度上出現了網絡故障,將不再刪除心跳過期的服務。
那么這個閥值是多少呢?Eureka Server在運行期間,會統計心跳失敗的比例在15分鍾內是否低於85%,如果低於85%,Eureka Server則任務是網絡故障,不會刪除心跳過期服務。
這種服務保護算法叫做Eureka Server的服務保護模式。
這種不刪除的,90秒沒有心跳的服務,稱為無效服務,但是還是保存在服務列表中。如果Consumer到注冊中心發現服務,則Eureka Server會將所有好的數據(有效服務數據)和壞的數據(無效服務數據)都返回給Consumer。
2 服務保護模式的存在必要性
因為同時保留"好數據"與"壞數據"總比丟掉任何數據要更好,當網絡故障恢復后,Eureka Server會退出"自我保護模式"。
Eureka還有客戶端緩存功能(也就是微服務的緩存功能)。即便Eureka Server集群中所有節點都宕機失效,微服務的Provider和Consumer都能正常通信。
微服務的負載均衡策略會自動剔除死亡的微服務節點(Robbin)。
只要Consumer不關閉,緩存始終有效,直到一個應用下的所有Provider訪問都無效的時候,才會訪問Eureka Server重新獲取服務列表。
3 關閉服務保護模式
可以通過全局配置文件來關閉服務保護模式,商業項目中不推薦關閉服務保護,因為網絡不可靠很容易造成網絡波動、延遲、斷線的可能。如果關閉了服務保護,可能導致大量的服務反復注冊、刪除、再注冊。導致效率降低。在商業項目中,服務的數量一般都是幾十個,大型的商業項目中服務的數量可能上百、數百,甚至上千:
# 關閉自我保護:true為開啟自我保護,false為關閉自我保護 eureka.server.enableSelfPreservation=false # 清理間隔(單位:毫秒,默認是60*1000),當服務心跳失效后多久,刪除服務。 eureka.server.eviction.interval-timer-in-ms=60000
4 優雅關閉服務(優雅停服)
在Spring Cloud中,可以通過HTTP請求的方式,通知Eureka Client優雅停服,這個請求一旦發送到Eureka Client,那么Eureka Client會發送一個shutdown請求到Eureka Server,Eureka Server接收到這個shutdown請求后,會在服務列表中標記這個服務的狀態為down,同時Eureka Client應用自動關閉。這個過程就是優雅停服。
如果使用了優雅停服,則不需要再關閉Eureka Server的服務保護模式。
POM依賴:
優雅停服是通過Eureka Client發起的,所以需要在Eureka Client中增加新的依賴,這個依賴是autuator組件,添加下述依賴即可。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> </dependency>
修改全局配置文件:
Eureka Client默認不開啟優雅停服功能,需要在全局配置文件中新增如下內容:
# 啟用shutdown,優雅停服功能
endpoints.shutdown.enabled=true
# 禁用密碼驗證
endpoints.shutdown.sensitive=false
發起shutdown請求:
必須通過POST請求向Eureka Client發起一個shutdown請求。請求路徑為:http://ip:port/shutdown。可以通過任意技術實現,如:HTTPClient、form表單,AJAX等。
建議使用優雅停服方式來關閉Application Service/Application Client服務。