文章目录
Spring Cloud(04)——Eureka的简介和部署使用
上一篇Spring Cloud(03)——构建支付模块和消费者订单模块以及工程重构中,我们搭建了一个简易的Rest服务,有RestFul API,服务提供者和服务消费者。下面来实现如何部署eureka注册中心!
1、Spring Cloud Eureka简介
-
Eureka 是 Netflix 微服务套件的一部分,基于 Netflix Eureka 做了二次封装,主要负责实现微服务架构中的服务治理功能。Eureka是Netflix中的一个开源框架,所以它可以利用Netfilix中其他的组件,如zull等。
-
Eureka 是一个基于 REST 的服务,并且提供了基于 Java 的客户端组件,能够非常方便地将服务注册到Eureka 中进行统一管理。
-
服务治理是微服务架构中必不可少的一部分,阿里开源的 Dubbo 框架(Dubbo 中也有几种注册中心,基于 Zookeeper、基于 Redis等,不过用得最多的还是 Zookeeper 方式)就是针对服务治理的。服务治理必须要有一个注册中心,除了用 Eureka 作为注册中心外,我们还可以使用 Consul、Etcd、Zookeeper 等来作为服务的注册中心。
Eureka包含两个组件:Eureka Server和Eureka Client。
-
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
-
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。Eureka client可再分为Service Provider和Service Consumer。
- Service Provider 服务提供方,将自身服务注册到Eureka,从而使服务消费方能够找到
-
Service Consumer服务消费方,从Eureka获取注册服务列表,从而能够消费服务
-
在应用启动后,使用Eureka Client连接到Eureka Server并维持心跳连接,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
-
Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
2、Eureka和Zookeeper比较
在分布式系统领域有个著名的 CAP 定理:
- C:Consistency ,一致性。
- A: Availability,可用性。
- P:Partition tolerance,网络分区容错。类似多机房部署,保证服务稳定性。
这三个特性在任何分布式系统中都不能同时满足,最多同时满足两个。
Eureka和Zookeeper的区别:
-
Zookeeper是CP,分布式协同服务,突出一致性。对ZooKeeper的的每次请求都能得到一致的数据结果,但是无法保证每次访问服务可用性。如请求到来时,zookeer正在做leader选举,此时不能提供服务,即不满足A可用性。
-
Euere是AP,高可用与可伸缩的Service发现服务,突出可用性。相对于Zookeeper而言,可能返回数据没有一致性,但是保证能够返回数据,服务是可用的。即不满足C一致性。
3、部署Eureka Server服务端
1、创建子模块 cloud-eureka-server7001
2、导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>com.cheng.springcloud</groupId>
<artifactId>cloud-api-commons</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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
3、编写配置文件
server:
port: 7001
eureka:
instance:
hostname: localhost # Eureka服务端实例名称
client:
register-with-eureka: false # 由于该应用为注册中心, 所以设置为false, 代表不向注册中心注册自己
fetch-registry: false # fetch-registry为false,表示我是一个注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#设置与Eureka Server交互的地址,以后服务注册和查询都需要依赖这个地址
defaultZone: http//${eureka.instance.hostname}:${server.port}/eureka/
4、编写主启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
5、测试
启动程序,访问请求http://localhost:7001/进行测试:
部署成功!
4、支付微服务8001注册进Eureka Server
Eureka Client客户端 cloud-provide-payment8001将注册进EurekaServer成为服务提供者
1、在服务提供者cloud-provide-payment8001模块添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、在服务提供者配置文件中添加eureka的配置
#eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
register-with-eureka: true #将自己注册进Eureka Server
#是否从eurekaServer抓取已有的注册信息,默认为true。单个eureka无所谓,eureka集群必须设置为true,才能配合ribbon使用负载均衡
fetchregistry: true
instance:
instance-id: springcloud-provider-dept-8001 #自定义状态信息
3、主启动类上加注解@EnableEurekaClient
4、测试
- 启动Eureka服务端 Eureka Server7001,
- 再启动服务提供者cloud-provide-payment8001模块
访问请求http://localhost:7001/进行测试:查看服务是否注册到注册中心,
注册成功!
5、订单微服务80注册进Eureka Server
Eureka Client客户端 cloud-consumer-order80将注册进EurekaServer成为服务消费者
1、在服务消费者cloud-consumer-order80模块添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、在服务提供者配置文件中添加eureka和spring的配置(配置文件注意空格)
spring:
application:
name: cloud-order-service
#eureka的配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
register-with-eureka: true #将自己注册进Eureka Server
#是否从eurekaServer抓取已有的注册信息,默认为true。单个eureka无所谓,eureka集群必须设置为true,才能配合ribbon使用负载均衡
fetchregistry: true
3、主启动类上加注解@EnableEurekaClient
4、测试
- 启动Eureka服务端 Eureka Server7001,
- 启动服务提供者cloud-provide-payment8001模块
- 启动服务消费者cloud-consumer-order80模块
访问请求http://localhost:7001/进行测试:发现两个微服务都注册进来了
6、搭建Eureka集群
6.1、Eureka集群原理
相互注册,相互守望,对外暴露
Eureka注册中心集群 实现了负载均衡+故障容错
6.2、Eureka集群环境搭建
1、参考cloud-eureka-server7001模块,新建cloud-eureka-server7002模块
- 复制7001的pom依赖到7002中
- 复制7001的配置文件到7002中,注意Eureka Server7002的端口号为7002
- 复制7001的主启动类及目录结构到7002中,7002主启动类名为EurekaMain7002
2、修改映射配置
-
找到C:\Windows\System32\drivers\etc目录下的hosts文件,
-
在hosts文件中添加映射:
127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com
修改完后就可以模拟两个Eureka Serve 服务端了(7001和7002)
3、修改两个Eureka Server服务端的yml配置文件
-
修改cloud-eureka-server7001的yml配置文件为:
server: port: 7001 eureka: instance: hostname: eureka7001.com # Eureka服务端实例名称 client: register-with-eureka: false # 由于该应用为注册中心, 所以设置为false, 代表不向注册中心注册自己 fetch-registry: false # fetch-registry为false,表示我是一个注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #设置与Eureka Server交互的地址,以后服务注册和查询都需要依赖这个地址 defaultZone: http://eureka7002.com:7002/eureka/22
-
修改cloud-eureka-server7002的yml配置文件为:
server: port: 7002 eureka: instance: hostname: eureka7002.com # Eureka服务端实例名称 client: register-with-eureka: false # 由于该应用为注册中心, 所以设置为false, 代表不向注册中心注册自己 fetch-registry: false # fetch-registry为false,表示我是一个注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #设置与Eureka Server交互的地址,以后服务注册和查询都需要依赖这个地址 defaultZone: http://eureka7001.com:7001/eureka/
4、测试
-
启动cloud-eureka-server7001模块
-
启动cloud-eureka-server7002模块
-
访问请求http://eureka7001.com:7001/,查看7001模块
- 访问请求http://eureka7002.com:7002/,查看7002模块
可以发现,两个集群之间相互注册了,一个由两个eureka节点组成的Eureka集群环境搭建成功!
6.3、订单、支付微服务注册进Eureka集群
将支付服务8001注册进Eureka集群
修改支付服务8001模块的yml配置文件里eureka的配置即可:
#eureka的配置
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #集群版
register-with-eureka: true #将自己注册进Eureka Server
#是否从eurekaServer抓取已有的注册信息,默认为true。单个eureka无所谓,eureka集群必须设置为true,才能配合ribbon使用负载均衡
fetchregistry: true
将订单服务80注册进Eureka集群
修改订单服务80模块的yml配置文件里eureka的配置即可:
#eureka的配置
eureka:
client:
service-url:
#defaultZone: http://localhost:7001/eureka/ 单机版
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #集群版
register-with-eureka: true #将自己注册进Eureka Server
#是否从eurekaServer抓取已有的注册信息,默认为true。单个eureka无所谓,eureka集群必须设置为true,才能配合ribbon使用负载均衡
fetchregistry: true
测试
-
启动cloud-eureka-server7001模块和cloud-eureka-server7002模块
-
启动支付服务8001模块
-
启动订单服务80模块
-
访问http://eureka7001.com:7001/和http://eureka7002.com:7002/,查看两个微服务是否注册到集群里?
Eureka Server7001注册成功
Eureka Server7002注册成功
-
测试服务提供者8001提供的服务是否可以访问
访问查询服务:http://localhost/consumer/payment/get/1
访问添加服务:http://localhost/consumer/payment/create?serial=%E7%A5%9E%E6%98%8E
测试成功!
6.4、配置支付微服务集群
1、参考支付模块cloud-provider-payment8001,新建cloud-provider-payment8002
- 将8001模块的pom依赖复制到8002模块中
- 把8001的java目录和resource目录复制到8002中
- 在yml配置文件中把8002模块的端口号改为8002
- 8002模块的主启动类名改为PaymentMain8002
2、修改8001模块和8002模块的controller
修改8001模块的controller
在原来的controller中加入端口号,用于区分服务提供者
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
//用于获取配置文件中的端口号
@Value("${server.port}")
private String serverPort;
@PostMapping(value = "/payment/create")
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("=======添加结果="+result);
if (result > 0){
return new CommonResult(200,"添加执行成功,serverPort="+serverPort,result);
}else {
return new CommonResult(500,"添加执行失败",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
log.info("=======查询结果="+payment);
if (payment != null){
return new CommonResult(200,"查询执行成功,serverPort="+serverPort,payment);
}else {
return new CommonResult(500,"查询id为"+id+"执行失败",null);
}
}
}
修改8002模块的controller
代码和上面一模一样,就不重复写了。
修改之后的支付模块8001和8002,在订单模块访问支付模块集群时,可以通过端口来区分访问的是哪个支付模块。
3、测试
-
启动cloud-eureka-server7001模块和cloud-eureka-server7002模块
-
启动支付服务8001和8002模块
-
启动订单服务80模块
-
访问http://eureka7001.com:7001/和http://eureka7002.com:7002/,查看支付微服务集群和订单微服务是否注册到eureka集群里?
Eureka Server7001注册成功
Eureka Server7002注册成功
-
测试支付微服务集群8001和8002提供的服务是否可以访问
访问查询服务:http://localhost/consumer/payment/get/1
查询成功,但我们刷新浏览器重新访问时,端口号并不会变化,也就说明我们访问的支付服务一直是支付微服务集群里的8001模块,这并没有实现负载均衡。
这是因为我们在订单80模块的controller中,要访问的url固定了:所以我们访问的一直都是8001。
public static final String REST_URL_PREFIX = "http://localhost:8001";
实现RestTemplate负载均衡功能
因此在多个服务提供者(支付微服务)的情况下,服务消费者(订单微服务)访问服务提供者不适合再通过url访问,而要通过服务名访问,因为支付微服务集群里的各个服务名是一样的。
订单80模块的controller中,把通过地址访问改为通过服务名访问:
//public static final String REST_URL_PREFIX = "http://localhost:8001";
public static final String REST_URL_PREFIX = "http://CLOUD-PAYMENT-SERVICE";
使用@LoadBalanced注解赋予RestTemplate负载均衡的能力:
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
再次访问查询服务:http://localhost/consumer/payment/get/1
发现第一次访问的服务时8001
刷新浏览器,重新访问,访问的服务为8002了
此后不断刷新,将会轮流访问8001和8002服务,这就实现了默认的负载均衡机制——轮询
7、完善actuator微服务信息
Spring Boot的actuator(健康监控)功能提供了很多监控所需的接口,可以对应用系统进行配置查看、相关功能统计等。
自定义服务实例名称
1、在支付微服务8001,支付微服务8002和订单微服务80的yml配置文件中增加instance配置
8001
#注意:instance和client同级
instance:
instance-id: springcloud-provider-payment8001 #自定义状态信息
8002
instance:
instance-id: springcloud-provider-payment8002 #自定义状态信息
80
instance:
instance-id: springcloud-consumer-order80
2、启动程序查看效果:
自定义服务实例名成功!
设置访问路径显示ip地址
在上面instance配置下面加一行配置:
8001
instance:
instance-id: springcloud-provider-payment8001
prefer-ip-address: true
8002
instance:
instance-id: springcloud-provider-payment8002
prefer-ip-address: true
80
instance:
instance-id: springcloud-consumer-order80
prefer-ip-address: true
查看效果:
定义状态信息
1、编写配置,info和eureka同级
info:
app.name: springcloud-pengcheng
company.name: wanli
2、点击状态信息的链接
3、页面跳转,并成功返回配置信息:
8、服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
1、修改支付模块8001的controller
注入DiscoveryClient,增加一个方法
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery(){
//得到服务清单列表
List<String> services = discoveryClient.getServices();
for (String service : services) {
log.info("服务列表:"+service);
}
//通过服务名获得该服务的所有服务实例
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;
}
2、在主启动类添加@EnableDiscoveryClient注解
3、测试
访问请求:http://localhost:8001/payment/discovery
可以看到eureka中已注册的服务名。
再看一下idea控制台的输出:
成功打印出eureka注册中心中所有已注册的服务名以及cloud-payment-service服务下所有服务实例的信息。
9、自我保护模式
在admin页面出现如下错误“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.”,如下图:
说明Eureka进入了自我保护模式。
自我保护模式:某时刻一个微服务不能用了,Eureka不会立刻清理该服务,依旧会对该微服务的信息进行保存。
- 保护模式,是Eureka 提供的一个特性,在默认的情况下,这个属性是打开的,而且也建议线上都使用这个特性。
- 如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,此时会触发Eureka Server进入保护模式,进入自我保护模式后,将会保护服务注册表中的信息,不再删除服务注册表中的数据。当网络故障恢复后,Eureka Server会自动退出自我保护模式
可以通过下面的配置将自我保护模式关闭,这个配置是在 eureka-server 中:
eureka:
server:
enable-self-preservation: false
自我保护模式关闭后,在eureka界面会给出提示: