服务提供者
服务注册:
服务提供者在启动的时候通过发送Rest请求的方式将自己注册到Eureka Server上,同时带上了自身服务的一些元数据信息。Eureka Server在收到这个请求后,将元数据信息存储在一个双层结构Map中,第一层的key是服务名,第二层的key是具体服务的实例名。
服务同步:
两个服务提供者分别注册到两个不同的服务注册中心上,因为服务中心之间是相互注册为服务的,所以当服务提供者发送注册请求到一个服务注册中心时,它会将该请求转发给集群中的其他注册中心,从而实现服务注册中心之间的服务同步,通过服务同步,两个服务提供者的服务信息就可以通过这两台服务注册中心的任意一台获取到。
服务续约:
服务提供者在注册完后,会定期向注册中心发送一个“心跳"来告诉Eureka Server自己还存活着,以防被Eureka Servertichu踢出服务列表。
eureka.instance.lease-renewal-interval-in-seconds= 30 这个参数是定义服务续约的调用间隔时间,默认为30秒。
eureka.instance.lease-expiration-duration-in-seconds= 90 这个参数是定义服务失效时间,默认为90秒。
服务消费者
获取服务:
将自身注册为服务消费客户端。当我们启动服务消费者的时候,他会发送一个REST请求给服务注册中心,来获取上面注册的服务清单。为了性能考虑,Eureka Server会维护一份只读的服务清单来返回给客户端,同时该缓存清单会每隔30s更新一次,
获取服务时服务消费者的基础,必须确保eureka.client.fetch-registry=true参数没有被修改为false,该值默认为true。若希望修改缓存清单的更新时间,可以通过eureka.client.registry-fetch-interval-seconds=30参数进行修改。
获取服务:
发送REST请求给服务注册中心,服务注册中心会返回给服务消费者一个只读的服务清单。该缓存清单默认是30秒更新一次。
eureka.client.registry-fetch-interval-seconds= 30 这个参数用来修改缓存清单更新的时间间隔,时间单位为秒。
服务调用:
服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据信息,服务消费者可以根据需要调用哪个实例。在Ribbon中默认采用轮询的方式进行调用,从而实现客户端的负载均衡。
服务注册中心
失效剔除:
当某个服务提供者出现网络故障、内存溢出等各种原因而不能正常工作,服务中心并未受到”服务下线“的请求。为了能及时将这些无法提供服务的实例剔除,Eureka Server在启动的时候会创建一个定时任务,默认是60秒一次,将没有续约也就是没有发送”心跳“的服务剔除出去。
自我保护:
我们在本地调试Eureka程序的时候,基本上都会碰到一个红色的警告信息:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
这是Eureka Server启动了自我保护机制,自我保护机制的工作机制是如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,在开发环境因为不会有网络故障等原因,所以很容易出现这个警告。启动自我保护后此时会出现以下几种情况:
1、Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
2、Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
3、当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像Zookeeper那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。
eureka.server.enable-self-preservation= false 可以通过这个配置设置为false来关闭自我保护机制,默认识开启的,且建议在生产环境开启自我保护
一、高可用注册中心代码
注册中心1:
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zxy</groupId> <artifactId>eureka-service-center1</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka-service</name> <description>Forward project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置文件
1 #定义注册中心端口号为8888 2 server: 3 port: 8888 4 spring: 5 application: 6 name: server-center1 7 eureka: 8 instance: 9 hostname: center1 10 client: 11 service-url: 12 defaultZone: http://127.0.0.1:9999/eureka/ 13 # 注册中心没必要将自己注册给自己 14 register-with-eureka: false 15 # 注册中心不需要去检索服务,调用服务,故而不需要获取注册服务清单 16 fetch-registry: false 17 server: 18 peer-node-read-timeout-ms: 12000 19 enable-self-preservation: false
启动
1 package com.zxy.forward; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 @EnableEurekaServer 7 @SpringBootApplication 8 public class Center1Application { 9 10 public static void main(String[] args) { 11 SpringApplication.run(Center1Application.class, args); 12 } 13 }
注册中心2:
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zxy</groupId> <artifactId>eureka-client-center2</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka-client-center2</name> <description>Forward project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置文件
1 server: 2 port: 9999 3 spring: 4 application: 5 name: server-center2 6 eureka: 7 instance: 8 appname: center2 9 client: 10 service-url: 11 defaultZone: http://127.0.0.1:8888/eureka/ 12 # 注册中心没必要将自己注册给自己 13 register-with-eureka: false 14 # 注册中心不需要去检索服务,调用服务,故而不需要获取注册服务清单 15 fetch-registry: false 16 server: 17 # 设置读取超时时间 18 peer-node-read-timeout-ms: 12000 19 # 关闭自我保护 20 enable-self-preservation: false
启动
1 package com.zxy.demo; 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.netflix.eureka.server.EnableEurekaServer; 7 import org.springframework.scheduling.annotation.EnableScheduling; 8 9 @SpringBootApplication 10 @EnableEurekaServer 11 public class Center2Application { 12 13 public static void main(String[] args) { 14 SpringApplication.run(Center2Application.class, args); 15 } 16 }
二、服务提供者代码
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zxy</groupId> <artifactId>eureka-client-provider1</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka-client</name> <description>Forward project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>net.logstash.logback</groupId> <artifactId>logstash-logback-encoder</artifactId> <version>4.11</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置
server: port: 1001 spring: application: name: provider eureka: client: service-url: defaultZone: http://127.0.0.1:8888/eureka/ # 服务提供者如果也需要作为消费者则可以获取注册清单,如果单纯作为服务提供者则不需要获取服务清单 fetch-registry: false # 服务提供者需要提供服务,所以需要将自己注册在注册中心上 register-with-eureka: false # 服务提供者需要把下面的实例名关闭,否则在按照消费端的代码调用是会找不到主机名(启动单实例提供者把下面注释掉) # instance: # hostname: provider1
启动类
1 package com.zxy.demo; 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 ProviderApplication { 10 11 public static void main(String[] args) { 12 SpringApplication.run(ProviderApplication.class, args); 13 } 14 }
接口类
1 package com.zxy.demo.proapi; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RestController; 7 8 @RestController 9 public class HelloWorldController { 10 private Logger logger = LoggerFactory.getLogger(this.getClass()); 11 12 @RequestMapping(value="/welcome") 13 public String getWelcome() { 14 String str = "hello,welcome the world!!!"; 15 logger.info(str); 16 System.out.println(str); 17 return str; 18 } 19 20 }
三、消费者代码
消费者添加一个负载均衡的模板和依赖
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zxy</groupId> <artifactId>eureka-client-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eureka-client-consumer</name> <description>Forward project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置
1 server: 2 port: 2001 3 spring: 4 application: 5 name: consumer 6 eureka: 7 # instance: 8 # hostname: consumer 9 client: 10 service-url: 11 defaultZone: http://127.0.0.1:8888/eureka 12 # 如果需要提供接口共别人调用也可以将其注册在注册中心 13 # register-with-eureka: false 14 #获取服务是服务消费者的基础,故而下面的设置必须为true,默认是true 15 # fetch-registry: false
启动
1 package com.zxy.demo; 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 ConsumerApplication { 13 14 @Bean 15 @LoadBalanced 16 RestTemplate restTemplate() { 17 return new RestTemplate(); 18 } 19 public static void main(String[] args) { 20 SpringApplication.run(ConsumerApplication.class, args); 21 } 22 23 }
接口的类
1 package com.zxy.demo.consumer; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 import org.springframework.web.bind.annotation.RestController; 7 import org.springframework.web.client.RestTemplate; 8 9 @RestController 10 public class ConsumerController { 11 @Autowired 12 private RestTemplate template; 13 @RequestMapping(value="/msg0",method=RequestMethod.GET) 14 public String getStr() { 15 System.out.println("I am consumer msg0!"); 16 return template.getForEntity("http://provider/welcome", String.class).getBody(); 17 } 18 19 @RequestMapping(value="/msg1",method=RequestMethod.GET) 20 public String msg() { 21 RestTemplate t = new RestTemplate(); 22 System.out.println("I am consumer msg1!"); 23 return t.getForEntity("http://127.0.0.1:1001/welcome", String.class).getBody(); 24 } 25 }
结果