【Spring Cloud】03_SpringCloud Alibaba 微服务解决方案

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

微服务简介

背景分析

讲微服务之前,我们先分析以下单体应用。所谓单体应用一般是基于idea/eclipse,maven等建一个工程,然后基于SpringBoot,spring,mybatis框架进行整合,接下来再写一堆dao、mapper、service、controller,再加上一些的配置文件,有可能还会引入redis、elasticsearch、mq等其它项目的依赖,开发好之后再将项目打包成一个jar包/war包。然后再将包扔到类似tomcat这样的web服务中,最后部署到公司提供给你的linux服务器上。 接下来,你针对服务提供的访问端口(例如8080端口)发起http请求,请求会由tomcat直接转交给你的spring web组件,进行一层一层的代码调用。

对于这样的设计一般适合企业的内部应用,访问量不大,体积比较小,5人以内的团队即可开发和维护。但对于一些大型互联网项目,假如需要10人以上的开发和维护团队,仅频繁的创建代码分支,编写业务功能,然后合并分支,就会出现很多代码冲突。每次解决这些大量的代码冲突,可能就会耗费好几天的时间。基于这样的背景微服务诞生了。

微服务架构设计中,建议超出需要10人开发和维护的项目要进行系统拆分,就是把大系统拆分为很多小系统,几个人负责一个服务这样每个服务独立的开发、测试和上线,代码冲突少了,每次上线就回归测试自己的一个服务即可,测试速度快了,上线是独立的,只要向后兼容接口就行了,不需要跟别人等待和协调,技术架构和技术版本的升级,几个人ok就行,成本降低,更加灵活了。

在这里插入图片描述

什么是微服务

微服务架构(MSA)的基础是将单个应用程序开发为一组小型独立服务,这些独立服务在自己的进程中运行,独立开发和部署。如图所示:

在这里插入图片描述
这些服务使用轻量级 API 通过明确定义的接口进行通信。这些服务是围绕业务功能构建的,每项服务执行一项功能。由于它们是独立运行的,因此可以针对各项服务进行更新、部署和扩展,以满足对应用程序特定功能的需求。

生活中的微服务,如图所示:

在这里插入图片描述

程序中的微服务,就是将各个业务系统的共性再进行抽取,做成独立的服务,如图所示:

在这里插入图片描述

总之,微服务是分布式系统中的一种流行的架构模型,它并不是银弹,所以,也不要寄希望于微服务构架能够解决所有的问题。微服务架构主要解决的是如何快速地开发和部署我们的服务,这对于一个能够适应快速开发和成长的公司是非常必要的。同时,微服务设计中有很多很不错的想法和理念,通过学习微服务架构我们可以更快的迈向卓越。

SpringCloud Alibaba 微服务解决方案

概述

Spring Cloud Alibaba 是Spring Cloud的一个子项目,致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

核心组件分析

Spring Cloud Alibaba 默认提供了如下核心功能(先了解):

  • 服务限流降级:
    默认支持 WebServlet、OpenFeign、RestTemplate、Spring Cloud Gateway, RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。

  • 服务注册与发现:
    基于Spring Cloud 服务注册与发现标准,借助Nacos进行实现,默认还集成了 Ribbon 的支持。

  • 分布式配置管理:
    基于Nacos支持分布式系统中的外部化配置,配置更改时自动刷新。

  • 消息驱动能力:
    基于Spring Cloud Stream 为微服务应用构建消息驱动能力。

  • 分布式事务:
    使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。

  • 分布式任务调度:
    提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker上执行。

解决方案架构设计

基于Spring Cloud Alibaba实现的微服务,解决方案设计架构如图所示:

在这里插入图片描述

构建SpringCloud 聚合项目并进行环境初始化

创建空项目

打开Idea,创建一个空项目(Empty Project),项目名为GitCGB2105IVProjects,例如:

在这里插入图片描述
在这里插入图片描述

项目初始化配置

第一步:配置maven环境(只要是新的工作区,都要重新配置),注意本地库选择新的位置不要与其它项目共用本地库。

在这里插入图片描述

第二步:配置JDK编译环境

在这里插入图片描述

第三步:配置项目编码方式

在这里插入图片描述

创建聚合父工程

我们后续在创建微服务工程进行学习时,相关服务的依赖及版本的管理可以放在此工程下。

第一步:创建父工程模块,例如:

在这里插入图片描述

第二步:删除工程中的src目录(父工程不需要这个目录),例如:

在这里插入图片描述
第三步:添加核心依赖及版本管理,例如:

<dependencyManagement>
    <dependencies>
          
            <!--Spring boot 依赖(定义了微服务规范)-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.2.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
          
            <!--Spring Cloud 依赖(定义了微服务规范)-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR9</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
            
            <!--Spring Cloud Alibaba依赖(基于spring微服务规范做了具体落地实现)-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.6.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
            
        </dependencies>
    </dependencyManagement>

总结(Summary)

总之,微服务是一个架构设计方式,此架构中的每个服务(service)都是针对一组功能而设计的,并专注于解决特定的问题。如果开发人员逐渐将更多代码增加到一项服务中并且这项服务变得复杂,那么可以将其拆分成多项更小的服务。接下来进行独立的开发、测试、部署、运行、维护。进而更好,更灵活的处理客户端的请求并提高系统的可靠性,可扩展性。

Nacos注册中心应用实践

背景分析

在微服务中,首先需要面对的问题就是如何查找服务(软件即服务),其次,就是如何在不同的服务之间进行通信?如何更好更方便的管理应用中的每一个服务,如何建立各个服务之间联系的纽带,由此注册中心诞生(例如淘宝网卖家提供服务,买家调用服务)。

市面上常用注册中心有Zookeeper(雅虎Apache),Eureka(Netfix),Nacos(Alibaba),Consul(Google),那他们分别都有什么特点,我们如何进行选型呢?我们主要从社区活跃度,稳定性,功能,性能等方面进行考虑即可。本次微服务的学习,我们选择Nacos,它很好的支持了阿里的双11活动,不仅可以做注册中心,还可以作为配置中心,稳定性和性能都很好。

Nacos概述

Nacos(DynamicNaming and Configuration Service)是一个应用于服务注册与发现、配置管理的平台。它孵化于阿里巴巴,成长于十年双十一的洪峰考验,沉淀了简单易用、稳定可靠、性能卓越的核心竞争力。其官网地址如下:

https://nacos.io/zh-cn/docs/quick-start.html

构建Nacos服务

在这里插入图片描述

准备工作

第一:确保你电脑已配置JAVA_HOME环境变量(Nacos启动时需要),例如:
在这里插入图片描述
第二:确保你的MySQL版本为5.7以上(MariaDB10.5以上),例如
在这里插入图片描述

下载与安装

第一步:Nacos下载,可在浏览器直接输入如下地址:

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

第二步:选择对应版本,直接下载,如图所示:
在这里插入图片描述
第三步:解压Nacos(最好不要解压到中文目录下),其目录结构如下:
在这里插入图片描述

初始化配置

第一步:登陆mysql,执行老师发给同学们的sql脚本。例如,我们可以使用mysql自带客户端,在命令行首先登录mysql,然后执行如下指令:

source d:/nacos-mysql.sql 

执行成功以后,会创建一个nacos_config数据库,打开数据库会看到一些表,例如:

在这里插入图片描述

说明:在执行此文件时,要求mysql的版本大于5.7版本(MariaDB最好10.5.11),否则会出现如下错误:

在这里插入图片描述

第二步:打开/conf/application.properties里打开默认配置,并基于你当前环境配置要连接的数据库,连接数据库时使用的用户名和密码(假如前面有"#"要将其去掉):

### 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=root

服务启动与访问

第一步:启动Nacos服务

Linux/Unix/Mac启动命令(standalone代表着单机模式运行,非集群模式):

./startup.sh -m standalone

Windows启动命令(standalone代表着单机模式运行,非集群模式):

startup.cmd -m standalone

说明:

  1. 执行执行令时要么配置环境变量,要么直接在nacos/bin目录下去执行。

  2. nacos启动时需要本地环境变量中配置了JAVA_HOME(对应jdk的安装目录)

  3. 一定要确保你连接的数据库(nacos_config)是存在的。

  4. 假如所有的配置都正确,还连不上,检查一下你有几个数据库(mysql…)

第二步:访问Nacos服务

