Spring Cloud Alibaba解决方案

Spring 专栏收录该内容
5 篇文章 0 订阅

Nacos

Nacos的配置

Nacos下载 解压

https://github.com/alibaba/nacos/releases

执行Sql文件,创建Nacos支持库

E:\nacos-server-1.4.1\nacos\conf\nacos-mysql.sql

Sql文件中没有创建库的命令需要手动创建库或者在sql文件中添加
DROP DATABASE IF EXISTS `nacos_config`;
CREATE DATABASE `nacos_config` DEFAULT character set utf8mb4;
SET names utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
USE `nacos_config`;

配置Nacos链接数据库

E:\nacos-server-1.4.1\nacos\conf\
### If use MySQL as datasource:
spring.datasource.platform=mysql

#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=123

启动

E:\nacos-server-1.4.1\nacos\bin 跳转到 cmd界面
启动单机模式
startup.cmd -m standalone

登录

http://localhost:8848/nacos
默认账号密码为nacos/nacos.

注册中心

服务注册与发现

项目创建

... 省略创建项目

修改配置文件

application.yml / application.properties 配置文件
spring:
  application:
    name: sca-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

Ribbon负载均衡策略

在这里插入图片描述

服务的发现及调用的4种方式

RestTemplate

调用流程

  • 创建一个创建RestTemplate对象的方法,通过@bean注解将返回的对象交给Spring进行管理
  • 通过@Autowired注解将Bean容器中的RestTemplate对象注入到指定的属性中
  • 使用RestTemplate对象进行远端的调用,需要传入两个参数(远端的URL地址,返回值类型(字节码对象,如果调用的远端服务没有返回值可以写null))

缺点

  • 没有负载均衡,可以自己手写负载均衡但是管理麻烦
  • 远端服务URL写死,不灵活

优点

  • 写法简单

场景

  • 适合服务是单点状态
@RestController
@RequestMapping("/provider/")
public class ProController{
    @Bean
    public RestTemplate objectfactory(){
        return new RestTemplate();
    }
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/hi")
    public void hello(){
        String url="http://localhost:8080/hello";
        restTemplate.getForObject(url,String.class);
    }
}

RestTemplate + LoadBalancerClient

调用流程

  • 创建RestTemplate对象后交给Spring进行管理
  • 通过@Autowirted 注解获取容器中的RestTemplate与loadBalancerClient对象(loadBalancerClient对象是spring容器自维护的)
  • 使用loadBalancerClient对象手动动态获取注册中心中的服务信息
  • 拼接URL地址,通过RestTemplate对象进行远端服务调用

优点

  • 手写动态的获取服务的ip+port,程序灵活
  • 有负载均衡的功能,默认采取轮询策略

缺点

  • 写法复杂,需要手动获取服务的ip+port信息
@RestController
    @RequestMapping("/provider/")
    public class ProController {

        @Bean
        public RestTemplate objectfactory(){
            return new RestTemplate();
        }
        @Autowired
        private  RestTemplate restTemplate;
        @Autowired
        private LoadBalancerClient loadBalancerClient;
        @GetMapping("/hi")
        public void Controller(){
            ServiceInstance servername = loadBalancerClient.choose("servername");
            String host = servername.getHost();
            int port = servername.getPort();
            String parameter ="";
            String url = String.format("http://%s/%s/hello/xx/%s",host,port,parameter);
            restTemplate.getForObject(url,String.class);
        }
    }

RestTemplate + @loadBalanced

调用流程

  • 创建一个创建RestTemplate对象交给Spring容器管理并描述@LoadBalanced (自动维护服务端的信息ip+port)
  • 使用@Autowired注解将容器中RestTemplate对象注入到指定属性
  • 调用RestTemplate对象进行远程调用

优点

  • 只需要在URL中写服务名,就会在调用RestTemplate对象发送请求时被拦截器进行拦截,拦截器将指定服务信息拼接到url中
  • 有负载均衡,默认是轮询策略
  • 写法简单

缺点

  • 不符合MVC设计思想,例如 C-Feign
  • 因为调用对象前会被拦截器进行拦截后处理业务,所以性能会下降
@RestController
@RequestMapping("/provider/")
public class ProController {
    @Bean
    @LoadBalanced
    public RestTemplate objectFactory(){
        return  new RestTemplate();
    }
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/test")
    public void Test(){
        String url= String.format("http://%s/hello","servername");
        restTemplate.getForObject(url,String.class);
    }
}

拦截器

