Java面试准备之SpringCloud

hresh 599 0

Java面试准备之SpringCloud

什么是 Spring cloud

Spring Cloud就是微服务系统架构的一站式解决方案,在平时我们构建微服务的过程中需要做如服务发现注册配置中心消息总线负载均衡断路器数据监控等操作,而 Spring Cloud 为我们提供了一套简易的编程模型,使我们能在 Spring Boot 的基础上轻松地实现微服务项目的构建。

SpringCloud 入门推荐阅读: https://blog.csdn.net/Herishwater/article/details/106292642

Spring Cloud 的服务发现框架——Eureka

Eureka 是一个基于 REST(Representational State Transfer)的服务,主要用于 AWS cloud, 提供服务定位(locating services)、负载均衡(load balancing)、故障转移(failover of middle-tier servers)。是 Netflix 开源的服务发现组件,包括 Server 和 Client 两部分.在 Spring Cloud 子项目 Spring Cloud Netflix 中。

Eureka Server 和 Eureka Client

  • Eureka Server : 提供服务发现的能力,各服务启动时,会向 Eureka Server 注册自己的信息(IP,端口,服务信息等),Eureka Server 会存储这些信息.
  • Eureka Client : 服务提供者,简化与 Eureka Server 的交互.

Eureka能做什么

微服务的注册与发现,功能类似于 Zookeeper。

原理讲解

  • Eureka 采用了 C-S 的架构设计,Eureka Server 作为服务注册功能的服务器,提供服务发现的能力。
  • 微服务启动后,会周期性(默认30秒)的向 Eureka Server 发送心跳以续约自己的”租期”。
  • 如果 Eureka Server 在一定时间内没有收到某个微服务实例(Eureka Client)的心跳,Eureka Server 将会注销该实例(默认90秒)。
  • 默认请求下,Eureka Server 同时也是 Eureka Client。多个 Eureka Server 实例,互相之间通过复制的方式,来实现服务注册表中的数据.(实现集群,高可用)
  • Eureka Client 会缓存注册表中的信息.这种方式有一定的优势。首先,微服务无需每次请求都查询Eureka Server,从而降低了 Eureka Server 的压力;其次,即使 Eureka Server 所有节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者并完成调用。这样,Eureka通过心跳检测,客户端缓存等机制,提高了系统的灵活性,可伸缩性和可用性。

什么是 RestTemplate?

RestTemplateSpring提供的一个访问Http服务的客户端类。简而言之就是微服务之间的调用使用 RestTemplate。比如消费者B调用服务生产者A提供的服务,代码如下:

服务消费者首先需要在配置类中注入 RestTemplate。

@Configuration
public class MyConfig {

    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

在消费者的 Controller 类中代码实现如下:

@RestController
public class DeptConsumerController {

    @Autowired
    RestTemplate restTemplate;

    private static final String REST_URL_PREFIX = "http://localhost:8001";

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept getDept(@PathVariable("id") long id) {
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> queryAll(){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    }

    @RequestMapping(name = "/consumer/dept/add")
    public boolean addDept(Dept dept){
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }

}

推荐阅读:SpringCloud入门学习之Rest服务

什么是负载均衡

负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。

负载均衡的分类

目前业界主流的负载均衡方案可分为两类:

集中式负载均衡:即在 consumer 和 provider 中间使用独立的负载均衡设施(可以是硬件,如F5,也可以是软件,如:Nginx),由该设施把访问请求通过某种策略转发到 provider。

进程内负载均衡:将负载均衡的逻辑集成到 consumer,consumer 从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择一个合适的 provider。

关于负载均衡分类想要深入了解的同学,可以参考负载均衡技术原理及分类

Ribbon 属于后者,它只是一个类库,集成在 consumer 中,consumer 通过它来获得合适的 provider 地址。

两种负载均衡方式架构图

Java面试准备之SpringCloud

Nginx负载均衡策略

  • 轮询 默认方式
  • weight 权重方式
  • ip_hash 依据ip分配方式
  • least_conn 最少连接方式
  • fair(第三方) 响应时间方式
  • url_hash(第三方) 依据URL分配方式

扩展阅读:nginx负载均衡的5种策略

Nginx如何实现高可用?

可以用nginx+keepalived保证高可用。

扩展阅读:Nginx学习笔记--高可用Nginx架构:keepalived+nginx

Nginx反向代理

正向代理代理客户端,反向代理代理服务器。

正向代理的应用

  1. 访问原来无法访问的资源
  2. 用作缓存,加速访问速度
  3. 对客户端访问授权,上网进行认证
  4. 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息

反向代理的应用

  1. 保护内网安全
  2. 负载均衡
  3. 缓存,减少服务器的压力

Nginx作为最近较火的反向代理服务器,安装在目的主机端,主要用于转发客户机请求,后台有多个http服务器提供服务,nginx的功能就是把请求转发给后台的服务器,决定哪台目标主机来处理当前请求。

参考文献:

Nginx反向代理与负载均衡

Nginx(三)------nginx 反向代理

什么是CAP原则

Java面试准备之SpringCloud

CAP 原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

CAP原则是NOSQL数据库的基石。

分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:

  • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
  • 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
  • 分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情 况,必须就当前操作在C和A之间做出选择。

著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。在此Zookeeper保证的是CP, 而Eureka则是AP。

Zookeeper保证CP

当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。

Eureka保证AP

Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:

  1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
  2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
  3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中

因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像 Zookeeper那样使整个注册服务瘫痪。

Ribbon常见的负载均衡策略

// RoundRobinRule 轮询,默认策略,按序获取provider
// RandomRule 随机
// AvailabilityFilteringRule : 可用性敏感策略,会先过滤掉,跳闸,访问故障的服务~,对剩下的进行轮询~
// RetryRule : 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
//WeightedResponseTimeRule 权重轮询策略,根据每个Provider的响应时间分配一个权重,响应时间越长权重越小,被选中的可能性就越低,初次会使用轮询策略,直到分配好权重
//BestAvailableRule 最少并发数策略,选择正在请求中并发数量小的provider,除非这个provider在熔断中

声明式服务调用Feign

spring cloud feign 基于Netfix Feign实现,整合了spring cloud Ribbonspring cloud Hystrix, 是声明式的 web service 客户端,它让微服务之间的调用变得更简单了,类似 controller 调用 service。SpringCloud 集成了 Ribbon 和 Eureka,可在使用 Feign 时提供负载均衡的 http 客户端。

它能做什么?

我们在上一节讲述 Ribbon 的使用时,利用它对 RestTemplate 的请求拦截来实现对依赖服务的接口调用,而 RestTemplate 已经实现了对 http 请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以我们需要针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用,模块化的代码重复出现在每一个客户端中,代码量会比较大。

比如我们之前案例中使用到的 springcloud-consumer-dept-80 项目,其中的 DeptConsumerController 类封装了对 http 请求的处理方法,如果我们此时再新建一个服务消费者,同样需要处理 dept 对象的相关逻辑,则需要复制一份 DeptConsumerController 代码。

spring cloud feign在此基础上做了进一步封装,由它来帮助我们定义和实现依赖服务接口的定义。在spring cloud feign的实现下,我们只需创建一个接口并调用注解的方式来配置它,即可完成对服务提供方的接口绑定,简化了使用spring cloud ribbon时自动封装服务调用客户端的开发量。

服务雪崩

服务雪崩的产生过程

服务雪崩效应是一种因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程。

服务雪崩的原因

  • 流量激增:比如异常流量、用户重试导致系统负载升高;
  • 缓存击穿:假设A为client端,B为Server端,假设A系统请求都流向B系统,请求超出了B系统的承载能力,就会造成B系统崩溃;
  • 程序Bug:代码循环调用的逻辑问题,资源未释放引起的内存泄漏等问题;
  • 硬件故障:比如宕机,机房断电,光纤被挖断等。
  • 线程同步等待:系统间经常采用同步服务调用模式,核心服务和非核心服务共用一个线程池和消息队列。如果一个核心业务线程调用非核心线程,这个非核心线程交由第三方系统完成,当第三方系统本身出现问题,导致核心线程阻塞,一直处于等待状态,而进程间的调用是有超时限制的。大量的等待线程会占用系统资源,一旦系统资源被耗尽,服务将会转为不可用状态,从而引发服务雪崩。

通过实践发现,线程同步等待是最常见引发雪崩效应的场景。

服务雪崩的应对策略

针对上述雪崩产生的场景,有很多应对方案,但没有一个万能的模式能够应对所有场景。

  1. 流量控制
    • 网关限流。因为 Nginx 的高性能, 目前一线互联网公司大量采用 Nginx+Lua 的网关进行流量控制, 由此而来的 OpenResty 也越来越热门.
    • 用户交互限流。用户交互限流的具体措施有:1、 采用加载动画,提高用户的忍耐等待时间.;2、提交按钮添加强制等待时间机制。
    • 关闭重试
  2. 改进缓存模式
    • 缓存预加载
    • 同步改为异步刷新
  3. 服务自动扩容
    • AWS的auto scaling
  4. 针对硬件故障,多机房容灾,跨机房路由,异地多活等。
  5. 针对同步等待,使用 Hystrix 做故障隔离,熔断器机制等可以解决依赖服务不可用的问题。

Hystrix

在分布式环境中,不可避免地会有许多服务依赖项中的某些失败。Hystrix 是一个库,可通过添加延迟容限和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix 通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。

总体来说Hystrix就是一个能进行熔断降级的库,通过使用它能提高整个系统的弹性。

Hystrix的工作原理

  • 防止任何单个依赖项耗尽所有容器(例如Tomcat)用户线程。
  • 减少负载并快速失败,而不是排队。
  • 在可行的情况下提供备用,以保护用户免受故障的影响。
  • 使用隔离技术(例如隔板,泳道和断路器模式)来限制任何一种依赖关系的影响。
  • 通过近实时指标,监视和警报优化发现时间
  • 通过在Hystrix的大多数方面中以低延迟传播配置更改来优化恢复时间,并支持动态属性更改,这使您可以通过低延迟反馈环路进行实时操作修改。
  • 防止整个依赖项客户端执行失败,而不仅仅是网络流量失败。

如何通过 Hystrix 解决服务雪崩?

降级

超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据

实现一个 fallback 方法,当请求后端服务出现异常的时候,可以使用 fallback 方法返回值。

隔离(线程池隔离和信号量隔离)

限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。

熔断

当失败率(网络故障、超时造成的失败率)达到阈值自动触发降级,熔断器触发的快速失败会进行快速恢复 。

请求合并

对于高并发情况下的多次请求合并为一个请求。

推荐阅读:SpringCloud入门学习之Hystrix

微服务网关Zuul

ZUUL 是从设备和 web 站点到 Netflix 流应用后端的所有请求的前门。作为边界服务应用,ZUUL 是为了实现动态路由、监视、弹性和安全性而构建的。

Zuul中最关键的就是路由和过滤器

  • SpringCloud Zuul 通过与 SpringCloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从Eureka 中获得了所有其他微服务的实例信息。外层调用都必须通过API网关,使得将维护服务实例的工作交给了服务治理框架自动完成
  • 在API网关服务上进行统一调用来对微服务接口做前置过滤,以实现对微服务接口的拦截和校验

Zuul与getway的区别

Zuul 1.x, 是一个基于阻塞 I/O 的 API。

Zuul 2.x理念更先进, 想基于 Netty 非阻塞和支持长连接, 但 Spring Cloud 目前还没有整合。

Spring Cloud Gateway建立在SpringFramework 5、Project Reactor和Spring Boot 2之上,是异步、非阻塞 API。gateway 之所以性能好,因为底层使用 WebFlux,而 webFlux 底层使用 netty通信(NIO)。

Spring Cloud配置管理Config

Spring Cloud Config 项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用

  • 简单来说,使用Spring Cloud Config就是将配置文件放到统一的位置管理(比如GitHub),客户端通过接口去获取这些配置文件。
  • 在GitHub上修改了某个配置文件,应用加载的就是修改后的配置文件。

服务降级和熔断如何处理

服务降级

1、服务提供方实现服务降级,写一个兜底的方法,比如说超时问题,参数 execution.isolation.thread.timeoutInMilliseconds ,在设定的时间范围内返回,即执行正常流程;超过指定时间,则执行指定的服务降级方法。

2、服务消费者实现服务降级,比如说通过 feign调用时,销售模块调用财务模块的某个方法,具体操作同上个操作差不多。

服务降级可能存在的问题

  1. 每个业务方法对应一个兜底的方法,代码膨胀
  2. 业务方法自定义降级方法 混合在一起(业务逻辑方法 和 处理服务降级、熔断方法 揉在一块)

问题1的解决方法:可以定义一个全局通用的 服务降级 方法,这样就可以使用全局 服务降级 方法,解决代码膨胀问题。

问题2的解决方法:解决混合在一起的问题,只需要为 Feign 定义的接口 添加统一的服务降级类 即可实现解耦。即:来一个实现类,实现Feign 接口,并重写方法(重写的就是"服务降级"处理方法)。

服务熔断

服务雪崩时,可以通过 Hystrix 的熔断来处理,处理手段和服务降级相似。

参考文献

Spring Cloud 整合 Hystrix 实现服务降级、服务熔断、服务限流

RPC

RPC 解决了什么问题?

从上面对 RPC 介绍的内容中,概括来讲RPC 主要解决了:让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单。

常见的 RPC 框架总结?

  • RMI(JDK自带): JDK自带的RPC,有很多局限性,不推荐使用。
  • Dubbo: Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。目前 Dubbo 已经成为 Spring Cloud Alibaba 中的官方组件。
  • gRPC :gRPC是可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,运行状况检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。
  • Hessian: Hessian是一个轻量级的remotingonhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。
  • Thrift: Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于thrift研发一套分布式服务框架,增加诸如服务注册、服务发现等功能。

RPC与HTTP

rpc是远端过程调用,其调用协议通常包含:传输协议 和 序列化协议。

传输协议

比如著名的 grpc,它底层使用的是 http2 协议;还有 dubbo 一类的自定义报文的 tcp 协议

序列化协议

例如基于文本编码的 json 协议;也有二进制编码的 protobuf、hession 等协议;还有针对 java 高性能、高吞吐量的 kryo 和 ftc 等序列化协议

为什么要使用自定义 tcp 协议的 rpc 做后端进程通信?

要解决这个问题就应该搞清楚 http 使用的 tcp 协议,和我们自定义的 tcp 协议在报文上的区别。

  首先要 否认 一点 http 协议相较于 自定义tcp 报文协议,增加的开销在于连接的建立与断开

  第一、http协议是支持连接池复用的,也就是建立一定数量的连接不断开,并不会频繁的创建和销毁连接

  第二、http也可以使用 protobuf 这种二进制编码协议对内容进行编码

  因此二者即 http 和 rpc 最大的区别还是在传输协议上。

  通用定义的http1.1协议的tcp报文包含太多废信息,一个POST协议的格式大致如下

HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>

即使编码协议也就是 body 是使用二进制编码协议,报文元数据也就是header头的键值对却使用了文本编码,非常占字节数。如上图所使用的报文中有效字节数仅仅占约 30%,也就是70%的时间用于传输元数据废编码。当然实际情况下报文内容可能会比这个长,但是报头所占的比例也是非常可观的。

  那么假如我们使用自定义tcp协议的报文如下

Java面试准备之SpringCloud

  报头占用的字节数也就只有16个byte,极大地精简了传输内容。

  这也就是为什么后端进程间通常会采用 自定义tcp协议 的 rpc 来进行通信的原因。

HTTP 和 RPC 是同一级别,还是被 RPC 包含?

Java面试准备之SpringCloud

两者都是,其中一个是 和 RPC并列的,都是跨应用调用方法的解决方案;另一个则是被RPC包含的,是RPC通信过程的可选协议之一。

Restful 也属于 RPC 吗?

不属于。

基于Restful的远程过程调用有着明显的缺点,主要是效率低、封装调用复杂。当存在大量的服务间调用时,这些缺点变得更为突出。

RPC 牺牲可读性提升效率、易用性是可取的

关于 RPC 的简要实现推荐阅读:Java实现简单的RPC框架(美团面试)

参考文献:

RPC 和 HTTP 理解,看完这一篇就够了

服务开发中,如何实现自我保护从而避免系统过载

如果CPU资源不足以应对请求负载,一般来说所有的请求都会变慢,CPU负载过高会造成一系列的副作用,主要包括以下几项:

正在处理的(in-flight) 的请求数量上升

服务器逐渐将请求队列填满,意味着延迟上升,同时队列会用更多的内存

线程卡住,无法处理请求

cpu死锁或者请求卡主

rpc服务调用超时

cpu的缓存效率下降

由此可见防止服务器过载的重要性不言而喻,而防止服务器过载又分为下面几种常见的策略:

  • 提供降级结果

  • 在过载情况下主动拒绝请求

  • 调用方主动拒绝请求

  • 提前进行压测以及合理的容量规划

参考文献:https://blog.csdn.net/weixin_33581873/article/details/114180655

发表评论 取消回复
表情 图片 链接 代码

分享