打开浏览器,输入http://localhost:8848/nacos地址,出现如下登陆页面:
在这里插入图片描述
其中,默认账号密码为nacos/nacos

服务注册与调用入门(重点)

业务描述

创建两个项目Module分别为服务提供者和服务消费者,两者都要注册到NacosServer中(这个server本质上就是一个web服务,端口默认为8848),然后服务提供者可以为服务消费者提供远端调用服务(例如支付服务为服务提供方,订单服务为服务消费方),如图所示:
在这里插入图片描述

生产者服务创建及注册

第一步:创建服务提供者工程(module名为sca-provider),继承parent工程(01-sca),其pom.xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>01-sca</artifactId>
        <groupId>com.jt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>sca-provider</artifactId>
    <dependencies>
        <!--Web服务-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--服务的注册和发现(我们要讲服务注册到nacos)-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

第二步:创建并修改配置文件application.yml(或者application.properties),实现服务注册,关键代码如下:

server:
   port: 8600
spring:
  application:
    name: sca-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

注意:服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则

第三步:创建启动类,并定义处理请求的控制层对象和方法,关键代码如下:

package com.cy;

@SpringBootApplication
public class ProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }

    /**定义Controller对象(这个对象在spring mvc中给他的定义是handler),
     * 基于此对象处理客户端的请求*/
    @RestController
    public class ProviderController{
        //@Value默认读取项目配置文件中配置的内容
        //8080为没有读到server.port的值时,给定的默认值
        @Value("${server.port:8080}")
        private String server;
        
        //http://localhost:8081/provider/echo/tedu
        @GetMapping("/provider/echo/{msg}")
        public String doRestEcho1(@PathVariable String msg){
            return server+" say hello "+msg;
        }
    }
  }

第四步:启动启动类,然后刷先nacos服务,检测是否服务注册成功,如图所示:

在这里插入图片描述

第五步:打开浏览器,输入http://localhost:8081/provider/echo/msa,然后进行访问。

在这里插入图片描述

消费者服务发现及调用

第一步:创建服务消费者(module名为sca-consumer),继承parent工程(01-sca),其pom.xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>01-sca</artifactId>
        <groupId>com.jt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>sca-consumer</artifactId>
    
   <dependencies>
    <!--Web服务-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--服务的注册和发现(我们要讲服务注册到nacos)-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    </dependencies>
</project>

第二步:修改配置文件application.yml,关键代码如下:

server:
  port: 8610
spring:
  application:
    name: sca-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #从哪里去查找服务

第三步:创建启动类并实现服务消费,关键代码如下:

package com.cy;
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    @RestController
    public class ConsumerController{
        
        @Value("${spring.application.name}")
        private String appName;
        
        @Autowired
        private RestTemplate restTemplate;
        
        @GetMapping("/consumer/doRestEcho1")
        public String doRestEcho01(){
            String url = "http://localhost:8610/provider/echo/"+appName;
            System.out.println("request url:"+url);
           return restTemplate.getForObject(url, String.class);
        }
    }
}

其中,创建RestTemplate对象(是一个简单的模板方法 API),然后基于此对象进行远程服务调用。

第四步:启动消费者服务,并在浏览器输入http://localhost:8060/consumer/doRestEcho1地址进行访问,假如访问成功会出现,如图所示效果:

在这里插入图片描述

在这里插入图片描述

小节面试分析

  • 为什么要将服务注册到nacos?(为了更好的查找这些服务)

  • 在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?(5秒心跳)

  • 对于Nacos服务来讲它是如何判定服务实例的状态?(检测心跳包,15,30)

  • 服务启动时如何找到服务启动注册配置类?(NacosNamingService)

  • 服务消费方是如何调用服务提供方的服务的?(RestTemplate)

服务负载均衡设计及实现(重点)

非负载均衡方式手动添加调用服务器


@Bean//将创建的对象交给Bean管理,多次调用也只创建一次
public RestTemplate restTemplate(){
    return new RestTemplate();
}

@RestController
public class ConsumerController{

	@Value("${spring.application.name}")
	private String appName;
	
	@Autowired//将对象注入,可以使用对象名直接调用对象方法
	private RestTemplate restTemplate;
	
	//直接调用(非负载均衡方式)
	//http://localhost:8610/consumer/doRestEcho1
	@GetMapping("/consumer/doRestEcho1")
	public String doRestEcho1() throws InterruptedException {
		
		//手动自己写负载均衡算法(随机调用服务列表中的服务对象)
		//调用谁?sca-provider中的一个url
		String url1="http://localhost:8600/provider/echo/"+appName;
		String url2="http://localhost:8611/provider/echo/"+appName;
		String url3="http://localhost:8612/provider/echo/"+appName;
		
		String urls[]=new String[]{url1,url2,url3};
		
		//随机获取一个小于urls数组长度的整数
		int n=new Random().nextInt(urls.length);
		System.out.println("n="+n);
		
		//如何调用?
		return restTemplate.getForObject(urls[n],String.class);
	}
}

负载均衡方式——入门案例一

一个服务实例可以处理请求是有限的,假如服务实例的并发访问比较大,我们会启动多个服务实例,让这些服务实例采用一定策略均衡(轮询,权重,随机,hash等)的处理并发请求,在Nacos中服务的负载均衡(Nacos客户端负载均衡)是如何应用的?

第一步:修改ConsumerController类,注入 LoadBalancerClient (负载均衡客户端)对象,并添加doRestEcho02方法,然后进行服务访问。

@Bean//将创建的对象交给Bean管理,多次调用也只创建一次
public RestTemplate restTemplate(){
	return new RestTemplate();
}
  
@RestController
public class ConsumerController{
	
	@Value("${spring.application.name}")
	private String appName;
	       
	 @Autowired
	 private LoadBalancerClient loadBalancerClient;
  
	 @GetMapping("/consumer/doRestEcho02")
	public String doRestEcho02(){
		    ServiceInstance serviceInstance = loadBalancerClient.choose("sca-provider");//基于服务名获取服务实例
		    String url = String.format("http://%s:%s/provider/echo/%s",
		    serviceInstance.getHost(),//获取服务实例的IP
		    serviceInstance.getPort(),//获取服务实例的Port
		    appName);
		    
		    System.out.println("request url:"+url);
		    return restTemplate.getForObject(url, String.class);
	}
}

第二步:打开Idea服务启动配置,如图所示:

在这里插入图片描述
第三步:修改并发运行选项(假如没有找到这个选项我们需要通过搜索引擎基于组合查询的方法,去找到对应的解决方案,例如搜索 idea allow parallel run),如图所示:
在这里插入图片描述
第四步:修改sca-provider的配置文件端口,分别以8600,8601,8602端口方式进行启动。

http://localhost:8600/consumer/doRestEcho02
server:
  port: 8600/8601/8602
spring:
  application:
    name: sca-provider
  cloud:
    nacos:
      server-addr: localhost:8848

在这里插入图片描述

启动成功以后,访问nacos的服务列表,检测服务是否成功注册,如图所示:
在这里插入图片描述
然后会发现sca-provider的两个服务都可以处理sca-consumer的请求。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

这里多个实例并发提供服务的方式为负载均衡,这里的负载均衡实现默认是因为Nacos 集成了 Ribbon 来实现的,Ribbon配合RestTemplate,可以非常容易的实现服务之间的访问。

Ribbon是Spring Cloud核心组件之一,它提供的最重要的功能就是客户端的负载均衡(客户端可以采用一定算法,例如轮询访问,访问服务端实例信息),这个功能可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡方式的服务调用。

负载均衡方式——入门案例二

在RestTemplate上添加 @LoadBalanced 注解

作用:在使用 RestTemplate 的时候,如果 RestTemplate 上面有这个注解,那么 这个 RestTemplate 调用的远程地址,会走负载均衡器。

简单原理:使用了这个注解以后 ,会在 restTemplate 里面通过 restTemplate.setInterceptors 放入 LoadBalancerInterceptor(负载均衡拦截器),这个过滤器会在请求远程成接口的时候动态判断请求的域是不是负载均衡支持的服务的地址。如果是,那么就会代理使用这个负载均衡器来调用。

@LoadBalanced注解使用的理解

@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
	final ClientHttpRequestExecution execution) throws IOException {
   final URI originalUri = request.getURI();
   String serviceName = originalUri.getHost();
   Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
   return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}