public ClientHttpResponse intercept(final HttpRequest request, 
    final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
	final URI originalUri = request.getURI();
	String serviceName = originalUri.getHost();
	return this.loadBalancer.execute(serviceName, 
    requestFactory.createRequest(request, body, execution));
}

Feign

创建流程

  • 引入组件依赖,Spring基于OpenFeign研发了Starter
  • 在启动类添加@EnableFeignClients
  • 创建Feign接口,自动实现接口方法
    • 容错解决方案接口实现
    • 配置文件新增
  • 创建C层进行调用Feign接口中的实现方法

调用流程

  • 通过@EnableFeignClients注解告知SpringCloud启动Feign Starter组件
  • Feign Starter组件在项目启动过程中注册全局配置,扫描包下有@FeignClient注解描述的接口,由系统底层创建接口实现类(JDK代理类)并构建类对象,然后交给Spring进行管理
  • 接口被调用时被动态代理类逻辑拦截,将@FeignClient请求信息通过编码器生成Request对象,基于此对象进行远程过程调用
  • 请求对象由Ribbon进行负载均衡,挑选一个健康的实例
  • 通过Client携带Request调用远端服务返回请求响应
  • 通过解码器生成Response返回客户端,将信息流解析为借口返回数据

细节说明

  • Feign接口@FeignClient注解字段说明
    • name=servername 代表远程调用的服务名,如果没有指定ContextId那么也代表交给Spring Bean容器中bean对象的名字,如果有多个接口调用同一个远程服务,那么Bean容器中的bean对象的名字就会重复,会报bean对象名字已经注册过了,如果想覆盖之前的bean对象,可以在配置文件中加入 spring.main.allow-bean-definition-overriding=true
    • CentextId=xxx 代表Bean容器中bean对象的名字
    • fallbackFactory=xxx 代表如果Feign远程调用远端服务器失败,就会使用这个属性定义的接口也就是失败解决方案

优点

  • 符合MVC设计思想,对结构的优化,由C层 -> F接口

缺点

  • 写法麻烦?
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@SpringBootApplication
@EnableFeignClients
public class conApplication {
    public static void main(String[] args) {
        SpringApplication.run(conApplication.class, args);
    }
@FeignClient(name="sca-provider",contextId="remoteProviderService",fallbackFactory = ProviderFallbackFactory.class)
interface RemoteProviderService{
    @GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
    public String echoMessage(@PathVariable("string") String string);
}
@Component
public class ProviderFallbackFactory
        implements FallbackFactory<RemoteProviderService> {
    /**
     * 此方法会在RemoteProviderService接口服务调用时,出现了异常后执行.
     * @param throwable 用于接收异常
     */
    @Override
    public RemoteProviderService create(Throwable throwable) {
        return (msg)->{
                return "服务维护中,稍等片刻再访问";
        };
    }
}
feign:  
  hystrix:
    enabled: true #默认值为false
@RestController
@RequestMapping("/consumer/ ")
public class FeignConsumerController {
    @Autowired
    private RemoteProviderService remoteProviderService;
    /**基于feign方式的服务调用*/
    @GetMapping("/echo/{msg}")
    public String doFeignEcho(@PathVariable  String msg){
        //基于feign方式进行远端服务调用(前提是服务必须存在)
        return remoteProviderService.echoMessage(msg);
    }
}

在这里插入图片描述

  • 第一种:服务比较少,例如就两个服务,一个服务消费,一个服务提供,就不需要注册中心,不需要负载均衡.
  • 第二种:并发比较大,服务服务比较多,我们需要管理服务,就需要注册中心,我们还需要服务间的负载均衡.但代码编写的复杂多相对高一些,我们需要自己获取ip,获取端口,拼接字符串等.
  • 第三种:我们要基于第二种进行代码简化,底层提供了一种拦截器,把基于服务名获取服务实例的过程在拦截器中做了封装,简化了代码的开发.但是加了拦截器多少会在性能少有一点损耗.
  • 第四种方式主要是从代码结构上做一个挑战,我们前面三种基于RestTemplate进行服务调用,本身属于一种远程服务调用业务,能够将这种业务写到一个业务对象中,Feign方式就诞生了,它主要对代码结构的一种优化.

配置中心

服务链接配置中心

添加依赖

  <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>

修改配置文件

将application.yml 修改为 bootstrap.yml
spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yml

Nacos配置
在这里插入图片描述

管理模型

有三部分组成 命名空间-组-服务ID
在这里插入图片描述

  • Namespace 命名空间,对不同环境进行隔离,例如隔离 开发环境 / 生产环境
  • Group 分组将若干服务或者若干配置归为一组
  • Service/DataId 某一个服务或配置集,一个对应一个配置文件

指定空间、分组

修改配置文件

server:
  port: 8070
spring:
  application:
    name: nacos-config
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848 # 配置中心的地址
        group: DEV_GROUP_51 # 指定组
        file-extension: yml # 配置服务后缀
        namespace: 5c27fe4a-1141-4836-a14e-cbac77fb2130 # 指定空间 指定的是空间的UUID

共享配置

创建共享配置
修改配置文件

spring:
  application:
    name: nacos-config
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        # 命名空间
        namespace: 83ed55a5-1dd9-4b84-a5fe-a734e4a6ec6d
        # 分组名
        # group: DEFAULT_GROUP
        # 配置中心文件扩展名
        file-extension: yml
        # 共享配置
        shared-configs[0]:
                data-id: app-public-dev.yml
                group: DEFAULT_GROUP
                refresh: true #默认false

Sentinel

Sentinel可为秒杀、抢购、抢票、拉票等高并发应用,提供API接口层面的流量限制,让突然暴涨而来的流量用户访问受到统一的管控,使用合理的流量放行规则使得用户都能正常得到服务。

安装

https://github.com/alibaba/Sentinel/releases
sentinel-dashboard-1.8.0.jar

启动

E:\sentinel-dashboard-1.8.0  - cmd
java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar

访问
http://localhost:8180/#/login
用户:sentinel
密码:sentinel

服务与Sentine链接

导入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

修改配置文件

spring:
  cloud:
    sentinel:
      transport:
         port: 8099 #跟sentinel控制台交流的端口,默认为8719,如果8719被占用会自动选择一个未被占用的端口 例如8720
         dashboard: localhost:8180 # 指定sentinel控制台地址

设置限流

在这里插入图片描述

流控规则分析

阈值类型

QPS:每秒请求次数
线程数:调用相关URL对应的资源
单机阈值:允许每秒请求的次数

限流模式

直接:本身业务达到阈值时,直接进行限流
关联:当关联的资源达到阈值就限流自己,例如高并发场景下,本身为购物车接口,关联的对象是支付接口时,那么优先支付接口进行操作,本身接口操作就可以减少(两个业务相比较,不重要的业务关联重要的业务,当重要的业务达到阈值则限流不重要的业务,优先让重要的业务执行)
链路:通过@SentinelResource注解描述资源为链路端点,限流调用指定链路端点的业务们(标注一个方法,给标注的方法进行设置阈值,哪个业务调用标注的方法就限流哪个业务),Sentinl1.7版本后,Sentnel Web过滤器,统一使用(默认聚合)sentinel_spring_web_context作为所有URL的入口,通过配置文件可以取消默认聚合入口

sentinel:
     web-context-unify: false

流控效果

快速失败: 当满足限流条件时,直接给页面进行返回失败 Blocked by Sentinel (flow limiting)
WarmUp预热:经过预热时长,才到达OPS阈值(例如秒杀系统开启的瞬间,会有大量流量上来很有可能把系统搞崩、预热方式可以把流量慢慢放进来,慢慢的把阈值增长到设置的阈值)
排队等待: 阈值类型必须是OPS,让请求以均匀的速度通过(例如某一时刻出现大流量,之后流量就恢复稳定可以采取排队模式、大流量来时让流量请求进行排队,等流量恢复后再慢慢进行处理)

设置降级

在这里插入图片描述

如何降级

保证服务高可用的重要措施之一,由于调用关系的复杂性,如果链路中的某个资源不稳定时,最终会产生请求的堆积
降级的作用是为了让有问题的资源接收请求时,让请求快速失败,不影响其他资源避免影响其他资源而导致级联错误

降级规则分析

慢调用比例:大于最大RT阈值的请求称为慢调用、超时时长由用户设置
异常比例:异常出现的比例
异常数 异常出现的次数
三种状态
熔断(OPEN):当满足降级规则后则触发熔断,并设置熔断时长
探测(HALFOPEN):当熔断时长过后,状态由熔断改为探测
关闭(CLOSED):
如果接下来的一个请求小于最大RT,说明慢调用已经恢复则结束熔断,将探测状态改为关闭状态
如果接下来的一个请求大于最大RT,说明慢调用还没有恢复,将探测状态改为熔断状态

慢调用属性

最大RT: 超时毫秒
Sentinel 默认RT上限时长为4900ms,如果需要更改RT上限时长通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx 进行配置
比例阈值: 百分比 0~1
熔断时长: 拒绝所有请求
最小请求数: 如果请求数小于指定的值则不发生熔断

异常比例属性

异常比例阈值:发生异常的请求数+请求总数 0~1
熔断时长:熔断的时间
最小请求数:请求数小于最小请求数则不发生熔断

异常数量属性

异常数:请求发生异常的数量
熔断时长:熔断的时间
最小请求数:请求数小于最小请求数则不发生熔断

设置热点

![在这里插入图片描述](https://img-blog.csdnimg.cn/9f4b56f68a6648b7b71f267b4c623dde.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTQ5Nzc1,size_16,color_FFFFFF,t_70

概述

经常访问的资源,热点参数限流会统计传入的参数的热点数据并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。其中,Sentinel会利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。

热点线路规则分析

在这里插入图片描述

基本选项

参数索引:用户传入的参数, http:xxx/xx/?id=2&name=hj 参数索引0则对应id 1则对应name值
单机阈值:点击的次数
统计窗口时长:多长时间

高级选项

参数类型:传入参数的类型
参数值:传入参数的值
限流阈值:访问的次数

设置授权

在这里插入图片描述

概述

很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的黑白名单控制的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。例如微信中的黑名单。

注意

授权需要在后端写参数的解析
定义请求解析器,用于对请求进行解析,并返回解析结果,sentinel底层 在拦截到用户请求以后,会对请求数据基于此对象进行解析,判定是否符合黑白名单规则

@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String origin = request.getParameter("origin");
        return origin;
    }
}

