服務註冊中心是服務實現註冊化和管理化的核心組件,相似於目錄服務的做用,主要用來存儲服務信息,例如服務提供者url串、路由信息等。服務註冊中心是SOA架構中最基礎的設施之一。php
1.服務的註冊java
2 服務的發現mysql
1 Dubo的註冊中心Zookeeperlinux
2 SpringCloud的Eurekaweb
1.服務管理算法
2.服務的依賴關係管理spring
Eureka是Netflix開發的服務發現組件,自己是一個基於Rest的服務,SpringCloud將其集成在其子項目中,以實現SpringCloud的服務註冊與發現,同時還提供了負載均衡和註冊sql
Eureka Servershell
經過Register、Get、Renew等接口提供服務的註冊與發現。數據庫
Application Service(Service Provider)
服務提供方
把自身的服務實例註冊到Eureka Server中
Application Client(Service Consumer)
服務調用方
經過Eureka Server獲取服務列表,消費服務
<?xml version="1.0" encoding="UTF-8"?>
<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 https://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.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.luyi</groupId>
<artifactId>springcloud-eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-eureka-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<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>
複製代碼
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
複製代碼
spring.application.name=eureka-server
#修改服務器端口
server.port=8761
#是否將本身註冊到eureka-server中,默認爲true
eureka.client.registerWithEureka=false
#是否從Eureka-server中獲取服務信息,默認爲true
eureka.client.fetchRegistry=false
複製代碼
springcloud-eureka-server-ha
在搭建Eureka集羣時,須要添加多個配置文件,而且使用SpringBoot的多環境配置方式,集羣中須要多少節點就添加多少個配置文件
Eureka1
spring.application.name=eureka-server
#修改服務器端口
server.port=8761
#設置Eureka實例名稱,以配置文件的變量爲主
eureka.instance.hostname=eureka1
#設置服務註冊中心地址,指向另外一個註冊中心
eureka.client.serviceUrl.defaultZone=http://eureka2:8761/eureka/
複製代碼
Eureka2
spring.application.name=eureka-server
#修改服務器端口
server.port=8761
#設置Eureka實例名稱,以配置文件的變量爲主
eureka.instance.hostname=eureka2
#設置服務註冊中心地址,指向另外一個註冊中心
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/
複製代碼
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--定義日誌文件的存儲地址 勿在 LogBack 的配置中使用相對路徑-->
<property name="LOG_HOME" value="${catalina.base}/logs/" />
<!-- 控制檯輸出 -->
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日誌輸出編碼 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日誌消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</layout>
</appender>
<!-- 按照天天生成日誌文件 -->
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日誌文件輸出的文件名-->
<FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日誌消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</layout>
<!--日誌文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 日誌輸出級別 -->
<root level="DEBUG">
<appender-ref ref="Stdout" />
<appender-ref ref="RollingFile" />
</root>
<!--日誌異步到數據庫 -->
<!-- <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> 日誌異步到數據庫 <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> 鏈接池 <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <url>jdbc:mysql://127.0.0.1:3306/databaseName</url> <user>root</user> <password>root</password> </dataSource> </connectionSource> </appender> -->
</configuration>
複製代碼
部署環境:jdk1.8
將項目打包
將jar包上傳到/usr/local/eureka文件夾下
#!/bin/bash
cd `dirname $0`
CUR_SHELL_DIR=`pwd`
CUR_SHELL_NAME=`basename ${BASH_SOURCE}`
JAR_NAME="項目名稱"
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
複製代碼
添加權限
chmod -R 755 server.sh
vi /etc/hosts
192.168.234.130 eureka1
192.168.234.131 eureka2
複製代碼
./server.sh start #啓動
./server.sh stop #中止
複製代碼
springcloud-eureka-provider
<?xml version="1.0" encoding="UTF-8"?>
<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 https://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.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.luyi</groupId>
<artifactId>springcloud-eureka-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-eureka-provider</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
複製代碼
//表示Eureka的客戶端
@EnableEurekaClient
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
複製代碼
spring.application.name=eureka-provider
server.port=9090
#設置服務註冊中心地址,向全部註冊中心作註冊
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/,http://eureka2:8761/eureka/
複製代碼
C:\Windows\System32\drivers\etc
192.168.234.130 eureka1 192.168.234.131 eureka2
@RestController
public class UserController {
@RequestMapping("/user")
public List<User> getUsers(){
List<User> users = new ArrayList<>();
users.add(new User(1, "zhangsan", 20));
users.add(new User(2, "lisi", 22));
users.add(new User(3, "wangwu", 30));
return users;
}
}
複製代碼
/** * Author: LuYi * Date: 2019/11/6 12:30 * Description: 描述 */
public class User {
private Integer userid;
private String username;
private Integer userage;
public User() {
}
public User(Integer userid, String username, Integer userage) {
this.userid = userid;
this.username = username;
this.userage = userage;
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getUserage() {
return userage;
}
public void setUserage(Integer userage) {
this.userage = userage;
}
}
複製代碼
服務的消費者和生產者都須要在Eureka註冊中心註冊
spring.application.name=eureka-consumer
server.port=9091
#設置服務註冊中心地址,向全部註冊中心作註冊
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/,http://eureka2:8761/eureka/
複製代碼
@Service
public class UserService {
@Autowired
private LoadBalancerClient loadBalancerClient; //ribbon:負載均衡器
public List<User> getUsers(){
//選擇調用的服務的名稱
//ServiceInstance:封裝了服務的基本信息,如:ip、端口號
ServiceInstance si = loadBalancerClient.choose("eureka-provider");
//拼接訪問服務的url
StringBuffer sb = new StringBuffer();
//http://localhost:9090/user
sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user");
//SpringMVC RestTemplate
RestTemplate restTemplate = new RestTemplate();
ParameterizedTypeReference<List<User>> type = new ParameterizedTypeReference<List<User>>() {
};
//ResponseEntity:封裝了返回值信息
ResponseEntity<List<User>> entity = restTemplate.exchange(sb.toString(), HttpMethod.GET, null, type);
return entity.getBody();
}
}
複製代碼
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/consumer")
public List<User> getUsers(){
return userService.getUsers();
}
}
複製代碼
CAP原則又稱爲CAP定理,指的是在分佈式系統中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性),三者不可兼得,只能三選二。
CAP由Eric Brewer在2000年PODC會議上提出。該猜測在兩年後被證實成立,稱爲咱們熟悉的CAP定理
通常狀況下,微服務在Eureka註冊後,每隔30s會發送一次心跳包,同時會按期刪除90s沒有發送心跳包的服務。
微服務自身的緣由
微服務和Eureka之間網絡的緣由
若是是由於微服務故障,不會致使大批量出現沒法收不到心跳包的狀況,只會引起部分故障,而網絡故障則會致使大規模收不到心跳包。
考慮到這個區別,Eureka設置了一個閾值,若是在短期內大規模的收不到心跳包,就會判斷爲是網絡故障,這樣Eureka就不會刪除心跳過時的服務
閾值是多少呢
15分鐘內判斷是否低於85%
Eureka Server在運行期間,會判斷心跳失敗比例在15分鐘內是否達到85%
這種算法叫作Eureka Server的自我保護模式。
修改Eureka Server配置文件
#關閉自我保護:true爲開啓,false爲關閉
eureka.server.enable-self-preservation=false
#清理間隔(單位:毫秒,默認是60*1000)
eureka.server.eviction-interval-timer-in-ms=60000
複製代碼
#啓動shutdown
endpoints.shutdown.enabled=true
#禁用密碼驗證
endpoints.shutdown.sensitive=false
複製代碼
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 建立Httpclient對象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 建立uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 建立http GET請求
HttpGet httpGet = new HttpGet(uri);
// 執行請求
response = httpclient.execute(httpGet);
// 判斷返回狀態是否爲200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 建立Httpclient對象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 建立Http Post請求
HttpPost httpPost = new HttpPost(url);
// 建立參數列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模擬表單
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");
httpPost.setEntity(entity);
}
// 執行http請求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 建立Httpclient對象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 建立Http Post請求
HttpPost httpPost = new HttpPost(url);
// 建立請求內容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 執行http請求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultString;
}
public static void main(String[] args) {
String url ="http://127.0.0.1:9090/shutdown";
//該url必需要使用doPost方式來發送
HttpClientUtil.doPost(url);
}
}
複製代碼
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
複製代碼
#開啓 http basic 的安全認證
security.basic.enabled=true
security.user.name=user
security.user.password=123456
複製代碼
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka2:8761/eureka/
複製代碼
spring.application.name=eureka-provider
server.port=9090
#設置服務註冊中心地址,向全部註冊中心作註冊
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
#啓動shutdown
endpoints.shutdown.enabled=true
#禁用密碼驗證
endpoints.shutdown.sensitive=false
複製代碼