当使用RestTemplate进行远程服务调用时,假如需要负载均衡,可以在RestTemplate对象构建时,使用@LoadBalanced对构建RestTemplate的方法进行修饰,例如在ConsumerApplication中构建RestTemplate对象:

@Bean
@LoadBalanced //这个注解描述RestTemplate对象时,系统底层会对RestTemplate对象的请求进行拦截
public RestTemplate loadBalanceRestTemplate(){
	return new RestTemplate();
}

@RestController
public class ConsumerController{
	
	@Value("${spring.application.name}")
	private String appName;
	       
	 @Autowired
	 private LoadBalancerClient loadBalancerClient;
  
     //负载均衡应用方式2:@LoadBalance
    //http://localhost:8610/consumer/doRestEcho3
    @GetMapping("/consumer/doRestEcho3")
     public String doRestEcho3(){
         String serviceId="sca-provider";//直接放入sca-provider
         String url=String.format("http://%s/provider/echo/%s",serviceId,appName);
         //底层在基于loadBalanceRestTemplate对象访问方式,会启动一个拦截器
         return loadBalanceRestTemplate.getForObject(url,String.class);
     }
}

Ribbon负载均衡策略(了解)

名称解释
RoundRobinRule轮训策略
RandomRule随机策略
BestAvailableRule过滤出故障服务器后,选择一个并发量最小的
WeightedResponseTimeRule针对响应时间加权轮询
AvailabilityFilteringRule可用过滤策略,先过滤出故障的或并发请求大于间值的一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个
ZoneAvoidanceRule从最佳区域实例集合中选择一个最优性能的服务实例
RetryRule选择一个Server,如果失败,重新选择一个Server重试

当系统提供的负载均衡策略不能满足我们需求时,我们还可以基于IRule接口自己定义策略。

小节面试分析

  • @Bean注解的作用?(一般用于配置类内部,描述相关方法,用于告诉spring此方法的返回值要交给spring管理,bean的名字默认为方法名,假如需要指定名字可以@Bean(“bean的名字”),最多的应用场景是整合第三方的资源-对象)

  • @Autowired注解的作用?(此注解用于描述属性,构造方法,set方法等,用于告诉spring框架,按找一定的规则为属性进行DI操作,默认按属性,方法参数类型查找对应的对象,假如只找到一个,则直接注入,类型多个时还会按照属性名或方法参数名进行值的注入,假如名字也不同,就出报错.)

  • Nacos中的负责均衡底层是如何实现的?(通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务)

  • Ribbon 是什么?(Netflix公司提供的负载均衡客户端,一般应用于服务的消费方法)

  • Ribbon 可以解决什么问题? (基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口)

  • Ribbon 内置的负载策略都有哪些?(8种,可以通过查看IRule接口的实现类进行分析)

  • @LoadBalanced的作用是什么?(描述RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略。)

  • 我们可以自己定义负载均衡策略吗?(可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现)

基于Feign的远程服务调用(重点)

背景分析

服务消费方基于rest方式请求服务提供方的服务时,一种直接的方式就是自己拼接url,拼接参数然后实现服务调用,但每次服务调用都需要这样拼接,代码量复杂且不易维护,此时Feign诞生。

Feign是什么

Feign 是一种声明式Web服务客户端,底层封装了对Rest技术的应用,通过Feign可以简化服务消费方对远程服务提供方法的调用实现。如图所示:

在这里插入图片描述
Feign 最早是由 Netflix 公司进行维护的,后来 Netflix 不再对其进行维护,最终 Feign 由一些社区进行维护,更名为 OpenFeign。

Feign应用实践(掌握)

第一步:在服务消费方,添加项目依赖(SpringCloud团队基于OpenFeign研发了starter),代码如下

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

第二步:在启动类上添加 @EnableFeignClients 注解,代码如下:

@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {}

@EnableFeignClients注解描述启动类时,用于告诉springboot在启动时,扫描启动类所在包或子包中的类,假如接口上有@FeignClient定义的feign客户端,则对这样的接口创建其实现类(注册到IOC容器中),在实现类内部帮我们进行远程服务调用

第三步:定义Http请求API,基于此API借助OpenFeign访问远端服务,创建service包并在包中创建RemoteProviderService类,代码如下:

//这个service是一个feign类型的接口对象
@FeignClient(name="sca-provider")//sca-provider为服务提供者名称
interface RemoteProviderService{
    /*
    * @GetMapping表示以get请求方式调用远端服务
    * "/provider/echo/{msg}"为远程调用服务的url
    * */
    @GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
    public String echoMessage(@PathVariable("string") String string);
}

其中,@FeignClient描述的接口底层会为其创建实现类。

  • @FeignClient 注解用于描述远程服务调用接口,这个接口不需要你实现写实现类, 你只需要定义访问规则即可(例如请求方式,请求url,请求参数)。

  • @FeignClient 注解描述的接口的实现类对象会默认交给spring管理,这个bean对象的名字默认就是name属性指定的值,这个name还有一个层面的含义,就是你远程调用的服务名。

@FeignClient

FeignClient注解被@Target(ElementType.TYPE)修饰,表示FeignClient注解的作用目标在接口上

//@FeignClient(value = "fast-maindata-service",contextId = "MaindataServiceClient")
@FeignClient(url = "47.100.79.142:30041",name = "MaindataServiceClient")
public interface MaindataServiceClient extends IMaindataPharmacyController {
}

声明接口之后,在代码中通过@Resource注入之后即可使用。

@FeignClient标签的常用属性如下:

  • name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。

  • url: url一般用于调试,可以手动指定@FeignClient调用的地址。

  • decode404:当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException。

  • configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract。

  • fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口。

  • fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。

  • path: 定义当前FeignClient的统一前缀。

第四步:创建FeignConsumerController中并添加feign访问,代码如下:

@RestController
@RequestMapping("/feign/ ")
public class FeignConsumerController {
    @Autowired
    private RemoteProviderService remoteProviderService;
    
    /**基于feign方式的服务调用*/
    @GetMapping("/echo/{msg}")
    public String doFeignEcho(@PathVariable  String msg){
        //基于feign方式进行远端服务调用(前提是服务必须存在)
        return remoteProviderService.echoMessage(msg);
    }
}

第五步:启动消费者服务,在浏览器中直接通过feign客户端进行访问,如图所示(反复刷新检测其响应结果):

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Feign配置进阶实践

一个服务提供方通常会提供很多资源服务,服务消费方基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId,服务启动就会失败,例如假如在服务消费方再添加一个如下接口,消费方启动时就会启动失败,例如:

 @FeignClient(name="sca-provider")
 public interface RemoteOtherService {
     @GetMapping("/doSomeThing")
     public String doSomeThing();
}

在这里插入图片描述

其启动异常:

The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.

在这里插入图片描述

此时我们需要为远程调用服务接口指定一个contextId,作为远程调用服务的唯一标识即可,例如:

在这里插入图片描述

在这里插入图片描述

@FeignClient(name="sca-provider",contextId="remoteProviderService")//sca-provider为服务提供者名称
interface RemoteProviderService{
    @GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
    public String echoMessage(@PathVariable("string") String string);
}
  • 说明:假如@FeignClient注解中添加了contextId属性,则这个属性值默认会作为当前bean对象的名字,此时name的值仅仅作为要调用的服务名对待,一般推荐contextId的值默认为@FeignClient注解描述的接口的名字(首字母小写)。

还有,当我们在进行远程服务调用时,假如调用的服务突然不可用了或者调用过程超时了,怎么办呢?

一般服务消费端会给出具体的容错方案(配置熔断工厂),如下方式

@FeignClient 中 fallbackFactory与fallback方法不能同时使用,这个两个方法其实都类似于Hystrix(类似于断容器)的功能,当网络不通时返回默认的配置数据。

方式一:使用fallbackFactory 实现熔断

第一步:在服务消费端入口文件开启feign注解功能,添加@EnableFeignClients注解

@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

第二步:在Feign访问接口中应用FallbackFactory对象:fallbackFactory = ProviderFallbackFactory.class,例如:

@FeignClient(name = "sca-provider", 
			 contextId = "remoteProviderService",
             fallbackFactory = ProviderFallbackFactory.class)//sca-provider为nacos中的服务名
public interface RemoteProviderService {
    @GetMapping("/provider/echo/{msg}")
    public String echoMsg(@PathVariable String msg);
}

