微服务专题08-Spring Cloud 服务发现

前言

前面的章节我们讲了Spring Cloud 配置管理 。本节,继续微服务专题的内容分享,共计16小节,分别是:

本节内容重点为:

  • Zookeeper 客户端:介绍 Spring Cloud Discovery 结合 Apache Zookeeper 客户端的基本使用方法,包括服务发现激活、Eureka 客户端注册配置 以及 API 使用等
  • Zookeeper 服务器:介绍 Apache Zookeeper 服务器作为服务注册中心的搭建方法

各个注册中心的对比

比较点EurekaZookeeperConsul
运维熟悉度相对陌生熟悉更陌生
一致性(CAP)AP(最终一致性)CP(一致性强)AP(最终一致性)
一致性协议HTTP 定时轮训ZABRAFT
通讯方式HTTP REST自定义协议HTTP REST
更新机制Peer 2 Peer(服务器之间) + Scheduler(服务器和客户端)ZK WatchAgent 监听的方式
适用规模20 K ~ 30 K 实例(节点)10K ~ 20K 实例(节点)< 3K 实例(节点)
性能问题简单的更新机制、复杂设计、规模较大时 GC 频繁扩容麻烦、规模较大时 GC 频繁3K 节点以上,更新列表缓慢

为什么推荐使用 ZK 作为 Spring Cloud 的基础设施

CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。

一致性模型

Zookeeper保证CP:一致性(Consistency)、分区容错性(Partition tolerance)。

在这里插入图片描述
上图描述的是客户端分别请求以Zookeeper或者Eureka作为注册中心访问时,当服务端的一个应用实例挂掉的情况,注册中心做出了不同的反映的情形:

  • 对于Zk来说,为了保证CP原则,返回给客户端的应用实例变为 3- 1 = 2 个。
  • 对于Eureka来说,为了保证AP原则,返回给客户端的应用实例仍为3个,但是挂掉的实例是访问不到的。

不难想象,对于Eureka来说,当服务端应用实例挂掉后,以上图的情形来说会有1/3 的概率,获取不到实例,为了避免这种情况的发生,我们通常可以引入服务网关实现 “读写分离”的架构来解决这样的问题。

基于服务网关实现读写分离的架构

所谓的“读写分离”指的是:

  • 写:是客户端直接向Eureka注册中心进行服务注册。
  • 读:是服务网关通过拉取Eureka注册中心的数据,从而使客户端从服务网关间接的订阅数据。

在这里插入图片描述

我们知道,Eureka注册中心基于内存型的注册模式,将服务注册与服务发现分离开来的好处就在于防止二者并行,假设Client 的QPS是10w甚至更高,这时使Eureka注册中心直接暴露给客户端,必定会造成Eureka挂掉。

实际的业务场景,通常来说,即服务注册的请求相对较小,请求一次注册即可,更多的是服务发现,从Eureka拉取数据量比较大,即读多写少。

基于以上的结论,我们认为既然读多写少,我们就可以在读的时候做些文章,实现网关的分布式高可用,将网关拉取的数据加入缓存,再持久化到数据库,使客户端在读取数据的时候间接访问数据库,同时可以将数据库分库分表等等操作,这样就可以极大地扩展基于Eureka注册中心的性能。

维护相对熟悉

Zookeeper生态环境比较好

  • zookeeper 可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用 zookeeper 来做一些元数据、配置信息的管理,包括 dubbo 注册中心不也支持 zookeeper 么?

  • hadoop、hdfs、yarn 等很多大数据系统,都选择基于 zookeeper 来开发 HA 高可用机制。

  • Zookeeper实现分布式锁也是比较常见的一种方式。

配置中心和服务注册中心单一化

所谓的单一化指的就是,使用Zookeeper做注册中心,Zookeeper同样可以做配置中心,如果在 Spring Cloud 传统的配置方面,使用Eureka 做注册中心、Git/JDBC 做配置中心。

Spring Cloud Discovery

通过zookeeper 的实验,我们可以同时测试两种效果:

  • 注册发现

  • 配置管理

Spring Cloud 增加 ZK 依赖
  • 错误配置(高 ZK Client 版本 3.5,低服务器版本 3.4)
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        </dependency>
    </dependencies>
  • 正确配置
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-all</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.12</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>
启动 ZK

关于zk的使用以及原理,我们在前面分布式专题已经讲到,这里我们就以Windows版本做一个回顾吧!
zookeeper官网下载:zookeeper官网下载

