Spring cloud 基本架构图

面试题 - 图1

Eureka:服务注册中心

Feign:服务调用

Ribbon:负载均衡

Zuul/Spring Cloud Gatway:网关

这么多的系统,电商系统包含了20个子系统,每个子系统有20个核心接口,一共电商系统有400个接口,这么多的接口,直接对外暴露,前后端分离的架构,难道你让前端的同学必须记住你的20个系统的部署的机器,他们去做负载均衡,记住400个接口
微服务那块,网关
灰度发布统一熔断统一降级统一缓存统一限流统一授权认证
Hystrix链路追踪stream、很多组件,Hystrix这块东西,其实是会放在高可用的环节去说的,并不是说一个普通系统刚开始就必须得用的,没有用好的话,反而会出问题,Hystrix线路熔断的框架,必须得设计对应的一整套的限流方案、熔断方案、资源隔离、降级机制,配合降级机制来做

Spring Cloud 组件原理

eureka 原理图

面试题 - 图2

Eureka 缓存的设计目的

优化并发 并发冲突,如果操作服务注册表,读时加锁防止写,写时加锁不能读,效率降低。

Feign 原理

在配置类上,加上@EnableFeginClients,那么该注解是基于@Import注解,注册有关Fegin的解析注册类,这个类是实现 ImportBeanDefinitionRegistrar 这个接口,重写registryBeanDefinition 方法。他会扫描所有加了@FeginClient 的接口,然后针对这个注解的接口生成动态代理,然后你针对fegin的动态代理去调用他方法的时候,此时会在底层生成http协议格式的请求。

Ribbo 原理

底层的话,使用HTTP通信的框架组件,HttpClient,先得使用Ribbon去本地的Eureka注册表的缓存里获取出来对方机器的列表,然后进行负载均衡,选出一台机器,接着针对那台机器发送 Http请求过去即可

Zuul 原理

配置一下不同的请求路径和服务的对应关系,你的请求到了网关,他直接查找到匹配的服务,然后就直接把请求转发给服务的某台机器,Ribbon从Eureka本地的缓存列表里获取一台机器,负载均衡,把请求直接用HTTP通信扩建发送到指定机器上去。

Spring Cloud 和 Dubbo 的区别

Dubbo,RPC的性能比HTTP的性能更好,并发能力更强,经过深度优化的RPC服务框架,性能和并发能力是更好一些

很多中小型公司而言,其实稍微好一点的性能,Dubbo一次请求10ms,Spring Cloud耗费20ms,对很多中小型公司而言,性能、并发,并不是最主要的因素

Spring Cloud这套架构原理,走HTTP接口和HTTP请求,就足够满足性能和并发的需要了,没必要使用高度优化的RPC服务框架

Dubbo之前的一个定位,就是一个单纯的服务框架而已,不提供任何其他的功能,配合的网关还得选择其他的一些技术

Spring Cloud,中小型公司用的特别多,老系统从Dubbo迁移到Spring Cloud,新系统都是用Spring Cloud来进行开发,全家桶,主打的是微服务架构里,组件齐全,功能齐全。网关直接提供了,分布式配置中心,授权认证,服务调用链路追踪,Hystrix可以做服务的资源隔离、熔断降级、服务请求QPS监控、契约测试、消息中间件封装、ZK封装

胜是胜在功能齐全,中小型公司开箱即用,直接满足系统的开发需求

Spring Cloud原来支持的一些技术慢慢的未来会演变为,跟阿里技术体系进行融合,Spring Cloud Alibaba,阿里技术会融入Spring Cloud里面去

你们的服务注册中心进行过选型调研吗?对比一下各种服务注册中心!

注册中心比较

比较点 Eureka Zookeeper Consul Nacos
一致性 AP(最终一致性) CP(强一致性) CP CP+AP
时效性 服务注册心跳时间
注册表检查时间
readwrite 同步到 readonly时间
消费方拉取readonly注册表时间
快,强一致性 同步延迟 CP 快
AP 慢
适用规模 20K~30K实例(节点) 10K~20K实例(节点) < 3K实例(节点)
通信方式 Http Rest 自定义协议 Http Rest HTTP/DNS
集群模式 Peer 2 Peer(服务器之间) leader 读/写
follower 读
Agent 监听的方式
性能问题 简单的更新机制,
设计复杂(扩容麻烦)
扩容麻烦,
规模较大时GC频繁
3K节点以上,
更新列表缓慢
运维熟悉度 相对陌生 熟悉 更陌生
一次性协议 http定时轮询 ZAB RAFT

