Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件的一部分,基于 Netflix Eureka 做了二次封装,主要负责实现微服务架构中的服务治理功能。
Spring Cloud Eureka 是一个基于 REST 的服务,并且提供了基于 Java 的客户端组件,能够非常方便地将服务注册到 Spring Cloud Eureka 中进行统一管理。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XhIlCNf6-1665802569838)(7.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15RlMTjx-1665802569839)(8.png)]
Eureka 是基于 AP 原则构建的。
CAP 原则又称 CAP 定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
在分布式系统领域有个著名的 CAP 定理,即 C 为数据一致性;A 为服务可用性;P 为服务对网络分区故障的容错性。这三个特性在任何分布式系统中都不能同时满足,最多同时满足两个。
1.在保证C和P的情况下
为了保证数据一致性,data1需要将数据复制给data2,即data1和data2需要进行通信。但是由于网络是不可靠的,我们系统有保证了分区容忍性,也就是说这个系统是可以容忍网络的不可靠的。这时候data2就不一定能及时的收到data1的数据复制消息,当有请求向data2访问number数据时,为了保证数据的一致性,data2只能阻塞等待数据真正同步完成后再返回,这时候就没办法保证高可用性了。
所以,在保证C和P的情况下,是无法同时保证A的。
2.在保证A和P的情况下
为了保证高可用性,data1和data2都有在有限时间内返回。同样由于网络的不可靠,在有限时间内,data2有可能还没收到data1发来的数据更新消息,这时候返回给客户端的可能是旧的数据,和访问data1的数据是不一致的,也就是违法了C。
也就是说,在保证A和P的情况下,是无法同时保证C的。
3.在保证A和C的情况下
如果要保证高可用和一致性,只有在网络情况良好且可靠的情况下才能实现。这样data1才能立即将更新消息发送给data2。但是我们都知道网络是不可靠的,是会存在丢包的情况的。所以要满足即时可靠更新,只有将data1和data2放到一个区内才可以,也就丧失了P这个保证。其实这时候整个系统也不能算是一个分布式系统了。
想要保证 AP 就要用 Eureka,想要保证 CP 就要用 Zookeeper。
Dubbo 中大部分都是基于 Zookeeper 作为注册中心的。
Spring Cloud 框架中,更多考虑的服务可用性,所以首选 Eureka。
1 导入依赖
<!-- Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka服务中心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<!-- Spring Cloud -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2 搭建启动类
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
@EnableEurekaServer 注解表示当前服务层是一个Eureka注册中心服务
3 配置application.properties文件
#服务名称
spring.application.name=eureka-server
#服务中心地址
eureka.instance.hostname=localhost
#服务端口号
server.port=8761
# 由于该应用为注册中心, 所以设置为false, 代表不向注册中心注册自己
eureka.client.register-with-eureka=false
# 由于注册中心的职责就是维护服务实例, 它并不需要去检索服务, 所以也设置为 false
eureka.client.fetch-registry=false
#服务注册中心的配置内容,指定服务注册中心的位置
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
4 启动服务层,访问地址: http://localhost:8761,看到如下页面,服务中心搭建成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LChQh4J6-1665802569842)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCEae204d0a9bee3b313208cceed967cd64/608)]
Eureka控制台参数说明
Home
进入Eureka控制台首页,首先看HOME页的头部
System Status
Environment : 环境,默认为test, 该参数在实际使用过程中,可以不用更改
Data center : 数据中心,使用的是默认的是 “default”
Current time:当前的系统时间
Uptime : 已经运行了多少时间
Lease expiration enabled :是否启用租约过期 , 自我保护机制关闭时,该值默认是true, 自我保护机制开启之后为false。
Renews threshold : 每分钟最少续约数
Renews (last min) : 最后一分钟的续约数量(不含当前,1分钟更新一次)
红字提醒
系统在三种情况下会出现红色加粗的字体提示:
1.在配置上,自我保护机制关闭,且检测心跳的阈值小于指定阈值,会有如下提示
RENEWALS ARE LESSER THAN THE THRESHOLD. THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
2.自我保护机制开启了,检测心跳的阈值小于指定阈值,会有如下提示
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.
3.在配置上,自我保护机制关闭了,会有如下提示
THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
DS Replicas
Eureka集群节点副本
General Info
total-avail-memory : 总共可用的内存
environment : 环境名称,默认test
num-of-cpus : CPU的个数
current-memory-usage : 当前已经使用内存的百分比
server-uptime : 服务启动时间
registered-replicas : 相邻集群复制节点
unavailable-replicas :不可用的集群复制节点,如何确定不可用? 主要是server1 向 server2和server3 发送接口查询自身的注册信息,
如果查询不到,则默认为不可用 , 也就是说如果Eureka Server自身不作为客户端注册到上面去,则相邻节点都会显示为不可用。
available-replicas :可用的相邻集群复制节点
Instance Info
ipAddr:当前实例IP地址
status:当前实例状态 .up 上线,down 下线
3 搭建服务提供者服务
1 导入依赖
<!-- Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 基于eureka服务中心的 服务提供者组件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!-- Spring Cloud -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2 编程启动类,添加Eureka 的客户端注解
@SpringBootApplication
@EnableEurekaClient//当前服务是一个 Eureka 的客户端。
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@Value("${server.port}")
private Integer port;
@GetMapping("/hello")
public String hello(String userName) {
JSONObject obj = new JSONObject();
obj.put("port", port);
obj.put("userName", userName);
return obj.toString();
}
}
3 创建application.properties文件,配置信息如下:
#服务名称
spring.application.name= eureka-client-user-service
server.port=8081
#这是我们之前启动的 Eureka 服务的地址,在启动的时候需要将自身的信息注册到 Eureka 中去。
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
#使用真实ip地址
eureka.instance.prefer-ip-address=true
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
#健康检查地址
eureka.instance.status-page-url=http://${spring.cloud.client.ip-address}:${server.port}/actuator/health
4 启动服务提供者服务,刷新 注册中心服务 地址,看到信息如下:说明 服务提供者 注册成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-skMuJvdN-1665802569844)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCEf8172a87ecaca9393fab5baffb69f65a/635)]
4 搭建消费者服务
1 导入依赖。依赖和服务提供者服务一样,这里就不贴了
2 编写启动类。启动类也和服务提供者服务一样,这里不贴了
3 配置application.properties文件,代码如下:
spring.application.name= eureka-client-xiaofei-service
server.port=8082
#这是我们之前启动的 Eureka 服务的地址,在启动的时候需要将自身的信息注册到 Eureka 中去。
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
4 配置RestTemplate ,RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,能够大大提高客户端的编写效率。我们通过配置 RestTemplate 来调用接口。
@LoadBalanced 注解,这个注解会自动构造 LoadBalancerClient 接口的实现类并注册到 Spring 容器中。这是负载均衡的注解
代码如下所示
@Configuration
public class RestBean {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
5 定义消费 ‘服务提供者服务’ 的控制器,代码如下:
@RestController
@RequestMapping("/xiaofei")
public class UserXiaofeiController {
//注入restTemplate
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello(String userName) {
// return restTemplate.getForObject("http://localhost:8801/user/hello?userName=" + userName, String.class);
Map<String, Object> map = new HashMap<>();
map.put("userName", userName);
//url中的ip和端口换成服务提供者的服务名称,实现负载均衡
return restTemplate.getForObject("http://eureka-client-user-productor/user/hello?userName={userName}", String.class, map);
}
}
6 刷新注册中心地址,查看消费者服务 是否注册成功。如图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGmeVoSH-1665802569846)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCE39e9d2c0180fd19051dd0f9563b962fc/666)]
7 输入 消费者服务的控制器方法地址: http://localhost:8082/xiaofei/callHello ,查看结果
是否获取到 ‘服务提供者服务’ 提供的数据,如图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GXYURY0p-1665802569847)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCE86c75e920db8cd988e1430c2c07f08e6/672)]
5 Eureka注册中心开启密码认证
Eureka 自带了一个 Web 的管理页面,方便我们查询注册到上面的实例信息,但是有一个问题:如果在实际使用中,注册中心地址有公网 IP 的话,必然能直接访问到,这样是不安全的。所以我们需要对 Eureka 进行改造,加上权限认证来保证安全性。
1 重构我们的注册中心服务,添加依赖
<!--用密码验证登录注册中心所需依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2 配置安全管理类
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 关闭csrf
http.csrf().disable();
// 支持httpBasic
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
3 在application.propertis 配置文件,添加用户名和密码配置
#用户名
spring.security.user.name=rock
#密码
spring.security.user.password=123456
4 修改客户端的注册中心地址:服务提供者服务和消费者服务注册中心地址添加 用户名和密码信息
#这是我们之前启动的 Eureka 服务的地址,在启动的时候需要将自身的信息注册到 Eureka 中去。
eureka.client.serviceUrl.defaultZone=http://rock:123456@localhost:8761/eureka/
5 搭建完毕,这是访问注册中心服务,需要输入用户名和密码才能访问。
6 Spring Cloud使用Eureka集群搭建实现高可用服务注册中心
前面我们搭建的注册中心只适合本地开发使用,在生产环境中必须搭建一个集群来保证高可用。Eureka 的集群搭建方法很简单:每一台 Eureka 只需要在配置中指定另外多个 Eureka 的地址就可以实现一个集群的搭建了。
下面我们以 2 个节点为例来说明搭建方式。假设我们有 one 和 two两台机器,需要做的就是:
说明:这里我就用在一个电脑上,用2个相同项目,端口不一样,来代替one机器和two机器
1 创建一个新的 注册服务中心 项目 springcloud-demo-eureka-server-one,导入依赖,依赖不变,这里就不贴了
2 添加新的配置文件application-one.properties
server.port=8761
#服务注册中心的配置内容,指定服务注册中心的位置
eureka.client.serviceUrl.defaultZone=http://rock:123456@localhost:8762/eureka/
3 添加新的配置文件application-two.properties
server.port=8762
#服务注册中心的配置内容,指定服务注册中心的位置
eureka.client.serviceUrl.defaultZone=http://rock:123456@localhost:8761/eureka/
4 配置application.properties, 配置 spring.profiles.active=one ,这里的one是指向 application-one.properties 配置文件
#服务名称
spring.application.name=eureka-server-one
#服务中心地址
eureka.instance.hostname=localhost
# 由于该应用为注册中心, 所以设置为false, 代表不向注册中心注册自己
eureka.client.register-with-eureka=false
# 由于注册中心的职责就是维护服务实例, 它并不需要去检索服务, 所以也设置为 false
eureka.client.fetch-registry=false
#用户名
spring.security.user.name=rock
#密码
spring.security.user.password=123456
# 指定不同的环境
spring.profiles.active=one
5 配置启动类 和 安全管理配置类,这里也和前面一样,这里就不贴了
6 创建第二个注册服务中心 项目springcloud-demo-eureka-server-two,步骤1,2,3 都一样,这里不重复贴了
7 配置springcloud-demo-eureka-server-two 的application.properties, 配置 spring.profiles.active=two ,这里的one是指向 application-two.properties 配置文件
#服务名称
spring.application.name=eureka-server-two
#服务中心地址
eureka.instance.hostname=localhost
# 由于该应用为注册中心, 所以设置为false, 代表不向注册中心注册自己
eureka.client.register-with-eureka=false
# 由于注册中心的职责就是维护服务实例, 它并不需要去检索服务, 所以也设置为 false
eureka.client.fetch-registry=false
#用户名
spring.security.user.name=rock
#密码
spring.security.user.password=123456
# 指定不同的环境
spring.profiles.active=two
8 在客户端中我们通过配置 eureka.client.serviceUrl.defaultZone 来指定对应的注册中心,当我们的注册中心有多个节点后,就需要修改 eureka.client.serviceUrl.defaultZone 的配置为多个节点的地址,多个地址用英文逗号隔开即可:
把所有客户端的注册中心地址改成如下:
#这是我们之前启动的 Eureka 服务的地址,在启动的时候需要将自身的信息注册到 Eureka 中去。
eureka.client.serviceUrl.defaultZone=http://rock:123456@localhost:8761/eureka/,http://rock:123456@localhost:8762/eureka/
9 先启动这两个注册中心项目,然后启动其他客户端项目:在地址 http://localhost:8761/ 和 http://localhost:8762/,都能看到客户端注册的服务,如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QcqwE7Ui-1665802569848)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCE9712be7e6575befb95e9fc91239ffc37/775)]
10 然后我们关闭其中任意一个 注册中心服务,我们访问 消费者服务的地址 http://localhost:8082/xiaofei/callHello,都可以接收到
服务提供者服务 提供的数据。说明集群搭建成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dQDo4R9R-1665802569849)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCE5dda11cd94a8b9c09fceeef1dd636b60/783)]
11 结论:无论那个注册中心服务出现问题,客户端应用都能继续使用存活的注册中心。
保护模式主要在一组客户端和 Eureka Server 之间存在网络分区场景时使用。一旦进入保护模式,Eureka Server 将会尝试保护其服务的注册表中的信息,不再删除服务注册表中的数据。当网络故障恢复后,该 Eureka Server 节点会自动退出保护模式。如果在 Eureka 的 Web 控制台看到下图 所示的内容,就证明 Eureka Server 进入保护模式了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kaRd6T65-1665802569850)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCE1e9508719c75e5bd8a651205f6560959/801)]
1 在注册中心服务 配置文件中,加入下面配置
#关闭自我保护模式
eureka.server.enable-self-preservation=false
2 再次访问注册中心,如果下图所示,说明配置关闭成功
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3rYi1YT-1665802569851)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCE01c5174c51be61cc815a53472833423b/796)]
客户端在注册时,服务的 Instance ID 的默认值的格式如下:
s p r i n g . c l o u d . c l i e n t . h o s t n a m e : {spring.cloud.client.hostname}: spring.cloud.client.hostname:{spring.application.name}{server.port}}
翻译过来就是“主机名:服务名称:服务端口”。当我们在 Eureka 的 Web 控制台查看服务注册信息的时候,如图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zJs6J2Yf-1665802569851)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCEb8ebac9a5b6203f930035ccb995466b7/799)]
很多时候我们想把 IP 显示在上述格式中,不想显示主机名。在客户端服务的配置文件中里添加如下配置即可:
# 定义实例ID格式
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
定义之后我们看到的就是 eureka-client-user-service:192.168.2.13:8081,一看就知道是哪个服务,在哪台机器上,端口是多少。如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4SMRggoF-1665802569852)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCE23d906bcb1434eeda3b2230037cc5f08/822)]
1 如果我们点击下面实例连接。跳转的地址默认是: 主机名+Port/actuator/info。如图2所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chLgXZds-1665802569853)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCE9d50cc8e13224c5eec3957682d0c17c8/832)]
可以在配置文件中添加如下信息在actuator/info中显示
info:
app.name: eurekaservice-01
company.name: www.zhq.com
build.artifactId: @project.artifactId@
build.version: @project.version@
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SCYJFWiR-1665802569854)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCEc7234ba95141a0f6e3a4f9773a254698/834)]
2 我们可以自定义这个跳转的地址,在客户端服务定义如下配置:
注意:user/hello 是客户端服务的控制器方法的自定义地址,请自行补上
#实例链接显示ip地址
eureka.instance.preferIpAddress=true
#自定义实例url
eureka.instance.status-page-url=http://${spring.cloud.client.ip-address}:${server.port}/user/hello
3 配置完毕后,重启 注册中心服务 和 客户端服务,再次点击,就可以看到我们自定义的实例地址了了。如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hwOEA1P2-1665802569855)(https://note.youdao.com/yws/public/resource/d64ed1b894660deb7447249f26dc1020/xmlnote/WEBRESOURCEa602d065187ddf8c894284416f0e7fcc/848)]
在实际开发过程中,我们可能会不停地重启服务,由于 Eureka 有自己的保护机制,故节点下线后,服务信息还会一直存在于 Eureka 中。我们可以通过增加一些配置让移除的速度更快一点,当然只在开发环境下使用,生产环境下不推荐使用。
1 在 注册中心 服务 添加下面配置
#关闭自我保护模式
eureka.server.enable-self-preservation=false
# 过期实例应该启动并运行的时间间隔 默认 60000 毫秒,这里我改成5000毫秒(开发中配置,生成环境请还原)
eureka.server.eviction-interval-timer-in-ms=5000
2 在 客户端 服务 添加依赖
<!--开启健康检查的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3 在 客户端 服务 添加配置
#开启健康检查(默认开启)
eureka.client.healthcheck.enabled=true
# 表示 Eureka Client 发送心跳给 server 端的频率 默认 30 秒
# 这里我改成5秒(开发中配置,生成环境请还原)
eureka.instance.lease-renewal-interval-in-seconds=5
# 表示 Eureka Server 至上一次收到 client 的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则移除该client
# 默认 90 秒 这里我改成5秒(开发中配置,生成环境请还原)
eureka.instance.lease-expiration-duration-in-seconds=5
java
org.springframework.boot
spring-boot-starter-actuator
3 在 客户端 服务 添加配置
```java
#开启健康检查(默认开启)
eureka.client.healthcheck.enabled=true
# 表示 Eureka Client 发送心跳给 server 端的频率 默认 30 秒
# 这里我改成5秒(开发中配置,生成环境请还原)
eureka.instance.lease-renewal-interval-in-seconds=5
# 表示 Eureka Server 至上一次收到 client 的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则移除该client
# 默认 90 秒 这里我改成5秒(开发中配置,生成环境请还原)
eureka.instance.lease-expiration-duration-in-seconds=5