第三步: 创建ProviderFallbackFactory类,定义FallbackFactory接口的实现,代码如下:

package com.cy.service.factory;
/**
 * 基于此对象处理RemoteProviderService接口调用时出现的服务中断,超时等问题
 */
@Service
public class ProviderFallbackFactory
        implements FallbackFactory<RemoteProviderService> {
    /**
     * 此方法会在RemoteProviderService接口服务调用时,出现了异常后执行。
     * @param throwable 用于接收异常
     * 
     * 当实现一个接口的匿名内部类时,接口中重写的方法只有一个时,可以使用lamda表达式
     */
    @Override
    public RemoteProviderService create(Throwable throwable) {
        return msg -> "服务维护中,稍等片刻再访问";
    }
}

第四步:在配置文件application.yml中添加如下配置,启动feign方式调用时的服务中断处理机制。同时在controller层调用RemoteOtherService 服务的接口。

feign:  
  hystrix: #hystrix 含义是熔断(就相当于服务停止了)或降级
    enabled: true #默认值为false
@RestController
@RequestMapping("/feign/")
public class FeignConsumerController {
    @Autowired
    private RemoteProviderService remoteProviderService;

    //http:localhost:8610/feign/echo/帅文文
    @GetMapping("echo/{msg}")
    public String doFeignEcho(@PathVariable String msg){
     return remoteProviderService.echoMsg(msg);
    }
}

第五步:在服务提供方对应的方法中添加Thread.sleep(500000)模拟耗时操作,然后启动服务进行访问测试。

  //http://localhost:8600/provider/echo/tedu
  @GetMapping("/provider/echo/{msg}")
  public String doRestEcho1(@PathVariable String msg){
      Thread.sleep(50000);//模拟耗时操作
      return server+" say hello "+msg;
  }

在这里插入图片描述

方式二:实现fallback方法

第一步:在服务消费端入口文件开启feign注解功能,添加@EnableFeignClients注解

@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

第二步:创建一个调用远程服务的接口,同时在@FeignClient注解中使用fallback默认返回方法(断容器):fallback = ProviderFallback.class

@FeignClient(name = "sca-provider",
             contextId = "remoteOtherService",
             fallback = ProviderFallback.class)
public interface RemoteOtherService {
    @GetMapping("/provider/echo/{msg}")
    String echoMsg(@PathVariable("msg") String msg);
}

第三步:写ProviderFallback类,并实现RemoteOtherService 接口,当网络不通或者访问失败时,返回固定/默认内容

@Service
public class ProviderFallback implements RemoteOtherService {
    @Override
    public String echoMsg(String msg) {
        return "服务器出了点小差,稍等片刻再访问";
    }
}

第四步:在配置文件application.yml中添加如下配置,启动feign方式调用时的服务中断处理机制。同时在controller层调用RemoteOtherService 服务的接口。

feign:  
  hystrix: #hystrix 含义是熔断(就相当于服务停止了)或降级
    enabled: true #默认值为false

@RestController
@RequestMapping("/feign/")
public class FeignConsumerController {
    @Autowired
    private RemoteOtherService remoteOtherService;

    //http:localhost:8610/feign/echo02/帅文文
    @GetMapping("echo02/{msg}")
    public String doFeignEcho02(@PathVariable String msg){
        return remoteOtherService.echoMsg(msg);
    }

}

第五步:在服务提供方对应的方法中添加Thread.sleep(500000)模拟耗时操作,然后启动服务进行访问测试。

  //http://localhost:8600/provider/echo/tedu
  @GetMapping("/provider/echo/{msg}")
  public String doRestEcho1(@PathVariable String msg){
      Thread.sleep(50000);//模拟耗时操作
      return server+" say hello "+msg;
  }

在这里插入图片描述

Feign 调用过程分析(了解)

Feign应用过程分析(底层逻辑先了解):

  1. 通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。

  2. Feign Starter 在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。

  3. 接口被调用时被动态代理类逻辑拦截,将 @FeignClient 请求信息通过编码器生成 Request对象,基于此对象进行远程过程调用。

  4. 请求对象经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。

  5. 通过 Client 携带 Request 调用远端服务返回请求响应。

  6. 通过解码器生成 Response 返回客户端,将信息流解析成为接口返回数据。

小节面试分析

  • 为什么使用feign?(基于Feign可以更加友好的实现服务调用,简化服务消费方对服务提供方方法的调用)。

  • @FeignClient注解的作用是什么?(告诉Feign Starter,在项目启动时,为此注解描述的接口创建实现类-代理类)

  • Feign方式的调用,底层负载均衡是如何实现的?(Ribbon)

  • @EnableFeignCleints 注解的作用是什么?(描述配置类,例如启动类)

总结(Summary)

图解服务调用方案

在这里插入图片描述
这张图描述了远程服务调用的几中方式:

  • 第一种:服务比较少,例如就两个服务,一个服务消费,一个服务提供,就不需要注册中心,不需要负载均衡。

  • 第二种:并发比较大,服务服务比较多,我们需要管理服务,就需要注册中心,我们还需要服务间的负载均衡。但代码编写的复杂多相对高一些,我们需要自己获取ip,获取端口,拼接字符串等。

  • 第三种:我们要基于第二种进行代码简化,底层提供了一种拦截器,把基于服务名获取服务实例的过程在拦截器中做了封装,简化了代码的开发。但是加了拦截器多少会在性能少有一点损耗。

  • 第四种方式主要是从代码结构上做一个挑战,我们前面三种基于RestTemplate进行服务调用,本身属于一种远程服务调用业务,能够将这种业务写到一个业务对象中,Feign方式就诞生了,它主要对代码结构的一种优化。

重难点分析

  • 何为注册中心?(用于记录服务信息的一个web服务,例如淘宝平台,滴滴平台,美团外卖平台,……)

  • 注册中心的核心对象?(服务提供方,服务消费方,注册中心-Registry)

  • 市面上常用注册中心?(Google-Consul,Alibaba-Nacos,…)

  • 微服务架构下项目的构建过程?(聚合工程)

  • Nacos安装、启动、服务的注册、发现机制以及实现过程?

  • Feign的基本应用以及底层底层调用原理?

FAQ分析

  • Nacos是什么,提供了什么特性(服务的注册、发现、配置)?

  • 你为什么会选择Nacos?(活跃度、稳定、性能、学习成本)

  • Nacos的官网?(nacos.io)

  • Nacos在github的源码?(github.com/alibaba/nacos)

  • Nacos在windows环境下安装?(解压即可使用)

  • Nacos在windows中的的初步配置?(application.properties访问数据库的数据源)

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

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

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

  • 注册中心的核心数据是什么?(服务的名字和它对应的网络地址)

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

  • Nacos健康检查的方式?(基于心跳包机制进行实现)

  • Nacos是如何保证高可用的?(重试,本地缓存、集群)

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

  • Feign方式的调用过程,其负载均衡是如何实现?(Ribbon)

Nacos服务配置中心应用实践

配置中心简介

背景分析

我们知道,除了代码之外,软件还有一些配置信息,比如数据库的用户名和密码,还有一些我们不想写死在代码里的东西,例如像线程池大小、队列长度等运行参数,以及日志级别、算法策略等, 还有一些是软件运行环境的参数,如Java 的内存大小,应用启动的参数,包括操作系统的一些 参数配置…… 所有这些东西,我们都叫做软件配置

以前,我们把软件配置写在一个配置文件中,就像 Windows 下的 ini 文件,或是 Linux 下的 conf 文件。然而,在分布式系统下,这样的方式就变得非常不好管理,并容易出错

假如生产环境下,项目现在正在运行,此时修改了配置文件,我们需要让这些配置生效,通常的做法是不是要重启服务。但重启是不是会带来系统服务短时间的暂停,从而影响用户体验呢,还有可能会带来经济上的很大损失(例如双11重启下服务)。基于这样的背景,配置中心诞生了。

配置中心概述

配置中心最基础的功能就是存储一个键值对,用户发布一个配置(configKey),然后客户端获取这个配置项(configValue);进阶的功能就是当某个配置项发生变更时,不停机就可以动态刷新服务内部的配置项,例如,在生产环境上我们可能把我们的日志级别调整为 error 级别,但是,在系统出问题我们希望对它 debug 的时候,我们需要动态的调整系统的行为的能力,把日志级别调整为 debug 级别。

还有,当你设计一个电商系统时,设计大促预案一定会考虑,同时涌进来超过一亿人并发访问的时候,假如系统是扛不住的,你会怎么办,在这个过程中我们一般会采用限流,降级。

系统的限流和降级本质上来讲就是从日常的运行态切换到大促态的一个行为的动态调整,这个本身天然就是配置起到作用的一个相应的场景。

配置中心的选型

在面向分布式的微服务系统中,如何通过更高效的配置管理方式,实现微服务系统架构持续“无痛”的演进,并动态调整和控制系统的运行时态,配置中心的选型和设计起着举足轻重的作用。

市场上主流配置中心有Apollo(携程开源),nacos(阿里开源),Spring Cloud Config(Spring Cloud 全家桶成员)。

我们在对这些配置中心进行选型时重点要从产品功能、使用体验、实施过程和性能等方面进行综合考量。

本次我们选择nacos,此组件不仅提供了注册中心,还具备配置中心的功能。

小节面试分析

  • 什么是配置中心?(存储项目配置信息的一个服务)
  • 为什么要使用配置中心?(集中管理配置信息,动态发布配置信息)
  • 市场上有哪些主流的配置中心?(Apollo,nacos,……)

Nacos配置快速入门

添加依赖

在已有的sca-provider项目中添加如配置依赖,例如:

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

在这里插入图片描述

修改配置文件

将项目中的application.yml的名字修改为bootstrap.yml配置文件(启动优先级最高),代码如下:

spring:
  application:
    name: sca-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yml # Configure the data format of the content, default to properties

在这里插入图片描述

配置文件加载顺序

优先级依次降低
在这里插入图片描述

Nacos基本配置

打开nacos配置中心,新建配置,如图所示:
在这里插入图片描述

其中Data ID的值要与bootstrap.yml中定义的spring.application.name的值相同(服务名-假如有多个服务一般会创建多个配置实例,不同服务对应不同的配置实例)。

Controller处理器操作

在 ProviderController 中添加一个获取日志级别(debug<info<warn<error)的方法,代码如下:

@RefreshScope //动态刷新配置
@RestController
public class ProviderController{
    private static final Logger log =
     LoggerFactory.getLogger(ProviderApplication.class);
    
    /**
    * 不添加@RefreshScope注解的话,对象只会编译一次
    * logLevel中的值不会随着配置的改变而动态改变
    */
    @Value("${logging.level.com.jt:error}")
    private String logLevel;
    
    @GetMapping("/provider/doGetLogLevel")
    public String doGetLogLevel(){
       log.trace("==log.trace==");//跟踪
       log.debug("==log.debug==");//调试
       log.info("==log.info==");//常规信息
       log.warn("==log.warn==");//警告
       log.error("==log.error==");//错误信息
        return "log level is "+logLevel;
    }
    ……
}

其中,@RefreshScope的作用是在配置中心的相关配置发生变化以后,能够及时看到类中属性值的更新(底层是通过重新创建Controller对象的方式,对属性进行了重新初始化),Controller编写好以后,启动配置中心服务,然后进行访问测试。

@RefreshScope 动态刷新配置,此注解用于告诉spring,一旦在配置发生变化时,重新构建标记对象。

打开浏览器直接在地址栏输入http://localhost:8600/provider/doGetLogLevel,检测输出结果是否为我们配置中配置的信息,如图所示。

在这里插入图片描述

在这里插入图片描述

假如对配置的信息访问不到,请检测项目配置文件的名字是否为bootstrap.yml,检查配置文件中spring.application.name属性的值是否与配置中心的data-id名相同,还有你读取的配置信息缩进以及空格写的格式是否正确。

Nacos配置动态更新实现

修改Nacos的日志级别配置并重新重新发布,如图所示:

在这里插入图片描述

在这里插入图片描述

刷新浏览器url,检测其配置输出。

在这里插入图片描述

在这里插入图片描述

日志信息本地化实现

#日志配置
logging:
  level:
    com.cy: debug
  file:
    path: D:\CGB2105Resource\logs  #日志输出到文件的路径,默认文件名为spring.log
  pattern:
    console: '%-5level %msg %n' #控制台中
    file: '%d %-5level %thread %logger %msg %n' #本地文件中

# %d 表示日期时间
# %-5level 日志级别
# %thread 线程名
# %logger 日志输出方(哪个类中输出的日志)
# %msg 具体的日志信息
# %n  换行 
# 等等... ...

在这里插入图片描述
控制台中输出自定义格式日志:
在这里插入图片描述

本地文件中输出自定义文件格式:
在这里插入图片描述

小节面试分析

  • 配置中心一般都会配置什么内容?(可能会经常变化的配置信息,例如连接池,日志、线程池、限流熔断规则)

  • 什么信息一般不会写到配置中心?(服务端口,服务名,服务的注册地址,配置中心)

  • 项目中为什么要定义bootstrap.yml文件?(此文件被读取的优先级比较高,可以在服务启动时读取配置中心的数据)

  • Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗?(可以从内存,客户端获取了配置中心的配置信息以后,会将配置信息在本地内存中存储一份。)

  • 微服务应用中我们的客户端如何获取配置中心的信息?(我们的服务一般首先会从内存读取配置信息,同时我们的微服务还可以定时向nacos配置中心发请求拉取(pull)更新的配置信息)

拉取(Pull)配置信息
在这里插入图片描述

  • 微服务应用中客户端如何感知配置中心数据变化?(当数据发生变化时,nacos找到它维护的客户端,然后通知客户端去获取更新的数据,客户端获取数据以后更新本地内存,并在下次访问资源时,刷新@Value注解描述的属性值,但是需要借助@RefreshScope注解对属性所在的类进行描述)

  • 服务启动后没有从配置中心获取我们的配置数据是什么原因?(依赖,配置文件名字bootstrap.yml,配置中心的dataId名字是否正确,分组是否正确,配置的名字是否正确,缩进关系是否正确,假如是动态发布,类上是否有@RefreshScope注解)

  • 你项目中使用的日志规范是什么?(SLF4J)

  • 你了解项目中的日志级别吗?(debug,info,error,…,可以基于日志级别控制日志的输出)

IDEA连接数据库(入门)

在这里插入图片描述

连接成功:

在这里插入图片描述

添加数据库:

在这里插入图片描述
在这里插入图片描述

Nacos配置管理模型

概述

Nacos 配置管理模型由三部分构成,如图所示:

在这里插入图片描述
其中:

  • Namespace:命名空间,对不同的环境进⾏隔离,⽐如隔离开发环境和⽣产环境
  • Group:分组,将若⼲个服务或者若⼲个配置集归为⼀组
  • Service/DataId:某⼀个服务或配置集,一般对应一个配置文件

命名空间设计

Nacos中的命名空间一般用于配置隔离,这种命名空间的定义一般会按照环境(开发,生产等环境)进行设计和实现.我们默认创建的配置都存储到了public命名空间,如图所示:

在这里插入图片描述

创建新的开发环境并定义其配置,然后从开发环境的配置中读取配置信息,该如何实现呢?

第一步:创建新命名空间,如图所示:

在这里插入图片描述

命名空间成功创建以后,会在如下列表进行呈现。

在这里插入图片描述

在指定命名空间下添加配置,也可以直接取配置列表中克隆,例如:

在这里插入图片描述

在这里插入图片描述

克隆成功以后,我们会发现在指定的命名空间中有了我们克隆的配置,如图所示:

在这里插入图片描述

此时我们修改dev命名空间中Data Id的sca-provider配置,如图所示:

在这里插入图片描述

修改项目module中的配置文件bootstrap.yml,添加如下配置,关键代码如下:

spring:
  application:
    name: sca-provider #服务名
  cloud:
    nacos:
      discovery: #服务的注册和发现
        server-addr: localhost:8848 #nacos server
      config: #服务配置中心(将可能发生的配置)
        server-addr: localhost:8848
        file-extension: yml #配置中心指定的配置信息格式的扩展名
        group: DEFAULT_GROUP
        namespace: c22e1150-3d3e-4a4e-87ce-e64608fcbbf4
        ……

在这里插入图片描述

其中,namespace后面的字符串为命名空间的id,可直接从命名空间列表中进行拷贝。

重启服务,继续刷新http://localhost:8600/provider/doGetLogLevel地址。检测输出,看看输出的内容是什么,是否为dev命名空间下配置的内容,如图所示:

在这里插入图片描述
在这里插入图片描述

我们还可以创建生产环境,依次类推进行设计和实现即可。

分组设计及实现

当我们在指定命名空间下,按环境或服务做好了配置以后,有时还需要基于服务做分组配置,例如,一个服务在不同时间节点(节假日,活动等)切换不同的配置,可以在新建配置时指定分组名称,如图所示:

在这里插入图片描述
在这里插入图片描述

配置发布以后,修改boostrap.yml配置类,在其内部指定我们刚刚创建的分组,代码如下:

server:
  port: 8600
spring:
  application:
    name: nacos-config
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        group: DEV_GROUP_51 # Group, default is DEFAULT_GROUP
        file-extension: yml # Configure the data format of the content, default to properties
        namespace: 5c27fe4a-1141-4836-a14e-cbac77fb2130

在这里插入图片描述

在ProviderController类中添加属性和方法用于获取和输出DEV_GROUP_51配置中设置的线程数,代码如下:

@Value("${server.tomcat.threads.max:200}")
private Integer maxThread;

@RequestMapping("/provider/doGetMaxThread")
public String doGetMaxThread(){
    return "server.threads.max is  "+maxThread;
}

然后重启服务,进行测试,检测内容输出,如图所示:

在这里插入图片描述

共享配置设计及读取

当同一个namespace的多个配置文件中都有相同配置时,可以对这些配置进行提取,然后存储到nacos配置中心的一个或多个指定配置文件,哪个微服务需要,就在服务的配置中设置读取即可。例如:

第一步:在nacos中创建一个共享配置文件,例如:

在这里插入图片描述

在这里插入图片描述

第二步:在指定的微服务配置文件(bootstrap.yml)中设置对共享配置文件的读取,例如:见红色区域内容。

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

在这里插入图片描述

第三步:在指定的业务类中读取和应用共享配置即可,例如:

@Value("${page.pageSize:10}")
private Integer pageSize;
@GetMapping("/provider/doGetPageSize")
public String doGetPageSize(){
    //return String.format()
    return "page size is "+pageSize;
}

第四步:启动服务进行访问测试。

在这里插入图片描述

小节面试分析

  • Nacos配置管理模型的背景?(环境不同配置不同)

  • Nacos配置中的管理模型是怎样的?(namespace,group,service/data-id)

  • Nacos客户端(微服务)是否可以读取共享配置?(可以)

总结(Summary)

重难点分析

  • 配置中心的选型。(市场活跃度、稳定性、性能、易用)

  • Nacos配置中心基本应用。(新建,修改、删除配置以后,在Nacos客户端应用配置)

  • 配置管理模型应用。(namespace,group,service/dataId)

  • Nacos配置变更的动态感知。(底层原理分析)

FAQ分析

  • 为什么需要配置中心?(动态管理发布配置,无需重启服务,更好保证服务的可用)

  • 配置中一般要配置什么内容?(经常变化的配置数据-日志级别,线程池、连接池、…)

  • 市面上有哪些主流配置中心?(Nacos,….)

  • 配置中心选型时要重点考虑哪些因素?(市场活跃度、稳定性、性能、易用)

  • Nacos客户端(微服务业务)如何动态感知配置中心数据变化的?(nacos2.0之前nacos客户端采用长轮询机制每隔30秒拉取nacos配置信息.)

  • Nacos配置管理模型是怎样的?(命名空间-namespace,分组-group,服务实例-dataId)

Sentinel限流熔断应用实践

Sentinel简介

背景分析

在我们日常生活中,经常会在淘宝、天猫、京东、拼多多等平台上参与商品的秒杀、抢购以及一些优惠活动,也会在节假日使用12306 手机APP抢火车票、高铁票,甚至有时候还要帮助同事、朋友为他们家小孩拉投票、刷票,这些场景都无一例外的会引起服务器流量的暴涨,导致网页无法显示、APP反应慢、功能无法正常运转,甚至会引起整个网站的崩溃。

我们如何在这些业务流量变化无常的情况下,保证各种业务安全运营,系统在任何情况下都不会崩溃呢?我们可以在系统负载过高时,采用限流、降级和熔断,三种措施来保护系统,由此一些流量控制中间件诞生。例如Sentinel。

Sentinel概述

Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。

Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

Sentinel核心分为两个部分:

  • 核心库(Java 客户端):能够运行于所有 Java 运行时环境,同时对Dubbo /Spring Cloud 等框架也有较好的支持。

  • 控制台(Dashboard):基于 Spring Boot 开发,打包后可以直接运行。

安装Sentinel服务

CMD中执行命令启动Sentinel

Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能,其控制台安装步骤如下:

第一步:打开sentinel下载网址

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

第二步:下载Jar包(可以存储到一个sentinel目录),如图所示:

在这里插入图片描述

第三步:在sentinel对应目录,打开命令行(cmd),启动运行sentinel

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

检测启动过程,如图所示:

在这里插入图片描述

IDEA中执行脚本启动Sentinel

在这里插入图片描述
在这里插入图片描述
script options 中输入:

-Dserver.port=8180 
 -Dcsp.sentinel.dashboard.server=localhost:8180 
 -Dproject.name=sentinel-dashboard 
 -jar 
 D:/sentinel/sentinel-dashboard-1.8.0.jar

在这里插入图片描述

在这里插入图片描述

访问Sentinal服务

第一步:假如Sentinal启动ok,通过浏览器进行访问测试,如图所示:

在这里插入图片描述
第二步:登陆sentinel,默认用户和密码都是sentinel,登陆成功以后的界面如图所示:

在这里插入图片描述

Sentinel入门

概述

我们系统中的数据库连接池,线程池,nginx的瞬时并发,MQ消息等在使用时都会跟定一个限定的值,这本身就是一种限流的设计。限流的目的防止恶意请求流量、恶意攻击,或者防止流量超过系统峰值。

Sentinel集成

第一步:Sentinel 应用于服务消费方(Consumer),在消费方添加依赖如下:

<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>

第二步:打开服务消费方配置文件application.yml,添加sentinel配置,代码如下:

spring:
  cloud:
    sentinel:
      transport:
         port: 8099 #跟sentinel控制台交流的端口,随意指定一个未使用的端口即可
         dashboard: localhost:8180 # 指定sentinel控制台地址

在这里插入图片描述

第三步:启动服务提供者,服务消费者,然后在浏览器访问消费者url,如图所示:

在这里插入图片描述
第四步:刷新sentinel 控制台,检测服务列表,如图所示:

在这里插入图片描述

Sentinel的控制台其实就是一个SpringBoot编写的程序,我们需要将我们的服务注册到控制台上,即在微服务中指定控制台的地址,并且还要在消费端开启一个与sentinel控制台传递数据端的端口,控制台可以通过此端口调用微服务中的监控程序来获取各种信息

Sentinel限流快速入门

我们设置一下指定接口的流控(流量控制),==QPS(每秒请求次数)==单机阈值为1,代表每秒请求不能超出1次,要不然就做限流处理,处理方式直接调用失败。

第一步:选择要限流的链路,如图所示:

在这里插入图片描述

第二步:设置限流策略,如图所示:

在这里插入图片描述
添加成功:

在这里插入图片描述

第三步:反复刷新访问消费端端服务,检测是否有限流信息输出,如图所示:

在这里插入图片描述

Sentinel流控规则分析

阈值类型分析

  • QPS(Queries Per Second):当调用相关url对应的资源时,QPS达到单机阈值时,就会限流。

  • 线程数:当调用相关url对应的资源时,线程数达到单机阈值时,就会限流。

设置限流模式

Sentinel的流控模式代表的流控的方式,默认【直接】,还有关联,链路。

直接模式

Sentinel默认的流控处理就是【直接->快速失败】。

在这里插入图片描述

关联模式

当关联的资源达到阈值,就限流自己。

例如设置了关联资源为/ur2时,假如关联资源/url2的qps阀值超过1时,就限流/url1接口(是不是感觉很霸道,关联资源达到阀值,是本资源接口被限流了)。

这种关联模式有什么应用场景呢?我们举个例子,订单服务中会有2个重要的接口,一个是读取订单信息接口一个是写入订单信息接口。在高并发业务场景中,两个接口都会占用资源,如果读取接口访问过大,就会影响写入接口的性能

业务中如果我们希望写入订单比较重要,要优先考虑写入订单接口。那就可以利用关联模式;在关联资源上面设置写入接口,资源名设置读取接口就行了;这样就起到了优先写入,一旦写入请求多,就限制读的请求。例如:

