一、SpringCloud整体架构
SpringCloud是一套基于SpringBoot的分布式微服务的技术解决方案,它提供了5大核心组件和3大核心配件。SpringCloud只是Spring官方提供的一套微服务标准定义,目前真正的实现有两套体系用的比较多,一个是基于Netflix公司的开源组件集成的一套微服务解决方案Spring Cloud Netflix;一个是基于阿里巴巴开源组件集成的一套微服务解决方案Spring Cloud Alibaba。
(一)5大核心组件
1.注册中心:ZK,Eureka,nacos,consul,etcd
微服务刚兴起时使用Redis和Mysql做注册中心。只要能划分服务边界就可以用来做注册中心。
2.配置中心:apollo,nacos,config(一般和Eureka搭配使用),disconf
理论上,所有的注册中心都可以用来做配置中心,只是缺少相关功能。
(1)如何进行配置统一管理?
集中的存储的位置。每个组件存储位置不一样,比如Config有本地存储中心和远程存储中心。
(2)如何进行动态的更新配置的变更通知?
动态监听。需要支持发布-订阅模式,Nacos支持发布订阅模式但是Config不支持。
(3)安全性问题
通过权限控制来保证,有权限才能访问。
3.网关:Spring Cloud Gateway,zuul,zuul2
网关的本质是过滤器链,即过滤器是网关的升级版。
(1)前置过滤器:主要用来做校验
(2)后置过滤器:主要用来做修改
4.负载均衡:Ribbon,LoadBalance
Spring Cloud Alibaba 1.0版本和Spring Cloud Netflix全体系版本中都是使用Ribbon;Spring Cloud Alibaba 2.0及以上版本中使用LoadBalance(基于Ribbon实现)。默认采用轮询的负载均衡算法。
(1)常见的负载均衡算法
①加权
②轮询
③随机
④哈希
⑤最大空闲连接数
⑥最小活跃连接数
(2)nginx中的和架构中的负载均衡区别
网关中nginx负载均衡是集中整个项目前置流量然后进行负载均衡;而架构中负载均衡是指模块与模块之间、服务与服务之间的负载均衡,它们是消费者进程内的负载均衡。
5.声明式远程调用:Feign,OpenFeign
(1)底层实现原理
@FeignClient注解通过动态代理的方式调用@RequestMapping注解上的URL。
(2)Feign和OpenFeign的优点
同时符合Restful风格和RPC方式调用,严格来说它俩属于RPC方式调用。
(二)3大核心配件(根据项目规模来决定是否存在)
1.断路器:Sentinel组件(SEMAPHORE信号量隔离),Hystrix(Thread线程池隔离)
2.日志监控:Promethus+Graphana
3.链路追踪:skywalking,CAT,pingpoint,zipkin
skywalking比较消耗性能因为它的性能指标比较详细,和CAT刚好相反。Sleuth+zipkin(收集数据、展示)搭配使用进行链路追踪。
(三)Spring Cloud Netflix体系组件
1.Ribbon:负载均衡
原理:
通过LoadBalancerInterceptor拦截器对客户端请求进行拦截,然后通过ILoadBalancer(ZoneAwareLoaderBalancer)接口提供的选择服务的方法,结合不同负载均衡的策略选择一个合适的服务然后返回。
当然ServerListUpdater也会启动一个定时任务每30s通过Nacos客户端获取存活的服务列表数据。
2.Hystrix:服务熔断
3.zuul:网关
4.Eurke:服务注册与发现
客户端会定时向服务端发送心跳检测请求,服务端也会定时检测超过20s没有给服务端发送请求的客户端并发送探活请求到客户端,如果客户端没有响应则剔除该该客户端。
5.Feign:服务调用
(四)Spring Cloud Alibaba体系组件
1.Dubbo:消息通讯
2.Nacos:服务注册和发现。既可以是注册中心也可以是配置中心
3.Seata:事务隔离
4.Sentinel:熔断降级
5.OpenFeign:服务调用
二、微服务划分的方式
(一)业务域:简洁、代码成型快
(二)数据域:需要考虑数据的交互问题
数据量特别大时需要考虑分库分表等情况。
(三)领域:怎么方便怎么来
这种方式下需求不能经常变,否则领域也需要跟着变。
三、Http和RPC区别
在实际实践过程中,往往通过请求链路来估算服务器内存消耗情况。请求链路指的是一个完整的请求所经历的数据流向,比如:用户登录->首页->商品详情页->加入购物车->勾选订单->立即结算->扣减库存->支付->发货->物流。其中,请求链路可能会涉及到不在同一个机器上节点上的服务与服务之间的RPC远程调用(Dubbo/GRPC/Thrift)。
(一)Http协议
1.优点
直接以裸协议的方式支持RestFul风格的通信。Http和RPC两种协议之间并不是毫无关系,它们之间可以看成是互补的关系。使用支持Http协议的Restful风格请求的方式开发效率很高,直接使用restTemplate.post()或restTemplate.get()方式就可以直接进行请求的调用,而且这种方式的可读性很高,因为开发时我们会把方法名和方法参数写也进去,并且这种方式提供了防火墙和跨语言的支持。
2.缺点
Http协议在OSI七层网络模型中位居第七层,使用效率并不高因为请求过程中会产生大量无用信息,而且请求时需要对参数和参数值进行封装。Http协议使用时比较繁琐主要是指1.1版本。
(二)RPC协议
RPC是打包好的一系列功能的工具集,包括动态代理、序列化与反序列化、通信、异常处理。而通信又包括基于http协议的七层网络通信和基于TCP协议以及UDP协议的四层网络通信。通信仅仅是这一系列功能的一部分功能。
四、微服务三高特性
(一)高并发
(二)高可用
理论上,微服务架构下的所有服务、组件等必须是集群模式以避免单点故障。
(三)高性能
线性(横向/水平)动态扩容,扩容过程应该是无状态的。无状态是指两个相同的请求发起者在服务端的请求链路中是否具备上下文的关系,一般的做法是将上次请求链路保存在服务端的session中,下次相同链路的请求可以直接从session中获取请求,这样以来前后两次请求链路之间就具备上下文的关系。在这种情况下,可以将前一次请求链路保存在Redis中,而不是session中,通过这种方式来保证无状态。
五、组件的服务治理
注册中心分为三个角色:注册中心服务端(双层Map结构:第一层Map是服务名,第二层Map是服务名对应的服务实例)、服务提供者、服务消费者。
(一)服务注册
动态感知功能属于服务注册功能的一部分。动态感知通过Spring中的多路广播器里面的监听器监听事件来实现(监听器模式)。
(二)服务续约
服务续约也叫心跳检测,服务提供者通过向注册中心服务端发送请求来告诉自己处于存活状态。
(三)服务获取
服务消费者从注册中心获取服务提供者列表的过程。
(四)服务调用
服务消费者获取到服务提供者地址后,通过动态代理的方式发起服务调用。
(五)服务下线(即时下线)
服务提供者可以给注册中心主动发送请求通知自己要服务下线。除此之外,服务提供者如果在一定时间内没有再发送请求给到注册中心进行服务续约会自动被清除,这个过程叫失效剔除。Eureka的失效剔除时间是90s。
(六)失效剔除(非即时下线)
(七)自我保护
防止由于网络等原因造成短暂时间内服务提供者没有发送心跳请求到注册中心被误判而将服务下线,当出现这种情况时服务消费者会将这些暂时没有心跳的服务地址保存起来防止被误判而剔除。
(八)服务同步
多个注册中心之间的数据同步。数据同步时Nacos(AP模式)会主动push信息到其他节点,另外两种同步方式分别是主动从其他节点pull数据和手动push的方式进行数据同步。
六、微服务架构特性:CAP定理
CAP定理又叫CAP原则,指的是分布式系统中的三要素:C(Consistency,一致性),A(Availability,可用性),P(Partition,分区容错性),CAP原则是指这三个要素中最多只能同时满足两个。
(一)分区容错性
分区指的是由于网络原因或者其他不可控因素导致集群中某些节点不联通的情况,而分区容错性指的是当分布式系统中出现分区时,集群仍然能够正常对外提供服务。在一个分布式系统中,分区是无法避免的,所以CAP定理的三个因素中只能同时满足两个两个因素,即AP模式或者CP模式。
(二)CP模式
该模式属于强一致性,它牺牲了一定的可用性,因为集群节点为了保证高可用性需要频繁在集群节点之间进行数据同步,而在数据同步过程中在短暂时间内集群是无法对外提供服务的,所以CP模式中并不意味着完全没有可用性,只是会出现短暂性的不可用。
Nacos可以是AP模式或者CP模式,但是在同一状态下只有一种模式。在CP模式下Nacos使用Raft算法来保证数据一致性。Raft算法中有三种角色:Leader(领导者)、Follower(跟随者)、Candidate(候选者)、Leader Election(领导选举)。
(三)AP模式
该模式属于弱一致性。为了保证集群的高可用,集群节点之间数据同步的频率就没有那么高,所以在该模式中集群节点之间的数据会出现短暂的不一致性。
在AP模式下Nacos使用
七、网关的功能
(一)日志监控(两大体系)
1.ELK(Elasticsearch+Logstash+Kibana)+Beats:也叫Elastic Stack体系
ELK是一款开源的日志收集系统,Logstash采集到的数据经过Elasticsearch编排之后通过Kibana展示出来,集成Beats之后可以更加详细地看到文件或元数据信息。
ELK体系默认的消息队列是Redis,而不是MQ。消息队列不适合做流量队列,因为其即时性不高而且存储也没有Redis方便。
2.Prometheus(收集数据)+Grafana(展示数据)
该体系很不好使用,因为需要自定义的参数太多,虽然有模板插件可以使用,而且其展示面板看起来很酷炫。
(二)限流(使用断路器)
1.Sentinel
默认的熔断策略是SEMAPHORE信号量模式,但是它也可以设置成Thread线程池模式。在网关中一般使用Sentinel进行限流。
2.Hystrix
默认的熔断策略是Thread线程池模式,但是它也可以设置成SEMAPHORE信号量模式。服务和服务之间的调用限流一般使用Hystrix。在线程池模式中,当线程耗尽时新进来的请求将会进入流量队列等待处理。
(三)负载均衡(整个项目所有前置流量的分发)
(四)认证鉴权
八、常见分布式解决方案
(一)分布式事务
seata是比较常见的分布式事务解决方案,除此之外还有很多解决方案,比如基于本地消息表。
大部分的分布式事务都是基于多阶段提交和补偿机制实现。
假如有这样一个场景:
比如A服务中生成订单后需要发送订单结算单到结算中心(异步),然后调用B服务完成支付,正常情况下订单支付成功后该笔订单算是正常完成了,假如该笔订单支付成功了但是结算中心在结算订单时却出现了异常,这个时候就需要使用补偿机制来解决这个问题,可以将结算时出现的错误记录在结算错误表中,后面通过人工核对的方式进行补偿。
另外一个场景:
A服务调用B服务,因为B服务失败了所以A服务需要进行回滚,假如A服务回滚失败这个时候解决办法就可以使用补偿机制。
(二)分布式锁
Redis最常用用来做分布式锁,还有zk也可以用来做分布式锁。使用Redis做分布式锁时主要通过其setnx px nx key value指令实现,通过该指令执行时的返回值来确定是否加锁成功。该指令返回1就表示key本来不存在,表示获得分布式锁(加锁成功),如果指令返回0则表示锁被占用,因此表示抢占锁失败(加锁失败)。
分布式锁存放在一个统一的地方,以此来保证多个不同机器节点对于共享资源访问的安全性。
(三)分布式定时任务调度
1.如何防止重跑?
定时任务调度框架中是通过硬拼接for update的方式来加锁,以防止两个不同实例同时拿到数据库中的cron表达式造成重跑问题。
2.如何防止漏跑?
xxxJob中通过实践论算法来保证任务不会漏跑,因为每隔一段时间会尝试重新跑。
九、Eureka注册表多级缓存架构
在注册中心的三个角色(注册中心服务端、服务提供者、服务消费者)中,将注册中心服务端的整个内存块又分成了注册表内存、只读缓存、读写缓存,这样设计的主要目的是为了减少注册表内存的读写竞争压力,如果没有另外两个内存区域,那么每次服务注册和消费都会经过注册表内存,显然会影响处理性能。
十、微服务中常用的限流算法
(一)令牌桶算法
往令牌桶中放令牌的速度取决于令牌生成的速度,一般情况下令牌生成速度是远远赶不上业务消耗令牌的速度的。没有拿到令牌时请求会进入队列不会被处理。
(二)漏桶算法(经典的削峰填谷算法)
超过桶容量的后半部分请求会按顺序进入流量队列待后续处理,超过桶容量的请求是否可以直接丢弃取决于业务场景。
(三)计数器算法(通常使用Redis来实现)
使用计数器在周期内对请求进行累加,当达到设定阈值时会触发限流策略。该算法的缺点在于不能处理迸发流量。
十一、熔断和降级的区别
熔断一般发生在某个服务宕机时,而降级是出于整个服务的业务来考虑。降级一般从最外层服务开始。