授权规则分析

流控应用 参数的值(后端程序设计好)
黑名单 符合流控应用的不让访问
白名单 符合流控应用的值才可以访问

流程

在这里插入图片描述

系统规则

在这里插入图片描述

概述

系统在生产环境运行过程中,我们经常需要监控服务器的状态,看服务器CPU、内存、IO等的使用率;主要目的就是保证服务器正常的运行,不能被某些应用搞崩溃了;而且在保证稳定的前提下,保持系统的最大吞吐量。
长期以来,系统自适应保护的思路是根据硬指标,即系统的负载 (load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当 load 开始好转,则恢复流量的进入。

系统规则分析

LOAD 当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5
RT:超时时间
线程数: 线程的数量
入口QPS:入口资源的OPS数
CPU使用率:Cpu使用率

Sentinel 异常处理

默认提供了异常处理机制,如果默认处理机制不满足需求时我们可以进行自定义处理机制,实现BlockExceptionHandler接口,并将对象交给Spring管理

@Component
public class ServiceBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,BlockException e) throws Exception {
         //response.setStatus(601);
         //设置响应数据的编码
         response.setCharacterEncoding("utf-8");
         //告诉客户端要响应的数据类型以及客户端以什么编码呈现数据
         response.setContentType("text/html;charset=utf-8");
         PrintWriter pw=response.getWriter();
         Map<String,Object> map=new HashMap<>();
         if(e instanceof DegradeException){//降级、熔断
             map.put("status",601);
             map.put("message", "服务被熔断了!");
         }else if(e instanceof FlowException){
             map.put("status",602);
             map.put("message", "服务被限流了!");
         }else{
             map.put("status",603);
             map.put("message", "Blocked by Sentinel (flow limiting)");
         }
         //将map对象转换为json格式字符串
         String jsonStr=new ObjectMapper().writeValueAsString(map);
         pw.println(jsonStr);
         pw.flush();
    }
}

Gateway

网关本质上要提供一个各种服务访问的入口,并提供服务接收并转发所有内外部的客户端调用,还有就是权限认证,限流控制等等。Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 等技术开发的一个网关组件,它旨在为微服务架构提供一种简单有效的统一的 API入口,负责服务请求路由、组合及协议转换,并且基于 Filter 链的方式提供了权限认证,监控、限流等功能。

配置

导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

