前言
服务治理
服务治理是微服务中最为核心和基础的模块,主要实现各个微服务的自动化注册和发现;为什么使用服务治理:随着业务的发展,系统功能越来越复杂,相应的微服务也不断增加,那么多的微服务应用当修改服务命名等,如果通过手工维护方式,容易出现命名冲突等问题;
服务注册: 在服务治理框架中,通过创建一个注册中心,每个服务向注册中心注册自己的服务,将端口号,版本号,通讯协议等信息上报注册中心,注册中心按照服务名进行分类,当注册上来后,注册中心会根据心跳方式去检测上报的服务是否存活,如不存活就赐予飞机票;
服务发现
在服务治理框架中,服务调用不是指定具体实例来调用,而是通过请求服务名来调用;服务调用方调用时不知具体服务在何方,而是通过注册中心获取服务清单,当服务调用方发起调用时通过某种轮训机制取出一个服务去掉用;
高可用的注册中心
当注册中心发生故障时候会导致服务群瘫痪,所以需要高可用的注册中心,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 分析