Eureka、ZooKeeper
Dubbo作为服务框架的,一般注册中心会选择zk
Spring Cloud作为服务框架的,一般服务注册中心会选择Eureka

(1)服务注册发现的原理

集群模式 面试题 - 图3
Eureka,peer-to-peer,部署一个集群,但是集群里每个机器的地位是对等的,各个服务可以向任何一个Eureka实例服务注册和服务发现,集群里任何一个Euerka实例接收到写请求之后,会自动同步给其他所有的Eureka实例 面试题 - 图4
ZooKeeper,服务注册和发现的原理,Leader + Follower两种角色,只有Leader可以负责写也就是服务注册,他可以把数据同步给Follower,读的时候leader/follower都可以读

(2)一致性保障:CP or AP

CAP,C是一致性,A是可用性,P是分区容错性
CP,AP

ZooKeeper是有一个leader节点会接收数据, 然后同步写其他节点,一旦leader挂了,要重新选举leader,这个过程里为了保证C,就牺牲了A,不可用一段时间,但是一个leader选举好了,那么就可以继续写数据了,保证一致性

Eureka是peer模式,可能还没同步数据过去,结果自己就死了,此时还是可以继续从别的机器上拉取注册表,但是看到的就不是最新的数据了,但是保证了可用性,强一致,最终一致性

(3)服务注册发现的时效性
zk,时效性更好,注册或者是挂了,一般秒级就能感知到

eureka,默认配置非常糟糕,服务发现感知要到几十秒,甚至分钟级别,上线一个新的服务实例,到其他人可以发现他,极端情况下,可能要1分钟的时间,ribbon去获取每个服务上缓存的eureka的注册表进行负载均衡

服务故障,隔60秒才去检查心跳,发现这个服务上一次心跳是在60秒之前,隔60秒去检查心跳,超过90秒没有心跳,才会认为他死了,2分钟都过去
30秒,才会更新缓存,30秒,其他服务才会来拉取最新的注册表
三分钟都过去了,如果你的服务实例挂掉了,此时别人感知到,可能要两三分钟的时间,一两分钟的时间,很漫长

(4)容量

zk,不适合大规模的服务实例,因为服务上下线的时候,需要瞬间推送数据通知到所有的其他服务实例,所以一旦服务规模太大,到了几千个服务实例的时候,会导致网络带宽被大量占用

eureka,也很难支撑大规模的服务实例,因为每个eureka实例都要接受所有的请求,实例多了压力太大,扛不住,也很难到几千服务实例

之前dubbo技术体系都是用zk当注册中心,spring cloud技术体系都是用eureka当注册中心这两种是运用最广泛的,但是现在很多中小型公司以spring cloud居多,所以后面基于eureka说一下服务注册中心的生产优化

画图阐述一下你们的服务注册中心部署架构,生产环境下怎么保证高可用?

面试题 - 图5
面试题 - 图6

你们系统遇到过服务发现过慢的问题吗?怎么优化和解决的?

zk,一般来说还好,服务注册和发现,都是很快的

eureka,必须优化参数

  • 服务器到注册中心心跳时间设置
  • 注册中心定时检测心跳时间设置
  • 心跳失效时间设置
  • readWrite缓存定更新到readOnly时间设置
  • 客户端定时拉取readWrite缓存时间设置

服务发现的时效性变成秒级,几秒钟可以感知服务的上线和下线

说一下自己公司的服务注册中心怎么技术选型的?生产环境中应该怎么优化?

  • 可用性
  • 时效性
  • 数据一致性 CP AP
  • 容量

通过集群保证可用性

你们对网关的技术选型是怎么考虑的?能对比一下各种网关技术的优劣吗?

