一、Eureka簡介
Eureka是Netflix開發(fā)的服務(wù)發(fā)現(xiàn)框架,本身是一個(gè)基于REST的服務(wù),主要用于定位運(yùn)行在AWS域中的中間層服務(wù),以達(dá)到負(fù)載均衡和中間層服務(wù)故障轉(zhuǎn)移的目的。SpringCloud將它集成在其子項(xiàng)目spring-cloud-netflix中,以實(shí)現(xiàn)SpringCloud的服務(wù)發(fā)現(xiàn)功能。
1、Eureka組件
Eureka包含兩個(gè)組件:Eureka Server和Eureka Client。
1.1 Eureka Server
Eureka Server提供服務(wù)注冊(cè)服務(wù),各個(gè)節(jié)點(diǎn)啟動(dòng)后,會(huì)在Eureka Server中進(jìn)行注冊(cè),這樣Eureka Server中的服務(wù)注冊(cè)表中將會(huì)存儲(chǔ)所有可用服務(wù)節(jié)點(diǎn)的信息,服務(wù)節(jié)點(diǎn)的信息可以在界面中直觀的看到。
Eureka Server本身也是一個(gè)服務(wù),默認(rèn)情況下會(huì)自動(dòng)注冊(cè)到Eureka注冊(cè)中心。
如果搭建單機(jī)版的Eureka Server注冊(cè)中心,則需要配置取消Eureka Server的自動(dòng)注冊(cè)邏輯。畢竟當(dāng)前服務(wù)注冊(cè)到當(dāng)前服務(wù)代表的注冊(cè)中心中是一個(gè)說不通的邏輯。
Eureka Server通過Register、Get、Renew等接口提供服務(wù)的注冊(cè)、發(fā)現(xiàn)和心跳檢測等服務(wù)。
2.1 Eureka Client
Eureka Client是一個(gè)java客戶端,用于簡化與Eureka Server的交互,客戶端同時(shí)也具備一個(gè)內(nèi)置的、使用輪詢(round-robin)負(fù)載算法的負(fù)載均衡器。在應(yīng)用啟動(dòng)后,將會(huì)向Eureka Server發(fā)送心跳,默認(rèn)周期為30秒,如果Eureka Server在多個(gè)心跳周期內(nèi)沒有接收到某個(gè)節(jié)點(diǎn)的心跳,Eureka Server將會(huì)從服務(wù)注冊(cè)表中把這個(gè)服務(wù)節(jié)點(diǎn)移除(默認(rèn)90秒)。
Eureka Client分為兩個(gè)角色,分別是:Application Service(Service Provider)和Application Client(Service Consumer)
2.1.1 Application Service
服務(wù)提供方,是注冊(cè)到Eureka Server中的服務(wù)。
2.1.2 Application Client
服務(wù)消費(fèi)方,通過Eureka Server發(fā)現(xiàn)服務(wù),并消費(fèi)。
在這里,Application Service和Application Client不是絕對(duì)上的定義,因?yàn)镻rovider在提供服務(wù)的同時(shí),也可以消費(fèi)其他Provider提供的服務(wù);Consumer在消費(fèi)服務(wù)的同時(shí),也可以提供對(duì)外服務(wù)。
2、Eureka Server架構(gòu)原理簡
Register(服務(wù)注冊(cè)):把自己的IP和端口注冊(cè)給Eureka。
Renew(服務(wù)續(xù)約):發(fā)送心跳包,每30秒發(fā)送一次。告訴Eureka自己還活著。
Cancel(服務(wù)下線):當(dāng)provider關(guān)閉時(shí)會(huì)向Eureka發(fā)送消息,把自己從服務(wù)列表中刪除。防止consumer調(diào)用到不存在的服務(wù)。
Get Registry(獲取服務(wù)注冊(cè)列表):獲取其他服務(wù)列表。
Replicate(集群中數(shù)據(jù)同步):eureka集群中的數(shù)據(jù)復(fù)制與同步。
Make Remote Call(遠(yuǎn)程調(diào)用):完成服務(wù)的遠(yuǎn)程調(diào)用。
Eureka Server
Eureka Server既是一個(gè)注冊(cè)中心,同時(shí)也是一個(gè)服務(wù)。那么搭建Eureka Server的方式和以往搭建Dubbo注冊(cè)中心ZooKeeper的方式必然不同,那么首先搭建一個(gè)單機(jī)版的Eureka Server注冊(cè)中心。
二、搭建單機(jī)版Eureka Server
Eureka已經(jīng)被Spring Cloud繼承在其子項(xiàng)目spring-cloud-netflix中,搭建Eureka Server的方式還是非常簡單的。只需要通過一個(gè)獨(dú)立的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 默認(rèn)配置啟動(dòng)器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- spring cloud Eureka Server 啟動(dòng)器 -->
<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本身也是一個(gè)服務(wù),同時(shí)又是一個(gè)注冊(cè)中心。在Spring Cloud中,啟動(dòng)的微服務(wù)會(huì)自動(dòng)的搜索注冊(cè)中心并注冊(cè)服務(wù),那么在單機(jī)版Eureka Server環(huán)境中,當(dāng)前服務(wù)注冊(cè)到當(dāng)前服務(wù)中,明顯是不合適的。所以搭建Eureka Server單機(jī)版時(shí),需要提供特殊的全局配置,避免回路注冊(cè)邏輯。
同理,Eureka Server服務(wù)在注冊(cè)中心中發(fā)現(xiàn)服務(wù)列表邏輯也是不必要的。畢竟注冊(cè)中心是一個(gè)中立的服務(wù)管理平臺(tái),如果是單機(jī)版Eureka Server環(huán)境中,Eureka Server服務(wù)再去發(fā)現(xiàn)服務(wù)列表,明顯也是不必要的。也需要通過全局配置,避免回路發(fā)現(xiàn)邏輯。
# 設(shè)置spring應(yīng)用命名,可以自定義,非必要
spring.application.name=eureka-server
# 設(shè)置Eureka Server WEB控制臺(tái)端口,自定義
server.port=8761
#是否將自己注冊(cè)到Eureka-Server中,默認(rèn)的為true
eureka.client.registerWithEureka=false
#是否從Eureka-Server中獲取服務(wù)注冊(cè)信息,默認(rèn)為true
eureka.client.fetchRegistry=false
啟動(dòng)類配置:
啟動(dòng)Eureka Server注冊(cè)中心,和普通的SpringBoot應(yīng)用的啟動(dòng)沒有太大的區(qū)別。只需要在啟動(dòng)類上增加@EnableEurekaServer注解,來開啟Eureka Server服務(wù)即可。
注意:此處@SpringCloudApplication注解定義啟動(dòng)類。@SpringCloudApplication注解定義啟動(dòng)類涉及到hystrix相關(guān)內(nèi)容。
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
訪問Eureka Server WEB控制臺(tái):通過IP和端口,使用瀏覽器訪問即可查看Eureka Server中的信息。本例中訪問地址為:http://localhost:8761/
三、搭建集群版Eureka Server
注冊(cè)中心作為微服務(wù)架構(gòu)中的核心功能,其重要性不言而喻。所以單機(jī)版的Eureka Server在可靠性上并不符合現(xiàn)在的互聯(lián)網(wǎng)開發(fā)環(huán)境。集群版的Eureka Server才是商業(yè)開發(fā)中的選擇。
Eureka Server注冊(cè)中心的集群和Dubbo的ZooKeeper注冊(cè)中心集群在結(jié)構(gòu)上有很大的不同。
ZooKeeper注冊(cè)中心集群搭建后,集群中各節(jié)點(diǎn)呈現(xiàn)主從關(guān)系,集群中只有主節(jié)點(diǎn)對(duì)外提供服務(wù)的注冊(cè)和發(fā)現(xiàn)功能,從節(jié)點(diǎn)相當(dāng)于備份節(jié)點(diǎn),只有主節(jié)點(diǎn)宕機(jī)時(shí),從節(jié)點(diǎn)會(huì)選舉出一個(gè)新的主節(jié)點(diǎn),繼續(xù)提供服務(wù)的注冊(cè)和發(fā)現(xiàn)功能。
而Eureka Server注冊(cè)中心集群中每個(gè)節(jié)點(diǎn)都是平等的,集群中的所有節(jié)點(diǎn)同時(shí)對(duì)外提供服務(wù)的發(fā)現(xiàn)和注冊(cè)等功能。同時(shí)集群中每個(gè)Eureka Server節(jié)點(diǎn)又是一個(gè)微服務(wù),也就是說,每個(gè)節(jié)點(diǎn)都可以在集群中的其他節(jié)點(diǎn)上注冊(cè)當(dāng)前服務(wù)。又因?yàn)槊總€(gè)節(jié)點(diǎn)都是注冊(cè)中心,所以節(jié)點(diǎn)之間又可以相互注冊(cè)當(dāng)前節(jié)點(diǎn)中已注冊(cè)的服務(wù),并發(fā)現(xiàn)其他節(jié)點(diǎn)中已注冊(cè)的服務(wù)。所以Eureka Server注冊(cè)中心集群版在搭建過程中有很多的方式,找到一個(gè)最合適最可靠的搭建方式才能稱為一個(gè)稱職的程序員。
集群版Eureka Server可以通過Spring Boot多環(huán)境配置方式快速搭建。只要?jiǎng)?chuàng)建一個(gè)合適的Eureka Server工程,通過多個(gè)全局配置即可完成快速搭建。
本案例中搭建一個(gè)雙節(jié)點(diǎn)的Eureka Server集群。
Linux版本為: CentOS 6.5
JDK版本為: 1.8
POM依賴
和單機(jī)版Eureka Server相同。
全局配置文件
本例中的兩個(gè)節(jié)點(diǎn)分別會(huì)搭建在兩個(gè)Linux系統(tǒng)中,為這兩個(gè)Linux系統(tǒng)分別定義域名為eurekaserver1和eurekaserver2。
在集群搭建過程中,全局配置文件的定義非常重要。其中euraka.client.serviceUrl.defaultZone屬性是用于配置集群中其他節(jié)點(diǎn)的。如果有多個(gè)節(jié)點(diǎn),使用逗號(hào)','分隔。
有部分程序員只配置某一個(gè)集群節(jié)點(diǎn)信息,通過集群節(jié)點(diǎn)間的注冊(cè)通訊實(shí)現(xiàn)節(jié)點(diǎn)的全面發(fā)現(xiàn)。這種配置形式是不推薦的。因?yàn)?strong>Eureka Server在服務(wù)管理上,會(huì)根據(jù)連帶責(zé)任來維護(hù)服務(wù)列表,如果某集群節(jié)點(diǎn)宕機(jī),那么通過這個(gè)節(jié)點(diǎn)注冊(cè)過來的服務(wù)都會(huì)連帶刪除。
#eurekaserver1配置
spring.application.name=eureka-server
server.port=8761
# 設(shè)置eureka實(shí)例名稱,建議與配置文件的變量相同,必須和Linux系統(tǒng)域名相同
eureka.instance.hostname=eurekaserver1
# 設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心,使用域名作為訪問路徑
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即可實(shí)現(xiàn)打包過程。打包后的jar文件保存在工程中的target目錄中,并上傳打包后的jar文件到Linux系統(tǒng)。
設(shè)置Linux主機(jī)域名
修改/etc/hosts文件,設(shè)置主機(jī)域名。將主機(jī)域名和IP進(jìn)行綁定。新增內(nèi)容如下:兩個(gè)Linux系統(tǒng)修改內(nèi)容相同。
192.168.2.115 eurekaserver1
192.168.2.116 eurekaserver2
使用命令啟動(dòng)Eureka Server
可以在Linux終端中,通過java命令來啟動(dòng)Eureka Server。在啟動(dòng)的時(shí)候,可以通過啟動(dòng)參數(shù)來設(shè)置有效的配置環(huán)境。具體命令如下:
java -jar -Dspring.profiles.active=eurekaserver1 spring-cloud-eureka-server-cluster-1.0.jar
其中-Dspring.profiles.active啟動(dòng)參數(shù),用于定義本次啟動(dòng)的Eureka Server應(yīng)用的有效全局配置命名,也就是全局配置文件的后綴。SpringBOOT在啟動(dòng)的時(shí)候,會(huì)根據(jù)啟動(dòng)參數(shù)來決定讀取的有效全局配置文件是哪一個(gè)。
也可以定義一個(gè)shell文件來簡化操作。具體shell內(nèi)容如下:
#!/bin/bash
cd `dirname $0`
CUR_SHELL_DIR=`pwd`
CUR_SHELL_NAME=`basename ${BASH_SOURCE}`
JAR_NAME="項(xiàng)目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
設(shè)置好shell啟動(dòng)腳本后,需要提供可執(zhí)行權(quán)限:shell腳本文件名自己修改:chmod 755 xxx.sh
腳本使用方式為:
#啟動(dòng)Eureka Server
./xxx.sh start
#關(guān)閉Eureka Server
./xxx.sh stop
四、Eureka Server安全認(rèn)證
Eureka Server作為Spring Cloud中的服務(wù)注冊(cè)中心,如果可以任意訪問的話,那么其安全性太低。所以Spring Cloud中也有為Eureka Server提供安全認(rèn)證的方式??梢允褂胹pring-boot-starter-security組件來為Eureka Server增加安全認(rèn)證。
POM依賴:
<!-- spring boot security安全認(rèn)證啟動(dòng)器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
修改全局配置文件,在全局配置文件中,開啟基于http basic的安全認(rèn)證。
# eurekaserver1配置
spring.application.name=eureka-server
server.port=8761
eureka.instance.hostname=eurekaserver1
# 使用http basic安全認(rèn)證語法,在集群通信中增加認(rèn)證信息。 http://用戶名:密碼@地址:端口/eureka/
eureka.client.serviceUrl.defaultZone=http://test:123456@eurekaserver2:8761/eureka/
# 開啟基于http basic的安全認(rèn)證
security.basic.enabled=true
# 設(shè)置安全認(rèn)證用戶名
security.user.name=test
# 設(shè)置安全認(rèn)證密碼
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定理,指的是在一個(gè)分布式系統(tǒng)中,Consistency(數(shù)據(jù)一致性)、 Availability(服務(wù)可用性)、Partition tolerance(分區(qū)容錯(cuò)性),三者不可兼得。CAP由Eric Brewer在2000年P(guān)ODC會(huì)議上提出。該猜想在提出兩年后被證明成立,成為我們熟知的CAP定理。
?
Eureka和ZooKeeper的特性
?
六、Eureka Client
在Spring Cloud中,開發(fā)Eureka Client組件還是非常方便的。我們?cè)陂_發(fā)過程中,不需要像Dubbo那樣關(guān)注服務(wù)的角色。無論是Provider還是Consumer都是一個(gè)微服務(wù)客戶端,只是在編碼層面上,服務(wù)消費(fèi)者代碼比較麻煩。
1、Application Service服務(wù)提供者開發(fā)
POM依賴:
如果Eureka 開啟了security安全校驗(yàn)機(jī)制,那么Eureka Client在開發(fā)的時(shí)候,依賴的jar包需要額外依賴一個(gè)actuator插件。那么可以修改Eureka Client工程中的啟動(dòng)器,把spring-cloud-starter-eureka改為spring-cloud-starter-eureka-server。Eureka-server啟動(dòng)器中包含HTTP Basic安全認(rèn)證相關(guān)的jar包,實(shí)際上spring-cloud-starter-eureka-server啟動(dòng)器包含啟動(dòng)器spring-cloud-starter-eureka中的所有的包。
<!-- spring cloud Eureka Client 啟動(dòng)器,因?yàn)镋ureka Server開啟了安全校驗(yàn),
所有需要依賴更大范圍的jar包資源spring-cloud-starter-eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- actuator 組件是Spring Boot的監(jiān)控組件,actuator一旦應(yīng)用,在啟動(dòng)的時(shí)候,會(huì)發(fā)布一系列的URL服務(wù)。包含一個(gè)shutdown服務(wù),代表優(yōu)雅關(guān)閉
當(dāng)Spring Boot 應(yīng)用中的actuator組件接收到shutdown請(qǐng)求的時(shí)候,會(huì)觸發(fā)優(yōu)雅關(guān)閉。
如果當(dāng)前應(yīng)用中有Eureka Client的集成,則會(huì)觸發(fā)Eureka Client向Eureka Server發(fā)起一個(gè)shutdown優(yōu)雅停服的請(qǐng)求 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
?
全局配置:
在配置Eureka Server信息時(shí),建議將Eureka Server集群中的所有節(jié)點(diǎn)依次配置,Eureka Client在注冊(cè)服務(wù)的時(shí)候,會(huì)根據(jù)節(jié)點(diǎn)列表依次訪問Eureka Server集群節(jié)點(diǎn),只要注冊(cè)成功,后續(xù)Eureka Server節(jié)點(diǎn)不再訪問注冊(cè)。雖然Eureka Server集群各節(jié)點(diǎn)可以相互發(fā)現(xiàn)服務(wù),但是Eureka Server集群中每個(gè)節(jié)點(diǎn)對(duì)服務(wù)的管理都使用連帶責(zé)任,及從某Eureka Server節(jié)點(diǎn)發(fā)現(xiàn)服務(wù)A,如果這個(gè)Eureka Server節(jié)點(diǎn)宕機(jī),則A服務(wù)同時(shí)從服務(wù)列表中刪除。
# 定義SpringBoot應(yīng)用的名稱,建議必須提供。在SpringCloud中,對(duì)服務(wù)的最大粒度的管理是使用應(yīng)用命名的
# 最好是一個(gè)應(yīng)用一個(gè)名稱,在Consumer角色開發(fā)的時(shí)候,比較容易查找Provider
spring.application.name=eureka-application-service
server.port=8081
# 配置Eureka Server的地址信息,如果是Eureka Server集群,多個(gè)節(jié)點(diǎn)使用逗號(hào)','分割。
# 如果開啟了安全認(rèn)證,使用HTTP Bacic格式提供用戶名和密碼。
# 如果Eureka Server是一個(gè)集群,那么配置Eureka Server節(jié)點(diǎn)信息的時(shí)候,建議將所有的Eureka Server節(jié)點(diǎn)信息都配置上
# 實(shí)際上,只配置一個(gè)Eureka Server節(jié)點(diǎn)其實(shí)就可以了,但是,Eureka Server對(duì)服務(wù)的管理有連帶責(zé)任。如果只配置一個(gè)Eureka Server節(jié)點(diǎn),那么會(huì)導(dǎo)致級(jí)聯(lián)刪除的風(fēng)險(xiǎn),可能導(dǎo)致服務(wù)不可靠
# 如果配置了多個(gè)Eureka Server節(jié)點(diǎn),Eureka不會(huì)將當(dāng)期的服務(wù)同時(shí)注冊(cè)到所有Eureka Server節(jié)點(diǎn)上
# 從第一個(gè)配置的Eureka Server節(jié)點(diǎn)開始注冊(cè),如果注冊(cè)成功,后續(xù)的Eureka Server節(jié)點(diǎn)不再重復(fù)注冊(cè)
# 每30秒,Eureka Client發(fā)送一個(gè)心跳到Eureka Server上,如果心跳沒有反饋,則從已配置的Eureka Server節(jié)點(diǎn)列表的下一個(gè)服務(wù)節(jié)點(diǎn)繼續(xù)注冊(cè)。
# 這樣做可以保證服務(wù)的可靠性,降低服務(wù)連帶責(zé)任導(dǎo)致的服務(wù)不可靠。
# 如果多個(gè)Eureka Client需要注冊(cè),建議Eureka Server的服務(wù)列表順序是隨機(jī)排列的。
# 如:有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集群的特性。
# 因?yàn)镋ureka Server和Eureka Client對(duì)心跳的監(jiān)測都是3*間隔時(shí)間的,所以會(huì)有服務(wù)列表數(shù)據(jù)的不同步可能。
# 所以在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,優(yōu)雅停服功能,配置actuator的優(yōu)雅關(guān)閉
# actuator 組件監(jiān)聽shutdown請(qǐng)求地址的時(shí)候,要求請(qǐng)求的method必須是POST
# shutdown的請(qǐng)求地址是使用:@PostMapping或@RequestMapping(method=RequestMethod.POST)
endpoints.shutdown.enabled=true
# 禁用密碼驗(yàn)證
endpoints.shutdown.sensitive=false
建議:如果有多個(gè)服務(wù)功能需要注冊(cè),那么在設(shè)置Eureka Server信息的時(shí)候,推薦異序排列。如:現(xiàn)在有3個(gè)工程A、B、C需要注冊(cè)服務(wù)到Eureka Server集群中,集群節(jié)點(diǎn)有三個(gè),分別是e1、e2、e3,那么在工程中推薦配置為,A工程配置-e1,e2,e3,B工程配置e2,e3,e1,C工程配置e3,e1,e2。這樣可以更好的利用Eureka Server集群的特性。
啟動(dòng)類:
需要在啟動(dòng)類上新增注解@EnableEurekaClient,代表當(dāng)前應(yīng)用開啟Eureka客戶端,應(yīng)用啟動(dòng)后,會(huì)自動(dòng)將服務(wù)注冊(cè)到Eureka Server中。
/**
* Eureka Client啟動(dòng)類
* 是使用SpringBoot啟動(dòng)類實(shí)現(xiàn)啟動(dòng)的
* @EnableEurekaClient 注解,就是用于通知SpringBoot應(yīng)用,當(dāng)前應(yīng)用是一個(gè)Irk客戶端
* 需要做一下服務(wù)的注冊(cè)。
* 使用全局配置文件application.properties配置Eureka Server的相關(guān)信息
* 如:Eureka Server的地址,個(gè)數(shù),是否需要安全認(rèn)證等。
*/
@EnableEurekaClient
@SpringBootApplication
public class EurekaApplicationServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplicationServiceApplication.class, args);
}
}
對(duì)外接口:
Eureka Client的Application Server對(duì)外需要暴露接口方法,接口定義如Rest請(qǐng)求定義方式:
@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;
}
}
因此服務(wù)也可以作為url請(qǐng)求直接調(diào)用(Security Basic安全認(rèn)證導(dǎo)致返回結(jié)果為XML類型,而不是JSON類型):
2、Application Client服務(wù)消費(fèi)者開發(fā)
在Spring Cloud中,服務(wù)消費(fèi)方代碼的開發(fā)確實(shí)比較麻煩,并不像Dubbo那么直接注入服務(wù)接口代理對(duì)象,通過代理對(duì)象方法直接訪問遠(yuǎn)程服務(wù)。在Spring Cloud中,微服務(wù)的提供是通過REST風(fēng)格提供的,也就是服務(wù)的調(diào)用是基于HTTP協(xié)議的,所以在服務(wù)調(diào)用上比較麻煩,具體詳見案例代碼。
POM依賴:
同Application Service工程。
全局配置:
因?yàn)槎际潜镜貑?dòng),需要修改服務(wù)端口。推薦修改spring應(yīng)用命名。在Eureka Server中,對(duì)服務(wù)的管理是基于spring應(yīng)用名稱的,所以不同的服務(wù)推薦使用不同的應(yīng)用名稱。
spring.application.name=eureka-application-client
server.port=8080
# 點(diǎn)對(duì)點(diǎn)直連是不發(fā)現(xiàn)服務(wù),不是不注冊(cè)服務(wù)。
# 任何Eureka Client都必須注冊(cè)。如果沒有配置Eureka Server節(jié)點(diǎn)列表,則注冊(cè)失敗。Eureka client無法正常啟動(dòng)。
eureka.client.serviceUrl.defaultZone=http://eurekaserver1:111111@eurekaserver1:8761/eureka/,http://eurekaserver2:222222@eurekaserver2:8761/eureka/
# 設(shè)置負(fù)載均衡策略 eureka-application-service為調(diào)用的服務(wù)的名稱
# 沒有配置全部服務(wù)的負(fù)載均衡策略的方式。因?yàn)椴皇敲總€(gè)服務(wù)都可以使用相同負(fù)載均衡策略的。
# 如:搜索服務(wù)和注冊(cè)服務(wù)就不能使用相同的負(fù)載均衡策略。
eureka-application-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
# 關(guān)閉ribbon訪問注冊(cè)中心Eureka Server發(fā)現(xiàn)服務(wù)
ribbon.eureka.enabled=false
# 配置服務(wù)列表,其中eureka-application-service代表要訪問的服務(wù)的應(yīng)用名,如果有多個(gè)服務(wù)結(jié)點(diǎn)組成集群,多個(gè)節(jié)點(diǎn)的配置信息使用逗號(hào)','分隔。
# 配置服務(wù)列表,需要配置要調(diào)用的服務(wù)的名字和服務(wù)所在的位置。
# 服務(wù)的名字,就是Application Service中配置的spring.application.name。
# 服務(wù)的位置,就是服務(wù)的所在ip和端口。
# 如果服務(wù)位置有多個(gè),也就是服務(wù)集群,那么使用逗號(hào)','分割多個(gè)服務(wù)列表信息。
eureka-application-service.ribbon.listOfServers=localhost:8083
?
啟動(dòng)類:
同Applicaiton Service一致。
請(qǐng)求調(diào)用類:
/**
* 在這里開發(fā)Eureka Client中的Application Client角色。就是consumer服務(wù)的消費(fèi)者。
* 服務(wù)消費(fèi)者需要在注冊(cè)中心中發(fā)現(xiàn)服務(wù)列表的。且同時(shí)將自己注冊(cè)到注冊(cè)中心的服務(wù)列表中。(參考如下截圖)
*
* consumer在消費(fèi)provider的時(shí)候,是通過LoadBalancer來實(shí)現(xiàn)的。
* LoadBalancer簡介 : 是Eureka client內(nèi)置的一個(gè)負(fù)載均衡器。復(fù)雜在發(fā)現(xiàn)的服務(wù)列表中選擇服務(wù)應(yīng)用,獲取服務(wù)的IP和端口。
* 實(shí)現(xiàn)服務(wù)的遠(yuǎn)程調(diào)用。
*
* application client代碼開發(fā)相比較dubbo的consumer開發(fā)麻煩很多。
*
*/
@RestController
public class TestApplicationClientController {
/**
* ribbon負(fù)載均衡器,其中記錄了從Eureka Server中獲取的所有服務(wù)信息。
* 這些服務(wù)的信息是IP和端口等。應(yīng)用名稱,域名,主機(jī)名等信息。
*/
@Autowired
private LoadBalancerClient loadBalancerClient;
/**
* 通過HTTP協(xié)議,發(fā)起遠(yuǎn)程服務(wù)調(diào)用,實(shí)現(xiàn)一個(gè)遠(yuǎn)程的服務(wù)消費(fèi)。
* @return
*/
@GetMapping
public List<Map<String, Object>> test() {
// 通過spring應(yīng)用命名,獲取服務(wù)實(shí)例ServiceInstance對(duì)象
// ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口
/*
* 在Eureka中,對(duì)所有注冊(cè)到Eureka Server中的服務(wù)都稱為一個(gè)service instance服務(wù)實(shí)例。
* 一個(gè)服務(wù)實(shí)例,就是一個(gè)有效的,可用的,provider單體實(shí)例或集群實(shí)例。
* 每個(gè)service instance都和spring application name對(duì)應(yīng)。
* 可以通過spring application name查詢service instance
*/
ServiceInstance si =
this.loadBalancerClient.choose("eureka-application-service");
// 拼接訪問服務(wù)的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,用于快速發(fā)起REST請(qǐng)求的模板對(duì)象。
/*
* RestTemplate是SpringMVC提供的一個(gè)用于發(fā)起REST請(qǐng)求的模板對(duì)象。
* 基于HTTP協(xié)議發(fā)起請(qǐng)求的。
* 發(fā)起請(qǐng)求的方式是exchange。需要的參數(shù)是: URL, 請(qǐng)求方式, 請(qǐng)求頭, 響應(yīng)類型,【URL rest參數(shù)】。
*/
RestTemplate rt = new RestTemplate();
/*
* 創(chuàng)建一個(gè)響應(yīng)類型模板。
* 就是REST請(qǐng)求的響應(yīng)體中的數(shù)據(jù)類型。
* ParameterizedTypeReference - 代表REST請(qǐng)求的響應(yīng)體中的數(shù)據(jù)類型。
*/
ParameterizedTypeReference<List<Map<String, Object>>> type =
new ParameterizedTypeReference<List<Map<String, Object>>>() {
};
/*
* ResponseEntity:封裝了返回值信息,相當(dāng)于是HTTP Response中的響應(yīng)體。
* 發(fā)起REST請(qǐng)求。
*/
ResponseEntity<List<Map<String, Object>>> response =
rt.exchange(sb.toString(), HttpMethod.GET, null, type);
/*
* ResponseEntity.getBody() - 就是獲取響應(yīng)體中的java對(duì)象或返回?cái)?shù)據(jù)結(jié)果。
*/
List<Map<String, Object>> result = response.getBody();
return result;
}
}
LoadBanlancerClient中包含了所有的服務(wù)注冊(cè)信息,如下圖示例:
七、服務(wù)保護(hù)
1 服務(wù)保護(hù)模式
服務(wù)保護(hù)模式(自我保護(hù)模式):一般情況下,微服務(wù)在Eureka上注冊(cè)后,會(huì)每30秒發(fā)送心跳包,Eureka通過心跳來判斷服務(wù)時(shí)候健康,同時(shí)會(huì)定期刪除超過90秒沒有發(fā)送心跳服務(wù)。
導(dǎo)致Eureka Server接收不到心跳包的可能:一是微服務(wù)自身的原因,二是微服務(wù)與Eureka之間的網(wǎng)絡(luò)故障。通常微服務(wù)的自身的故障只會(huì)導(dǎo)致個(gè)別服務(wù)出現(xiàn)故障,一般不會(huì)出現(xiàn)大面積故障,而網(wǎng)絡(luò)故障通常會(huì)導(dǎo)致Eureka Server在短時(shí)間內(nèi)無法收到大批心跳。慮到這個(gè)區(qū)別,Eureka設(shè)置了一個(gè)閥值,當(dāng)判斷掛掉的服務(wù)的數(shù)量超過閥值時(shí),Eureka Server認(rèn)為很大程度上出現(xiàn)了網(wǎng)絡(luò)故障,將不再刪除心跳過期的服務(wù)。
那么這個(gè)閥值是多少呢?Eureka Server在運(yùn)行期間,會(huì)統(tǒng)計(jì)心跳失敗的比例在15分鐘內(nèi)是否低于85%,如果低于85%,Eureka Server則任務(wù)是網(wǎng)絡(luò)故障,不會(huì)刪除心跳過期服務(wù)。
這種服務(wù)保護(hù)算法叫做Eureka Server的服務(wù)保護(hù)模式。
這種不刪除的,90秒沒有心跳的服務(wù),稱為無效服務(wù),但是還是保存在服務(wù)列表中。如果Consumer到注冊(cè)中心發(fā)現(xiàn)服務(wù),則Eureka Server會(huì)將所有好的數(shù)據(jù)(有效服務(wù)數(shù)據(jù))和壞的數(shù)據(jù)(無效服務(wù)數(shù)據(jù))都返回給Consumer。
2 服務(wù)保護(hù)模式的存在必要性
因?yàn)橥瑫r(shí)保留"好數(shù)據(jù)"與"壞數(shù)據(jù)"總比丟掉任何數(shù)據(jù)要更好,當(dāng)網(wǎng)絡(luò)故障恢復(fù)后,Eureka Server會(huì)退出"自我保護(hù)模式"。
Eureka還有客戶端緩存功能(也就是微服務(wù)的緩存功能)。即便Eureka Server集群中所有節(jié)點(diǎn)都宕機(jī)失效,微服務(wù)的Provider和Consumer都能正常通信。
微服務(wù)的負(fù)載均衡策略會(huì)自動(dòng)剔除死亡的微服務(wù)節(jié)點(diǎn)(Robbin)。
只要Consumer不關(guān)閉,緩存始終有效,直到一個(gè)應(yīng)用下的所有Provider訪問都無效的時(shí)候,才會(huì)訪問Eureka Server重新獲取服務(wù)列表。
3 關(guān)閉服務(wù)保護(hù)模式
可以通過全局配置文件來關(guān)閉服務(wù)保護(hù)模式,商業(yè)項(xiàng)目中不推薦關(guān)閉服務(wù)保護(hù),因?yàn)榫W(wǎng)絡(luò)不可靠很容易造成網(wǎng)絡(luò)波動(dòng)、延遲、斷線的可能。如果關(guān)閉了服務(wù)保護(hù),可能導(dǎo)致大量的服務(wù)反復(fù)注冊(cè)、刪除、再注冊(cè)。導(dǎo)致效率降低。在商業(yè)項(xiàng)目中,服務(wù)的數(shù)量一般都是幾十個(gè),大型的商業(yè)項(xiàng)目中服務(wù)的數(shù)量可能上百、數(shù)百,甚至上千:
# 關(guān)閉自我保護(hù):true為開啟自我保護(hù),false為關(guān)閉自我保護(hù)
eureka.server.enableSelfPreservation=false
# 清理間隔(單位:毫秒,默認(rèn)是60*1000),當(dāng)服務(wù)心跳失效后多久,刪除服務(wù)。
eureka.server.eviction.interval-timer-in-ms=60000
4 優(yōu)雅關(guān)閉服務(wù)(優(yōu)雅停服)
在Spring Cloud中,可以通過HTTP請(qǐng)求的方式,通知Eureka Client優(yōu)雅停服,這個(gè)請(qǐng)求一旦發(fā)送到Eureka Client,那么Eureka Client會(huì)發(fā)送一個(gè)shutdown請(qǐng)求到Eureka Server,Eureka Server接收到這個(gè)shutdown請(qǐng)求后,會(huì)在服務(wù)列表中標(biāo)記這個(gè)服務(wù)的狀態(tài)為down,同時(shí)Eureka Client應(yīng)用自動(dòng)關(guān)閉。這個(gè)過程就是優(yōu)雅停服。
如果使用了優(yōu)雅停服,則不需要再關(guān)閉Eureka Server的服務(wù)保護(hù)模式。
POM依賴:
優(yōu)雅停服是通過Eureka Client發(fā)起的,所以需要在Eureka Client中增加新的依賴,這個(gè)依賴是autuator組件,添加下述依賴即可。
?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
修改全局配置文件:
Eureka Client默認(rèn)不開啟優(yōu)雅停服功能,需要在全局配置文件中新增如下內(nèi)容:
# 啟用shutdown,優(yōu)雅停服功能
endpoints.shutdown.enabled=true
# 禁用密碼驗(yàn)證
endpoints.shutdown.sensitive=false
發(fā)起shutdown請(qǐng)求:
必須通過POST請(qǐng)求向Eureka Client發(fā)起一個(gè)shutdown請(qǐng)求。請(qǐng)求路徑為:http://ip:port/shutdown??梢酝ㄟ^任意技術(shù)實(shí)現(xiàn),如:HTTPClient、form表單,AJAX等。
建議使用優(yōu)雅停服方式來關(guān)閉Application Service/Application Client服務(wù)。
今天就分享到這里了,喜歡的點(diǎn)贊關(guān)注收藏!文章來源:http://www.zghlxwxcb.cn/news/detail-772085.html
關(guān)注github:uqcharles.Github.io文章來源地址http://www.zghlxwxcb.cn/news/detail-772085.html
到了這里,關(guān)于SpringCloud之Eureka注冊(cè)中心原理及其搭建的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!