分布式 服务
2024-03-24 21:49:03 0 举报
AI智能生成
分布式 服务
作者其他创作
大纲/内容
API网关
API网关(API Gateway)是一种架构模式,它是将一些服务共有的功能整合在一起,独立部署为单独的一层,用来解决一些服务治理的问题。
本质
请求转发 + 请求过滤。
功能
请求转发:将请求转发到目标微服务。
负载均衡:根据各个微服务实例的负载情况或者具体的负载均衡策略配置对请求实现动态的负载均衡。
安全认证:对用户请求进行身份验证并仅允许可信客户端访问 API,并且还能够使用类似 RBAC 等方式来授权。
参数校验:支持参数映射与校验逻辑。
日志记录:记录所有请求的行为日志供后续使用。
监控告警:从业务指标、机器指标、JVM 指标等方面进行监控并提供配套的告警机制。
流量控制:对请求的流量进行控制,也就是限制某一时刻内的请求数。
熔断降级:实时监控请求的统计信息,达到配置的失败阈值后,自动熔断,返回默认值。
响应缓存:当用户请求获取的是一些静态的或更新不频繁的数据时,一段时间内多次请求获取到的数据很可能是一样的。
对于这种情况可以将响应缓存起来。这样用户请求可以直接在网关层得到响应数据,无需再去访问业务服务,减轻业务服务的负担。
对于这种情况可以将响应缓存起来。这样用户请求可以直接在网关层得到响应数据,无需再去访问业务服务,减轻业务服务的负担。
响应聚合:某些情况下用户请求要获取的响应内容可能会来自于多个业务服务。
网关作为业务服务的调用方,可以把多个服务的响应整合起来,再一并返回给用户。
网关作为业务服务的调用方,可以把多个服务的响应整合起来,再一并返回给用户。
灰度发布:将请求动态分流到不同的服务版本(最基本的一种灰度发布)。
异常处理:对于业务服务返回的异常响应,可以在网关层在返回给用户之前做转换处理。
这样可以把一些业务侧返回的异常细节隐藏,转换成用户友好的错误提示返回。
这样可以把一些业务侧返回的异常细节隐藏,转换成用户友好的错误提示返回。
API 文档: 如果计划将 API 暴露给组织以外的开发人员,那么必须考虑使用 API 文档,例如 Swagger 或 OpenAPI。
协议转换:通过协议转换整合后台基于 REST、AMQP、Dubbo 等不同风格和实现技术的微服务,面向 Web Mobile、开放平台等特定客户端提供统一服务。
证书管理:将 SSL 证书部署到 API 网关,由一个统一的入口管理接口,降低了证书更换时的复杂度。
网关系统
Netflix Zuul
Zuul 是 Netflix 开发的一款提供动态路由、监控、弹性、安全的网关服务,基于 Java 技术栈开发,可以和 Eureka、Ribbon、Hystrix 等组件配合使用。
Zuul 主要通过过滤器(类似于 AOP)来过滤请求,从而实现网关必备的各种功能。
Zuul 1.x 基于同步 IO,性能较差。Zuul 2.x 基于 Netty 实现了异步 IO,性能得到了大幅改进。
Spring Cloud Gateway
SpringCloud Gateway 属于 Spring Cloud 生态系统中的网关,其诞生的目标是为了替代老牌网关 Zuul。
原理
为了提升网关的性能,SpringCloud Gateway 基于 Spring WebFlux 。
Spring WebFlux 使用 Reactor 库来实现响应式编程模型,底层基于 Netty 实现同步非阻塞的 I/O。
工作流程
路由判断:客户端的请求到达网关后,先经过 Gateway Handler Mapping 处理,
这里面会做断言(Predicate)判断,看下符合哪个路由规则,这个路由映射后端的某个服务。
这里面会做断言(Predicate)判断,看下符合哪个路由规则,这个路由映射后端的某个服务。
请求过滤:然后请求到达 Gateway Web Handler,这里面有很多过滤器,组成过滤器链(Filter Chain),
这些过滤器可以对请求进行拦截和修改,比如添加请求头、参数校验等等,有点像净化污水。
然后将请求转发到实际的后端服务。这些过滤器逻辑上可以称作 Pre-Filters,Pre 可以理解为“在...之前”。
这些过滤器可以对请求进行拦截和修改,比如添加请求头、参数校验等等,有点像净化污水。
然后将请求转发到实际的后端服务。这些过滤器逻辑上可以称作 Pre-Filters,Pre 可以理解为“在...之前”。
服务处理:后端服务会对请求进行处理。
响应过滤:后端处理完结果后,返回给 Gateway 的过滤器再次做处理,逻辑上可以称作 Post-Filters,Post 可以理解为“在...之后”。
响应返回:响应经过过滤处理后,返回给客户端。
总结
客户端的请求先通过匹配规则找到合适的路由,就能映射到具体的服务。
然后请求经过过滤器处理后转发给具体的服务,服务处理后,再次经过过滤器处理,最后返回给客户端。
然后请求经过过滤器处理后转发给具体的服务,服务处理后,再次经过过滤器处理,最后返回给客户端。
断言
一种编程术语,说白了它就是对一个表达式进行 if 判断,结果为真或假,如果为真则做这件事,否则做那件事。
在 Gateway 中,如果客户端发送的请求满足了断言的条件,则映射到指定的路由器,就能转发到指定的服务上进行处理。
常见的路由断言配置规则
路由和断言关系
一对多:一个路由规则可以包含多个断言。
同时满足:如果一个路由规则中有多个断言,则需要同时满足才能匹配。
第一个匹配成功:如果一个请求可以匹配多个路由,则映射第一个匹配成功的路由。
过滤器分类
按请求和响应
Pre 类型:在请求被转发到微服务之前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。
Post 类型:微服务处理完请求后,返回响应给网关,网关可以再次进行处理,例如修改响应内容或响应头、日志输出、流量监控等。
按照作用范围
GatewayFilter:局部过滤器,应用在单个路由或一组路由上的过滤器。标红色表示比较常用的过滤器。
GlobalFilter:全局过滤器,应用在所有路由上的过滤器。
限流过滤器
对应的接口是 RateLimiter,RateLimiter 接口只有一个实现类 RedisRateLimiter (基于 Redis + Lua 实现的限流),提供的限流功能比较简易且不易使用。
自定义全局异常处理
SpringBoot 项目
只需要在项目中配置 @RestControllerAdvice和 @ExceptionHandler就可以了。
Spring Cloud Gateway
提供了多种全局处理的方式,比较常用的一种是实现 ErrorWebExceptionHandler 并重写其中的 handle 方法。
功能
不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,限流。
与 Zuul 2.x
差别不大,也是通过过滤器来处理请求。不过,目前更加推荐使用 Spring Cloud Gateway 而非 Zuul,Spring Cloud 生态对其支持更加友好。
优点
简单易用、成熟稳定、与 Spring Cloud 生态系统兼容、Spring 社区成熟等等。
缺点
一般还需要结合其他网关一起使用比如 OpenResty。并且,其性能相比较于 Kong 和 APISIX,还是差一些。
动态路由实现
基于 Nacos 注册中心。(推荐)
OpenResty
一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。
作用
用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
原理
基于 Nginx,主要还是看中了其优秀的高并发能力。
自定义
需要编写 C 语言的模块,并重新编译 Nginx。
在 Nginx 内部里嵌入 Lua 脚本,使得可以通过简单的 Lua 语言来扩展网关的功能,比如实现自定义的路由规则、过滤器、缓存策略等。
Lua
Lua 是一种非常快速的动态脚本语言,它的运行速度接近于 C 语言。
LuaJIT 是 Lua 的一个即时编译器,它可以显著提高 Lua 代码的执行效率。
LuaJIT 将一些常用的 Lua 函数和工具库预编译并缓存,这样在下次调用时就可以直接使用缓存的字节码,从而大大加快了执行速度。
Kong
一款基于 OpenResty (Nginx + Lua)的高性能、云原生、可扩展、生态丰富的网关系统。
组成
Kong Server:基于 Nginx 的服务器,用来接收 API 请求。
Apache Cassandra/PostgreSQL:用来存储操作数据。
Kong Dashboard:官方推荐 UI 管理工具,当然,也可以使用 RESTful 方式 管理 Admin api。
默认使用 Apache Cassandra/PostgreSQL 存储数据,Kong 的整个架构比较臃肿,并且会带来高可用的问题。
Kong 提供了插件机制来扩展其功能,插件在 API 请求响应循环的生命周期中被执行。
Kong 本身就是一个 Lua 应用程序,并且是在 Openresty 的基础之上做了一层封装的应用。
归根结底就是利用 Lua 嵌入 Nginx 的方式,赋予了 Nginx 可编程的能力,这样以插件的形式在 Nginx 这一层能够做到无限想象的事情。
归根结底就是利用 Lua 嵌入 Nginx 的方式,赋予了 Nginx 可编程的能力,这样以插件的形式在 Nginx 这一层能够做到无限想象的事情。
APISIX
定义
APISIX 是一款基于 OpenResty 和 etcd 的高性能、云原生、可扩展的网关系统。
etcd 是使用 Go 语言开发的一个开源的、高可用的分布式 key-value 存储系统,使用 Raft 协议做分布式共识。
与传统 API 网关相比,APISIX 具有动态路由和插件热加载,特别适合微服务系统下的 API 管理。
APISIX 与 SkyWalking(分布式链路追踪系统)、Zipkin(分布式链路追踪系统)、Prometheus(监控系统) 等 DevOps 生态工具对接都十分方便。
作为 Nginx 和 Kong 的替代项目,APISIX 目前已经是 Apache 顶级开源项目,并且是最快毕业的国产开源项目。
国内目前已经有很多知名企业(比如金山、有赞、爱奇艺、腾讯、贝壳)使用 APISIX 处理核心的业务流量。
国内目前已经有很多知名企业(比如金山、有赞、爱奇艺、腾讯、贝壳)使用 APISIX 处理核心的业务流量。
APISIX 同样支持定制化的插件开发。开发者除了能够使用 Lua 语言开发插件,还能通过其它方式开发来避开 Lua 语言的学习成本。
避开 Lua
通过 Plugin Runner 来支持更多的主流编程语言(比如 Java、Python、Go 等等)。
通过这样的方式,可以让后端工程师通过本地 RPC 通信,使用熟悉的编程语言开发 APISIX 的插件。
这样做的好处是减少了开发成本,提高了开发效率,但是在性能上会有一些损失。
通过这样的方式,可以让后端工程师通过本地 RPC 通信,使用熟悉的编程语言开发 APISIX 的插件。
这样做的好处是减少了开发成本,提高了开发效率,但是在性能上会有一些损失。
使用 Wasm(WebAssembly) 开发插件。Wasm 被嵌入到了 APISIX 中,用户可以使用 Wasm 去编译成 Wasm 的字节码在 APISIX 中运行。
Wasm
基于堆栈的虚拟机的二进制指令格式,一种低级汇编语言,旨在非常接近已编译的机器代码,并且非常接近本机性能。
Wasm 最初是为浏览器构建的,但是随着技术的成熟,在服务器端看到了越来越多的用例。
Wasm 最初是为浏览器构建的,但是随着技术的成熟,在服务器端看到了越来越多的用例。
Shenyu
一款基于 WebFlux 的可扩展、高性能、响应式网关,Apache 顶级开源项目。
Shenyu 通过插件扩展功能,插件是 ShenYu 的灵魂,并且插件也是可扩展和热插拔的。
不同的插件实现不同的功能。Shenyu 自带了诸如限流、熔断、转发、重写、重定向、和路由监控等插件。
不同的插件实现不同的功能。Shenyu 自带了诸如限流、熔断、转发、重写、重定向、和路由监控等插件。
选择
最常用的是 Spring Cloud Gateway、Kong、APISIX 这三个。
对于公司业务以 Java 为主要开发语言的情况下,Spring Cloud Gateway 通常是个不错的选择。
Kong 和 APISIX
APISIX 基于 etcd 来做配置中心,不存在单点问题,云原生友好;
而 Kong 基于 Apache Cassandra/PostgreSQL ,存在单点风险,需要额外的基础设施保障做高可用。
而 Kong 基于 Apache Cassandra/PostgreSQL ,存在单点风险,需要额外的基础设施保障做高可用。
APISIX 支持热更新,并且实现了毫秒级别的热更新响应;而 Kong 不支持热更新。
APISIX 的性能要优于 Kong 。
APISIX 支持的插件更多,功能更丰富。
RPC
定义
RPC(Remote Procedure Call) 即远程过程调用,通过名字我们就能看出 RPC 关注的是远程调用而非本地调用。
通过 RPC 可以帮助我们调用远程计算机上某个服务的方法,这个过程就像调用本地方法一样简单。
组成
客户端(服务消费端):调用远程方法的一端。
客户端 Stub(桩):这其实就是一代理类。代理类主要做的事情很简单,就是把你调用方法、类、方法参数等信息传递到服务端。
网络传输:网络传输就是要把调用的方法的信息传输到服务端,然后服务端执行完之后再把返回结果通过网络传输传输回来。
网络传输的实现方式有很多种,比如最近基本的 Socket 或者性能以及封装更加优秀的 Netty(推荐)。
网络传输的实现方式有很多种,比如最近基本的 Socket 或者性能以及封装更加优秀的 Netty(推荐)。
服务端 Stub(桩):这个桩就不是代理类了。
这里的服务端 Stub 实际指的就是接收到客户端执行方法的请求后,去执行对应的方法然后返回结果给客户端的类。
这里的服务端 Stub 实际指的就是接收到客户端执行方法的请求后,去执行对应的方法然后返回结果给客户端的类。
服务端(服务提供端):提供远程方法的一端。
过程
服务消费端(client)以本地调用的方式调用远程服务。
客户端 Stub(client stub) 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化):RpcRequest。
客户端 Stub(client stub) 找到远程服务的地址,并将消息发送到服务提供端。
服务端 Stub(桩)收到消息将消息反序列化为 Java 对象: RpcRequest。
服务端 Stub(桩)根据RpcRequest中的类、方法、方法参数等信息调用本地的方法。
服务端 Stub(桩)得到方法执行结果并将组装成能够进行网络传输的消息体:RpcResponse(序列化)发送至消费方。
客户端 Stub(client stub)接收到消息并将消息反序列化为 Java 对象:RpcResponse ,这样也就得到了最终结果。
框架
Dubbo
定义
Apache Dubbo 是一款微服务框架,为大规模微服务实践提供高性能 RPC 通信、流量治理、可观测性等解决方案,涵盖 Java、Golang 等多种语言。
Dubbo 提供了从服务定义、服务发现、服务通信到流量管控等几乎所有的服务治理能力,
支持 Triple 协议(基于 HTTP/2 之上定义的下一代 RPC 通信协议)、应用级服务发现、Dubbo Mesh (Dubbo3 赋予了很多云原生友好的新特性)等特性。
支持 Triple 协议(基于 HTTP/2 之上定义的下一代 RPC 通信协议)、应用级服务发现、Dubbo Mesh (Dubbo3 赋予了很多云原生友好的新特性)等特性。
Dubbo 是由阿里开源,后来加入了 Apache 。正是由于 Dubbo 的出现,才使得越来越多的公司开始使用以及接受分布式架构。
Dubbo 除了能够应用在分布式系统中,也可以应用在现在比较火的微服务系统中。
但由于 Spring Cloud 在微服务中应用更加广泛,所以,一般提 Dubbo,大部分是分布式系统的情况。
但由于 Spring Cloud 在微服务中应用更加广泛,所以,一般提 Dubbo,大部分是分布式系统的情况。
核心能力
面向接口代理的高性能 RPC 调用。
智能容错和负载均衡。
服务自动注册和发现。
高度可扩展能力。
运行期流量调度。
可视化的服务治理与运维。
总结
Dubbo 不光可以调用远程服务,还提供了一些其他开箱即用的功能,比如智能负载均衡。
分布式
把整个系统拆分成不同的服务然后将这些服务放在不同的服务器上减轻单体服务的压力提高并发量和性能。
核心角色
Container: 服务运行容器,负责加载、运行服务提供者。必须。
Provider: 暴露服务的服务提供方,会向注册中心注册自己提供的服务。必须。
Consumer: 调用远程服务的服务消费方,会向注册中心订阅自己所需的服务。必须。
Registry: 服务注册与发现的注册中心。注册中心会返回服务提供者地址列表给消费者。非必须。
Monitor: 统计服务的调用次数和调用时间的监控中心。服务消费者和提供者会定时发送统计数据到监控中心。 非必须。
Invoker
Invoker 是 Dubbo 领域模型中非常重要的一个概念,就是 Dubbo 对远程调用的抽象。
分类
服务提供 Invoker
服务消费 Invoker
整体设计分层
config 配置层:Dubbo 相关的配置。支持代码配置,同时也支持基于 Spring 来做配置,以 ServiceConfig, ReferenceConfig 为中心。
proxy 服务代理层:调用远程方法像调用本地的方法一样简单的一个关键,真实调用过程依赖代理类,以 ServiceProxy 为中心。
registry 注册中心层:封装服务地址的注册与发现。
cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心。
monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心。
protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心。
exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心。
transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心。
serialize 数据序列化层:对需要在网络传输的数据进行序列化。
SPI
SPI(Service Provider Interface) 机制被大量用在开源项目中,它可以帮助我们动态寻找服务/功能(比如负载均衡策略)的实现。
原理
将接口的实现类放在配置文件中,在程序运行过程中读取配置文件,通过反射加载实现类。这样可以在运行时,动态替换接口的实现类。
Java 本身就提供了 SPI 机制的实现。不过,Dubbo 没有直接用,而是对 Java 原生的 SPI 机制进行了增强,以便更好满足自己的需求。
自定义负载均衡策略
创建对应的实现类 XxxLoadBalance 实现 LoadBalance 接口或者 AbstractLoadBalance 类。
将这个实现类的路径写入到 resources 目录下的 META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance 文件中即可。
微内核架构
定义
微内核架构模式(有时被称为插件架构模式)是实现基于产品应用程序的一种自然模式。
可让用户添加额外的应用如插件,到核心应用,继而提供了可扩展性和功能分离的用法。
可让用户添加额外的应用如插件,到核心应用,继而提供了可扩展性和功能分离的用法。
微内核架构包含两类组件:核心系统(core system) 和 插件模块(plug-in modules)。
核心系统提供系统所需核心能力,插件模块可以扩展系统的功能。因此, 基于微内核架构的系统,非常易于扩展功能。
我们常见的一些 IDE,都可以看作是基于微内核架构设计的。绝大多数 IDE 比如 IDEA、VSCode 都提供了插件来丰富自己的功能。
Dubbo 采用 微内核(Microkernel) + 插件(Plugin) 模式,简单来说就是微内核架构。微内核只负责组装插件。
通常情况下,微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期。Dubbo 不想依赖 Spring 等 IoC 容器,
也不想自己造一个小的 IoC 容器(过度设计),因此采用了一种最简单的 Factory 方式管理插件:JDK 标准的 SPI 扩展机制 (java.util.ServiceLoader)。
也不想自己造一个小的 IoC 容器(过度设计),因此采用了一种最简单的 Factory 方式管理插件:JDK 标准的 SPI 扩展机制 (java.util.ServiceLoader)。
负载均衡策略
负载均衡
旨在优化资源使用,最大化吞吐量,最小化响应时间,并避免任何单个资源的过载。
策略
在集群负载均衡时,Dubbo 提供了多种均衡策略,默认为 random 随机调用。
在 Dubbo 中,所有负载均衡实现类均继承自 AbstractLoadBalance,该类实现了 LoadBalance 接口,并封装了一些公共的逻辑。
策略
RandomLoadBalance
根据权重随机选择(对加权随机算法的实现)。这是 Dubbo 默认采用的一种负载均衡策略。
原理
假如有两个提供相同服务的服务器 S1 和 S2,S1 权重为 7,S2 权重为 3。把这些权重值分布在坐标区间会得到:S1->[0, 7) ,S2->[7, 10)。
生成[0, 10) 之间的随机数,随机数落到对应的区间,我们就选择对应的服务器来处理请求。
生成[0, 10) 之间的随机数,随机数落到对应的区间,我们就选择对应的服务器来处理请求。
LeastActiveLoadBalance
最小活跃数负载均衡
原理
Dubbo 就认为谁的活跃数越少,谁的处理速度就越快,性能也越好,这样就优先把请求给活跃数少的服务提供者处理。
如果有多个服务提供者的活跃数相等,那就再走一遍 RandomLoadBalance 。
ConsistentHashLoadBalance
一致性 Hash 负载均衡策略
原理
没有权重的概念,具体是哪个服务提供者处理请求是由你的请求的参数决定的,也就是说相同参数的请求总是发到同一个服务提供者。
为避免数据倾斜问题(节点不够分散,大量请求落到同一节点),还引入虚拟节点的概念。通过虚拟节点可以让节点更加分散,有效均衡各个节点的请求量。
RoundRobinLoadBalance
加权轮询负载均衡
原理
轮询就是把请求依次分配给每个服务提供者。加权轮询就是在轮询的基础上,让更多的请求落到权重更大的服务提供者上。
序列化协议
Dubbo 支持多种序列化方式:JDK 自带的序列化、hessian2(默认)、JSON、Kryo、FST、Protostuff,ProtoBuf 等等。
Motan
Motan 是新浪微博开源的一款 RPC 框架,据说在新浪微博正支撑着千亿次调用。不过很少看到有公司使用,而且网上的资料也比较少。
Motan 更像是一个精简版的 Dubbo,可能是借鉴了 Dubbo 的思想,Motan 的设计更加精简,功能更加纯粹。
gRPC
gRPC 是 Google 开源的一个高性能、通用的开源 RPC 框架。
面向移动应用开发并基于 HTTP/2 协议标准而设计(支持双向流、消息头压缩等功能,更加节省带宽),基于 ProtoBuf 序列化协议,支持众多开发语言。
ProtoBuf( Protocol Buffer)
一种更加灵活、高效的数据格式,可用于通讯协议、数据存储等领域,基本支持所有主流编程语言且与平台无关。
不过,通过 ProtoBuf 定义接口和数据类型还挺繁琐的,这是一个小问题。
gRPC 的通信层的设计还是非常优秀的,Dubbo-go 3.0 的通信层改进主要借鉴了 gRPC。
gRPC 的设计导致其几乎没有服务治理能力。如果要解决这个问题,就需要依赖其他组件,比如腾讯的 PolarisMesh(北极星)。
Thrift
Apache Thrift 是 Facebook 开源的跨语言的 RPC 通信框架,目前已经捐献给 Apache 基金会管理。
由于其跨语言特性和出色的性能,在很多互联网公司应用,有能力的公司甚至会基于 thrift 研发一套分布式服务框架,增加诸如服务注册、服务发现等功能。
Thrift 支持多种不同的编程语言,包括C++、Java、Python、PHP、Ruby等(相比于 gRPC 支持的语言更多 )。
ZooKeeper
定义
ZooKeeper 是一个开源的分布式协调服务。
设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
原语
操作系统或计算机网络用语范畴。由若干条指令组成,用于完成一定功能的一个过程。
具有不可分割性,即原语的执行必须是连续的,在执行过程中不允许被中断。
具有不可分割性,即原语的执行必须是连续的,在执行过程中不允许被中断。
ZooKeeper 为我们提供了高可用、高性能、稳定的分布式数据一致性解决方案。
ZooKeeper 将数据保存在内存中,性能是不错的。 在“读”多于“写”的应用程序中尤其地高性能,因为“写”会导致所有的服务器间同步状态。
功能
数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
这些功能的实现主要依赖于 ZooKeeper 提供的 数据存储+事件监听 功能。
特点
顺序一致性: 从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去。
原子性: 所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的。
单一系统映像: 无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是一致的。
可靠性: 一旦一次更改请求被应用,更改的结果就会被持久化,直到被下一次更改覆盖。
实时性: 每个客户端的系统视图都是最新的。
重要概念
Data model(数据模型)
采用层次化的多叉树形结构,每个节点上都可以存储数据,这些数据可以是数字、字符串或者是二进制序列。
每个节点还可以拥有 N 个子节点,最上层是根节点以“/”来代表。
每个数据节点在 ZooKeeper 中被称为 znode,它是 ZooKeeper 中数据的最小单元。每个 znode 都有一个唯一的路径标识。
注意
ZooKeeper 主要是用来协调服务的,而不是用来存储业务数据的,所以不要放比较大的数据在 znode 上,每个节点的数据大小上限是 1M 。
znode(数据节点)
每个数据节点在 ZooKeeper 中被称为 znode,它是 ZooKeeper 中数据的最小单元。每个 znode 都有一个唯一的路径标识。
分类
持久(PERSISTENT)节点:一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
临时(EPHEMERAL)节点:临时节点的生命周期是与客户端会话(session)绑定的,会话消失则节点消失 。临时节点只能做叶子节点 ,不能创建子节点。
持久顺序(PERSISTENT_SEQUENTIAL)节点:除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。
临时顺序(EPHEMERAL_SEQUENTIAL)节点:除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性。
组成
stat:状态信息。
data:节点存放的数据的具体内容。(包括版本信息)
版本(version)
dataVersion:当前 znode 节点的版本号
cversion:当前 znode 子节点的版本
aclVersion:当前 znode 的 ACL 的版本。
ACL(权限控制)
ZooKeeper 采用 ACL(AccessControlLists)策略来进行权限控制,类似于 UNIX 文件系统的权限控制。
znode 操作权限
CREATE : 能创建子节点。
READ:能获取节点数据和列出其子节点。
WRITE : 能设置/更新节点数据。
DELETE : 能删除子节点。
ADMIN : 能设置节点 ACL 的权限。
注意
CREATE 和 DELETE 这两种权限都是针对 子节点 的权限控制。
身份认证
world:默认方式,所有用户都可无条件访问。
auth:不使用任何 id,代表任何已认证的用户。
digest:用户名/密码认证方式:username:password 。
ip : 对指定 ip 进行限制。
Watcher(事件监听器)
Watcher(事件监听器)是 ZooKeeper 中的一个很重要的特性。
ZooKeeper 允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,
ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。
ZooKeeper 允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,
ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。
会话(Session)
Session 可以看作是 ZooKeeper 服务器与客户端的之间的一个 TCP 长连接。
通过连接,客户端能够通过心跳检测与服务器保持有效的会话,也能向服务器发送请求并接受响应,还能通过该连接接收来自服务器的 Watcher 事件通知。
通过连接,客户端能够通过心跳检测与服务器保持有效的会话,也能向服务器发送请求并接受响应,还能通过该连接接收来自服务器的 Watcher 事件通知。
在为客户端创建会话之前,服务端首先会为每个客户端都分配一个 sessionID。
由于 sessionID是 ZooKeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个 sessionID 的,
因此,无论是哪台服务器为客户端分配的 sessionID,都务必保证全局唯一。
由于 sessionID是 ZooKeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个 sessionID 的,
因此,无论是哪台服务器为客户端分配的 sessionID,都务必保证全局唯一。
集群
定义
为了保证高可用,最好是以集群形态来部署 ZooKeeper,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障),那么仍然是可用的。
集群间通过 ZAB 协议(ZooKeeper Atomic Broadcast)来保持数据的一致性。
最典型集群模式:Master/Slave 模式(主备模式)。
通常 Master 服务器作为主服务器提供写服务,其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。
通常 Master 服务器作为主服务器提供写服务,其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。
集群角色
在 ZooKeeper 中没有选择传统的 Master/Slave 概念,而是引入了 Leader、Follower 和 Observer 三种角色。
Leader
为客户端提供读和写的服务,负责投票的发起和决议,更新系统状态。
Follower
为客户端提供读服务,如果是写服务则转发给 Leader。参与选举过程中的投票。
Observer
为客户端提供读服务,如果是写服务则转发给 Leader。
不参与选举过程中的投票,也不参与“过半写成功”策略。在不影响写性能的情况下提升集群的读性能。此角色于 ZooKeeper3.3 系列新增的角色。
不参与选举过程中的投票,也不参与“过半写成功”策略。在不影响写性能的情况下提升集群的读性能。此角色于 ZooKeeper3.3 系列新增的角色。
选举过程
触发
当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,就会进入 Leader 选举过程,这个过程会选举产生新的 Leader 服务器。
Leader election(选举阶段):节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。
Discovery(发现阶段):在这个阶段,followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。
Synchronization(同步阶段):主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。同步完成之后准 leader 才会成为真正的 leader。
Broadcast(广播阶段):这个阶段才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。
服务器状态
LOOKING:寻找 Leader。
LEADING:Leader 状态,对应的节点为 Leader。
FOLLOWING:Follower 状态,对应的节点为 Follower。
OBSERVING:Observer 状态,对应节点为 Observer,该节点不参与 Leader 选举。
奇数台
ZooKeeper 集群在宕掉几个 ZooKeeper 服务器之后,如果剩下的 ZooKeeper 服务器个数大于宕掉的个数的话整个 ZooKeeper 才依然可用。
假如我们有 3 台,那么最大允许宕掉 1 台 ZooKeeper 服务器,如果我们有 4 台的的时候也同样只允许宕掉 1 台。
脑裂
保证可用性的同时,会发生一种机房间网络线路故障,导致机房间网络不通,而集群被割裂成几个小集群,这时候子集群各自选主导致“脑裂”的情况。
防止
ZooKeeper 的过半机制导致不可能产生 2 个 leader,因为少于等于一半是不可能产生 leader 的,这就使得不论机房的机器如何分配都不可能发生脑裂。
ZAB协议
定义
Paxos 算法是 ZooKeeper 的灵魂。但 ZooKeeper 并没有完全采用 Paxos 算法 ,而是使用 ZAB 协议作为其保证数据一致性的核心算法。
ZAB(ZooKeeper Atomic Broadcast 原子广播) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。
ZooKeeper 主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。
模式
崩溃恢复:当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,
ZAB 协议就会进入恢复模式并选举产生新的 Leader 服务器。
当选举产生了新的 Leader 服务器,同时集群中已经有过半的机器与该 Leader 服务器完成了状态同步之后,ZAB 协议就会退出恢复模式。
其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和 Leader 服务器的数据状态保持一致。
ZAB 协议就会进入恢复模式并选举产生新的 Leader 服务器。
当选举产生了新的 Leader 服务器,同时集群中已经有过半的机器与该 Leader 服务器完成了状态同步之后,ZAB 协议就会退出恢复模式。
其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和 Leader 服务器的数据状态保持一致。
消息广播:当集群中已经有过半的 Follower 服务器完成了和 Leader 服务器的状态同步,那么整个服务框架就可以进入消息广播模式了。
当一台同样遵守 ZAB 协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个 Leader 服务器在负责进行消息广播,
那么新加入的服务器就会自觉地进入数据恢复模式:找到 Leader 所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。
当一台同样遵守 ZAB 协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个 Leader 服务器在负责进行消息广播,
那么新加入的服务器就会自觉地进入数据恢复模式:找到 Leader 所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。
应用
Kafka : ZooKeeper 主要为 Kafka 提供 Broker 和 Topic 的注册以及多个 Partition 的负载均衡等功能。
不过,在 Kafka 2.8 之后,引入了基于 Raft 协议的 KRaft 模式,不再依赖 Zookeeper,大大简化了 Kafka 的架构。
不过,在 Kafka 2.8 之后,引入了基于 Raft 协议的 KRaft 模式,不再依赖 Zookeeper,大大简化了 Kafka 的架构。
Hbase : ZooKeeper 为 Hbase 提供确保整个集群只有一个 Master 以及保存和提供 regionserver 状态信息(是否在线)等功能。
Hadoop : ZooKeeper 为 Namenode 提供高可用支持。
命名服务:可以通过 ZooKeeper 的顺序节点生成全局唯一 ID。
数据发布/订阅:通过 Watcher 机制 可以很方便地实现数据发布/订阅。
当你将数据发布到 ZooKeeper 被监听的节点上,其他机器可通过监听 ZooKeeper 上节点的变化来实现配置的动态更新。
当你将数据发布到 ZooKeeper 被监听的节点上,其他机器可通过监听 ZooKeeper 上节点的变化来实现配置的动态更新。
分布式锁:通过创建唯一节点获得分布式锁,当获得锁的一方执行完相关代码或者是挂掉之后就释放锁。分布式锁的实现也需要用到 Watcher 机制。
注意
这些功能的实现基本都得益于 ZooKeeper 可以保存数据的功能,但是 ZooKeeper 不适合保存大量数据,这一点需要注意。
0 条评论
下一页