网关的核心功能

(1)动态路由:新开发某个服务,动态把请求路径和服务的映射关系热加载到网关里去;服务增减机器,网关自动热感知

(2)灰度发布

(3)授权认证

(4)性能监控:每个API接口的耗时、成功率、QPS

(5)系统日志

(6)数据缓存

(7)限流熔断

几种技术选型

Kong、Zuul、Nginx+Lua(OpenResty)、自研网关

Kong:Nginx里面的一个基于lua写的模块,实现了网关的功能
Zuul:基于Java开发,核心网关功能都比较简单,但是比如灰度发布、限流、动态路由之类的,很多都要自己做二次开发。高并发能力不强,部署到一些机器上去,还要基于Tomcat来部署,Spring Boot用Tomcat把网关系统跑起来;Java语言开发,可以直接把控源码,可以做二次开发封装各种需要的功能
Nginx+Lua(OpenResty): 直接通过nginx来当做网关
自研网关:自己来写类似Zuul的网关,基于Servlet、Netty来做网关,实现上述所有的功能

如果网关需要抗每秒10万的高并发访问,你应该怎么对网关进行生产优化?

面试题 - 图7
Zuul网关部署的是什么配置的机器,部署32核64G,对网关路由转发的请求每秒抗个小几万请求是不成问题的,几台Zuul网关机器
每秒是1万请求,8核16G的机器部署Zuul网关,5台机器就够了

生产级的网关,应该具备我刚才说的几个特点和功能:

(1)动态路由:新开发某个服务,动态把请求路径和服务的映射关系热加载到网关里去;服务增减机器,网关自动热感知

(2)灰度发布:基于现成的开源插件来做

(3)授权认证

(4)限流熔断

(5)性能监控:每个API接口的耗时、成功率、QPS

(6)系统日志

(7)数据缓存

如果需要部署上万服务实例,现有的服务注册中心能否抗住?如何优化?

Eureka 和 ZK都是扛不住了,(可以主动说出注册中心的缺点

eureka:peer-to-peer,每台机器都是高并发请求,有瓶颈

zookeeper:服务上下线,全量通知其他服务,网络带宽被打满,有瓶颈

  1. 可以加一个数据库层(或者是 redis缓存层),每个服务定时通过数据库(redis缓存层)来更新服务注册表,然后数据库(redis缓存层)定时拉取注册中心来更新注册表。
  2. 可以自研,类似于 redis 集群 加主备架构,将压力分散开。按需拉取局部的注册表。比如说服务A在,注册中心1,那么只用拉取注册中心1的注册表。而不用将注册中心1,2,3,4等等其他注册拉取过来。缓解压力。

面试题 - 图8

说说生产环境下,你们是怎么实现网关对服务的动态路由的?

  • 通过数据库+网关定时拉取数据库 服务注册中心配置。
  • 首先开发注册中心配置系统,通过页面可以动态的将增加新老服务。写入到数据库。
  • 同时也可以通过拉取eureka来最新的服务注册中心配置。写入到数据库。
  • 网关定时10秒拉取数据库的最新配置。

这样好处减少了eureka的压力,同时当注册中心服务宕机,也不影响当前网关的路由。

你们是如何基于网关实现灰度发布的?说说你们的灰度发布方案?

  1. 准备一个数据库和一个表(也可以用Apollo配置中心、Redis、ZooKeeper,其实都可以),放一个灰度发布启用表
  2. 写一个zuul的filter,对每个请求,zuul都会调用这个filter
  3. 当尝试新版本发布,修改新服务的版本为 new
  4. 通过页面 修改配置中心,或者修改数据库表,开灰度发布。
  5. 开灰度发布 网关的 filter 就会随机 百分之1的请求 带上 new 版本。这样请求就会跑到新服务
  6. 当新服务使用一段时间没有问题,再将old服务全部替换成 new服务 版本设置为 current,关闭灰度发布。

    说说你们一个服务从开发到上线,服务注册、网关路由、服务调用的流程?

    spring cloud 原理图。
    注册中心 eureka
    网关 zuul
    服务调用 fegin
    负载均衡 ribbon