我目前使用的版本是:3.5.8。

  1. 修改默认配置文件

zk默认读取zoo.cfg文件,从官网上下载的是没有的,我们这里复制一份,并修改zoo.cfg
文件:
在这里插入图片描述
我们可以在zookeeper根目录下创建一个/data文件夹用来存放数据。

tickTime=2000
initLimit=10
syncLimit=5
# 这里需要使用 "//" 
dataDir=D://apache-zookeeper-3.5.8-bin//data
clientPort=2181
  1. zookeeper的启动与停止

首先双击zkServer.sh启动服务端,然后双击启动zkCli.sh 进入命令行
在这里插入图片描述
停止zookeeper可以直接杀死2181 端口即可:

首先查看2181端口所在的进程号:

netstat -aon|findstr “2181”

然后直接kill:

taskkill /pid 10756 /F

在这里插入图片描述

  1. zookeeper 命令行

通过命令 ls / 查看节点,我们发现,本地localhost:2181 的服务已经启动成功!
在这里插入图片描述

编写核心类
  1. 引导类,使用@EnableDiscoveryClient 作为服务发现客户端:
@SpringBootApplication
@EnableDiscoveryClient // 尽可能使用 @EnableDiscoveryClient
public class ZkDSClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZkDSClientApplication.class, args);
    }
}
  1. 编写Controller,根据 DiscoveryClient 的api 返回所有的服务名称,id,主机,端口号等信息
@RestController
public class ServiceController {

    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 返回所有的服务名称
     *
     * @return
     */
    @GetMapping("/services")
    public List<String> getAllServices() {
        return discoveryClient.getServices();
    }

    @GetMapping("/service/instances/{serviceName}")
    public List<String> getAllServiceInstances(@PathVariable String serviceName) {
        return discoveryClient.getInstances(serviceName)
                .stream()
                .map(s ->
                        s.getServiceId() + " - " + s.getHost() + ":" + s.getPort()
                ).collect(Collectors.toList());
    }

}

我们注意到,接口 DiscoveryClient 是来自于 org.springframework.cloud.client.discovery下的。

并且功能有如下几种:

public interface DiscoveryClient {

	/**
	 * A human readable description of the implementation, used in HealthIndicator
	 * @return the description
	 */
	String description();

	/**
	 * Get all ServiceInstances associated with a particular serviceId
	 * @param serviceId the serviceId to query
	 * @return a List of ServiceInstance
	 */
	List<ServiceInstance> getInstances(String serviceId);

	/**
	 * @return all known service ids
	 */
	List<String> getServices();

}

其中 ServiceInstance 我们看看有哪些属性和方法?

public interface ServiceInstance {
	// 实例 id
	String getServiceId();
	//主机地址
	String getHost();
	// 端口号
	int getPort();
	//是否是https请求
	boolean isSecure();
	// 服务的 uri
	URI getUri();
	//与服务实例关联的键值对元数据
	Map<String, String> getMetadata();
	//实例方案
	default String getScheme() {
		return null;
	}
}
设置配置文件
spring.application.name = spring-cloud-service-discovery-client

# 服务端口
server.port = 7070

# 管理端口
management.server.port = 7071

# 开放 所有Web 管理 Endpoints
management.endpoints.web.exposure.include = *
结果测试
  • 首先看服务发现的效果:

启动项目:
在这里插入图片描述

我们将7070端口作为服务端口,在zookeeper节点上查看一下是否注册成功:
在这里插入图片描述
我们发现,ZK 节点路径(/services/spring-cloud-service-discovery-client/)
由此可以得出结论:ZK 服务发现节点规则(/services/{spring.application.name}/{serviceId_UUID}/)。

  • 接下来看一下服务注册的效果:

直接访问浏览器:http://localhost:7071/actuator/env

在这里插入图片描述
依据之前编写的Controller我们访问一下:

http://localhost:7070/services
(返回所有的服务名称)
在这里插入图片描述

http://localhost:7070/service/instances/spring-cloud-service-discovery-client
(指定某个实例,并获取详情信息)
在这里插入图片描述

后记

Eureka 2.0 不开源,Eureka 1.x 还可以用的

本节代码地址:https://github.com/harrypottry/microservices-project/tree/master/spring-cloud-project/spring-cloud-service-discovery-client

更多架构知识,欢迎关注本套Java系列文章Java架构师成长之路

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值