SpringCloud和分布式
组件
注册发现:Eureka
负载均衡:Ribbon
断路器: Hystrix
服务网管:Zuul
分布式配置:Config
业务背景
假设开发一个电商网站,要实现支付订单的功能,流程如下:
- 创建一个订单后,如果用户立刻支付了这个订单,我们需要将订单状态更新为“已支付”
- 扣减相应的商品库存
- 通知仓储中心,进行发货
- 给用户的这次购物增加相应的积分
Eureka
注册中心,记录了一个注册表,哪个服务在哪台服务器上的哪个端口
解决微服务过多而不方便管理的问题
微服务通过Eureka Client获取被调用服务的信息,Client通过请求Eureka Server获取服务信息,发送请求。
- Eureka Client: 负责将这个服务的信息注册到Eureka Server中
- Eureka Server: 注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号
自我保护机制
如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1.Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务 2.Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用) 保证可用性,牺牲一致性 3.当网络稳定时,当前实例新的注册信息会被同步到其它节点中 因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。
Zookeeper
ZK保证CP,即保证一致性
Eureka保证AP,优先可用性
Feign
声明式客户端
解决微服务间调用的问题,帮助我们简化服务间的调用,例如建立连接、构造请求、解析响应
原理:使用动态代理,对于添加@FeignClient注解的接口,创建动态代理
Feign会根据@RequestMapping等注解,动态构造出要请求的服务地址。
Ribbon
负载均衡工具。
解决多个微服务实例的场景下,请求如何分发的问题。
Ribbon从Eureka Client获取服务的注册列表,默认使用Round Robin算法,选择目标服务器
常见的复杂均衡策略:随机 (Random),轮询 (RoundRobin),一致性哈希 (ConsistentHash)
Nginx
Nginx- 服务端负载均衡: nginx是客户端所有请求统一交给nginx,他来实现负载均衡属于服务器端的负载均衡
Ribbon - 客户端负载均衡:Ribbon获取服务的注册信息后,在本地轮询来实现负载均衡,适用于服务间调用的场景
Hystrix
熔断降级
解决服务雪崩的问题
服务雪崩:一个微服务故障引发多个微服务失效的连锁反应
服务熔断:熔断机制是应对雪崩效应的一种微服务链路保护机制,当请求失败达到一定次数或者比例,直接返回某个错误码
熔断机制的注解是@HystrixCommand.
服务降级:微服务分级,在资源不够的场景,关闭部分微服务,保证核心业务正常运行
Zuul
网络路由,类似于门面模式,对外提供统一的网管入口,由Zuul网管抓饭请求到具体服务
外部应用,如浏览器,移动端不需要了解具体服务,只需要想统一网管发送请求
解决问题:提供统一的降级、限流、认证授权、安全
Gateway
限流
处理方式:拒绝服务,排队,降级
算法:
1.滑动窗口
2.漏桶算法
3.令牌桶算法
Config
配置管理
解决问题:每个微服务由自己的配置信息,难以管理
可以做到配置,代码分离,单独管理,配置难以维护的问题
原理
1.配置文件存储在远端git(比如github,gitee等仓库),config-server从远端git拉取配置文件,并保存到本地git。 2.本地git和config-server的交互是双向的,因为当远端git无法访问时,会从本地git获取配置文件 3.config-client(即各个微服务),从config-server拉取配置文件
CAP
一致性,可用性,分区容错性
CA:mysql
CP: ZK
AP:
BASE
基本可用性:时间上的损失,功能上损失(保证核心功能,时延可能变大)
软状态:不同节点间数据同步存在时延
最终一致性:多个节点间的不同副本在一定时间后达到一致
2PC
事务分为2个阶段,表决阶段-执行阶段
分布式锁
实现方案
数据库
操作简单,性能开销大,在某个表中添加或删除记录
Redis
利用Redis的SETNX的原子特性,配合Redis过期策略,以及异常处理,实现过期自动释放锁
Zookeeper
在某个路径下,创建临时顺序节点,谁的id小,认为获得锁成功,其他节点增加一个watcher监听,实时获取到变化,性能不如redis
分布式ID
要求
全局唯一,高性能,能递增
1.uuid
生成简单,全局唯一,但是结果比较长,不能自增,没有含义,在数据库主键等场景不好使用
2.数据库自增ID
生成简单,单调递增,容易被猜测,并发访问压力大,DB单点故障
优化
多数据库,为了防止生成重复id,可以修改自增的步长
优化二
号段模式,每次返回一个号段,例如1-1000
3.Redis
利用redis的incr命令实现Id的原子自增
考虑redis持久化
4.雪花算法
Snowflake生成的是Long类型的ID,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特。
Snowflake ID组成结构:正数位(占1比特)+ 时间戳(占41比特)+ 机器ID(占5比特)+ 数据中心(占5比特)+ 自增值(占12比特),总共64比特组成的一个Long类型。