Spring Cloud Eureka 是Spring Cloud Netflix 微服务套件中的一部分,它基于Netflix Eureka 做了二次封装,主要负责完成微服务架构中的服务治理功能。Spring Cloud通过为Eureka增加了Spring Bot风格的自动化配置。服务治理是微服务架构中最为核心和基础的模块,主要用来实现微服务实例自动化注册与发现。
- 注册服务:就是将提供某个服务的模块信息(通常是这个服务的ip和端口)注册到1个公共的组件上
- 服务发现:就是新注册的这个服务模块能够及时的被其他调用者发现。不管是服务新增和服务删减都能实现自动发现
1.Eureka 示例
Eureka服务器端,我们称之为注册中心,Eureka支持以集群模式部署,各个微服务启动时,会通过 Eureka Client 向 Eureka Server 注册自己,Eureka Server 会存储该服务的信息也就是说,每个微服务的客户端和服务端,都会注册到 Eureka Server
Eureka客户端:主要处理服务的注册与发现。客户端服务通过注解和参数配置的方式嵌入在客户端应用程序的代码中。运行时,Eureka 客户端向注册中心注册自身提供的服务并周期性的发送心跳来更新它的服务租约。同时,它也能从服务端查询当前注册的服务信息并缓存到本地并周期的刷新服务状态
项目结构说明
项目模块说明
功能说明 | 模块名 | 端口 |
注册中心 | ereka-peer | 1000 |
注册服务 | ms-customer | 8001 |
消费服务 | ms-ribbon-consumer | 8002 |
eureka-peer 等Spring Boot应用在项目中是以模块组成springcloud项目下的pom.xm参考如下
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<groupId>springcloud</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>eureka-peer</module>
<module>ms-customer</module>
<module>ms-ribbon-consumer</module>
</modules>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.1 服务注册中心
Eureka服务器端,我们称之为注册中心,Eureka支持以集群模式部署,各个微服务启动时,会通过 Eureka Client 向 Eureka Server 注册自己,Eureka Server 会存储该服务的信息
1.1.1 搭建注册中心
创建一个基础Spring Boot工程, 命名为eureka-peer, 修改pom.xml 文件,添加依赖。作为Eureka服务器端需要添加引用spring-cloud-starter-eureka-server
spring-cloud-starter-eureka-server引入了spring-cloud-netflix-eureka-server包,此包自动引入eureka-clent, 可见我们在POM里引入spring-cloud-starter-eureka-server后项目即可以当做注册中心,其也同时是客户端,可以注册自己
参考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>
<parent>
<groupId>springcloud</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>eureka-peer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 服务注册中心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
<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>
</project>
修改EurekaPeer1Application 启动信息,添加@EnableEurekaServer注解,该注解的作用为 激活Eureka中的DiscouverClient实现,创建一个InfoController 主要用于测试项目启动成功
/**
*
* @EnableEurekaServer注解激活Eureka中的DiscouverClient实现
* @SpringBootApplication默认扫描CustomerApplication所在包所以需要scanBasePackages制定包名
*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaPeer1Application {
public static void main(String[] args) {
System.out.println("服务注册中心:http://127.0.0.1:1000/");
new SpringApplicationBuilder(EurekaPeer1Application.class).web(true).run(args);
}
}
//InfoController.java
@RestController
public class InfoController {
/**
* http://127.0.0.1:1000/info/
* @return
*/
@RequestMapping(value = "/info", method = RequestMethod.GET)
public String into() {
return new Date().toString();
}
}
修改application.properties,实现将当前应用也注册到Eureka Server
设置配置中eureka.client.register-with-eureka和eureka.client.fetch-registry参数配置为true,并设置EurekaServer地址, 实现项目启动后Eureka注册中心中自身
server.port=1000
spring.application.name=EUREKA-PEER
############ Eureka ################
#服务注册中心IP
eureka.instance.hostname=127.0.0.1
# 是否注册中心注册,默认=TRUE
eureka.client.register-with-eureka=true
# 是否检索服务
eureka.client.fetch-registry=true
#指定服务注册中心
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:1000/eureka/
############ Eureka ################
启动后控制台日志表示将EUREKA-PEER注册到服务中心
2021-10-25 08:28:19.985 INFO 6632 --- [ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application EUREKA-PEER with eureka with status UP
访问 http://127.0.0.1:1000/ , 查看Eureka 的注服务中有一个Eureka Instance ,名称为EUREKA-PEER,如下图
1.1.2 失效剔除
Eureka Server 也就是注册中心,当注册中心启动后,各个服务将会想注册中心注册自己,Eureka Server会维护一份只读的服务清单,Eureka Server在启动的时候会创建一个定时任务,默认每隔一段时间执行 将默认清单中超时没有续约的服务剔除出去。
#Eureka Server将默认清单中超过配置时间没有续约的服务剔除
eureka.instance.lease-expiration-duration-in-seconds=90
1.1.2 不注册自己
修改eureka.client.register-with-eureka和eureka.client.fetch-registry参数配置为false的时候eureka-peer启动后就不会向eureka注册中心注册自己 ,再次访问 http://127.0.0.1:1000/ 会发现Instances currentLy registered with Eureka栏是空的,没有之前的EUREKA-PEER服务
# 是否注册中心注册,默认=TRUE
eureka.client.register-with-eureka=false
# 是否检索服务
eureka.client.fetch-registry=false
1.2 服务注册
一般情况下,应用即是服务提供者也是服务消费者。例如:客户模块会使用其他已经注册的服务,同时客户模块也会向注册中心注册服务给其他模块使用
将一个Spring Boot应用注册到Eureka Server或者是从Eureka Server获取服务列表时,主要做两件事
- 应用启动类配置 @EnableDiscoveryClient 注解
- 在applicaiton.properties 中用eureka.client.serviceUrl.defaultZone参数指定服务注册中心的位置
1.2.1 搭建服务提供者
搭建完服务注册中心后,我们尝试将一个Spring Boot应用键入Eureka服务指令体系中, 创建一个ms-customer的Spring Boot项目
修改pom ,引入spring-cloud-starter-eureka
<?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">
<parent>
<artifactId>springcloud</artifactId>
<groupId>springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ms-customer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</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-eureka</artifactId>
</dependency>
</dependencies>
<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>
</project>
修改Spring Boot应用代码
- 修改启动添加@EnableDiscoveryClient ,将应用注册未Eureka的客户端应用,激活Eureke中DiscoverClient实现,获取服务发现的能力,
- 创建InfoController,实现当调用 http://127.0.0.1:8001/info/ 时 返回当前服务信息
//CustomerApplication.java 参考
@EnableDiscoveryClient //将应用注册为Eureka客户端应用,激活Eureke中DiscoverClient实现,获得服务发现的能力
@SpringBootApplication(scanBasePackages = {"com.customer"})
public class CustomerApplication {
public static void main(String[] args) {
System.out.println("Customer:http://127.0.0.1:8001/");
SpringApplication.run(CustomerApplication.class, args);
}
}
//InfoController.java参考
package com.customer.controller;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
public class InfoController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private DiscoveryClient discoveryClient;
/**
* http://127.0.0.1:8001/info/
*/
@RequestMapping(value = "/info", method = RequestMethod.GET)
public String into() {
ServiceInstance instance = discoveryClient.getLocalServiceInstance();
String message = " host:" + instance.getHost() + ",service_id:" + instance.getServiceId();
logger.info(message);
return message+" Time:"+new Date().toString();
}
}
application.properties 参考:
server.address=127.0.0.1
server.port=8001
#服务的名称
spring.application.name=MS-CUSTOMER
############ Eureka ################
#服务注册中心IP
eureka.instance.hostname=127.0.0.1
# 是否注册中心注册,默认=TRUE
eureka.client.register-with-eureka=true
# 是否检索服务
eureka.client.fetch-registry=true
#指定服务注册中心
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:1000/eureka/
#如果有两个注册中心时,参数配置多个节点
#eureka.client.serviceUrl.defaultZone=http://127.0.0.1:1111/eureka/,http://127.0.0.1:1000/eureka/
############ Eureka ################
先查看Eureka注册中心 ,打开 http://127.0.0.1:1000/ 会发现MS-CUSTOMER应用已经注册到Eureka注册中心 中
应用启动后调用 http://127.0.0.1:1111/info/ 查看MS-CUSTOMER应用信息
1.2.2 服务续约
上面提到过,Eureka Server会维护一个服务列表, 每隔一段时间更新列表。 当服务提供者向Eureka Server 注册中心注册后,服务提供者也会维持一个心跳告诉Eureka Server自己还活着,防止Eureka Server将自己从服务列表中删除,这个过程也叫服务续约,
#服务续约 :定义服务续约任务的调用间隔时间,默认30秒
eureka.instance.lease-renewal-interval-in-seconds=30
1.2.2 服务下线
当服务实例进行正常关闭操作的时候,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心,服务端接收到请求后,会将服务状态修改Wie下线(DOWN) ,并把该下线时间传播出去。
1.3 服务发现与消费
当我们启动服务消费者的时候,他会发送一个REST请求给服务注册中心,来获取上面注册的服务清单,服务消费者获取服务清单后,通过服务名可以获得具体的提供服务的实例名和该实例的元数据信息
1.3.1 搭建服务消费者
创建ms-ribbon-consumer应用,用于模拟消费 ms-customer服务
<?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">
<parent>
<artifactId>springcloud</artifactId>
<groupId>springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ms-ribbon-consumer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 注册服务提供 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
<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>
</project>
添加注解@EnableDiscoveryClient
package com.ribbon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient //将应用注册为Eureka客户端应用,激活Eureke中DiscoverClient实现,获得服务发现的能力
@SpringBootApplication
public class RibbonConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate resetTemplate(){
return new RestTemplate();
}
}
创建ConsumerController 来消费服务,通过访问http://127.0.0.1:8002/ribbon-consumer 实现调用 MS-CUSTOMER服务的info信息
package com.ribbon.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
/**
* http://127.0.0.1:8002/ribbon-consumer
*/
@RequestMapping(value = "/ribbon-consumer", method = RequestMethod.GET)
public String helloConsumer() {
return restTemplate.getForEntity("http://MS-CUSTOMER/info", String.class).getBody();
}
}
server.address=127.0.0.1
server.port=8002
#服务的名称
spring.application.name=MS-RIBBON-CONSUMER
############ Eureka ################
eureka.instance.hostname=localhost
#指定服务注册中心
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:1000/eureka/
############ Eureka ################
同时启动了三个应用,ms-customer会注册服务到服务中心, 应用ms-ribbon-consumer 可以通过服务中心调用ms-customer提供的
http://127.0.0.1:8002/ribbon-consumer ,可见获取的结果是MS-CUSTOMER应用的info
2.Eureka 配置整理
属性 |
属性作用说明 |
eureka.instance.hostname |
Eureka Serve服务实例主机名,例如localhost |
eureka.instance.lease-expiration-duration-in-seconds | Eureka Server将超过配置时间没有续约的服务剔除,默认90秒 |
eureka.client.serviceUrl.defaultZon |
指定服务注册中心的地址 |
eureka.client.register-with-eureka |
是否注册中心注册,默认=TRUE |
eureka.client.fetch-registry |
是否需要检索服务,默认=TRUE |
3.常见问题
常见启动错误
1. ClassNotFoundException
启动异常报错
Caused by: java.lang.ClassNotFoundException: org.springframework.cloud.context.named.NamedContextFactory
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_211]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_211]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[na:1.8.0_211]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_211]
解决方法
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2.javax.xml.bind.JAXBContext
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBContext
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
从Java9开始,Java SE 的整体jar 结构都进行高度模块化,因此不会自动加载javax.xml.bind内容
增加下面的依赖
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
3. eureka-client报错Cannot execute request on any known server
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
at com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RetryableEurekaHttpClient.java:111) ~[eureka-client-1.6.2.jar:1.6.2]
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) ~[eureka-client-1.6.2.jar:1.6.2]
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59) ~[eureka-client-1.6.2.jar:1.6.2]
application.properties 文件中配置向注册中心注册eureka.client.register-with-eureka=true ,但是没有配置eureka.client.serviceUrl.defaultZone
项目参考:GitHub - PNZBEIJINGL/spring-cloud-lab