您的位置:首页 > 其它

基于Zookeeper快速实现服务注册与发现

2020-07-14 06:00 141 查看

前言:

在一个单体架构里面,方法之间的调用直接调用接口就可以了,但是在分布式系统里面,服务有很多,每个服务的IP,端口,部署的机器可能都不相同,如果需要自己去维护这个调用关系,就非常麻烦,就必须借助服务注册与发现来实现了。

主要的服务注册与发现方案

Eureka、Consul、Zookeeper、nacos
区别:
Zookeeper保证了CP(C:一致性,P:分区容错性)

当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的信息,但不能容忍直接down掉不可用。也就是说,服务注册功能对高可用性要求比较高,但zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新选leader。问题在于,选取leader时间过长,30 ~ 120s,且选取期间zk集群都不可用,这样就会导致选取期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够恢复,但是漫长的选取时间导致的注册长期不可用是不能容忍的。

Eureka保证了AP(A:高可用)

Eureka保证了可用性,Eureka各个节点是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点仍然可以提供注册和查询服务。而Eureka的客户端向某个Eureka注册或发现是发生连接失败,则会自动切换到其他节点,只要有一台Eureka还在,就能保证注册服务可用,只是查到的信息可能不是最新的。除此之外,Eureka还有自我保护机制,如果在15分钟内超过85%的节点没有正常的心跳,那么Eureka就认为客户端与注册中心发生了网络故障,此时会出现以下几种情况:
①、Eureka不在从注册列表中移除因为长时间没有收到心跳而应该过期的服务。
②、Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点仍然可用)
③、当网络稳定时,当前实例新的注册信息会被同步到其他节点。
因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个微服务瘫痪

Nacos是阿里开源的

Nacos 支持基于 DNS 和基于 RPC 的服务发现。在Spring Cloud中使用Nacos,只需要先下载 Nacos 并启动 Nacos server,Nacos只需要简单的配置就可以完成服务的注册发现。
Nacos除了服务的注册发现之外,还支持动态配置服务。动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
一句话概括就是Nacos = Spring Cloud注册中心 + Spring Cloud配置中心。

下面主要介绍zookeeper的使用

实践步骤

1.首先启动zookeeper服务

我这里是使用docker容器直接启动的。
拉取zookeeper镜像之后直接docker run,后面设置相关参数

docker run -d --name zookeeper --publish 2181:2181 --volume  /etc/localtime:/etc/localtime --restart=always zookeeper:latest

然后docker ps可以看到zookeeper服务已经在运行

2. 创建一个SpringBoot-Provider生产者项目

  1. 导入相关pom依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
  1. 新建provider的一个调用controller
@RestController
public class HelloProvider {

@GetMapping("/say")
public String say(){
return "say hello zookeeper";
}

}

application.yml文件配置相关参数

这个参数可以设置这个服务是否被zookeeper发现,默认是true,false的话zookeeper就不会注册与发现这个服务了
spring.cloud.zookeeper.discovery.enabled: false

spring:
application:
name: spring-boot-provider
cloud:
zookeeper:
connect-string: 192.168.57.130:2181

至此生产者已经创建完成。

3. 创建SpringBoot-Consumer消费者

  1. 创建另外一个SpringBoot项目,作为消费者。启动类上加上@EnableFeignClients注解

  2. 导入坐标

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
  1. 创建一个fegin代理接口
    这里的FeignClient就是填写生产者的应用名称,然后getmapping填写生产者的服务映射地址。

通过Feign, 我们能把HTTP远程调用对开发者完全透明,得到与调用本地方法一致的编码体验。这一点与阿里Dubbo中暴露远程服务的方式类似,区别在于Dubbo是基于私有二进制协议,而Feign本质上还是个HTTP客户端。

@FeignClient("spring-boot-provider")
public interface SayHelloFeign {

@GetMapping("/say")
String say();

}

application.yml配置参数

server:
port: 8088

spring:
cloud:
zookeeper:
connect-string: 192.168.57.130:2181
application:
name: spring-boot-consumer
  1. 添加Consumer的访问接口
@RestController
public class ConsumerController {

@Autowired
private SayHelloFeign helloFeign;

@GetMapping("/consumer/say")
public String conSay(){
return helloFeign.say();
}

}

生产者和消费者服务启动,然后访问消费者路径 http://localhost:8088/consumer/say 就可以拿到生产者的接口数据。

原理分析


生产者和消费者是通过zookeeper注册中心进行方法调用的,注册中心保存了生产者和消费者的服务地址,IP,端口等信息。首先页面一个HTTP请求会走到消费者,消费者会根据注入的Fegin代理,通过应用名称,映射路径去注册中心找到一个服务生产者的调用地址,这个过程会有负载均衡的一个策略,然后拿到这个调用地址去调用生产者的服务。获取数据返回。

服务生产者和消费者还有注册中心都可以集群,以实现服务高可用。
集群有一个机制就是心跳,可以动态感知服务的上下线。
比如我在生产者集群了两个服务,那么zookeeper生产者就会有两个临时节点。如果生产者的其中一个服务宕机,那么zookeeper会依据心跳去剔除这个节点。

这个是节点里面的数据

{"name":"spring-boot-provider","id":"69f14c89-0946-4698-a009-4f2c0d8d15bc","address":"DESKTOP-BRT6REM",
"port":8080,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
"id":"application-1","name":"spring-boot-provider","metadata":{}},"registrationTimeUTC":1587560481823,
"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},
{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}}

如果服务生产者的地址有变动,那么注册中心就会基于一个长连接的方式推送给消费者,消费者也可以使用轮询的方式去获取最新的服务地址

如果zookeeper服务宕机了,那么生产者和消费者会报错,而请求也会报错


我这里没有做针对服务宕机而返回的一个机制,就是熔断器。

当zookeeper服务重新启动,消费者和生产者会自动恢复,请求也会重新调用成功。

小白一个,关于微服务这块的内容,还在持续学习中。。。。。。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: