服務註冊中心是服務實現註冊化和管理化的核心組件,相似於目錄服務的做用,主要用來存儲服務信息,例如服務提供者url串、路由信息等。服務註冊中心是SOA架構中最基礎的設施之一。java
1.服務的註冊mysql
2 服務的發現linux
1 Dubo的註冊中心Zookeeperweb
2 SpringCloud的Eureka算法
1.服務管理spring
2.服務的依賴關係管理sql
Eureka是Netflix開發的服務發現組件,自己是一個基於Rest的服務,SpringCloud將其集成在其子項目中,以實現SpringCloud的服務註冊與發現,同時還提供了負載均衡和註冊shell
經過Register、Get、Renew等接口提供服務的註冊與發現。數據庫
服務提供方apache
把自身的服務實例註冊到Eureka Server中
服務調用方
經過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
#!/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:WindowsSystem32driversetc
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就不會刪除心跳過時的服務
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