1、Spring Cloud 组件选型- 图

2、从上图可以看出, 目前主流的服务注册&发现的组件是 Nacos, 但是 Eureka 作为一个老牌经典的服务注册&发现技术还是有必要学习一下, 原因
(1) 一些早期的分布式 微服务项目使用的是 Eureka
(2) 后期的服务注册&发现组件/技术, 都参考了 Eureka 设计和理念

问题分析
1.在企业级项目中,服务消费访问请求会存在高并发
2.如果只有一个会员中心-提供服务,可用性差
3.所以,会员中心提供服务往往是一个集群,也就是说会有多个会员中心-提供服务微服务模块
4.那么这个时候,就存在一个问题就是服务消费方,怎么去发现可以使用的服务
5.当服务消费方,发现了可以使用的服务后(可能是多个,又存在一个问题就是到底调用 A 服务,还是 B 服务的问题,这就引出了服务注册和负载均衡)
6.Eureka 就可以解决上述问题

–服务注册与发现

1.Eureka采用了CS[client-server]的设计架构, Eureka Server 作为服务注册功能的服务器,它是服务注册中心。
2.系统中的其他微服务,使用 Eureka的客户端连接到 Eureka Server并维持心跳连接, 通过 Eureka Server 来监控系统中各个微服务是否正常运行。
3.在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如服务地址通讯地址等以别名方式注册到注册中心上。
4.服务消费者或者服务提供者,以服务别名的方式去注册中心上获取到实际的服务提供 者通讯地址,然后通过RPC调用服务。

1.搭建工程
说明:
1.e-commerce-eureka-server-9001作为eureka-server
2.e_commerce_center-common-api作为公共模块
3.e-commerce-center作为父工程,指定依赖的版本利用maven依赖的传递性对子模块依赖版本进行统一管理
4.member-service-consumer-80,调度会员中心-服务消费放
5.member-service-provider-10000,会员中心-服务提供方;两个都作为eureka-client客户端,均需注入到eureka-server端

2.引入相关依赖
eureka-server服务端
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
eureka-client客户端
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
3.修改application.yml
eureka-server服务端
server:
port: 9001
#配置eureka-server
eureka:
instance:
#服务实例名称|服务别名
hostname: localhost
#作为一个eureka-server为什么还需要配置client?因为后面eureka-server可能是集群会相互注册
client:
#配置不像注册中心注册自己
register-with-eureka: false
#表示自己就是注册中心,作用就是维护注册的服务实例,不需要去检索服务
fetch-registry: false
service-url:
#设置与eureka-server交互的模块,查询服务和注册都需要依赖的地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
eureka-client客户端
#配置eureka-client
eureka:
client:
#将自己注册到eureka-server
register-with-eureka: true
#表示从eureka-server获取注册信息
#如果是单节点是可以不配置的,但如果是集群则必须配置为true,才能配合Ribbon实现负载均衡功能
fetch-registry: true
service-url:
#表示将自己注册到哪个eureka-server
defaultZone: http://localhost:9001/eureka/
4.修改主启动
eureka-server服务端
//@EnableEurekaServer:表示该程序作为eureka-server
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
eureka-client客户端
//@EnableEurekaClient:表示该程序作为eureka-client
@EnableEurekaClient
@MapperScan(basePackages = {"com.llp.springcloud.dao"})
@SpringBootApplication
public class MemberApplication {
public static void main(String[] args) {
SpringApplication.run(MemberApplication.class, args);
}
}
5.完成测试
1.服务名就是客户端注册到eureka-server指定的名称
2.这里member-service-consumer-80和member-service-provider-10000两个服务对于eureka-server来说都是客户端,因此参考配置eureka-client即可



(1)默认情况下EurekaClient定时向EurekaServer端发送心跳包
(2)如果Eureka在server端在一定时间内(默认90秒)没有收到EurekaClient发送心跳包, 便会直接从服务注册列表中剔除该服务
(3)如果Eureka 开启了自我保护模式/机制, 那么在短时间(90秒中)内丢失了大量的服 务实例心跳,这时候EurekaServer会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通或者阻塞) 因为客户端还能正常发送心跳,只是网络延迟问题, 而保护机制是为了解决此问题而产生的
自我保护是属于 CAP 里面的 AP 分支, 保证高可用和分区容错性
自我保护模式是—种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服 务 。 使用自我保护模 式 , 可以让 Eureka 集 群更加的健壮 、稳 定 。 参 考 : CAP理论详解
测试 启动member-service-provider-10000 和 e-commerce-eureka-server-9001 ,让member-service-provider-10000 正确的注册,然后关闭member-service-provider-10000 ,观察注册的 member-service-provider-10000 服务是否还在.
#配置eureka-server
eureka:
instance:
#服务实例名称|服务别名
hostname: localhost
#作为一个eureka-server为什么还需要配置client?因为后面eureka-server可能是集群会相互注册
client:
#配置不像注册中心注册自己
register-with-eureka: false
#表示自己就是注册中心,作用就是维护注册的服务实例,不需要去检索服务
fetch-registry: false
service-url:
#设置与eureka-server交互的模块,查询服务和注册都需要依赖的地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
#禁用自我保护模式 false-禁用 true-开启
enable-self-preservation: false
#设置超时时间,在设置的时间(单位:毫秒)内,收不到心跳包就认为超时,默认90秒 如果超时就会将服务从eureka注册中心剔除
eviction-interval-timer-in-ms: 2000
#配置eureka-client
eureka:
client:
#将自己注册到eureka-server
register-with-eureka: true
#表示从eureka-server获取注册信息
#如果是单节点是可以不配置的,但如果是集群则必须配置为true,才能配合Ribbon实现负载均衡功能
fetch-registry: true
service-url:
#表示将自己注册到哪个eureka-server
defaultZone: http://localhost:9001/eureka/
instance:
#客户端像服务端发送心跳的间隔时间 默认30s
lease-renewal-interval-in-seconds: 1
#服务端收到最后一次心跳等待的时间上线,时间单位/s 默认90s 超时将剔除服务
lease-expiration-duration-in-seconds: 2
3.启动测试
可以看到eureka自我保护模式已经关闭了

