前言
服務治理
服務治理是微服務中最為核心和基礎的模塊,主要實現各個微服務的自動化注冊和發現;為什么使用服務治理:隨着業務的發展,系統功能越來越復雜,相應的微服務也不斷增加,那么多的微服務應用當修改服務命名等,如果通過手工維護方式,容易出現命名沖突等問題;
服務注冊: 在服務治理框架中,通過創建一個注冊中心,每個服務向注冊中心注冊自己的服務,將端口號,版本號,通訊協議等信息上報注冊中心,注冊中心按照服務名進行分類,當注冊上來后,注冊中心會根據心跳方式去檢測上報的服務是否存活,如不存活就賜予飛機票;
服務發現
在服務治理框架中,服務調用不是指定具體實例來調用,而是通過請求服務名來調用;服務調用方調用時不知具體服務在何方,而是通過注冊中心獲取服務清單,當服務調用方發起調用時通過某種輪訓機制取出一個服務去掉用;
高可用的注冊中心
當注冊中心發生故障時候會導致服務群癱瘓,所以需要高可用的注冊中心,eureka server支持高可用的配置,在eureka服務治理中,所有節點既是服務提供方也是服務消費方,注冊中心也一樣;eureka server的高可用實際上將自己當做服務向其他注冊中心注冊,多個服務相互注冊,這樣就形成了一組高可用的注冊中心,注冊中心相互注冊獲取清單,如果有一個掛了,也不會影響服務群;
高可用比率
通過時間計算;比如:保證一年的可用性為99.99%;
可用時間就是:365 * 24 * 3600 * 99.99%
不可用時間就是:365 * 24 * 3600 * 0.01% = 3153.6s
單台機器不可用比率:1%
兩台機器不可用比率:1% * 1%
N 機器不可用比率:1% ^ n
可靠性
一次調用a->b->c 99%->99%->99%=97%
a->b->c->d 99%->99%->99%->99%=96%
總結
增加機器可以提高可用性,增加服務調用會降低可靠性,同時降低了可用性;
spring-cloud-eureka-server
失效剔除
eureka.server.eviction-interval-timer-in-ms: 清理無效節點的時間間隔;
服務如果正常下線,會告知eureka server,eureka server會剔除當前下線的服務,但是如果異常下線,將不會告知eureka server,這時eureka server會啟動一個定時任務,默認每個一段時間(60s) 將當前清單中超過一段時間(90s)的沒有續約服務剔除
自我保護
eureka.server.enable-self-preservation:
eureka server 中會出現一個問題,如圖
該警告觸發Eureka Server 自我保護機制,服務注冊到eureka server都會維護一個心跳,告訴eureka server我還活着,eureka server 在運行期間,會統計心跳失敗的比例,15分鍾之內低於85%;他會保留當前的實例注冊信息,讓他們不過期,但是調用服務時候會出現調用失敗情況,這時候就要有容錯機制,請求重試或者斷路器等;這個系統默認開啟自我保護機制;官方解釋https://github.com/Netflix/eureka/wiki/Understanding-Eureka-Peer-to-Peer-Communication
更多的eureka server配置項查看EurekaServerConfigBean
訪問start.spring.io, 引入web,actuator,eureka-server
主程序入口
1 package cn.cold.springcloudeurekaserver.springcloudeurekaserverdemo; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 7 @SpringBootApplication 8 @EnableEurekaServer 9 public class SpringCloudEurekaServerDemoApplication { 10 11 public static void main(String[] args) { 12 SpringApplication.run(SpringCloudEurekaServerDemoApplication.class, args); 13 } 14 }
properties文件
application-peer1.properties
1 spring.application.name=spring-cloud-eureka-server 2 server.port=9090 3 #是否檢索服務 4 eureka.client.fetch-registry=false 5 #是否注冊到eureka,這里如果配置相互依賴那么必須要設置為true 6 eureka.client.register-with-eureka=true 7 #服務注冊中心的配置內容,指定服務注冊中心的位置 8 eureka.client.service-url.defaultZone=http://localhost:9091/eureka/
application-peer2.properties
1 spring.application.name=spring-cloud-eureka-server 2 server.port=9091 3 eureka.client.fetch-registry=false 4 eureka.client.register-with-eureka=true 5 eureka.client.service-url.defaultZone=http://localhost:9090/eureka/
以上兩個就可以構成高可用注冊中心,使用命令行進行打包,執行
java -jar spring-cloud-eureka-server-demo-0.0.1-SNAPSHOT.jar --management.endpoints.web.exposure.include=* --eureka.server.enable-self-preservation=false --spring.profiles.active=peer1
java -jar spring-cloud-eureka-server-demo-0.0.1-SNAPSHOT.jar --management.endpoints.web.exposure.include=* --eureka.server.enable-self-preservation=false --spring.profiles.active=peer2
分別啟動兩個注冊中心,讓他們相互注冊如圖:


spring-cloud-eureka-client
start.spring.io,引入web,actuator,eureka discovery
這里我們將刪除src目錄,然后添加pom文件中
<packaging>pom</packaging>
然后在當前目錄添加maven子項目,如圖
創建子項目后,在父項目的pom一定要有
<modules> <module>user-api</module> <module>user-consumer</module> <module>user-service-provider</module> </modules>
目錄結構,如圖



user-api
User.java
1 package cn.cold.user.api.domain; 2 3 /* 4 * @create 2019-05-07 19:46 5 * @Author 江湖人稱洗發水 6 * @Description //TODO 7 **/ 8 public class User { 9 private int id; 10 private String name; 11 12 public int getId() { 13 return id; 14 } 15 16 public void setId(int id) { 17 this.id = id; 18 } 19 20 public String getName() { 21 return name; 22 } 23 24 public void setName(String name) { 25 this.name = name; 26 } 27 28 @Override 29 public String toString() { 30 return "User{" + 31 "id=" + id + 32 ", name='" + name + '\'' + 33 '}'; 34 } 35 }
UserService.java
1 package cn.cold.user.api.service; 2 3 import cn.cold.user.api.domain.User; 4 5 import java.util.List; 6 7 /** 8 * @author mengll 9 * @date 2019/5/7 19:47 10 */ 11 public interface UserService { 12 13 boolean save(User user); 14 15 List<User> findAll(); 16 }
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.1.4.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>cn.cold</groupId> 12 <artifactId>user-api</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>user-api</name> 15 <description>Demo project for Spring Boot</description> 16 17 </project>
user-service-consumer
UserServiceProxy.java
1 package cn.cold.user.consumer.service; 2 3 import cn.cold.user.api.domain.User; 4 import cn.cold.user.api.service.UserService; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.stereotype.Service; 7 import org.springframework.web.client.RestTemplate; 8 9 import java.util.List; 10 11 /* 12 * @create 2019-05-07 19:49 13 * @Author 江湖人稱洗發水 14 * @Description //TODO 15 **/ 16 @Service 17 public class UserServiceProxy implements UserService { 18 19 private static final String PROVIDER_SERVER_URL_PREFIX = "http://user-service-provider"; 20 21 @Autowired 22 private RestTemplate restTemplate; 23 24 @Override 25 public boolean save(User user) { 26 //端口會自動加上 27 return restTemplate.postForEntity(PROVIDER_SERVER_URL_PREFIX + "/user/save", user, User.class) != null; 28 } 29 30 @Override 31 public List<User> findAll() { 32 return restTemplate.getForObject(PROVIDER_SERVER_URL_PREFIX + "/user/list", List.class); 33 } 34 }
UserRestApiController.java
1 package cn.cold.user.consumer.web.controller; 2 3 import cn.cold.user.api.domain.User; 4 import cn.cold.user.api.service.UserService; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.cloud.client.discovery.DiscoveryClient; 7 import org.springframework.web.bind.annotation.GetMapping; 8 import org.springframework.web.bind.annotation.PostMapping; 9 import org.springframework.web.bind.annotation.RequestParam; 10 import org.springframework.web.bind.annotation.RestController; 11 12 import java.util.List; 13 14 /* 15 * @create 2019-05-07 19:50 16 * @Author 江湖人稱洗發水 17 * @Description //TODO 18 **/ 19 @RestController 20 public class UserRestApiController { 21 22 @Autowired 23 private UserService userService; 24 @Autowired 25 private DiscoveryClient discoveryClient; 26 27 28 @PostMapping("/save") 29 public User save(@RequestParam String name) { 30 User user = new User(); 31 user.setName(name); 32 if (userService.save(user)) { 33 return user; 34 } else { 35 return null; 36 } 37 } 38 39 @GetMapping("/list") 40 public List<User> list(){ 41 System.out.println(discoveryClient.description()); 42 System.out.println("11111111111111"); 43 return userService.findAll(); 44 } 45 }
主程序
1 package cn.cold.user.consumer; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 import org.springframework.cloud.client.loadbalancer.LoadBalanced; 7 import org.springframework.context.annotation.Bean; 8 import org.springframework.web.client.RestTemplate; 9 10 @SpringBootApplication 11 @EnableDiscoveryClient 12 public class UserConsumerApplication { 13 14 public static void main(String[] args) { 15 SpringApplication.run(UserConsumerApplication.class, args); 16 } 17 18 @Bean 19 @LoadBalanced //負載均衡 20 public RestTemplate restTemplate(){ 21 return new RestTemplate(); 22 } 23 }
application.properties
1 spring.application.name=user-service-consumer 2 ## 服務消費方端口 3 server.port=8080 4 ## Eureka Server 服務 URL,用於客戶端注冊 5 eureka.client.serviceUrl.defaultZone=\ 6 http://localhost:9090/eureka,http://localhost:9091/eureka 7 ## Management 安全失效 8 management.endpoints.web.exposure.include=*
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>cn.cold.springcloudeurekaclient</groupId> 7 <artifactId>spring-cloud-eureka-client-demo</artifactId> 8 <version>0.0.1-SNAPSHOT</version> 9 </parent> 10 <groupId>cn.cold</groupId> 11 <artifactId>user-consumer</artifactId> 12 <version>0.0.1-SNAPSHOT</version> 13 <name>user-consumer</name> 14 <description>Demo project for Spring Boot</description> 15 16 <dependencies> 17 <dependency> 18 <groupId>cn.cold</groupId> 19 <artifactId>user-api</artifactId> 20 <version>${project.version}</version> 21 </dependency> 22 </dependencies> 23 24 </project>
user-service-provider
UserRepository.java
1 package cn.cold.user.service.provider.repository; 2 3 import cn.cold.user.api.domain.User; 4 import org.springframework.stereotype.Repository; 5 6 import java.util.ArrayList; 7 import java.util.Collection; 8 import java.util.List; 9 import java.util.concurrent.ConcurrentHashMap; 10 import java.util.concurrent.ConcurrentMap; 11 import java.util.concurrent.atomic.AtomicInteger; 12 import java.util.concurrent.atomic.AtomicLong; 13 14 /* 15 * @create 2019-05-07 20:17 16 * @Author 江湖人稱洗發水 17 * @Description //TODO 18 **/ 19 @Repository 20 public class UserRepository { 21 22 private List<User> repository = new ArrayList<>(); 23 24 private static final AtomicInteger idGenerator = 25 new AtomicInteger(0); 26 27 public List<User> findAll() { 28 return repository; 29 } 30 31 public boolean save(User user) { 32 Integer id = idGenerator.incrementAndGet(); 33 user.setId(id); 34 return repository.add(user); 35 } 36 }
UserServiceImpl.java
1 package cn.cold.user.service.provider.service; 2 3 import cn.cold.user.api.domain.User; 4 import cn.cold.user.api.service.UserService; 5 import cn.cold.user.service.provider.repository.UserRepository; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.stereotype.Service; 8 9 import java.util.List; 10 11 /* 12 * @create 2019-05-07 20:16 13 * @Author 江湖人稱洗發水 14 * @Description //TODO 15 **/ 16 @Service 17 public class UserServiceImpl implements UserService { 18 19 @Autowired 20 private UserRepository userRepository; 21 22 @Override 23 public boolean save(User user) { 24 return userRepository.save(user); 25 } 26 27 @Override 28 public List<User> findAll() { 29 return userRepository.findAll(); 30 } 31 }
UserServiceProviderRestApiController.java
1 package cn.cold.user.service.provider.web.controller; 2 3 import cn.cold.user.api.domain.User; 4 import cn.cold.user.api.service.UserService; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.web.bind.annotation.GetMapping; 7 import org.springframework.web.bind.annotation.PostMapping; 8 import org.springframework.web.bind.annotation.RequestBody; 9 import org.springframework.web.bind.annotation.RestController; 10 11 import java.util.List; 12 13 /* 14 * @create 2019-05-07 20:14 15 * @Author 江湖人稱洗發水 16 * @Description //TODO 17 **/ 18 @RestController 19 public class UserServiceProviderRestApiController { 20 @Autowired 21 private UserService userService; 22 23 @PostMapping("/user/save") 24 public User saveUser(@RequestBody User user) { 25 if (userService.save(user)) { 26 System.out.println("UserService 服務方:保存用戶成功!" + user); 27 return user; 28 } else { 29 return null; 30 } 31 } 32 33 @GetMapping("/user/list") 34 public List<User> list() { 35 return userService.findAll(); 36 } 37 }
主程序
1 package cn.cold.user.service.provider; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 7 @SpringBootApplication 8 @EnableDiscoveryClient 9 public class UserServiceProviderApplication { 10 11 public static void main(String[] args) { 12 SpringApplication.run(UserServiceProviderApplication.class, args); 13 } 14 15 }
application.properties
更多的eureka client配置項可以查看EurekaInstanceConfigBean和EurekaClientConfigBean;
org.springframework.cloud.spring-cloud-netflix-eureka-client.2.1.1.RELEASE.spring-cloud-netflix-eureka-client-2.1.1.RELEASE.jar!\META-INF\spring-configuration-metadata.json;這個json也有比較詳細描述
其他配置可在spring-configuration-metadata.json找到這里就不一一舉例了;
1 spring.application.name=user-service-provider 2 ## 服務消費方端口 3 server.port=7071 4 ## Eureka Server 服務 URL,用於客戶端注冊 5 eureka.client.serviceUrl.defaultZone=\ 6 http://localhost:9090/eureka,http://localhost:9091/eureka 7 ## Management 安全失效 8 management.endpoints.web.exposure.include=* 9 # 心跳時間,即服務續約間隔時間(缺省為30s) 10 eureka.instance.lease-renewal-interval-in-seconds=5 11 # 即服務續約到期時間(缺省為90s) 12 eureka.instance.lease-expiration-duration-in-seconds=15
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>cn.cold.springcloudeurekaclient</groupId> 7 <artifactId>spring-cloud-eureka-client-demo</artifactId> 8 <version>0.0.1-SNAPSHOT</version> 9 </parent> 10 <groupId>cn.cold</groupId> 11 <artifactId>user-service-provider</artifactId> 12 <version>0.0.1-SNAPSHOT</version> 13 <name>user-service-provider</name> 14 <description>Demo project for Spring Boot</description> 15 <dependencies> 16 <dependency> 17 <groupId>cn.cold</groupId> 18 <artifactId>user-api</artifactId> 19 <version>0.0.1-SNAPSHOT</version> 20 </dependency> 21 </dependencies> 22 23 </project>
以上就是eureka client的代碼;這里啟動一個consumer,然后啟動兩個provider, 這里consumer和一個provider用idea,另外一個provider用命令行啟動
java -jar user-service-provider-0.0.1-SNAPSHOT.jar --server.port=7070
注冊好了如圖:

測試


這里再次訪問http://localhost:8080/list ,一會有值,一會沒值,這里因為是兩個服務,沒有做數據同步,所以出現該情況,但是也證明了@LoadBalanced確實負載均衡了;
由於篇幅原因,配置的就到此為止,后期會補一些Netflix Ribbon和RestTemplate,還有一些eureka 分析