配置文件

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    gateway:
        routes: #配置网关路由规则
          - id: route01  #路由id,自己指定一个唯一值即可
            uri: http://localhost:8081/ #网关帮我们转发的url
            predicates: ###断言(谓此):匹配请求规则
              - Path=/nacos/provider/echo/**  #请求路径定义,此路径对应uri中的资源
            filters: ##网关过滤器,用于对谓词中的内容进行判断分析以及处理
              - StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos

分析配置文件参数
id:路由标识符,区别于其他 Route。
uri:路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
predicate:断言(谓词)的作用是进行条件判断,只有断言都返回真,才会执行路由。
filter:过滤器用于修改请求和响应信息。

负载均衡

网关才是服务访问的入口,所有服务都会在网关层面进行底层映射,所以在访问服务时,要基于服务serivce id(服务名)去查找对应的服务,让请求从网关层进行均衡转发,以平衡服务实例的处理能力。
引入依赖

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

修改配置文件

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true  #开启通过服务注册中心的serviceId创建路由
      routes:
        - id: route01
          ##uri: http://localhost:8081/
          uri: lb://sca-provider # lb为服务前缀,不能随意写
          predicates: ###匹配规则
              - Path=/nacos/provider/echo/**
          filters:
              - StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos

断言/谓词

Predicate(断言)又称谓词,用于条件判断,只有断言结果都为真,才会真正的执行路由。断言其本质就是定义路由转发的条件。
服务启动时会使用AbstractRoutePredicateFactory创建具体的谓词对象

自带谓词对象

可以通过idea ctrl+n 搜索AbstractRoutePredicateFactory查看全部的谓词对象,当谓词对象不满足时可以手动创建
时间
AfterRoutePredicateFactory:判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory:判断请求日期是否早于指定日期
BetweenRoutePredicateFactory:判断请求日期是否在指定时间段内
请求头
-Header=X-Request-Id, \d+
请求类型
-Method=GET
参数判断
-Query=pageSize,\d+

自定义谓词对象

继承AbstractRoutePredicateFactory类
重写apply类
apply方法的参数是自定义的配置类,可以在apply方法中直接获取使用配置参数
类的命名需要以RoutePredicateFactory结尾

过滤器

过滤器(Filter)就是在请求传递过程中,对请求和响应做一个处理。Gateway 的Filter从作用范围可分为两种:GatewayFilter与GlobalFilter。其中:
全局过滤器 GlobalFilter 应用所有路由
局部过滤器 GatewayFilter(AbstractGatewayFilterFactory) 应用单个或一组路由上

全局过滤器

全局过滤器(GlobalFilter)作用于所有路由, 无需配置。在系统初始化时加载,并作用在每个路由上。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。一般内置的全局过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们 自己编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验。 例如,当客户端第一次请求服务时,服务端对用户进行信息认证(登录) 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证 以后每次请求,客户端都携带认证的token 服务端对token进行解密,判断是否有效。

自定义全局过滤器
/**
 * 自定义全局过滤器,作用于所有路由
 */
//@Component
public class AuthGatewayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        //获取数据
        String path = request.getURI().getPath();
        String username = request.getQueryParams().getFirst("username");
        System.out.println(username);
        //处理数据
        if(!"admin".equals(username)) {
            ServerHttpResponse response = exchange.getResponse();
            Map<String,Object> map = new HashMap<>();
            map.put("message","request failure");
            map.put("status",503);
            Gson gson = new Gson();
            String jsonStr =  gson.toJson(map);
            byte[] bytes = jsonStr.getBytes();
            //响应内容使用该方式结束
            DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
            return response.writeWith(Mono.just(dataBuffer));
        }
        //返回结果
        return chain.filter(exchange);
    }
	//过滤器的优先级
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

网关层面限流

网关是所有请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多,我们采用Sentinel组件来实现网关的限流。Sentinel支持对SpringCloud Gateway、Zuul等主流网关进行限流。参考网址如下:
添加依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

增加配置文件内容

sentinel:
  transport:
    dashboard: localhost:8180 #Sentinel 控制台地址
    port: 8719 #客户端监控API的端口
  eager: true  #取消Sentinel控制台懒加载,即项目启动即连接

启动项目需要带参数
网关层面的限流与服务上的限流有区别,通过下面参数启动可以在sentinl控制台显示不一样的菜单

-Dcsp.sentinel.app.type=1

限流规则分析

在这里插入图片描述

基本属性

Routle ID 对单个路由做限流
API分组 对一组路由做限流

针对属性限流

Client IP 针对IP进行限流
Remote Host 针对域名进行限流
Header 针对请求头限制
URL参数 针对参数做限制
Cookie 针对Cookie做限流

属性值匹配

精确 具体的串 /nacos/hello/22
子串
正则 通过正则表达式匹配

阈值

OPS 请求
线程数 线程数
Burst size 应对突发请求时额外允许的请求数目

自定义网关返回值

Mono 是一个发出(emit)0-1个元素的Publisher。