停止服务后,eureka将服务从注册中心剔除




<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
9001
#配置eureka-server
eureka:
instance:
#服务实例名称|服务别名
hostname: eureka9001.com
#作为一个eureka-server为什么还需要配置client?因为后面eureka-server可能是集群会相互注册
client:
#配置不像注册中心注册自己
register-with-eureka: false
#表示自己就是注册中心,作用就是维护注册的服务实例,不需要去检索服务
fetch-registry: false
service-url:
#设置与eureka-server交互的模块,查询服务和注册都需要依赖的地址
defaultZone: http://eureka9002.com:9002/eureka/
9002
#配置eureka-server
eureka:
instance:
#服务实例名称|服务别名
hostname: eureka9002.com
#作为一个eureka-server为什么还需要配置client?因为后面eureka-server可能是集群会相互注册
client:
#配置不像注册中心注册自己
register-with-eureka: false
#表示自己就是注册中心,作用就是维护注册的服务实例,不需要去检索服务
fetch-registry: false
service-url:
#设置与eureka-server交互的模块,查询服务和注册都需要依赖的地址
defaultZone: http://eureka9001.com:9001/eureka/
说明:因为我们实在本机进行模拟,修该hosts文件,才能实现根据域名去本机查询ip地址(先在本机hosts文件查找,找不到再到DNS去查找)
#配置eureka 主机和ip的映射
127.0.0.1 eureka9001.com
127.0.0.1 eureka9002.com
启动 e-commerce-eureka-server-9001、e-commerce-eureka-server-9002


1.修改 resources/application.yml
#配置eureka-client
eureka:
client:
#将自己注册到eureka-server
register-with-eureka: true
#表示从eureka-server获取注册信息
#如果是单节点是可以不配置的,但如果是集群则必须配置为true,才能配合Ribbon实现负载均衡功能
fetch-registry: true
service-url:
#表示将自己注册到哪个eureka-server
#defaultZone: http://localhost:9001/eureka/
#将本微服务注册到多个 eurekaServer, 使用逗号隔开
defaultZone: http://eureka9001.com:9001/eureka/,http://eureka9002.com:9002/eureka/
2.完成测试


将 member-service-consumer-80 注册到 EurekaServer 集群(同上)


修改端口号、主启动即可,其他同member-service-provider-10000
server:
port: 10002
//@EnableEurekaClient:表示该程序作为eureka-client
@EnableEurekaClient
@MapperScan(basePackages = {"com.llp.springcloud.dao"})
@SpringBootApplication
public class MemberApplication10002 {
public static void main(String[] args) {
SpringApplication.run(MemberApplication10002.class, args);
}
}
启动 eureka server 集群(目前 2 台)
启动 member-service-provider-10000
启动 member-service-provider-10002



@Configuration
public class CustomizationBean {
// 配置注入RestTemplate bean/对象
//这里的@LoadBalanced 就是赋予 RestTemplate 负载均衡的能力
//默认是使用轮询算法来访问远程调用接口/地址
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
@Slf4j
@RestController
@RequiredArgsConstructor
public class MemberConsumerController {
//定义一个服务端基础地址
public static final String MEMBER_SERVICE_PROVIDER_URL = "http://MEMBER-SERVICE-PROVIDER";
private final RestTemplate restTemplate;
@PostMapping("/member/consumer/save")
public Result save(Member member) {
log.info("member:{}", member);
/**
* url:请求的完整地址
* member:通过restTemplate发出的post请求携带的数据
* Result.class: 返回对象类型
* 注意:restTemplate底层发送数据是通过json字符串进行传输的,服务端接受数据需要加上@RequestBody注解才能解析数据
* 传输的数据通过流进行传输,需要实现序列化接口
*/
return restTemplate.postForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);
}
@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);
}
}
交替访问member服务说明

1.先看需求分析示意图

这里我们以服务消费方, 去获取 Eureka Server 的服务注册信息为例
当然也可以在服务提供方获取 Eureka Server 的服务注册信息
MemberConsumerApplication主启动类添加@EnableDiscoveryClient注解
@EnableEurekaClient
@SpringBootApplication
@EnableDiscoveryClient
public class MemberConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(MemberConsumerApplication.class,args);
}
}
MemberConsumerController
//装配DiscoveryClient
private final DiscoveryClient discoveryClient;
@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={},port={},uri={}",
instance.getServiceId(),instance.getHost(),instance.getPort(),instance.getUri());
}
}
return discoveryClient;
}

注意事项和细节说明
正确的包: import org.springframework.cloud.client.discovery.DiscoveryClient;
错误的包: import com.netflix.discovery.DiscoveryClient;
2.在服务消费方使用 DiscoveryClient 来完成服务发现,同样在服务提供方/模块也 OK