目录
一、什么是Eureka
Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现与故障转移。服务注册与发现对于微服务架构来说非常重要,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件。
Springcloud封装了Netflix公司开发的Eureka模块来实现服务注册和发现。
Eureka采用了C-S的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。 而系统汇总的其他服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。 SpringCloud的一些其他模块(比如Zuul)就可以通过Eureka Server来发现系统中的其他微服务,并执行相关的逻辑。
Eureka系统架构图
1.1 Eureka 两组件
Eureka 包含两个组件 :Eureka Server和Eureka Client
Eureka Server提供服务注册服务
各个未付节点通过配置启动后,会再Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
Eureka Client通过注册中心进行访问
是一个Java客户端,用户简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30s)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中将这个服务节点移除(默认90s)。
Eureka 三大角色:
Eureka Server提供服务注册和发现
Service Provider服务提供方将自身服务注册到Eureka,从而使服务消费方能够找到
Service Consumer服务消费方从Eureka获取注册服务列表,从而能够消费服务。
二、Eureka构建 cloud-eureka-server7001
#2.1 pom文件
<dependencies>
<!--eureka-server服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--引入自己定义的api通用包-->
<dependency>
<groupId>com.diligentkong.springcloud</groupId>
<artifactId>cloud-api-common</artifactId>
<version>${project.version}</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>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
2.2 application.yml
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)
2.3 主启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
2.4 测试
访问 http://localhost:7001/
No instances available 没有服务被发现
因为没有注册服务进来,所以不可能有服务被发现
2.5 修改cloud-provider-payment8001,cloud-consumer-order80
在pom文件中添加:
<!-- 注册服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在其application.yml中添加
eureka:
client:
#表示是否将自己注册进EurekaServer中 默认为true
register-with-eureka: true
#是否从Eureka Server抓取已有的注册信息,默认为true 单节点无所谓,集群必须设置为true 才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
在其主启动类上添加注解: @EnableEurekaClient
测试:先启动7001 在启动8001 ,访问http://localhost:7001/
能看到8001服务注册进来
对cloud-consumer-order80进行同样的操作,cloud-consumer-order80的application.yml中添加内容
application:
name: cloud-order-service
启动80服务,访问http://localhost:7001/
问题:微服务RPC远程服务调用最核心的是什么?
答: 高可用,试想你的注册中心只有一个only one,它出故障了会导致整个微服务环境不可用,所以解决办法: 搭建Eureka注册中心集群,实现负载均衡 + 故障容错。
Eureka集群原理说明
三、EurekaServer集群环境构建
3.1 参考cloud-eureka-server7001,新建module cloud-eureka-server7002
7002的pom文件与7001中pom中的依赖一致
3.2 application.yml
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
3.3 主启动类
主启动类:
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7002.class,args);
}
}
3.4 修改映射配置
找到C:\Windows\System32\drivers\etc路径下的hosts文件,修改映射配置添加到hosts文件中
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
3.5 测试
启动cloud-eureka-server7002
访问:http://localhost:7002/
修改7001的application.yml
3.6 订单支付两个微服务注册进Eureka集群
将支付服务8001微服务与订单服务80微服务 发布到上面2台Eureka集群配置中
修改服务8001与80的yml,修改defaultZone的值
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #集群版
测试: 先启动Eureka Server,7001/7002服务
再启动服务提供者provider,8001
再启动消费者,80
访问地址: http://eureka7002.com:7002/
http://localhost/consumer/payment/get/1
四、搭建支付服务集群
因为一个服务可能有多台服务器支撑
参照8001搭建支付服务8002,只是在controll层做了微调,用来判断是访问的哪个服务
修改内容:
启动7001,7002,8001,8002,80访问地址http://localhost:8001/payment/get/1
{
code: 200,
message: "查询成功,serverPort: 8001",
data: {
id: 1,
serial: "尚硅谷"
}
}
访问http://localhost:8002/payment/get/1
{
code: 200,
message: "查询成功,serverPort: 8002",
data: {
id: 1,
serial: "尚硅谷"
}
}
但是访问: http://localhost/consumer/payment/get/1,结果一直是:
{
code: 200,
message: "查询成功,serverPort: 8001",
data: {
id: 1,
serial: "尚硅谷"
}
}
这是因为在80项目的controller层中,我们将请求路径写死了,访问地址不能写死。
修改OrderController :
// public static final String PAYMENT_URL = "http://localhost:8001";
//把它转化成微服务的名字才算是真正的微服务访问
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
访问 会出现500错误
报错信息:
There was an unexpected error (type=Internal Server Error,
status=500). I/O error on GET request for
“http://CLOUD-PAYMENT-SERVICE/payment/get/1”: CLOUD-PAYMENT-SERVICE;
nested exception is java.net.UnknownHostException:
CLOUD-PAYMENT-SERVICE
org.springframework.web.client.ResourceAccessException: I/O error on
GET request for “http://CLOUD-PAYMENT-SERVICE/payment/get/1”:
CLOUD-PAYMENT-SERVICE; nested exception is
java.net.UnknownHostException: CLOUD-PAYMENT-SERVICE
原因是:虽然我们配置了以服务名的方式访问,但不能确定是哪个一个服务。
解决办法:给restTemplate开启负载均衡,默认是轮循。
在order80服务中的,修改如下,只是添加了一个注解 @LoadBalanced ,后期会详解。
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //开启负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
重新访问http://localhost/consumer/payment/get/1 就会看到端口的变化,负载均衡效果达到了,8001 和8002端口交替出现。
Ribbon 和Eureka整合后Consumer可以直接调用服务而不再关心地址和端口号,且该服务还有负载的功能。
actuator 微服务信息完善
主机名称:服务名称修改
访问信息有IP信息提示
在8001与8002的yml配置文件中,添加
instance:
instance-id: payment8001/payment8002
prefer-ip-address: true #访问路径可以显示IP地址
五、服务发现
服务发现:
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息,
5.1 修改内容
修改8001的Controller,添加如下内容
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery(){
List<String> services = discoveryClient.getServices();
for (String element: services){
log.info("********************element:" + element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance:instances){
log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return this.discoveryClient;
}
8001的主启动类PaymentMain8001 上添加注解@EnableDiscoveryClient
5.2 测试
先启动EurekaServer 7001,7002再启动8001,访问
http://localhost:8001/payment/discovery
{
services: [
"cloud-payment-service"
],
order: 0
}
六、Eureka自我保护
为什么会产生Eureka自我保护机制?
为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除
6.1 什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险—因为微服务本身是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题----当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是 宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有的微服务(健康的和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
6.2 如何禁止自我保护
自我保护机制默认是开启的,使用方式:在7001,7002的yml中进行添加
server:
#关闭自我保护机制,保证不可用服务被及时剔除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
如果在Eureka Server的首页看到如下提示,说明Eureka进入了保护模式
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.
github源代码地址:https://github.com/diligentkong/cloud2020