1.新建Module-注册中心
作为注册中心
1.1配置 pom.xml
<!-- 引入 eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
1.2配置application.yml
server:
port: 9001
eureka:
instance:
hostname: localhost # Eureka 服务端的主机名
client:
register-with-eureka: false # 是否将自己注册到 Eureka 服务端,表明自己是一个 Eureka 注册中心,职责是维护服务实例,并不需要注册自己
fetch-registry: false # 是否从 Eureka 服务端获取注册信息
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与 eureka server 交互的模块,查询服务和注册服务都需要依赖这个地址
1.3新建启动类
@SpringBootApplication
@EnableEurekaServer //开启Eureka服务
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
1.4测试
浏览器访问 http://localhost:9001/
2.服务提供者
配置服务提供者的Module信息
2.1 服务提供者引入eureka-client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.2 配置application.yml
eureka:
client:
register-with-eureka: true # 将自己注册到 Eureka 服务端
fetch-registry: true # 是否从 Eureka 服务端获取注册信息,默认为true,集群必须为true,才能配合负载均衡
service-url:
defaultZone: http://localhost:9001/eureka/ #设置与 eureka server 交互的模块,查询服务和注册服务都需要依赖这个地址
2.3 设置启动类
@SpringBootApplication
@EnableEurekaClient //开启Eureka客户端
public class MemberApplication {
public static void main(String[] args) {
SpringApplication.run(MemberApplication.class, args);
}
}
2.4 测试
启动客户端的启动类后,访问 localhost:9001
,会看到新注册的实例
实例的名称是怎么确定的呢,这是在我们的application.yml自定义的
spring:
application:
name: member-service-provider #配置应用的名称
3.服务消费者
配置服务消费者的Module信息
3.1 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.2 配置 application.yml
eureka:
client:
register-with-eureka: true # 将自己注册到 Eureka 服务端
fetch-registry: true # 是否从 Eureka 服务端获取注册信息,默认为true,集群必须为true,才能配合负载均衡
service-url:
defaultZone: http://localhost:9001/eureka/ #表示关联的eureka服务端地址,注册/拉取服务信息
3.3 配置启动类
@SpringBootApplication
@EnableEurekaClient
public class MemberConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(MemberConsumerApplication.class, args);
}
}
3.4 测试
启动,测试
4.Eureka自我保护模式
在以上测试中,我们会发现eureka页面中有以下红色提示:
- 默认情况下EurekaClient定时向EurekaServer端发送心跳包
- 如果Eureka在server端在一定时间内(默认90秒)没有收到EurekaClient发送心跳包便会直接从服务注册列表中剔除该服务
- 如果Eureka 开启了自我保护模式/机制, 那么在短时间(90秒中)内丢失了大量的服务实例心跳,这时候EurekaServer会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通或者阻塞) 因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的
- 可以在eureka-server端禁用自我保护模式,设置application.yml
server:
enable-self-preservation: false # 关闭自我保护机制
eviction-interval-timer-in-ms: 6000 # 设置心跳检测的时间间隔,默认是60秒
- 设置eureka-client端 application.yml
eureka:
instance:
lease-expiration-duration-in-seconds: 5 # Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒,默认90秒,超过这个时间服务端会将实例从注册列表中剔除
lease-renewal-interval-in-seconds: 2 # eureka客户端向服务端发送心跳的时间间隔,单位为秒,默认30秒
5.搭建EurekaServer集群
微服务RPC远程服务调用最核心的是实现高可用,如果注册中心只有1个,那么一旦出现故障,导致整个服务环境都不可用,解决方案就是搭建Eureka注册中心集群,并且实现负载均衡和故障容错。
5.1 新建Module-第二个注册中心
配置过程如第一个注册中心,区别是appication.yml的配置:
server:
port: 9002
eureka:
instance:
hostname: eureka9002.com # 区别于9001的注册中心
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka9001:9001/eureka/ # 相互注册
因为是相互注册,所以把第一个注册中心的hostname改成了eureka9001.com
:
server:
port: 9001
eureka:
instance:
hostname: eureka9001.com # Eureka 服务端的主机名
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka9002.com:9002/eureka/
5.2 设置系统hosts
C:\Windows\System32\drivers\etc\hosts
加入
#eureka 主机名和 ip 映射
127.0.0.1 eureka9001.com
127.0.0.1 eureka9002.com
5.3 测试
浏览器打开对应的地址,会发现互相指向,按理说配置好hosts文件后,我应该访问eureka9001.com/9001
和 eureka9002.com/9002
,但是没能成功,先用localhost:9001
和localhost:9002
进行测试
5.4 服务提供者注册到Eureka集群
注册到多个eureka即可
启动后,进行测试,也就是到localhost:9001
和localhost:9002
查看注册成功
5.5 服务消费者注册到Eureaka集群
注册到多个eureka
6.搭建服务提供方集群
Eureka已经搭建好集群,实际中服务提供方也是会有多个形成一个集群
6.1 新建一个新的服务提供方
复制粘贴第一个服务提供方的代码,唯一要必须修改的地方是端口号,我们设置成10003,其中spring.application.name 必须统一,因为要作为一个集群提供服务
进行测试:
7.服务消费者使用服务集群
7.1 修改Controller
原先的Controller如下,具体来说,MEMBER_SERVICE_PROVIDER_URL被固定了:
@RestController
@Slf4j
public class MemberConsumerController {
public static final String MEMBER_SERVICE_PROVIDER_URL = "http://localhost:10002";
@Resource
private RestTemplate restTemplate;
@PostMapping("/member/consumer/save")
public Result<Member> save(Member member) {
return restTemplate.postForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);
}
@GetMapping("/member/consumer/get/{id}")
public Result<Member> get(@PathVariable("id") Long id) {
return restTemplate.getForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/get/" + id, Result.class);
}
}
我们修改成服务提供者[集群]的注册别名:
修改后:
public static final String MEMBER_SERVICE_PROVIDER_URL = "http://MEMBER-SERVICE-PROVIDER";
因为我们现在的服务提供者是集群,所以需要使用负载均衡,加上注解@LoadBalanced赋予RestTemplate负载均衡的能力, [即选择 MEMBER-SERVICE-PROVIDER 某一个服务访问]
@Configuration
public class CustomizationBean {
// 注入 RestTemplate
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
7.2 测试
启动服务消费者,进行测试
再发送一次
会发现当前负载均衡的策略是轮询算法
8.获取Eureka Server服务注册信息
以服务消费者为例获取eureka的服务注册信息
8.1 修改Controller
我们在cotroller加入代码
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/member/consumer/discovery")
public Object discovery() {
List<String> services = discoveryClient.getServices();
for (String element : services) {
System.out.println("======== 服 务 名 " + element +
"=======================");
List<ServiceInstance> instances = discoveryClient.getInstances(element);
for (ServiceInstance instance : instances) {
System.out.println(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
}
}
return this.discoveryClient;
}
其中,DiscoverClient引入下面包的:
8.2 修改启动类
添加@EnableDiscoveryClient注解
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class MemberConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(MemberConsumerApplication.class, args);
}
}
8.3 测试
启动后,浏览器访问http://localhost/member/consumer/discovery
浏览器返回:
{
"services": [
"member-service-consumer",
"member-service-provider"
],
"order": 0
}
后台返回: