Spring Cloud Eureka是对Netflix Eureka的二次封装。
新建Maven 工程,由于Spring Cloud应用是基于Spring Boot构建的,所以先导入SpringBoot依赖:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.1.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
dependencies>
设置Spring Milestones仓库:
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
在**src/main下新建Spring boot启动类:
@SpringBootApplication
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class);
}
}
添加spring cloud依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwich.RC1version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
补充:
关于Spring Boot和Spring Cloud版本对应关系参照Spring Cloud官网:https://spring.io/projects/spring-cloud#learn
添加eureka-server依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
在启动类上添加**@EnableEurekaServer**注解:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApp {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApp.class);
}
}
完整依赖:
<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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>cloud01-eureka-serverartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.1.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwich.RC1version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
project>
启动项目:
如果有如下报错
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'eurekaRegistration' defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$EurekaClientConfiguration.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$EurekaClientConfiguration; factoryMethodName=eurekaRegistration; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$EurekaClientConfiguration.class]] for bean 'eurekaRegistration': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration; factoryMethodName=eurekaRegistration; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration.class]] bound.
检查依赖是否导错了:
是:
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
不是:
<artifactId>spring-cloud-netflix-eureka-serverartifactId>
如果有如下报错:
com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187) ~[jersey-apache-client4-1.19.1.jar:1.19.1]
.....省略.....
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) ~[na:1.8.0_181]
.....省略.....
2018-12-17 14:22:32.324 WARN 16608 --- [freshExecutor-0] c.n.d.s.t.d.RetryableEurekaHttpClient : Request execution failed with message: java.net.ConnectException: Connection refused: connect
2018-12-17 14:22:32.324 ERROR 16608 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_UNKNOWN/localhost:8088 - was unable to refresh its cache! status = Cannot execute request on any known server
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
.....省略.....
是因为eureka-server服务端同是也是一个客户端,也需要注册到注册中心去,这里没有指定注册中心的位置(ip、端口等),所以会如上所示的错误!
解决办法,在application.yml(application.properties)中配置:
# 开启调示
debug: true
# 设置注册中心端口
server:
port: 8088
eureka:
# 设置当前实例的信息,一个eureka-server或者client都是一个实例
instance:
# 设置主机名
hostname: localhost
# 注册中心eureka-server本身也是一个客户端 client,所以配置client连接server的属性
client:
service-url:
# 设置注册中心的地址,${}用来读取配置文件中的属性的值
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/
启动项目,访问http://localhost:8088,即可打开eureka server的管理界面:
UNKNOWN既是注册的eureka client名,也就是注册的项目的名字,可以通过spring:
来修改:
application.name=EUREKA_SERVER
# 开启调示
debug: true
# 设置注册中心端口
server:
port: 8088
eureka:
# 设置当前实例的信息,一个eureka-server或者client都是一个实例
instance:
# 设置主机名
hostname: localhost
# 注册中心eureka-server本身也是一个客户端 client,所以配置client连接server的属性
client:
service-url:
# 设置注册中心的地址,${}用来读取配置文件中的属性的值
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/
# ----------------------------------------------
# 指定实例名
spring:
application:
name: EUREKA_SERVER
重启项目,并访问即可看到更改。
当然,如果当前项目作为注册中心存在,可以不配置向注册中心注册自己,也可以解决上面的错误:
# 开启调示
debug: true
# 设置注册中心端口
server:
port: 8088
eureka:
# 设置当前实例的信息,一个eureka-server或者client都是一个实例
instance:
# 设置主机名
hostname: localhost
# 注册中心eureka-server本身也是一个客户端 client,所以配置client连接server的属性
client:
# 不向注册中心注册自己
register-with-eureka: false
# 不去发现服务(查询服务),注册中心提供服务注册,不需要发现服务
fetch-registry: false
# 指定实例名
spring:
application:
name: EUREKA_SERVER
同样的方式新建一个boot项目,在导入eureka-server依赖的地方改成如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
dependencies>
新建启动类,并在启动类上标记**@EnableDiscoveryClient**注解:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApp {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApp.class);
}
}
在application.properties(application.yml)中配置信息:
# 设置当前项目端口
server:
port: 8089
# 设置注册中心地址,当前client要往注册中心注册
eureka:
client:
service-url:
defaultZone: http://localhost:8088/eureka
# 设置当前项目的名字
spring:
application:
name: EUREKA_CLIENT
启动当前项目(确保eureka-server项目已启动),访问eureka-server地址:http://localhost:8088,可以看到当前项目已注册到注册中心了:
新建一个controller,注入DiscoveryClient(org.springframework.cloud.client.discovery.DiscoveryClient),从DiscoveryClient中获取服务信息:
@RestController
public class HelloController {
@Autowired
private DiscoveryClient client;
private final Logger log = LoggerFactory.getLogger(this.getClass());
@GetMapping("/hello")
public String hello() {
// 获取服务
List<ServiceInstance> eureka_client = client.getInstances("EUREKA_CLIENT");
String description = client.description();
log.info(description);
for (ServiceInstance si : eureka_client) {
// 获取每个服务对应的信息
String host = si.getHost();
int port = si.getPort();
String serviceId = si.getServiceId();
log.info("ServiceId是:" + serviceId + " -> " + host + port);
}
return "hello eureka";
}
}
访问/hello地址,查看控制台打印日志:
ServiceId是:EUREKA_CLIENT -> localhost8089
如果系统中只有一个注册中心,那么这个注册中心如果宕机了,可能会引起整个系统的瘫痪。系统的正常运行需要有高可用的注册中心作为支撑。
这里以maven多模块做项目演示:
新建一个maven工程作为父工程,修改packaging为pom(如果新建工程中有src/…,删除),并修改pom文件,如下所示:
<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.0modelVersion>
<groupId>com.cloudgroupId>
<artifactId>cloud01artifactId>
<version>1.0-SNAPSHOTversion>
<packaging>pompackaging>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.1.RELEASEversion>
parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwich.RC1version>
<scope>importscope>
<type>pomtype>
dependency>
dependencies>
dependencyManagement>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
project>
在父工程下新建两个模块,分别为cloud-eureka-server01、cloud-eureka-server02,每个工程都引入相同的依赖,并配置启动类,启动类上标注**@EnableEurekaServer**注解如下:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
对两个项目的application.properties(application.yml)分别配置:
# 这是cloud-eureka-server01的配置
# 指定项目端口(这个项目作为注册中心,即注册中心端口)
server:
port: 8091
# 指定当前client注册到的注册中心的地址
eureka:
client:
service-url:
defaultZone: http://192.168.1.35:8092/eureka/
# 设置项目名
spring:
application:
name: eureka_server_01
# 这是cloud-eureka-server02的配置
# 指定项目端口(这个项目作为注册中心,即注册中心端口)
server:
port: 8092
# 指定当前client注册到的注册中心的地址
eureka:
client:
service-url:
defaultZone: http://192.168.1.35:8091/eureka/
# 设置项目名
spring:
application:
name: eureka_server_02
新建一个maven项目cloud-eureka-client01,导入如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
新建启动类,并在启动类上标注@EnableDiscoveryClient,并配置application.xml(application.yml):
server:
port: 9091
eureka:
instance:
prefer-ip-address: true
ip-address: 192.168.1.35
client:
service-url:
defaultZone: http://192.168.35:8091/eureka/,http://192.168.1.35:8092/eureka/
spring:
application:
name: eureka_client01
分别创建四个工程,如下图所示:
其中server01,server02作为注册中心,client01,client02作为客户端。分别配置好各自的关系,为client01提供一个/hello请求:
@RestController
public class HelloController {
@GetMapping("/hello")
public String getHello() {
return "Hello World!";
}
}
在client02中调用这个服务:
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaClient02App {
@Bean
@LoadBalanced // 开启客户端负载均衡
public RestTemplate restTempalte() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(EurekaClient02App.class, args);
}
}
@RestController
public class HiController {
@Autowired
private RestTemplate restTemplate;
// 消费服务
@GetMapping("/consumer")
public String getHello() {
// 在这里调用client01中的服务
ResponseEntity<String> response = restTemplate.getForEntity("http://EUREKA-CLIENT01/hello", String.class);
return "CONSUMER: " + response.getBody();
}
}
服务注册时通过发送Rest请求进行的,当一个客户端发送Rest请求到注册中心后,这个Rest请求也会被转发其他注册中心,从而实现了注册中心数据同步。
# 不注册自己
eureka.client.register-with-eureka=false
服务是通过向注册中心发送心跳来告诉注册中心自己提供的服务是可用的。Renews(续存,更新)。对renew的配置有:
# 服务更新(renew)间隔时间(心跳发送时间)
eureka.instance.lease-renewal-interval-in-seconds=30
# 服务失效时间
eureka.instance.lease-expiration-duration-in-seconds=90
注册中心会通过定时任务每隔60s清楚失效的服务。
服务消费者会每隔30s发送一个Rest请求到注册中心区获取服务清单,服务清单会被缓存直到下一次请求。
# 配置是否能获取服务注册信息,如果配置为false,则不能从注册中心获取
eureka.client.fetch-registry=true
# 配置去注册中心获取服务清单的时间间隔
eureka.client.registry-retch-inverval-seconds=30
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可能错误地声称实例已经启动,而实际上并没有。续费低于阈值,因此实例不会为了安全而过期。
出现上述情况,是由于Eureka Server(注册中心)每隔15分钟,会统计一次接收到的心跳,如果用实例的心跳没有被接收到,并且没有被接收到心跳的实例站总实例的85%及以上,那么 会触发注册中心的保护机制,它会尽可能的保护这个实例不会过期、不被剔除,而保留所有的节点。
# 关闭注册中心保护机制
eureka.server.enable-self-preservation=false
如上图所示,服务的命名是localhost:eureka-client:8090,即:
eureka.instance.instanceId=${spring.cloud.client.hostname}:${spring.application.name}:${server.port}
以上是默认命名规则。通过下面的方式修改命名规则:
eureka:
client:
service-url:
defaultZone: http://localhost:8070/eureka/
instance:
# 这里与修改命名无关,这里配置的是访问时是通过ip(为true时)访问还是hostname访问
prefer-ip-address: true
# 指定ip
ip-address: 192.168.1.10
# 修改命名规则为ip:端口
instance-id: ${
eureka.instance.ip-address}:${
server.port}