在这里插入图片描述
添加成功:
在这里插入图片描述
在这里插入图片描述

链路模式

链路模式只记录指定链路入口的流量。也就是当多个服务对指定资源调用时,假如流量超出了指定阈值,则进行限流。

被调用的方法用 @SentinelResource 进行注解,然后分别用不同业务方法对此业务进行调用,假如A业务设置了链路模式的限流,在B业务中是不受影响的。例如现在设计一个业务对象,代码如下(为了简单,可以直接写在启动类内部):

@Service
public class ConsumerService{
    @SentinelResource("doGetResource")
    public String doGetResource(){
        return "doGetResource";
    }
}

接下来我们在/consumer/doRestEcho1对应的方法中对ConsumerService中的doGetResource方法进行调用(应用consumerService对象之前,要先在doRestEcho01方法所在的类中进行consumerService值的注入)。例如:

 @GetMapping("/consumer/doRestEcho1")
    public String doRestEcho01() throws InterruptedException {
        consumerService.doGetResource();
        //Thread.sleep(200);
        String url="http://localhost:8081/provider/echo/"+server;
        //远程过程调用-RPC
        return restTemplate.getForObject(url,String.class);//String.class调用服务响应数据类型
    }

其路由规则配置如下:

在这里插入图片描述

在这里插入图片描述

添加成功:

在这里插入图片描述

频繁对链路/consumer/doRestEcho2进行访问,检测是否会出现500异常。
在这里插入图片描述

说明,流控模式为链路模式时,假如是sentinel 1.7.2以后版本Sentinel Web过滤器默认会聚合所有URL的入口为sentinel_spring_web_context,因此单独对指定链路限流会不生效,需要在application.yml添加如下语句来关闭URL PATH聚合,例如:

sentinel:
     web-context-unify: false

不设置web-context-unify之前,Sentinel Web过滤器默认会聚合所有URL的入口为sentinel_spring_web_context:

在这里插入图片描述

yml中设置 web-context-unify: false

在这里插入图片描述
设置web-context-unify之后:
在这里插入图片描述

修改配置以后,重新sentinel,并设置链路流控规则,然后再频繁对链路/consumer/doRestEcho1进行访问,检测是否会出现500异常。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

设计限流效果(了解)

快速失败

流量达到指定阀值,直接返回报异常。(类似路前方坍塌,后面设定路标,让后面的车辆返回)

WarmUp (预热)

WarmUp也叫预热,根据codeFactor(默认3)的值,(阀值/codeFactor)为初始阈值,经过预热时长,才到达设置的QPS的阈值,假如单机阈值为100,系统初始化的阈为 100/3 ,即阈值为33,然后过了10秒,阈值才恢复到100。这个预热的应用场景,如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。例如:

在这里插入图片描述

排队等待

从字面上面就能够猜到,匀速排队,让请求以均匀的速度通过,阈值类型必须设成QPS,否则无效。比如有时候系统在某一个时刻会出现大流量,之后流量就恢复稳定,可以采用这种排队模式,大流量来时可以让流量请求先排队,等恢复了在慢慢进行处理,例如:

在这里插入图片描述

小节面试分析

  • Sentinel是什么?(阿里推出一个流量控制平台,防卫兵)

  • 类似Sentinel的产品你知道有什么?(hystrix-一代微服务产品)

  • 你了解哪些限流算法?(计数器、令牌桶、漏斗算法,滑动窗口算法,…)

  • Sentinel 默认的限流算法是什么?(滑动窗口算法)

  • 你了解sentinel中的阈值应用类型吗?(两种-QPS,线程数)

  • Sentinel 限流规则中默认有哪些限流模式?(直连,关联,链路)

  • Sentinel的限流效果有哪些?(快速失败,预热,排队)

  • Sentinel 为什么可以对我们的业务进行限流,原理是什么?
    在这里插入图片描述

当我们在服务中添加了Sentinel依赖以后,Sentinel会为我们的服务提供(自动添加)一个SpringMVC拦截器,这个拦截器会对请求进行拦截。

在这里插入图片描述

然后基于请求url获取sentinel控制台中设置好的流控规则,然后采用一定的算法对请求url要访问的资源进行流量限制,在规则内的才能通过访问资源。

Sentinel降级入门

概述

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。

Sentinel 熔断降级在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。

准备工作

修改ConumserController 类中的doRestEcho01方法,假如没有创建即可,基于此方法演示慢调用过程下的限流,代码如下:

     //AtomicLong 类支持线程安全的自增自减操作
    private AtomicLong atomicLong=new AtomicLong(1);
    @GetMapping("/consumer/doRestEcho1")
    public  String doRestEcho01() throws InterruptedException {
        //consumerService.doGetResource();
        //获取自增对象的值,然后再加1
        long num=atomicLong.getAndIncrement();
        if(num%2==0){//模拟50%的慢调用比例
           Thread.sleep(200);
        }
        String url="http://localhost:8081/provider/echo/"+server;
        //远程过程调用-RPC
        return restTemplate.getForObject(url,String.class);//String.class调用服务响应数据类型
    }

第一步:服务启动后,选择要降级的链路,如图所示:

在这里插入图片描述

第二步:选择要降级的链路,如图所示:

在这里插入图片描述

这里表示熔断策略为慢调用比例,表示链路请求数超过3时,假如平均响应时间假如超过200毫秒的有50%,则对请求进行熔断,熔断时长为10秒钟,10秒以后恢复正常。

在这里插入图片描述

第三步:对指定链路进行刷新,多次访问测试,假如出现了降级熔断,会出现如下结果:

在这里插入图片描述

我们也可以进行断点调试,在DefaultBlockExceptionHandler中的handle方法内部加断点,分析异常类型,假如异常类型为DegradeException则为降级熔断。

  • 降级可以理解为访问单个服务响应延迟的限制,
  • 熔断可以理解为访问A服务调用B服务,B服务响应延迟,对B服务进行熔断限制。

Sentinel 异常处理

系统提供了默认的异常处理机制,假如默认处理机制不满足我们需求,我们可以自己进行定义。定义方式上可以直接或间接实现BlockExceptionHandler接口,并将对象交给spring管理。

在这里插入图片描述

默认调用DefaultBlockExceptionHandler

package com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.util.StringUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * Default handler for the blocked request.
 *
 * @author kaizi2009
 */
public class DefaultBlockExceptionHandler implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        // Return 429 (Too Many Requests) by default.
        response.setStatus(429);

        PrintWriter out = response.getWriter();
        out.print("Blocked by Sentinel (flow limiting)");
        out.flush();
        out.close();
    }

}

自行定义异常处理类

@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();
    }
}

小节面试分析

  • 何为降级熔断?(让外部应用停止对服务的访问,生活中跳闸,路障设置-此路不通)

  • 为什么要进行熔断呢?(平均响应速度越来越慢或经常出现异常,这样可能会导致调用链堆积,最终系统崩溃)

  • Sentinel中限流,降级的异常父类是谁?(BlockException)

  • Sentinel 出现降级熔断时,系统底层抛出的异常是谁?(DegradeException)

  • Sentinel中异常处理接口是谁?(BlockExceptionHandler)

  • Sentinel中异常处理接口下默认的实现类为? (DefaultBlockExceptionHandler)

  • 假如Sentinel中默认的异常处理规则不满足我们的需求怎么办?(自己定义)

  • 我们如何自己定义Sentinel中异常处理呢?(直接或间接实现BlockExceptionHandler )
    Sent

Sentinel降级策略分析(拓展)

Sentinel熔断降级支持慢调用比例、异常比例、异常数三种策略

慢调用比例

慢调用指耗时大于阈值RT(Response Time)的请求称为慢调用,阈值RT由用户设置。其属性具体含义说明如下:

在这里插入图片描述
慢调用逻辑中的状态分析如下:

  • 熔断(OPEN):请求数大于最小请求数并且慢调用的比率大于比例阈值则发生熔断,熔断时长为用户自定义设置。

  • 探测(HALFOPEN):当熔断过了定义的熔断时长,状态由熔断(OPEN)变为探测(HALFOPEN)。

  • 关闭(CLOSED):如果接下来的一个请求小于最大RT,说明慢调用已经恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED),如果接下来的一个请求大于最大RT,说明慢调用未恢复,继续熔断,熔断时长保持一致

