3. Spring Cloud Eureka 服务注册与发现(超详细说明及使用)
文章目录
前言
1. Spring Cloud Eureka 的概述
从上个学习内容上我们可以知道 补充, 当中的 、Spring Cloud 组件选型- 图
从上图可以看出,目前主流的服务注册/发现的组件是 Nacos,但是Eureka 作为一个老牌经典的服务
注册/发现技术还是有必要回顾一下:
原因:
- 一些早期的分布式 微服务项目使用的是 Eureka ,大家工作中,完全有可能遇到这种情况。
- 后期的服务注册/发现组件/技术,都参考了 Eureka 的设计和理念,学习了 Eureka 后,我们上手
Nacos 容易很多,而且理解的更深刻。
分析上述我们上一篇内容补充 当中项目架构问题分析
当前项目问题分析:
- 在企业项目中,服务消费访问请求会存在高并发
- 如果只有一个会议中心-提供服务,可用性就比较差了
- 所以,会议中心提供服务往往是一个集群,也就是说会有多个会议中心-提供服务微服务模块
- 那么这个时候,就存在一个问题就是服务消费方,怎么去发现可以使用的服务(哪些服务又不可以使用)
- 当服务消费方,发现了可以使用的服务后(可能是多个,又存在一个问题就是到底调用 A服务,
还是调用B服务,这就引出了服务注册和负载均衡的问题) - Eureka 就可以解决上述问题
引入 Eureka 项目架构,一图胜千言
上图 Eureka 项目架构图解析:
- 会员中心——> 提供服务的,在项目中,会做成集群 ,提供高可用
- Eureka Server 有必要的话,也可以做成集群 。
- Eureka 包含两个组件: Eureka Server 和 Eureka Client
- Eureka Server 提供注册服务,各个微服务节点童工配置启动后,会在 EurekaServer 中进行注册,
这样Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。 - Eureka Client 通过注册中心进行访问,是一个Java客户端,用于简化 Eureka Server 的交互,
客户端同时也具备一个内置的,使用轮询(round-robin) 负载算法的负载均衡器。在应用启动后,
将会向 Eureka Server 发送心跳(默认周期为 30秒)。如果 Eureka Server 在多个心跳周期
内没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认90秒)
1.1 服务治理概述
Eureka 实现服务治理
在传统的 rpc 远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理困难,所以 需要治理服务之间依赖关系
服务治理实现服务调用、负载均衡、容错等,实现服务发现与注册。
二说分布式开发: https://jingyan.baidu.com/article/46650658def479f549e5f83e.html
1.2 服务注册与发现
-
Eureka 采用了CS[client-server-java] 基础我们讲过一个多人聊天项目。的设计架构,
Eureka Server 作为服务注册的服务器,它是服务注册中心 -
系统中的其它微服务,使用Eureka 的客户端连接到 Eureka Server 并维持心跳连接,
通过 Eureka Server 来监控系统中各个微服务是否正常运行 -
在服务注册与发现中,有一个注册中心,当服务启动的时候,会把当前自己服务器的信息,比如服务地址通讯地址等以别名方式注册到注册中心上。
-
服务消费者或者服务提供者,以服务别名的方式去注册中心上获取到实际的服务提供
者通讯地址,然后,通过 RPC 调用服务
2. 实践:创建单机 Eureka Server 注册中心
2.1 需求说明 + 图解
2.3 具体实现步骤
2.3.1 创建相关的 Moduel 以及 完成配置
- 创建 e-commerce-eureka-server-9001 微服务模块[作为注册中心]
父工程的 pom.xml-会做相应变化,管理 e-commerce-eureka-server-9001 微服务子模块
- 修改 e-commerce-eureka-server-9001 的 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">
<parent>
<artifactId>e-commerce-center</artifactId>
<groupId>com.rainbowsea</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>e-commerce-eureka-server-9001</artifactId>
<dependencies>
<!-- 引入web-starter 说明:这里我们使用版本仲裁(从父项目继承了版本)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--1. starter-actuator 是sprng boot 程序的监控系统,可以实现健康检查,info 信息等
2. 访问http://localhost:10000/actuator 可以看到相关链接,还可以做相关配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 引入 eureka-server 场景启动器 starter: 使用版本仲裁
注意:不要搞错了,有一个 starter 的
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- lombok 引入-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!-- 不进行转递依赖-->
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- 引入 test-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入我们自己对 bean 封装成 api 的模块内容-->
<dependency>
<groupId>com.rainbowsea</groupId>
<artifactId>e_commerce_center-common-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
- 创建 resources/application.yml
server:
port: 9001
# 配置 eureka-server
eureka:
instance:
hostname: localhost # 服务实例名或者别名
client:
# 配置不向注册中心注册自己,默认是 true
register-with-eureka: false
# 表示自己就是注册中心,作用就是维护注册服务实例,不需要去检索服务,默认是 true
fetch-registry: false
service-url:
#设置与 eureka server 交互的模块,查询服务和注册服务都需要依赖这个地址
# defaultZone: http://localhost:9001/eureka/ ,也可以使用别名,在同一个文件当中可以引用
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
特别说明:注意:这里从架构图上可以知道的是,这里的这个
e-commerce-eureka-server-9001
充当为Eureka Server
服务角色 ,所以自然,我们需要将其配置为 服务角色。
- 创建主启动类 com/rainbwosea/springcloud/EurekaApplication.java
package com.rainbowsea.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer // @EnableEurekaServer 表示该项目/该模块/该程序作为 Eureka Server 角色进行运行
@SpringBootApplication
public class EurekaApplication9001 {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication9001.class, args);
}
}
- 完成测试:
补充说明: Spring Eureka 完整的可视化页面展示
2.3.2 配置 member-service-consumer-80 作为 EurekaClient 可 以 拉 取/获取 e-commerce-eureka-server-9001 提供的服务信息
2.3.3 架构示意图
配置 member-serivce-consumer-10000 作为 EurekaClient 注册发现到 commerce-eureka-server-9001 服务当中,我们需要在 resource目录下的 application.yaml
文件当进行一个配置。如下图所示:
- 配置 member-serivce-consumer-80 作为 EurekaClient 注册发现到 commerce-eureka-server-9001 服务当中,我们需要在 resource目录下的
application.yaml
文件当进行一个配置。如下图所示:
server:
port: 80 # 浏览器默认就是 80 端口
spring:
application:
name: member-service-consumer-80 # 该项目/模块名称
eureka:
client:
register-with-eureka: true # 表示将自己注册到 Eureka-Server 当中
fetch-registry: true # 表示将信息发送到 Eureka-Server 当中
service-url:
# 表示将自己注册到那个 eureka-server
defaultZone: http://localhost:9001/eureka
- 同时我们也需要将该
member-service-consumer-80
项目,配置指明为是 Eureka Client 角色,在该项目的场景启动器上添加 注解即可 。
package com.rainbowsea.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
// 如果错误: 加入排除 DataSourceAutoConfiguration 自动配置
//@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableEurekaClient //表示将项目/模块/程序 标注作为 Eureka Client
@SpringBootApplication
public class MemberConsumerApplication80 {
public static void main(String[] args) {
SpringApplication.run(MemberConsumerApplication80.class, args);
}
}
运行测试:
注意:测试的时候,我们尽量先将 EukekatServer (也就是我们这里的
e-commerce-eureka-server-9001
) 注册中心启动,然后再将 Eukekat Client(也就是我们这里的member-service-consumer-80
) 启动。然后,打开浏览器输入:http://localhost:9001/ 查看结果。
2.3.4 将 member-service-provider-10000 作为 EurekaClient 注册到 e-commerce-eureka-server-9001 成为服务提供者
上述操作我们是已经将 定义为了一个 Eureka Server 服务器,那么我们就需要将 服务提供者
注册到我们的 Eureka Server 当中。注册到 Eureka Server 当中,换句话说:就是将我们 Eureka Client 告诉 Eureka Server 我还没有宕机,还活着,我还可以继续提供服务下去。
2.3.5 架构示意图
配置 member-service-provider-10000 作为 EurekaClient 注册发现到 commerce-eureka-server-9001 服务当中,我们需要在 resource目录下的 application.yaml
文件当进行一个配置。如下图所示:
server:
port: 10000
spring:
application:
name: member-service-provider-10000 # 配置应用的名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 配置 alibaba 的数据库连接池
password: MySQL123
username: root
url: jdbc:mysql://localhost:3306/e_commerce_center_db?useSSL=true&useUnicode=true&characterEncoding=UTF-8
mybatis:
mapper-locations: classpath:mapper/*.xml # 指定 mapper.xml 文件位置 classpath 表示 resources 目录下
type-aliases-package: com.rainbowsea.springcloud.entity # 实例 bean 类所在的包,这样可以通过类名的方式
# 配置 eureka-client
eureka:
client:
register-with-eureka: true # 表示将自己注册到 Eureka-Server
# 表示从 Eureka-Server 抓取注册信息
# 如果是单节点,是可以配置的,但是如果是一个集群,则必须配置true
# 才能配合 Ribbon 使用负载均衡
fetch-registry: true
service-url:
# 表示将自己注册到那个 eureka-server
defaultZone: http://localhost:9001/eureka
同时我们也需要将该 member-service-provider-10000
项目,配置指明为是 Eureka Client 角色,在该项目的场景启动器上添加 注解即可 。
package com.rainbowsea.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient 表示将该程序/项目/模块 标识为 eureka-client 端
@EnableEurekaClient
@SpringBootApplication
public class MemberApplication10000 {
public static void main(String[] args) {
SpringApplication.run(MemberApplication10000.class, args);
}
}
测试:
- 启动 e-commerce-eureka-server-9001
- 启动 member-service-provider-10000
- 浏览器: http://localhost:9001
3. Service Consumer 、Service Provider 、EurekaServer 的维护机制
示图:
4. Eureka 自我保护模式
4.1 自我保护模式理论
在默认情况下, Eureka 启动了自我保护模式(如图红字, 需要刷新页面, 可以看到)
-
默认情况下EurekaClient 定时向 EurekaServer 端发送 心跳包
-
如果 Eureka 在 server 端一定时间内(默认90秒) 没有收到 EurekaClient 发送心跳包,便会
直接从服务注册表中剔除该服务 -
如果Eureka 开启了**“自我保护机制/模式”** ,那么在短时间(90秒中)内丢失了大量的服务实例心跳。
这时候Eureka Server 会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通或者
阻塞)因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的。 -
自我保护是属于CAP里面的AP分支 ,保证高可用和分区容错性
-
自我保护模式是一种应对网络异常的安全保护措施,它的架构哲学是宁可同时保留所有微服务(健康
的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,
可以让 Eureka 集群更加的健壮,稳定,参考:
https://blog.csdn.net/wangliangluang/article/details/120626014
- 一致性: 所有节点在同一时间的数据完全一致
- 可用性:服务在正常时间内一直可用
- 分区容错性:系统在遇到节点或者网络分区故障的时候,仍然能够对外满足可用性或一致性的服务
测试:
启动member-service-provider-10000 和 e-commerce-eureka-server-9001
让member-service-provider-10000 正确的注册
然后关闭member-service-provider-10000
观察注册的 member-service-provider-10000 服务是否还在.
4.2 禁用自我保护模式(生产环境中, 一般不禁用)
演示禁用自我保护模式:
修改 e-commerce-eureka-server-9001 的 application.yml
修改 member-service-provider-10000 的 application.yml
启 动 e-commerce-eureka-server-9001 和 member-service-provider-10000
在 member-service-provider-10000 注册成功后,再关闭, 看看 eureka server 服务注册信息的变化
提醒:测试完毕后,别忘了恢复原状,启用自我保护
5. 搭建 EurekaServer 集群- 实现负载均衡故障容错
5.1 为什么需要集群 Eureka Server
示意图(上图)
为什么需要集群 Eureka Server
说明:
- 微服务 RPC 远程服务调用最核心的是实习高可用
- 如果注册中心只有1个,它出故障,会导致整个服务环境不可用
- 解决办法: 搭建 Eureka 注册中心集群,实习 负载均衡 + 故障容错
5.2 需求分析/图解
5.3 搭建 Eureka Server 集群
1. 创建 e-commerce-eureka-server-9002 微服务模块[作为注册中心]
- 修改 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">
<parent>
<artifactId>e-commerce-center</artifactId>
<groupId>com.rainbowsea</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>e-commerce-eureka-server-9002</artifactId>
<dependencies>
<!-- 引入web-starter 说明:这里我们使用版本仲裁(从父项目继承了版本)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--1. starter-actuator 是spring boot 程序的监控系统,可以实现健康检查,info 信息等
2. 访问http://localhost:9002/actuator 可以看到相关链接,还可以做相关配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 引入 eureka-server 场景启动器 starter: 使用版本仲裁
注意:不要搞错了,有一个 starter 的
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- lombok 引入-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!-- 不进行转递依赖-->
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- 引入 test-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入我们自己对 bean 封装成 api 的模块内容-->
<dependency>
<groupId>com.rainbowsea</groupId>
<artifactId>e_commerce_center-common-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
- 创建 resources/application.yml
server:
port: 9002
# 配置 eureka server
eureka:
instance:
hostname: eureka9002.com # 服务实例名/或者叫别名
client:
# 配置不向注册中心注册自己(因为自己就是注册中心),默认是 true
register-with-eureka: false
fetch-registry: false
service-url:
#设置与 eureka server 交互的模块,查询服务和注册服务都需要依赖这个地址
# defaultZone: http://localhost:9001/eureka/ ,也可以使用别名,在同一个文件当中可以引用
# 相互注册,这里应用注册到 eureka server 9001 上
defaultZone: http://eureka9001.com:9001/eureka/
- 创建主启动类 EurekaApplication9002.java
package com.rainbowsea.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //该注解标注,当前的项目/模块/程序作为 Eureka Server 角色
public class EurekaApplication9002 {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication9002.class, args);
}
}
- 修改 e-commerce-eureka-server-9001 微服务模块,修改其中的内容 resources/application.yml
让: e-commerce-eureka-server-9001 微服务模块 与 e-commerce-eureka-server-9002 相互注册 。
server:
port: 9001
# 配置 eureka-server
eureka:
instance:
hostname: eureka9001.com # 服务实例名或者别名
client:
# 配置不向注册中心注册自己,默认是 true
register-with-eureka: false
# 表示自己就是注册中心,作用就是维护注册服务实例,不需要去检索服务,默认是 true
fetch-registry: false
service-url:
#设置与 eureka server 交互的模块,查询服务和注册服务都需要依赖这个地址
# defaultZone: http://localhost:9001/eureka/ ,也可以使用别名,在同一个文件当中可以引用
# 相互注册,这里应用注册到 eureka server 9002 上
defaultZone: http://eureka9002.com:9002/eureka/
6. 补充:定义本地的域名机制
如下图所示:
无论是 9001 还是 9002 ,我们都定义了别名,如果想要,让定义的别名生效的话,我们需要在本地,设置域名映射。
设置本地的域名映射,需要找到本地电脑当中的 host
文件,在该 host
文件当中编写添加相应的域名映射。
host文件路径: C:\Windows\System32\drivers\etc\host
# 配置 eureka 主机 和 ip 的映射 127.0.0.1 eureka9001.com 127.0.0.1 eureka9002.com
# Copyright (c) 1993-2009 Microsoft Corp. # # This is a sample HOSTS file used by Microsoft TCP/IP for Windows. # # This file contains the mappings of IP addresses to host names. Each # entry should be kept on an individual line. The IP address should # be placed in the first column followed by the corresponding host name. # The IP address and the host name should be separated by at least one # space. # # Additionally, comments (such as these) may be inserted on individual # lines or following the machine name denoted by a '#' symbol. # # For example: # # 102.54.94.97 rhino.acme.com # source server # 38.25.63.10 x.acme.com # x client host # localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # ::1 localhost 127.0.0.1 account.wondershare.com # 配置 eureka 主机 和 ip 的映射 127.0.0.1 eureka9001.com 127.0.0.1 eureka9002.com
特别提醒:如果发现文件修改后无法保存,那么可以将文件复制到桌面上,然后,修改复制到桌面上的那份复制过来的 host 文件,修改保存完后,再拷贝回原来路径位置,进行一个替换。
运行测试:
- 启动 e-commerce-eureka-server-9001
- 启动 e-commerce-eureka-server-9002
- 浏览器: http://eureka9001.com:9001 浏览器: http://eureka9002.com:9002
将 member-service-consumer-80 注册到 EurekaServer 集群(目前 2 台)
修改 member-service-consumer-80 模块当中的:resources/application.yml
- 启动 e-commerce-eureka-server-9001 和 e-commerce-eureka-server-9002
- 启动 member-service-consumer-80
- 观察 member-service-consumer-80 是否注册到 Eureka 集群(目前 2 台)
浏览器输入: http://eureka9001.com:9001/
5.4 搭建会员中心服务提供方-集群
创建 member-service-provider-10002
参考 member-service-provider-10000 来创建 member-service-provider-10002 即可
创 建 好 后 , 使 用 member-service-provider-10000 的 源 码 和 配 置 替 换 member-service-provider-10002 生成的代码
(不要到磁盘整体拷贝,会出现关联到 member-service-provider-10000 的问题,很麻烦, 可以创建好新项目的包,然后再拷贝对应包下的文件,就不会出问题)
提醒,拷贝时不要忘记拷贝 resources/mapper/MemberMapper.xml
这些 xxx.xml
文 件。
创建 resources/application.yml
创建好 application.yml
从 member-service-provider-10000 拷贝 application.yml 的内容
修改端口号即可
server:
port: 10002
spring:
application:
name: member-service-provider # 配置应用的名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 配置 alibaba 的数据库连接池
password: MySQL123
username: root
url: jdbc:mysql://localhost:3306/e_commerce_center_db?useSSL=true&useUnicode=true&characterEncoding=UTF-8
mybatis:
mapper-locations: classpath:mapper/*.xml # 指定 mapper.xml 文件位置 classpath 表示 resources 目录下
type-aliases-package: com.rainbowsea.springcloud.entity # 实例 bean 类所在的包,这样可以通过类名的方式
# 配置 eureka-client
eureka:
client:
register-with-eureka: true # 示将自己注册到 Eureka-Server,因为自己是作为 eureka-client客户端的角色的
# 表示从 Eureka-Server 抓取注册信息
# 如果是单节点,是可以配置的,但是如果是一个集群,则必须配置true
# 才能配合 Ribbon 使用负载均衡
fetch-registry: true
service-url:
# 表示将自己注册到那个 eureka-server
# 将本微服务注册到多个 eureka - server 当中,使用逗号间隔即可
defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/
创建主启动类名(场景启动器)
启动 eureka server 集群(目前 2 台)
启动 member-service-provider-10000
启动 member-service-provider-10002
浏览器输入: http://eureka9001.com:9001/
浏览器输入: http://eureka9002.com:9002/
5.5 配置服务消费端 member-service-consumer-80 使用会员中心服务集群
因为 member-service-provider-10000 和 member-service-provider-10002 作为一个集 群提供服务, 因此需要将 spring.application.name 进行统一,方便用于负载均衡
这样消费方通过统一的别名进行负载均衡调用。
示意图:
修改 member-service-consumer-80 模块下的,MemberConsumerController.java
package com.rainbowsea.springcloud.controller;
import com.rainbowsea.springcloud.entity.Member;
import com.rainbowsea.springcloud.entity.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@RestController
@Slf4j
public class MemberConsumerController {
// 定义 MEMBER_SERVICE_PROVIDER_URL 这是一个基础 url地址
// 使用 shift+ctrl+u 进行字母大小写的切换
//private static final String MEMBER_SERVICE_PROVIDER_URL = "http://localhost:10000";
// 负载均衡统一别名,修改为提供服务模块的注册别名: 不需要配置指明端口号,因为是一个负载集群了。不是特指某个了Service 服务来处理了
/*
server:
port: 10000
spring:
application:
name: member-service-provider # 配置应用的名称
*/
/*
说明:
1. MEMBER-SERVICE-PROVIDER 就是服务提供方【集群】,注册到Eureka Server 的名称
2. 也就是服务提供方【集群】对外暴露的名称为 MEMBER-SERVICE-PROVIDER
3. MEMBER-SERVICE-PROVIDER 目前有两个 Availability Zones UP (2) - localhost:member-service-provider:10000 , localhost:member-service-provider:10002
还有一个 member-service-provider:10002
需要增加一个注解 @LoadBalanced 赋予 RestTemplate 才能有负载均衡的能力,也就是会根据你的负载均衡
来选择某个服务去访问,默认是“轮询算法”,当然我们也可以自己配置负载均衡算法
4. 在对应的 RestTemplate 配置类上的,@Bean注解下面加上 @LoadBalanced 注解。
*/
private static final String MEMBER_SERVICE_PROVIDER_URL = "http://MEMBER-SERVICE-PROVIDER";
// 装配 RestTemplate bean/对象
@Resource
private RestTemplate restTemplate;
@PostMapping("/member/consumer/save")
public Result<Member> save(Member member) {
// 1.第1个参数: 就是请求的完整的url:MEMBER_SERVICE_PROVIDER_URL + "/member/save" => http://localhost:10000/member/save
// 表示你向将该请求,发送给那个微服务处理,注意无论是返回值,还是参数, @PostMapping() 请求方式都要一一对应上对应处理的微服务上的内容
//2. 第2个参数: member : 就是通过 restTemplate 发出的 post 请求携带数据或者对象
//3. 第3个参数: Result.class ,微服务当中的对应处理的方法的,返回值,也就是返回对象类型
// 注意:坑点
// 这里通过 restTemplate 调用服务模块的接口,就是一个远程调用
//
log.info("member-service-consumer-80 save member={}", member);
return restTemplate.postForObject
(MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);
}
/*
1.与
@PostMapping("/member/save")
public Result save(Member member) {
int affected = memberService.save(member);
if (affected > 0) { // 说明添加成功
return Result.success("添加会员成功", affected);
} else {
return Result.error("401", "添加会员失败");
}
}
*/
/**
* 方法/接口,调用服务接口,返回 member 对象信息
*
* @param id
* @return
*/
@GetMapping("/member/consumer/get/{id}")
public Result<Member> getMemberById(@PathVariable("id") Long id) {
// 这里就用两个参数
// 第一个参数,因为是查询,所以这里我们直接字符串拼接上去
// 这里通过
return restTemplate.getForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/get/" + id, Result.class);
}
/*
保持一致:
* 提交方式
* 返回类型
* 参数
* 路径映射要对应上
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id) {
Member member = memberService.queryMemberById(id);
// 使用 Result 把查询到的结果返回
if (member != null) {
return Result.success("查询会员成功", member);
} else {
return Result.error("402", "ID" + id + "不存在");
}
}
*/
// 注意:有两个是这个包下的 : org.springframework.cloud.client.discovery.DiscoveryClient
@Resource // 装配到 Spring ioc 容器当中
private DiscoveryClient discoveryClient;
// 注意:想要获取到服务端信息,还需要在SpringBoot 启动场景上加上:@EnableDiscoveryClient // 启用服务发现 注解才行
@GetMapping("/member/consumer/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();
// 遍历 services
for (String service : services) {
log.info("服务名==={}",service);
// 再根据服务名获取更加详细的信息
List<ServiceInstance> instances = discoveryClient.getInstances(service);
for (ServiceInstance instance : instances) {
log.info("id={},host={},uri={}",instance.getServiceId(),instance.getHost(),instance.getUri());
}
}
return this.discoveryClient;
}
}
为了看到测试效果,修改服务提供方
在两个服务提供方的查询和添加返回结果处增加自己服务名称信息,如图, 其它位置可参考加入
/**
* 这里我们使用 url占位符 + @PathVariable
*
* @param id
* @return
*/
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id) {
Member member = memberService.queryMemberById(id);
// 使用 Result 把查询到的结果返回
if (member != null) {
return Result.success("查询会员成功 member-service-provider-10000 ", member);
} else {
return Result.error("402", "ID" + id + "不存在 member-service-provider-10000 ");
}
}
/**
* 这里我们使用 url占位符 + @PathVariable
*
* @param id
* @return
*/
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id) {
Member member = memberService.queryMemberById(id);
// 使用 Result 把查询到的结果返回
if (member != null) {
return Result.success("查询会员成功 member-service-provider-10002 ", member);
} else {
return Result.error("402", "ID" + id + "不存在 member-service-provider-10002 ");
}
}
特别说明: 我们想要能够实现 member 交替访问的话,需要在 RestTemplate 配置类上@Bean方法上加上该 @LoadBalanced注解的)才行。
底层是 Ribbon 支持算法 交替访问 member服务说明:
- 注解@LoadBalanced(在RestTemplate 配置类的@Bean方法上加上该 @LoadBalanced注解的)
底层是 Ribbon 支持算法
- Ribbon 和 Eureka 整合后 consumer 直接调用服务而不用再关心地址和端口号,且该服务还有负载功能
运行测试:
启动 member-service-consumer-80
浏览器访问:http://eureka9001.com:9001/
浏览器访问: http://localhost/member/consumer/get/1 这个是通过客户端 Eureka Client 发送的请求路径映射:
5.6 获取 Eureka Server 服务注册信息 - DiscoveryClient
先看需求分析示意图:
这里我们以服务消费方 去获取 Eureka Server 的服务注册信息为例讲解
当然也可以在服务提供方获取 Eureka Server 的服务注册信息。
5.5.1 具体操作步骤:
所在模块 member-service-consumer-80,修改:com.rainbowsea.springcloud.controller.MemberConsumerController
包当中的
增加如下代码:
@Resource // 装配到 Spring ioc 容器当中
private DiscoveryClient discoveryClient;
// 注意:想要获取到服务端信息,还需要在SpringBoot 启动场景上加上:@EnableDiscoveryClient // 启用服务发现 注解才行
@GetMapping("/member/consumer/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();
// 遍历 services
for (String service : services) {
log.info("服务名==={}",service);
// 再根据服务名获取更加详细的信息
List<ServiceInstance> instances = discoveryClient.getInstances(service);
for (ServiceInstance instance : instances) {
log.info("id={},host={},uri={}",instance.getServiceId(),instance.getHost(),instance.getUri());
}
}
return this.discoveryClient;
}
package com.rainbowsea.springcloud.controller;
import com.rainbowsea.springcloud.entity.Member;
import com.rainbowsea.springcloud.entity.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@RestController
@Slf4j
public class MemberConsumerController {
// 定义 MEMBER_SERVICE_PROVIDER_URL 这是一个基础 url地址
// 使用 shift+ctrl+u 进行字母大小写的切换
//private static final String MEMBER_SERVICE_PROVIDER_URL = "http://localhost:10000";
// 负载均衡统一别名,修改为提供服务模块的注册别名: 不需要配置指明端口号,因为是一个负载集群了。不是特指某个了Service 服务来处理了
/*
server:
port: 10000
spring:
application:
name: member-service-provider # 配置应用的名称
*/
/*
说明:
1. MEMBER-SERVICE-PROVIDER 就是服务提供方【集群】,注册到Eureka Server 的名称
2. 也就是服务提供方【集群】对外暴露的名称为 MEMBER-SERVICE-PROVIDER
3. MEMBER-SERVICE-PROVIDER 目前有两个 Availability Zones UP (2) - localhost:member-service-provider:10000 , localhost:member-service-provider:10002
还有一个 member-service-provider:10002
需要增加一个注解 @LoadBalanced 赋予 RestTemplate 才能有负载均衡的能力,也就是会根据你的负载均衡
来选择某个服务去访问,默认是“轮询算法”,当然我们也可以自己配置负载均衡算法
4. 在对应的 RestTemplate 配置类上的,@Bean注解下面加上 @LoadBalanced 注解。
*/
private static final String MEMBER_SERVICE_PROVIDER_URL = "http://MEMBER-SERVICE-PROVIDER";
// 装配 RestTemplate bean/对象
@Resource
private RestTemplate restTemplate;
@PostMapping("/member/consumer/save")
public Result<Member> save(Member member) {
// 1.第1个参数: 就是请求的完整的url:MEMBER_SERVICE_PROVIDER_URL + "/member/save" => http://localhost:10000/member/save
// 表示你向将该请求,发送给那个微服务处理,注意无论是返回值,还是参数, @PostMapping() 请求方式都要一一对应上对应处理的微服务上的内容
//2. 第2个参数: member : 就是通过 restTemplate 发出的 post 请求携带数据或者对象
//3. 第3个参数: Result.class ,微服务当中的对应处理的方法的,返回值,也就是返回对象类型
// 注意:坑点
// 这里通过 restTemplate 调用服务模块的接口,就是一个远程调用
//
log.info("member-service-consumer-80 save member={}", member);
return restTemplate.postForObject
(MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);
}
/*
1.与
@PostMapping("/member/save")
public Result save(Member member) {
int affected = memberService.save(member);
if (affected > 0) { // 说明添加成功
return Result.success("添加会员成功", affected);
} else {
return Result.error("401", "添加会员失败");
}
}
*/
/**
* 方法/接口,调用服务接口,返回 member 对象信息
*
* @param id
* @return
*/
@GetMapping("/member/consumer/get/{id}")
public Result<Member> getMemberById(@PathVariable("id") Long id) {
// 这里就用两个参数
// 第一个参数,因为是查询,所以这里我们直接字符串拼接上去
// 这里通过
return restTemplate.getForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/get/" + id, Result.class);
}
/*
保持一致:
* 提交方式
* 返回类型
* 参数
* 路径映射要对应上
@GetMapping("/member/get/{id}")
public Result getMemberById(@PathVariable("id") Long id) {
Member member = memberService.queryMemberById(id);
// 使用 Result 把查询到的结果返回
if (member != null) {
return Result.success("查询会员成功", member);
} else {
return Result.error("402", "ID" + id + "不存在");
}
}
*/
// 注意:有两个是这个包下的 : org.springframework.cloud.client.discovery.DiscoveryClient
@Resource // 装配到 Spring ioc 容器当中
private DiscoveryClient discoveryClient;
// 注意:想要获取到服务端信息,还需要在SpringBoot 启动场景上加上:@EnableDiscoveryClient // 启用服务发现 注解才行
@GetMapping("/member/consumer/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();
// 遍历 services
for (String service : services) {
log.info("服务名==={}",service);
// 再根据服务名获取更加详细的信息
List<ServiceInstance> instances = discoveryClient.getInstances(service);
for (ServiceInstance instance : instances) {
log.info("id={},host={},uri={}",instance.getServiceId(),instance.getHost(),instance.getUri());
}
}
return this.discoveryClient;
}
}
- 这里修改所在模块 member-service-consumer-80当中的主启动类(场景启动类)
com.rainbowsea.springcloud.MemberConsumerApplication80
package com.rainbowsea.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
// 如果错误: 加入排除 DataSourceAutoConfiguration 自动配置
//@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//@EnableEurekaClient 表示将项目/模块/程序 标注作为 Eureka Client
@EnableEurekaClient
@SpringBootApplication
@EnableDiscoveryClient // 启用服务发现
public class MemberConsumerApplication80 {
public static void main(String[] args) {
SpringApplication.run(MemberConsumerApplication80.class, args);
}
}
重启 member-service-consumer-80
浏览器输出 http://localhost/member/consumer/discovery
5.5.1.1 注意事项和细节说明
在引入 DiscoveryClient 时,不要引入错误的包
正确的包: import org.springframework.cloud.client.discovery.DiscoveryClient;
错误的包: import com.netflix.discovery.DiscoveryClient;
演示的是在服务消费方使用 DiscoveryClient 来完成服务发现,同样,在服务提供 方/模块也 OK
7. Eureka 后续说明
- Eureka 停更说明: https://github.com/Netflix/eureka/wiki
-
对于一些早期使用 Eureka 项目,掌握老师讲解技术基本可以应付了(这也是老师为什么还要讲Eureka的原因)
-
目前替代Eureka做服务注册和发现均衡负载的最佳组件是Nacos , 后面会重点讲解
Spring Cloud Alibaba -
虽然 Eureka 停更,目前用的不多,但是它的服务注册和发现均衡负载的思想是优先
的,有了Eureka的基础,我们学习Spring Cloud Alibaba Nacos会轻松很多
8. 总结:
- Spring Cloud Eureka 服务注册于发现
- Eureka 自我保护模式
默认情况下EurekaClient 定时向 EurekaServer 端发送 心跳包
如果 Eureka 在 server 端一定时间内(默认90秒) 没有收到 EurekaClient 发送心跳包,便会
直接从服务注册表中剔除该服务如果Eureka 开启了**“自我保护机制/模式”** ,那么在短时间(90秒中)内丢失了大量的服务实例心跳。
这时候Eureka Server 会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通或者
阻塞)因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的。自我保护是属于CAP里面的AP分支 ,保证高可用和分区容错性
自我保护模式是一种应对网络异常的安全保护措施,它的架构哲学是宁可同时保留所有微服务(健康
的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,
可以让 Eureka 集群更加的健壮,稳定,参考:
https://blog.csdn.net/wangliangluang/article/details/120626014
在生产环境中,一般不会禁用自我保护模式 。
- Eureka 集群-实现负载均衡,故障容错
为什么需要集群 Eureka Server
说明:
- 微服务 RPC 远程服务调用最核心的是实习高可用
- 如果注册中心只有1个,它出故障,会导致整个服务环境不可用
- 解决办法: 搭建 Eureka 注册中心集群,实习 负载均衡 + 故障容错
因为 member-service-provider-10000 和 member-service-provider-10002 作为一个集 群提供服务, 因此需要将 spring.application.name 进行统一,方便用于负载均衡
这样消费方通过统一的别名进行负载均衡调用。
特别说明: 我们想要能够实现 member 交替访问的话,需要在 RestTemplate 配置类上@Bean方法上加上该 @LoadBalanced注解的)才行。 底层是 Ribbon 支持算法 交替访问 member服务说明:
注解@LoadBalanced(在RestTemplate 配置类的@Bean方法上加上该 @LoadBalanced注解的) 底层是 Ribbon 支持算法
Ribbon 和 Eureka 整合后 consumer 直接调用服务而不用再关心地址和端口号,且该服务还有负载功能
- 定义本地的域名机制:
设置本地的域名映射,需要找到本地电脑当中的 host
文件,在该 host
文件当中编写添加相应的域名映射。
host文件路径: C:\Windows\System32\drivers\etc\host
127.0.0.1 account.wondershare.com
# 配置 eureka 主机 和 ip 的映射
127.0.0.1 eureka9001.com
127.0.0.1 eureka9002.com
特别提醒:如果发现文件修改后无法保存,那么可以将文件复制到桌面上,然后,修改复制到桌面上的那份复制过来的 host 文件,修改保存完后,再拷贝回原来路径位置,进行一个替换。
- 获取 Eureka Server 服务注册信息 - DiscoveryClient;在引入 DiscoveryClient 时,不要引入错误的包,正确的包: import org.springframework.cloud.client.discovery.DiscoveryClient;
9. 最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 3. Spring Cloud Eureka 服务注册与发现(超详细说明及使用)
发表评论 取消回复