SpringCloud

单体架构

    将业务的所有功能集中在一个项目中开发,打成一个包部署。它的优点是架构简单、部署成本低。
    缺点是团队协作成本高(项目打包时间非常长,改bug重新编译就是一种折磨)、系统分布效率低、系统可用性差(Tomcat的资源是有限的,当一个接口的访问量短时间内过高,是会影响所有接口的访问效率的,甚至会导致整个项目宕机)
    所以单体项目适合功能较少,访问量较小的项目。

微服务

    微服务架构,是服务化思想指导下的一套最佳实践架构方案。服务化,就是把单体架构中的功能模块拆分成多个独立的项目。当然了,拆分也是有一定的要求的,拆分粒度一定要小(按照业务来看,拆出去的项目要符合一个完整的功能,单一职责)、团队自治(每个拆分的项目都要有团队来维护)、服务自治(分开打包、分开部署)

SpringCloud(组件)

    SpringCloud作为微服务全家桶的技术栈,其包含了很多组件来解决服务拆分中带来的问题

服务治理(Nacos)

    如果不同服务之间存在数据的交换,那么就需要由一个服务调用另一个服务的接口,成为服务治理。服务治理的三个角色分别是

  1. 提供者,提供服务接口,供其他服务调用
  2. 调用者,调用其服务提供的接口
  3. 注册中心,记录并监控微服务各实例状态,推送服务变更状态

注册中心原理

    所以注册中心的作用就是为消费者提供提供者的地址,消费者可以从注册中心订阅和拉取服务信息

那么消费者如何得知服务的变更状态? 服务提供者通过心跳机制向注册中心报告自己的健康状态,当心跳异常时,注册中心就会将异常的服务剔除,并通知订阅了该服务的消费者。

    如果提供者有多个实例时,消费者可以通过负载均衡算法,从多个实例中选择一个

Nacos注册中心

服务注册

    Nacos是目前国内占比最多的注册中心组件,在docker配置好nacos,并且导入数据库后就可以使用,然后在pom引入nacos依赖和配置。

<!-- Nacos 服务注册发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
spring:
  application:
    name:service1 #服务名称
  cloud:
    nacos:
      server-addr:127.0.0.1:8848 #nacos地址

启动服务,就可以在nacos的服务管理-服务列表看到服务的实例

服务发现

    消费者需要连接Nacos以拉取和订阅服务,因此服务发现的前两步和服务的注册是一样的,后面在加上服务调用即可:

  1. 引入Nacos discovery依赖
  2. 配置nacos地址
  3. 服务发现

    使用DiscoveryClient对象的getInstances()方法获取实例列表(传参为服务名),然后实现负载均衡(使用Random实现,Random传参为列表size),随机从实例列表中取出一个实例,里面包含了实例的全部信息(主机名、端口号、原信息等等),然后使用RestTemplate向提供者发起网络请求,获得HTTP响应。这里就不需要再写死服务提供者的端口信息,一切由Nacos注册中心提供,我们只需要写入服务名即可。
在这里插入图片描述

OpenFeign

    如果不使用其他组件,微服务之间的调用是相当的麻烦,调用者的代码要先获取提供者服务实例列表;然后手写负载均,挑选一个实例;接着利用RestTemplate发起http请求,调用另外一个服务;最后还要进行解析结果,代码实现是很麻烦的。

OpenFeign如何解决这个问题

    OpenFeign是声明式的HTTP请求客户端,可以让我们写http请求时变得更加简单、优雅。

    引入OpenFeign和负载均衡(loadbalancer)依赖,通过在类名上添加@EnableFeignClient注解,启用OpenFeign功能。
    第三步新建接口,上面标注@FeignClient("服务名")注解,然后通过@GetMapping或者@PostMapping注解,添加请求路径参数,最后在定义方法时传递请求参数即可实现发送方的跨服务请求发送。当然了,方法不用自己实现。调用者就可以使用一行代码,调用这个接口的方法即可。
在这里插入图片描述