@Configuration
public class GatewayConfig {
    public GatewayConfig(){
        GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                ServerHttpResponse response = serverWebExchange.getResponse();
                Map<String,Object> map = new HashMap<>();
                map.put("message","有问题兄弟,网关限流了");
                map.put("status",429);

                Gson gson = new Gson();
                String jsonStr =  gson.toJson(map);

                return ServerResponse.ok().body(Mono.just(jsonStr),String.class);
            }
        });
    }
}

总结分析

Q:为什么要将服务注册到Nacos?
A:如果不把服务注册到Nacos中,那么每个消费方都需要在本地手动维护提供方的URL地址,管理混乱且冗余,如果把服务注册到Nacos中那么每个消费方只需要通过服务发现就可以获取在nacos中注册过的服务,这样管理简单且统一

Q:在Nacos中服务提供者时如何向Nacos注册中心续约的,Nacos服务如果判定服务实例的状态?
A1:服务每隔一段时间发送一次数据给注册中心
A2:Nacos每5秒检测服务是否发送了数据,15秒没有检测到服务发来的数据那么判定服务的状态为非健康状态,30秒没有检测到则删除该服务的注册信息

Q:服务启动时如果找到服务启动注册配置类?
A:Spring启动时会读取配置文件,配置文件中配置了Nacos的地址信息

Q:服务消费方式如何调用服务提供方?
A:使用RestTemplate进行远程调用,有4种方式xxxx

Q:Nacos是什么,提供了什么特性
A:提供了服务的注册,发现及配置

Q:你为什么会选择Nacos?
A:从活跃度、稳定性、性能及学习成本几个维度进行综合考虑才选择Nacos

Q:Nacos的官网?
A:nacos.io

Q:Nacos在github的源码?
A:github.com/alibaba/nacos

Q:Nacos服务注册的基本过程?(服务启动时发送web请求)
A:

Q:Nacos服务消费的基本过程?(服务启动时获取服务实例,然后调用服务)
A:

Q:Nacos服务负载均衡逻辑及设计实现?(Ribbon)
A:

Q:注册中心的核心数据是什么?
A:保存的服务的ID与对应的网络地址

Q:注册中心中心核心数据的存取为什么会采用读写锁?
A:底层安全和性能

Q:Nacos是如何保证高可用的?(重试,本地缓存、集群)
A:
重试:通过获取服务的列表信息,在访问成功之前逐个尝试
本地缓存:本地缓存了一份服务的列表信息
集群:多个Nacos服务

Q:Feign是什么,它的应用是怎样的,feign应用过程中的代理对象是如何创建的(JDK)?
A:

Q:Feign方式的调用过程,其负载均衡是如何实现?(Ribbon)
A:
Q:配置中心一般都会配置什么内容?(可能会经常变化的配置信息,例如连接池,日志、线程池、限流熔断规则)
A:
Q:什么信息一般不会写到配置中心?(服务端口,服务名,服务的注册地址,配置中心)
A:
Q:项目中为什么要定义bootstrap.yml文件?(此文件被读取的优先级比较高,可以在服务启动时读取配置中心的数据)
A:
Q:Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗?(可以从内存,客户端获取了配置中心的配置信息以后,会将配置信息在本地内存中存储一份.)
A:
Q:微服务应用中我们的客户端如何获取配置中心的信息?(我们的服务一般首先会从内存读取配置信息,同时我们的微服务还可以定时向nacos配置中心发请求拉取(pull)更新的配置信息)
A:
Q:微服务应用中客户端如何感知配置中心数据变化?(当数据发生变化时,nacos找到它维护的客户端,然后通知客户端去获取更新的数据,客户端获取数据以后更新本地内存,并在下次访问资源时,刷新@Value注解描述的属性值,但是需要借助@RefreshScope注解对属性所在的类进行描述)
A:
Q:服务启动后没有从配置中心获取我们的配置数据是什么原因?(依赖,配置文件名字bootstrap.yml,配置中心的dataId名字是否正确,分组是否正确,配置的名字是否正确,缩进关系是否正确,假如是动态发布,类上是否有@RefreshScope注解)
A:
Q:你项目中使用的日志规范是什么?(SLF4J)
A:
Q:你了解项目中的日志级别吗?(debug,info,error,…,可以基于日志级别控制日志的输出)
A:
Q:Nacos配置管理模型的背景?(环境不同配置不同)
A:
Q:Nacos配置中的管理模型是怎样的?(namespace,group,service/data-id)
A:
Q:Nacos客户端(微服务)是否可以读取共享配置?(可以)
A:
Q:配置中心的选型。(市场活跃度、稳定性、性能、易用)
A:
Q:Nacos配置中心基本应用。(新建,修改、删除配置以后,在Nacos客户端应用配置)
A:
Q:配置管理模型应用。(namespace,group,service/dataId)
A:
Q:Nacos配置变更的动态感知。(底层原理分析)
A:
Q:为什么需要配置中心?(动态管理发布配置,无需重启服务,更好保证服务的可用)
A:
Q:配置中一般要配置什么内容?(经常变化的配置数据-日志级别,线程池、连接池、…)
A:
Q:市面上有哪些主流配置中心?(Nacos,….)
A:
Q:配置中心选型时要重点考虑哪些因素?(市场活跃度、稳定性、性能、易用)
A:
Q:Nacos客户端(微服务业务)如何动态感知配置中心数据变化的?(nacos2.0之前nacos客户端采用长轮询机制每隔30秒拉取nacos配置信息.)
A:
Q:Nacos配置管理模型是怎样的?(命名空间-namespace,分组-group,服务实例-dataId)
A:
Sentinel是什么?(阿里推出一个流量控制平台,防卫兵)
类似Sentinel的产品你知道有什么?(hystrix-一代微服务产品)
你了解哪些限流算法?(计数器、令牌桶、漏斗算法,滑动窗口算法,…)
Sentinel 默认的限流算法是什么?(滑动窗口算法)
你了解sentinel中的阈值应用类型吗?(两种-QPS,线程数)
Sentinel 限流规则中默认有哪些限流模式?(直连,关联,链路)
Sentinel的限流效果有哪些?(快速失败,预热,排队)
Sentinel 为什么可以对我们的业务进行限流,原理是什么?
我们在访问web应用时,在web应用内部会有一个拦截器,这个拦截器会对请求的url进行拦截,拦截到请求以后,读取sentinel 控制台推送到web应用的流控规则,基于流控规则对流量进行限流操作。
何为降级熔断?(让外部应用停止对服务的访问,生活中跳闸,路障设置-此路不通)
为什么要进行熔断呢?(平均响应速度越来越慢或经常出现异常,这样可能会导致调用链堆积,最终系统崩溃)
Sentinel中限流,降级的异常父类是谁?(BlockException)
Sentinel 出现降级熔断时,系统底层抛出的异常是谁?(DegradeException)
Sentinel中异常处理接口是谁?(BlockExceptionHandler)
Sentinel中异常处理接口下默认的实现类为? (DefaultBlockExceptionHandler)
假如Sentinel中默认的异常处理规则不满足我们的需求怎么办?(自己定义)
我们如何自己定义Sentinel中异常处理呢?(直接或间接实现BlockExceptionHandler )
Sentinel 降级熔断策略有哪些?(慢调用,异常比例,异常数)
Sentinel 熔断处理逻辑中的有哪些状态?(Open,HalfOpen,Closed)
Sentinel 对服务调用进行熔断以后处于什么状态?(熔断打开状态-Open)
Sentinel 设置的熔断时长到期以后,Sentinel的熔断会处于什么状态?(探测-HalfOpen,假如再次访问时依旧响应时间比较长或依旧有异常,则继续熔断)
Sentinel 中的熔断逻辑恢复正常调用以后,会出现什么状态?(熔断关闭-closed)
如何理解Sentinel中的授权规则?(对指定资源的访问给出的一种简易的授权策略)
Sentinel的授权规则是如何设计的?(白名单和黑名单)
如何理解Sentinel中的白名单?(允许访问的资源名单)
如何理解Sentinel中的黑名单?(不允许访问的资源名单)、
Sentinel如何识别白名单和黑名单?(在拦截器中通过调用RequestOriginParser对象的方法检测具体的规则)
授权规则中RequestOriginParser类的做用是什么?(对流控应用值进行解析,检查服务访问时传入的值是否与RequestOriginParser的parseOrigin方法返回值是否相同。)
Sentinel诞生的背景?(计算机的数量是否有限,处理能力是否有限,并发比较大或突发流量比较大)
服务中Sentinel环境的集成,初始化?(添加依赖-两个,sentinel配置)
Sentinel 的限流规则?(阈值类型-QPS&线程数,限流模式-直接,关联,链路)
Sentinel 的降级(熔断)策略?(慢调用,异常比例,异常数)
Sentinel 的热点规则设计(掌握)?
Sentinel 系统规则设计?(了解,全局规则定义,针对所有请求有效)
Sentinel 授权规则设计?(掌握,黑白名单)
为什么要限流?
你了解的那些限流框架?(sentinel)
常用的限流算法有那些?(计数,令牌桶-电影票,漏桶-漏斗,滑动窗口)
Sentinel有哪些限流规则?(QPS,线程数)
Sentinel有哪些限流模式?(直接,关联-创建订单和查询订单,链路限流-北京六环外不限号,但是五环就限号)
Sentinel 的降级(熔断)策略有哪些?(慢调用-响应时长,异常比例-异常占比,异常数)
Sentinel 的热点规则中的热点数据?(热卖商品,微博大咖,新上映的电影)
如何理解Sentinel 授权规则中的黑白名单?
如何理解sentinel中的系统规则?(是对所有链路的控制规则,是一种系统保护策略)
Sentinel的常用系统规则有哪些?(RT,QPS,CPU,线程,Load-linux,unix)
Sentinel系统保护规则被触发以后底层会抛出什么异常?(SystemBlockException)

什么是网关?服务访问(流量)的一个入口,类似生活中的“海关“
为什么使用网关?(服务安全,统一服务入口管理,负载均衡,限流,鉴权)
Spring Cloud Gateway 应用的初始构建过程(添加依赖,配置)
Gateway 服务的启动底层是通过谁去实现的?(Netty网络编程框架-ServerSocket)
Gateway 服务做请求转发时一定要在注册中心进行注册吗?(不一定,可以直接通过远端url进行服务访问)

网关层面是如何实现负载均衡的?(通过服务名去查找具体的服务实例)
网关层面是如何通过服务名查找服务实例的?(Ribbon)
你了解Ribbon中的哪些负载均衡算法?(轮询,权重,hash,……可通过IRule接口进行查看分析)
网关进行请求转发的流程是怎样,有哪些关键对象?(XxxHandlerMapping,Handler,。。。)
网关层面服务的映射方式怎样的?(谓词-path,…,服务名/服务实例)
网关层如何记录服务的映射?(通过map,并要考虑读写锁的应用)

何为谓词?(网关中封装了判断逻辑的一个对象)
谓词逻辑的设计是怎样的?(谓词判断逻辑返回值为true则进行请求转发)
你了解哪些谓词逻辑?(path,请求参数,ip,请求方式,cookie,请求头,….)
我们可以自己定义谓词工厂对象吗?(可以的)

网关过滤器的作用是什么?(对请求和响应数据做一个预处理)
网关过滤器的类型有哪些?(局部过滤器,全局过滤器)
如何理解局部过滤器?(针对具体链路的应用的过滤器,需要进行配置)
你了解哪些局部过滤器?
如何理解全局过滤器?(作用于所有请求链路)
如何自己定义全局过滤器?(直接或间接实现GlobalFilter接口)
假如现在让你进行平台的网关自研设计,你可以吗?(可以)
网关层面结合sentinel实现限流,其限流的类型有几种?(两种-route id,api)
网关层面可以自定义限流后的异常处理结果吗?(可以)
你知道Sentinel底层限流的算法有哪些?(滑动窗口,令牌桶,漏斗,。。。)

Q:网关(Gateway)诞生的背景?
A:为了统一微服务访问的入口,对系统服务进行保护(包装具体微服务的路径),进行统一的认证、授权、限流,对请求做一些处理

Q:网关的选型?(Netifix Zuul,Spring Cloud Gateway,…)
A:

Q:如何实现Spring Cloud Gateway的入门实现(添加依赖,路由配置,启动类)
A:添加依赖spring-cloud-starter-gateway、配置路由(路由ID、uri、谓词、过滤器)、启动类

Q:Spring Cloud Gateway中的负载均衡?
A:通过Nacos的服务注册与发现、基于lb协议开启负载均衡、默认使用轮询策略,可以修改默认策略、在配置文件中加入NFLoadBalancerRuleClassName

Q:Spring Cloud Gateway中的断言配置?
A:常用的有针对 时间,请求方式,请求头进行逻辑判断,更多在AbstractRoutePredicateFactory类找就可以

Q:Spring Cloud Gateway中的过滤器配置?(掌握过滤器中的两大类型-局部和全局)
A:

Spring Cloud Gateway中的限流设计?(Sentinel)

Gateway在互联网架构中的位置?(nginx->gateway–>微服务–>微服务)
Gateway底层负载均衡的实现?(Ribbon)
Gateway应用过程中设计的主要概念?(路由id,路由uri,断言,过滤器)
Gateway中你做过哪些断言配置?(after,header,path,cookie,…)
Gateway中你用的过滤器有哪些?(添加前缀,去掉前缀,添加请求头,…,负载均衡,…)

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值