SpringCloud02-Eureka
SpringCloud02-Eureka
1.Eureka注册中心
- 传统的RPC框架,管理服务与服务之间依赖关系比较复杂,所以需要使用注册中心,管理服务于服务之间依赖关系。
- Eureka采用了CS的设计架构,Eureka Sever作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
- Eureka Server提供服务注册服务。各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
- Eureka Client通过注册中心获取服务地址。
2.搭建通用模块的cloud-api-commons
- pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.13</version>
</dependency>
</dependencies>
- 实体类Payment
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
private Long id;
private String serial;
}
- 响应统一结果CommonResult
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
private Integer code;
private String message;
private T data;
public CommonResult(Integer code, String message){
this(code, message, null);
}
}
3.创建Eureka Server微服务cloud-eureka-server7001
- pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
- 单机时Eureka Server的yml配置
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
# false表示不向注册中心注册自己,单机时不需要向注册中心注册自己。集群时需要相互注册。
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
# 设置与Eureka server交互的地址,Eureka Client查询服务和注册服务都需要依赖这个地址。
# 单机服务指向自己
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
# 关闭 Eureka的自我保护。默认true,开启。
enable-self-preservation: false
# 设置清理无效服务实例的时间。默认60秒清理一次。
eviction-interval-timer-in-ms: 60000
- Eureka Server启动类。
@EnableEurekaServer // Eureke Server端
@SpringBootApplication
public class CloudEurekaServer7001Main {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaServer7001Main.class, args);
}
}
4.创建Eureka Client微服务cloud-eureka-provider-payment8001
- pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 后续需要 下面的依赖时使用 同SpringCloud02-Eureka-4.1 代替 -->
<!-- 通用模块 -->
<dependency>
<groupId>com.my.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- yml配置
server:
port: 8001
spring:
application:
name: cloud-provider-payment # 微服务名称,显示在左边
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=UTF8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.my.springcloud.entity
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
# 是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
# 单机注册
defaultZone: http://127.0.0.1:7001/eureka
# Eureka Client从Eureka Server拉取配置信息的频率,默认30秒客户端从服务端拉取一下配置信息。
registry-fetch-interval-seconds: 30
instance:
# 修改Eureka界面微服务实例名显示为payment8001,即右边显示。
instance-id: payment8001
# 显示ip地址,当鼠标悬停时在右边微服务实例上时,左下角是否显示ip
prefer-ip-address: true
# eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,
# 在这个时间内若没收到下一次心跳,则将移除该instance,默认90秒。
lease-expiration-duration-in-seconds: 90
# Eureka Client发送心跳给Eureka Server的频率。默认30秒。
lease-renewal-interval-in-seconds: 30
- 启动类
@EnableEurekaClient
@SpringBootApplication
public class CloudEurekaProviderPaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaProviderPaymentMain8001.class, args);
}
}
- controller、service、dao、mapper......省略。
- 启动cloud-eureka-provider-payment8001,将其注册到cloud-eureka-server7001
5.创建Euerka Client微服务cloud-eureka-consumer-order80
- pom.xml、yml、Main相同。
- controller
@Slf4j
@RestController
public class OrderController {
public static final String PROVIDER_PAYMENT = "http://CLOUD-PROVIDER-PAYMENT";
@Resource
private RestTemplate restTemplate;
@PostMapping("/consumer/payment/create")
public CommonResult<?> create(@RequestBody Payment payment) {
CommonResult<?> commonResult = restTemplate.postForObject(PROVIDER_PAYMENT + "/payment/create",
payment, CommonResult.class);
if (commonResult == null || commonResult.getCode() == 500) {
log.info("创建失败,{}", commonResult);
}
return commonResult;
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<?> getById(@PathVariable("id") Long id) {
return restTemplate.getForObject(PROVIDER_PAYMENT + "/payment/get/" + id, CommonResult.class);
}
}
- RestTemplate配置类
@Configuration
public class RestTemplateConfig {
/**
* 负载均衡,通过微服务名称cloud-provider-payment调用cloud-eureka-provider-payment8001。
* 需要引入org.springframework.cloud.client.loadbalancer.LoadBalanced。
* 这里调用没有问题是因为
* spring-cloud-starter-netflix-eureka-client中spring-cloud-starter-loadbalancer。
* 在使用其他的注册中心时,需要检查是否引入spring-cloud-starter-loadbalancer。
* @return
*/
@LoadBalanced
@Bean("restTemplate")
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
6.Eureka Server集群原理
- Eureka Server 集群相互之间通过 Replicate 来同步数据,相互之间不区分主节点和从节点,所有的节点都是平等的。
- 节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。
- 如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点。当宕机的服务器重新恢复后,Eureka Server集群会再次将其纳入到服务器集群管理之中。
- Eureka Server 集群之间的状态是采用异步方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的。
- 相互注册,相互守望。
7.创建Eureka Server集群,创建cloud-eureka-server7002
- pom.xml、Main同上
- yml
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
# 集群时,Eureka Server也是一个Client,需要向其他Eureka Server注册
register-with-eureka: true
fetch-registry: true
service-url:
# Eureka集群时指向其他的注册中心
defaultZone: http://eureka7001.com:7001/eureka
- 修改本地hosts文件。
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
8.创建微服务提供者集群,创建cloud-eureka-provider-payment8002
- pom.xml、Main、业务代码省略。
- yml不同点,cloud-eureka-provider-payment8002需要注册到Eureka Server集群中。
spring:
application:
# 和cloud-eureka-provider-payment8001 的name保持一致,
# 这时Eureka界面cloud-provider-payment的右边有两个微服务实例,8001和8002
name: cloud-provider-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
# 集群注册
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
- cloud-eureka-consumer-order80通过 RestTemplate+LoadBalance 调用8001和8002的服务。
9.获取微服务信息的代码
@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class CloudEurekaProviderPaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaProviderPaymentMain8001.class, args);
}
}
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/payment/discovery")
public Object discovery() {
// 获取注册中心注册的服务列表,有两个服务 "cloud-consumer-order","cloud-provider-payment"
// 返回的数据
// {"services":["cloud-consumer-order","cloud-provider-payment"],"order":0}
List<String> services = discoveryClient.getServices();
for (String service : services) {
log.info("服务 ===== {}", service);
}
// CLOUD-PROVIDER-PAYMENT服务有两个实例,8001和8002
// 服务的实例名=CLOUD-PROVIDER-PAYMENT, 地址=192.168.2.103, 端口8001, uri=http://192.168.2.103:8001, instance_id=payment8001
// 服务的实例名=CLOUD-PROVIDER-PAYMENT, 地址=192.168.2.103, 端口8002, uri=http://192.168.2.103:8002, instance_id=payment8002
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLOUD-PROVIDER-PAYMENT");
for (ServiceInstance instance : serviceInstances) {
log.info("服务的实例名={}, 地址={}, 端口{}, uri={}, instance_id={}",
instance.getServiceId(),
instance.getHost(),
instance.getPort(),
instance.getUri(),
instance.getInstanceId());
}
return discoveryClient;
}
10.Eureka自我保护机制
-
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
-
如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制。
-
在Eureka进入自我保护时,Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务;Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用;当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
-
关闭Eureka自我保护机制。
eureka:
server:
# 关闭 Eureka的自我保护。默认true,开启。
enable-self-preservation: false
# 设置清理无效服务实例的时间。默认60秒清理一次。
eviction-interval-timer-in-ms: 60000
- 关闭自我保护模式之后,第一次访问Eureka提示。THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
11.Eureka总结
- Eureka Client默认30秒向Eureka Server发送心跳。
- Eureka Server发现Eureka Client90秒都没有发送心跳,下次清理时,直接剔除该Eureka Client。
- 默认60秒Eureka Server清理一次无效的Eureka Server。
- Eureka Client每30秒从Eureka Server拉取一次配置信息。
- Eureka Server自我保护模式默认开启,15分钟内超过85%的Eureka Clent都没有正常的心跳,Eureka Server自动进入自我保护模式。
- Eureka Client默认将自己注册到Eureka Server,并从Eureka Server拉取配置信息。
# Eureka Client配置
eureka:
client:
# 注册到Eureka Server
register-with-eureka: true
# 从Eureka Server拉取配置信息
fetch-registry: true
# 每30秒拉取配置信息
registry-fetch-interval-seconds: 30
instance:
# eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,
# 在这个时间内若没收到下一次心跳,则将移除该instance,默认90秒。
lease-expiration-duration-in-seconds: 90
# Eureka Client发送心跳给Eureka Server的频率。默认30秒。
lease-renewal-interval-in-seconds: 30
# Eureka Server配置
eureka:
server:
# 关闭 Eureka的自我保护。默认true,开启。
enable-self-preservation: true
# 设置清理无效服务实例的时间。默认60秒清理一次。
eviction-interval-timer-in-ms: 60000