注意:Sentinel默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置

异常比例

当资源的每秒请求数大于等于最小请求数,并且异常总数占通过量的比例超过比例阈值时,资源进入降级状态。其属性说明如下:

在这里插入图片描述

异常比例中的状态分析如下:

  • 熔断(OPEN):当请求数大于最小请求并且异常比例大于设置的阈值时触发熔断,熔断时长由用户设置。

  • 探测(HALFOPEN):当超过熔断时长时,由熔断(OPEN)转为探测(HALFOPEN)

  • 关闭(CLOSED):如果接下来的一个请求未发生错误,说明应用恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)。如果接下来的一个请求继续发生错误,说明应用未恢复,继续熔断,熔断时长保持一致。

异常数量

当资源近1分钟的异常数目超过阈值(异常数)之后会进行服务降级。注意,由于统计时间窗口是分钟级别的,若熔断时长小于60s,则结束熔断状态后仍可能再次进入熔断状态。其属性说明如下:

在这里插入图片描述

基于异常数的状态分析如下:

  • 熔断(OPEN):当请求数大于最小请求并且异常数量大于设置的阈值时触发熔断,熔断时长由用户设置。

  • 探测(HALFOPEN):当超过熔断时长时,由熔断(OPEN)转为探测(HALFOPEN)

  • 关闭(CLOSED):如果接下来的一个请求未发生错误,说明应用恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)如果接下来的一个请求继续发生错误,说明应用未恢复,继续熔断,熔断时长保持一致。

小节面试分析

  • Sentinel 降级熔断策略有哪些?(慢调用,异常比例,异常数)

  • Sentinel 熔断处理逻辑中的有哪些状态?(Open,HalfOpen,Closed)

  • Sentinel 对服务调用进行熔断以后处于什么状态?(熔断打开状态-Open)

  • Sentinel 设置的熔断时长到期以后,Sentinel的熔断会处于什么状态?(探测-HalfOpen,假如再次访问时依旧响应时间比较长或依旧有异常,则继续熔断)

  • Sentinel 中的熔断逻辑恢复正常调用以后,会出现什么状态?(熔断关闭-closed)

Sentinel热点规则分析(重点)

概述

何为热点?热点即经常访问的数据。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制。
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制。

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

快速入门

第一步:定义热点业务代码,如图所示:

     //http://ip:port/consumer/doFindById?id=10
        @GetMapping("/consumer/findById")
        @SentinelResource("res")
        public String doFindById(@RequestParam("id") Integer id){
            return "resource id is "+id;
        }

第二步:服务启动后,选择要限流的热点链路,如图所示:

在这里插入图片描述

第三步:设置要限流的热点,如图所示:

在这里插入图片描述

热点规则的限流模式只有QPS模式(这才叫热点)。
参数索引为 @SentinelResource注解的方法参数下标 ,0代表第一个参数,1代表第二个参数。单机阈值以及统计窗口时长表示在此窗口时间超过阈值就限流(上面表示限制单机两秒访问一次)。

第四步:多次访问热点参数方法,前端会出现如下界面,如图所示:

在这里插入图片描述
然后,在后台出现如下异常表示限流成功。

在这里插入图片描述

其中,热点参数其实说白了就是特殊的流控,流控设置是针对整个请求的;但是热点参数他可以设置到具体哪个参数,甚至参数针对的值,这样更灵活的进行流控管理。

一般应用在某些特殊资源的特殊处理,如:某些商品流量大,其他商品流量很正常,就可以利用热点参数限流的方案。

特定参数设计

配置参数例外项,如图所示:

在这里插入图片描述

这里表示参数值为5时阈值为1,其它参数值阈值为5,例如当我们访问http://ip:port/consumer/doRestEcho1?id=5时的限流阈值为1。

小节面试分析

  • 如何理解热点数据?(访问频度比较高的数据,某些商品、谋篇文章、某个视频)

  • 热点数据的限流规则是怎样的?(主要是针对参数进行限流设计)

  • 热点数据中的特殊参数如何理解?(热点限流中的某个参数值的阈值设计)

  • 对于热点数据的访问出现限流以后底层异常是什么?(ParamFlowException)

Sentinel系统规则(了解)

概述

系统在生产环境运行过程中,我们经常需要监控服务器的状态,看服务器CPU、内存、IO等的使用率;主要目的就是保证服务器正常的运行,不能被某些应用搞崩溃了;而且在保证稳定的前提下,保持系统的最大吞吐量。

长期以来,系统自适应保护的思路是根据硬指标,即系统的负载 (load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当 load 开始好转,则恢复流量的进入。

快速入门

Sentinel的系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 、线程数和CPU使用率五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。如图所示:

在这里插入图片描述

其中,

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。

  • CPU使用率:当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。

  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。

  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。

  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
    系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务。

小节面试分析

  • 如何理解sentinel中的系统规则?(是对所有链路的控制规则,是一种系统保护策略)
  • Sentinel的常用系统规则有哪些?(RT,QPS,CPU,线程,Load-linux,unix)
    Sentinel系统保护规则被触发以后底层会抛出什么异常?(SystemBlockException)

Sentinel授权规则(重要)

概述

很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的黑白名单控制的功能

黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。例如微信中的黑名单。

快速入门

sentinel可以基于黑白名单方式进行授权规则设计,如图所示:

在这里插入图片描述
黑白名单规则(AuthorityRule)非常简单,主要有以下配置项:

  • 资源名:即限流规则的作用对象
  • 流控应用:对应的黑名单/白名单中设置的规则值,多个值用逗号隔开。
  • 授权类型:白名单,黑名单(不允许访问)。

案例实现:

定义请求解析器,用于对请求进行解析,并返回解析结果

sentinel底层 在拦截到用户请求以后,会对请求数据基于此对象进行解析,判定是否符合黑白名单规则

第一步:定义RequestOriginParser接口的实现类,基于业务在接口方法中解析请求数据并返回。

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

第二步:定义流控规则,如图所示:

在这里插入图片描述

第三步:执行资源访问,检测授权规则应用,当我们配置的流控应用值为app1时,假如规则为黑名单,则基于

http://ip:port/path?origin=app1的请求不可以通过,会出现如下结果:

在这里插入图片描述

第四步:设计过程分析,如图所示:

在这里插入图片描述

小节面试分析

  • 如何理解Sentinel中的授权规则?(对指定资源的访问给出的一种简易的授权策略)

  • Sentinel的授权规则是如何设计的?(白名单和黑名单)

  • 如何理解Sentinel中的白名单?(允许访问的资源名单)

  • 如何理解Sentinel中的黑名单?(不允许访问的资源名单)

  • Sentinel如何识别白名单和黑名单?(在拦截器中通过调用RequestOriginParser对象的方法检测具体的规则)

  • 授权规则中RequestOriginParser类的做用是什么?(对流控应用值进行解析,检查服务访问时传入的值是否与RequestOriginParser的parseOrigin方法返回值是否相同。)

总结(Summary)

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

重难点分析

  • Sentinel诞生的背景?(计算机的数量是否有限,处理能力是否有限,并发比较大或突发流量比较大)
  • 服务中Sentinel环境的集成,初始化?(添加依赖-两个,sentinel配置)
  • Sentinel 的限流规则?(阈值类型-QPS&线程数,限流模式-直接,关联,链路)
  • Sentinel 的降级(熔断)策略?(慢调用,异常比例,异常数)
  • Sentinel 的热点规则设计(掌握)?
  • Sentinel 系统规则设计?(了解,全局规则定义,针对所有请求有效)
  • Sentinel 授权规则设计?(掌握,黑白名单)

FAQ分析

  • 为什么要限流?

  • 你了解的那些限流框架?(sentinel)

  • 常用的限流算法有那些?(计数,令牌桶-电影票,漏桶-漏斗,滑动窗口)

  • Sentinel有哪些限流规则?(QPS,线程数)

  • Sentinel有哪些限流模式?(直接,关联-创建订单和查询订单,链路限流-北京六环外不限号,但是五环就限号)

  • Sentinel 的降级(熔断)策略有哪些?(慢调用-响应时长,异常比例-异常占比,异常数)

  • Sentinel 的热点规则中的热点数据?(热卖商品,微博大咖,新上映的电影)

  • 如何理解Sentinel 授权规则中的黑白名单?

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

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

抵扣说明:

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

余额充值