最佳实践方式

    单独编写一个服务模块(api),每个模块抽取一个api模块,需要调用接口的添加对api模块的依赖。这个就可以不用更新服务就在所有的Feign接口里修改代码,只修改api模块里的即可。
    当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用,所以需要指定FeignClient所在的包(@EnableFeignClient(basePackages = "api包所在的位置")

网关(Spring Cloud Gateway)

    网关就是网络的关口,负责请求的路由、转发、身份校验
    如果不使用网关,则会存在微服务过多,前端不知道该请求哪个服务的问题。加上了网关后,前端只需要知道网关的地址就够了,网关在根据前端的请求,再去判断应该由哪个微服务去处理这个请求。这个判断的过程就是请求的路由
    接下来网关就会把请求转发到具体的微服务,这个过程就行路由的转发,如果一个微服务有多个实例,则会在多个实例之间进行负载均衡。
    当然了,在网关在路由转发之前,是肯定在请求的用户的身份进行校验,就不用在每个微服务之间做身份验证了。

    网关会从注册中心拉取这些微服务的地址,如果服务地址有变更,nacos将修改后的地址推送给网关。不仅如此,有了网关之后,每个微服务的地址就不会再暴露给前端,暴露的只有网关的地址,这对于微服务来说也是一种保护。

SpringCloud中的网关的实现包括两种,Spring Cloud GatewayNetfilx Zuul

Netfilx Zuul

  1. Netfix出品
  2. 基于Servlet的阻塞式编程
  3. 需要调优才能获得SpringCloudGateWay类似的性能

Spring Cloud Gateway

  1. Spring官方出品
  2. 基于WebFlux响应式编程
  3. 无需调优即可获得优异性能

响应式编程的吞吐能力要比阻塞式优秀很多,Spring Cloud Gateway 是现在主力的网关。

    引入相关依赖和配置,routes是路由,我们可以配置的路由规则不止一个,-开头,-就是代表一个元素。每个元素里有三个关键属性,id是路由的唯一标识(最好跟服务名保持一致);uri代表路由到哪个微服务;前端请求到达网关后,网关需要对请求作出判断,由哪个微服务进行处理,predicates是判断的依据。
在这里插入图片描述

路由属性

网关路由对应的Java类型是RouteDefinition,其中常见的属性有:

  1. id:路由的唯一标识
  2. uri:路由目标的地址
  3. predicates:路由断言,判断请求是否符合当前的路由
  4. filters:路由过滤器,对请求或相应做特殊处理

Spring提供了12种路由断言
在这里插入图片描述

网关中提供了30多种路由过滤器,每种过滤器都有独特的作用。
在这里插入图片描述
这段添加的过滤器就是为每个请求添加请求头,然后可以在Controller的方法中传参时加入@RequestHeader(value = "truth")拿到请求头。

-id:
 uri:
 predicates:
 filters:
  -AddRequestHeader=truth, you good
网关登录校验

    很多微服务在处理请求时都需要做登录校验,在所有的微服务都添加登录校验代码是不现实的,而且还需要把JWT的秘钥发过去,秘钥泄露的风险也会大大提高。所以,网关里设置登录校验是最理想的实现方式,而且JWT校验必须在网关将请求转发之前做,那么如何做呢?

网关请求处理流程——如何在网关转发之前做登录校验

    网关的底层是没有业务逻辑的,它要做的事情就是基于配置的路由规则,判断前端的请求由哪个微服务处理,然后将请求转发到对应的微服务。
    而这里对路由规则的判断就是由HandlerMapping的接口(路由映射器)处理的。HandlerMapping的默认实现是RoutePredicateHandlerMapping(路由断言),它会根据请求找到匹配的路由并存入上下文,然后把请求交给WebHandler处理(这也是责任链模式)。

    WebHandler是请求处理器,它的默认实现是FilteringWebHandler(过滤器处理器)。它会加载网关中配置的多个过滤器,放入集合并排序,形成过滤器链,然后依次执行这个过滤器

    过滤器的最后是默认的NettyRoutingFilter,Netty路由过滤器,负责将请求转发到微服务,当微服务返回结果后存入上下文。由此看出,请求转发到微服务之前和微服务处理完成之后都会调用。而过滤器内部都有PREPOST两部分逻辑。

    请求进来以后会先执行过滤器的PRE逻辑,如果PRE执行失败,那么会直接结束,不会再进行路由转发。

    那么我们就可以自定义过滤器,并且保证这个过滤器在NettyRoutingFilter之前,然后在编写登录校验的逻辑,放在PRE阶段。

网关请求处理流程——网关如何将用户信息传递到微服务

    网关是没有业务逻辑的,但是微服务需要得到用户的登录信息才能进行下一步业务,那么就有新的问题:网关如何将用户信息传递到微服务?
    之前可以用ThreadLocal保存,但是每个微服务都会运行在不同的服务器上,而ThreadLocal只会保存在单独的JVM中,这显然不行。所以可以将用户信息保存在请求头中

自定义过滤器

    网关过滤器有两种,分别是

  1. GateWayFilter:路由过滤器,作用与任意指定的路由;默认不生效,要配置到路由才生效。
  2. GlobalFilter:全局过滤器,作用范围是所有路由;声明后自动生效。

    定义一个类实现GlobalFilter接口,标注@Component定义为Bean,然后通过exchange.getRequest().getgetHeaders()获取到请求头信息。
    然后还需要将过滤器放在NettyRoutingFilter之前执行,那么就还需要实现Order接口进行排序,实现getOrder方法后返回一个int值,这个值越小,执行优先级越高。所以我们需要return的值小于NettyRoutingFilter定义的值(Integer.MaxValue)。
在这里插入图片描述

配置管理

    微服务重复配置过多,维护的成本高,需要给每个微服务都配置对应的yml文件。
    业务配置经常变动,每次修改都需要查询编译,重启服务。
    网关路由配置写死,如果变更则需要重启网关。

那么就需要将这些配置添加到Nacos中(建议全部添加)。
在命名空间中先新增命名空间,然后在配置管理-配置列表在自己的命名空间下新增配置。

其次还需要新建bootstarp.yaml文件,配置服务名和nacos地址等信息。

服务保护和分布式事务

雪崩问题

什么是雪崩

    微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。

雪崩产生的原因是什么
  1. 微服务之间相互调用,服务提供者出现故障或相互阻塞
  2. 服务调用者没有做好异常处理,导致自身故障
  3. 调用链中所有服务级联失败,导致整个集群故障。
解决问题的思路有哪些
  1. 尽量避免服务出现故障或阻塞
  2. 服务调用者要做好远程调用异常的后背方案(如果一旦出现异常,有相应的处理),避免故障扩散
服务保护方案——请求限流

    请求限流:限制访问接口的请求的并发量,避免服务因流量激增而出现故障。

使用限流器来处理,然后缓慢的放行请求去访问接口(削峰填谷、流量整形)。
在这里插入图片描述
    如果服务真的出现了故障,那么如何避免故障扩散?线程隔离

服务保护方案——线程隔离(服务降级)

    线程隔离也叫作舱壁模式,模拟船舱挡板的放水原理。通过限定每个业务能使用的线程数量而将业务故障隔离,避免故障扩散。

服务保护方案——服务熔断(服务降级)

    服务熔断:由断路器统计请求异常比例或慢调用比例,如果超出阈值则会熔断该业务,则拦截该接口的请求。熔断时期,所有请求快速失败,全部走fallback逻辑。

    线程隔离、服务熔断统称为服务降级方案

Sentinel组件

    上述所说的服务保护方案:请求限流、线程隔离和服务熔断都由Sentinel实现。它是阿里巴巴开源的一款微服务流量控制组件。
    引入Sentinel依赖,配置Sentinel地址,然后只需要重启服务,即可在Sentinel客户端(8090端口)访问

簇点连读:就是单机调用链路。是一次请求进入服务后经过的每一个诶Sentinel监控的资源链。
    默认情况下,Sentinel会监控SpringMVC的每一个Http接口。
    限流、熔断等都是针对簇点链路中的资源设置的。而资源名默认就是接口的请求路径。

请求限流

    在簇点链路后面点击流控button,即可对其做限流配置。其中QPS就是每秒的请求数量。
在这里插入图片描述

线程隔离

    当一个微服务出现阻塞或者故障时,调用的接口可能会被拖慢,甚至资源耗尽。所以必须要限制查询接口的可用线程数,实现线程隔离。
    首先让OpenFeign整合Sentinel:

feign:
 sentinel:
  enable:true #开启Feign对Sentinel的整合

然后在配置流控时选择并发线程数
在这里插入图片描述

Fallback

    如果业务掉接口的速度非常慢,每次都好几百毫秒,我们应该让它熔断,走Fallback流程,快速失败,快速返回。
    FeignClient的Fallback有两种配置方式:

  1. FallbackClass,无法对远程调用的异常做处理。
  2. FallbackFactory,可以对远程调用的异常做处理,通常都会选用这种。

步骤一:自定义类,实现FallbackFactory,编写某个FeignClient的fallback逻辑。
在这里插入图片描述
步骤二:将刚刚定义的UserClientFallbackFactory注册为一个Bean
在这里插入图片描述

步骤三:在UserClient接口中使用UserClientFallbackFactory
在这里插入图片描述

服务熔断

    当请求走线程隔离的过程中,是会有几个线程先去执行接口,然后等待长时间发现不行才走fallback的,还是会有线程做无效等待,占用线程资源。我们希望看到的是你这个接口访问这么慢,下次就直接不去访问你,这就是熔断。
    但是当接口恢复后,熔断是应该取消的,我们的断路器统计服务调用的异常比例,满请求比例,如果超出阈值则会熔断该服务,拦截一切该服务的请求。当服务恢复时,断路器会放行该服务的请求。
在这里插入图片描述
点击控制台中簇点资源的熔断按钮,即可配置熔断策略:
在这里插入图片描述

分布式事务

    在分布式的系统中,如果一个业务需要多个服务合作完成,而且每个服务都有事物,多个事物必须同时成功或失败,这样的事物就是分布式事物
    其中每个服务的事物就是应该分支事务,整个业务成为全局事务。

    分布式事物的解决思路:解决分布式事务,各个子事务必须能感知到彼此的事务状态,才能实现状态一致。

Seata组件

    Seata是阿里巴巴开源的分布式事物解决方案。
    事物协调者:需要有外部协调者的参与,来确保每个子事务都可以感知到彼此的状态。

    Seata事务管理中有三个重要角色:

  1. TC(事务协调者):维护全局和分布事务的状态,协调全局事务或回滚。
  2. TM(事务管理器):定义全局事务的范围,开始全局事务,提交或回滚全局事务。
  3. RM(资源管理器):管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态。

在这里插入图片描述

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值