0
点赞
收藏
分享

微信扫一扫

SpringCloud:Gateway之限流、熔断

古月无语 2022-12-07 阅读 153

目录

一、服务雪崩简介及压测实践演示

​编辑

二、sentinel简单模式之流控QPS案例

什么是Sentinel

​ 安装Sentinel控制台

三、sentinel流控简单模式之并发线程数案例

四、sentinel流控之关联模式&链路模式

关联模式

链路模式

五、sentinel降级之平均响应时间&异常比例模式

降级规则

 异常比例

一、服务雪崩简介及压测实践演示

高并发带来的问题

服务雪崩效应

启动端口为8080的商品微服务、订单微服务、用户微服务

注意:启动服务前一定要启动nacos的注册中心

测试是否能正常访问页面

使用压测工具,对请求进行压力测试

 下载解压后

 第一步:修改配置,并启动软件

用高级记事本打开

 

 双击快捷方式

 第二步:添加线程组

 第三步:配置线程并发数

 第四步:添加监听器

 第五步:添加HTTP取样

第六步:配置取样,并启动测试

点击可以放大字体

 启动,此时后台

 可以看见发送了4000个请求 并且没有出错

 修改配置文件中tomcat的并发数

1秒钟20个请求,最大连接数10,最大等待数10,最大线程数2,相当于一个线程1s能处理5个请求(2个处理10个请求)

修改shop-order中的application.yml

spring:
    application:
        name: shop-order
        cloud:
            nacos:
                discovery:
                    server-addr: localhost:8848
server:
    port: 8090
    tomcat:
        max-threads: 2     #最大线程数
        max-connections: 10  #最大连接数
        accept-count: 10     #最大线程等待数

再重新启动一下订单微服务

 此时再看 出现了大量的错误

再将并发数调低一点

全部成功

二、sentinel简单模式之流控QPS案例

什么是Sentinel

在shop-order的pom中添加Sentinel的依赖

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">


    <parent>
        <artifactId>spcloud-shop</artifactId>
        <groupId>com.cdl</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <artifactId>shop-order</artifactId>

    <dependencies>
        <!--springboot-web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--shop-common-->
        <dependency>
            <groupId>com.cdl</groupId>
            <artifactId>shop-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

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

    </dependencies>

</project>

​ 安装Sentinel控制台

1 下载jar包,解压到文件夹

2 启动控制台

解压后会得到一个jar包

# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)

 

 启动完之后就可以访问一下流控的监控平台

 登录成功

修改shop-order ,在里面加入有关控制台的配置

application.yml

spring:
    application:
        name: shop-order
    cloud:
        nacos:
            discovery:
                server-addr: localhost:8848
        sentinel:
            transport:
                port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
                dashboard: localhost:8080 # 指定控制台服务的地址
            web-context-unify: false
server:
    port: 8090
    tomcat:
        max-threads: 2     #最大线程数
        max-connections: 10  #最大连接数
        accept-count: 10     #最大线程等待数

 启动订单的微服务

 设置限流

 测试一下

正常点击订单微服务的页面 此时能访问

 非正常点即快速的不断地刷新

 可见已经受限制了,但是只要正常的再刷就还是能够访问

三、sentinel流控简单模式之并发线程数案例

将之前的流控删除

 此时非正常点击刷新不会有限制

此时需要用压测工具进行测试

 启动压测工具 然后不断的非正常刷新页面

这是直接流控模式

四、sentinel流控之关联模式&链路模式

关联模式

将刚刚的流控删除掉

 OrderCtroller 

package com.cdl.shoporder.Controller;

import com.cdl.model.Order;
import com.cdl.model.Product;
import com.cdl.model.User;
import com.cdl.shoporder.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-25 15:43
 */
@RestController
@RequestMapping("/order")
public class OrderCtroller {

    @Autowired
    private ProductService productService;
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/get/{uid}/{pid}")
    public Order get(@PathVariable("uid") Integer uid,
                     @PathVariable("pid") Integer pid,
                     HttpServletRequest request){

        User user = restTemplate.getForObject("http://shop-user/user/get/" + uid, User.class);
        Product product = productService.get(pid);

        Order order = new Order();
        order.setUsername(user.getUsername());
        order.setUid(user.getUid());
        order.setPprice(product.getPprice());
        order.setPname(product.getPname());
        order.setPid(product.getPid());
        order.setOid(System.currentTimeMillis());
        order.setNumber(product.getStock());
        return order;
    }


    //    流控模式:关联模式
    @RequestMapping("/message1")
    public String message1(){
        System.out.println("message1..................................");
        return "message1....";
    }

    //    sentinel中的熔断降级:平均响应时间
//    预测的结果:平均时间大于0.22s,那么会出现降级处理结果
//    如果平均时间小于0.22s,就正常响应结果
    @RequestMapping("/message2")
    public String message2() {
        try {
//        代表当前方法至少需要执行0.22s
            TimeUnit.MILLISECONDS.sleep(220);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "message2";
    }

}

添加关联模式

修改压测工具

 启动压测工具

 非正常也不受影响

 受影响

链路模式

第1步: 编写一个service,在里面添加一个方法message

OrderServiceImpl2 

package com.cdl.shoporder.service;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class OrderServiceImpl2 {
    @SentinelResource(value = "message", blockHandler = "failBlockHandler")
    public Map message() {
        Map map = new HashMap();
        map.put("code","200");
        map.put("msg","正常响应成功");
    	return map;
    }

    public Map failBlockHandler(BlockException be) {
        Map map = new HashMap();
        map.put("code","-1");
        map.put("msg","接口被限流了...");
        return map;
    }
}

第2步: 在Controller中声明两个方法,分别调用service中的方法message

package com.cdl.shoporder.Controller;

import com.cdl.model.Order;
import com.cdl.model.Product;
import com.cdl.model.User;
import com.cdl.shoporder.service.OrderServiceImpl2;
import com.cdl.shoporder.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-25 15:43
 */
@RestController
@RequestMapping("/order")
public class OrderCtroller {

    @Autowired
    private ProductService productService;
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/get/{uid}/{pid}")
    public Order get(@PathVariable("uid") Integer uid,
                     @PathVariable("pid") Integer pid,
                     HttpServletRequest request){

        User user = restTemplate.getForObject("http://shop-user/user/get/" + uid, User.class);
        Product product = productService.get(pid);

        Order order = new Order();
        order.setUsername(user.getUsername());
        order.setUid(user.getUid());
        order.setPprice(product.getPprice());
        order.setPname(product.getPname());
        order.setPid(product.getPid());
        order.setOid(System.currentTimeMillis());
        order.setNumber(product.getStock());
        return order;
    }


    //    流控模式:关联模式
    @RequestMapping("/message1")
    public String message1(){
        System.out.println("message1..................................");
        return "message1....";
    }


    @Autowired
    private OrderServiceImpl2 orderService;
    //        主要讲解 sentinel 中的 链路流控模式
    @RequestMapping("/message3")
    public Map message3() {
        return orderService.message();
    }
    @RequestMapping("/message4")
    public Map message4() {
        return orderService.message();
    }


}

删除掉之前的关联模式,重启订单的微服务

 

 sentinel控制台

 修改配置application.yml

  第3步: 禁止收敛URL的入口

第4步: 控制台配置限流规则

 

 注意:message3和message4调的是同一个方法

五、sentinel降级之平均响应时间&异常比例模式

降级规则

本质就是一个托底方案

在controller中增加一些代码用以证明

OrderCtroller 

package com.cdl.shoporder.Controller;

import com.cdl.model.Order;
import com.cdl.model.Product;
import com.cdl.model.User;
import com.cdl.shoporder.service.OrderServiceImpl2;
import com.cdl.shoporder.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-25 15:43
 */
@RestController
@RequestMapping("/order")
public class OrderCtroller {

    @Autowired
    private ProductService productService;
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/get/{uid}/{pid}")
    public Order get(@PathVariable("uid") Integer uid,
                     @PathVariable("pid") Integer pid,
                     HttpServletRequest request){

        User user = restTemplate.getForObject("http://shop-user/user/get/" + uid, User.class);
        Product product = productService.get(pid);

        Order order = new Order();
        order.setUsername(user.getUsername());
        order.setUid(user.getUid());
        order.setPprice(product.getPprice());
        order.setPname(product.getPname());
        order.setPid(product.getPid());
        order.setOid(System.currentTimeMillis());
        order.setNumber(product.getStock());
        return order;
    }


    //    流控模式:关联模式
    @RequestMapping("/message1")
    public String message1(){
        System.out.println("message1..................................");
        return "message1....";
    }

    //    sentinel中的熔断降级:平均响应时间
//    预测的结果:平均时间大于0.22s,那么会出现降级处理结果
//    如果平均时间小于0.22s,就正常响应结果
    @RequestMapping("/message2")
    public String message2() {
        try {
//        代表当前方法至少需要执行0.22s
            TimeUnit.MILLISECONDS.sleep(220);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "message2";
    }

    @Autowired
    private OrderServiceImpl2 orderService;
    //        主要讲解 sentinel 中的 链路流控模式
    @RequestMapping("/message3")
    public Map message3() {
        return orderService.message();
    }
    @RequestMapping("/message4")
    public Map message4() {
        return orderService.message();
    }


}

 点击熔断

解释这个配置: 在1秒钟发送5个请求,其中20%的请求时间是大于200ms,那么就做熔断降级处理

修改压测工具并且启动

 刷新被限流

 异常比例

 在controlle中添加一定的代码

package com.cdl.shoporder.Controller;

import com.cdl.model.Order;
import com.cdl.model.Product;
import com.cdl.model.User;
import com.cdl.shoporder.service.OrderServiceImpl2;
import com.cdl.shoporder.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-25 15:43
 */
@RestController
@RequestMapping("/order")
public class OrderCtroller {

    @Autowired
    private ProductService productService;
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/get/{uid}/{pid}")
    public Order get(@PathVariable("uid") Integer uid,
                     @PathVariable("pid") Integer pid,
                     HttpServletRequest request){

        User user = restTemplate.getForObject("http://shop-user/user/get/" + uid, User.class);
        Product product = productService.get(pid);

        Order order = new Order();
        order.setUsername(user.getUsername());
        order.setUid(user.getUid());
        order.setPprice(product.getPprice());
        order.setPname(product.getPname());
        order.setPid(product.getPid());
        order.setOid(System.currentTimeMillis());
        order.setNumber(product.getStock());
        return order;
    }


    //    流控模式:关联模式
    @RequestMapping("/message1")
    public String message1(){
        System.out.println("message1..................................");
        return "message1....";
    }

    //    sentinel中的熔断降级:平均响应时间
//    预测的结果:平均时间大于0.22s,那么会出现降级处理结果
//    如果平均时间小于0.22s,就正常响应结果
    @RequestMapping("/message2")
    public String message2() {
        try {
//        代表当前方法至少需要执行0.22s
            TimeUnit.MILLISECONDS.sleep(220);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "message2";
    }

    @Autowired
    private OrderServiceImpl2 orderService;
    //        主要讲解 sentinel 中的 链路流控模式
    @RequestMapping("/message3")
    public Map message3() {
        return orderService.message();
    }
    @RequestMapping("/message4")
    public Map message4() {
        return orderService.message();
    }

//    //    sentinel中的熔断降级:异常比例
    int i = 0;
    @RequestMapping("/message5")
    public String message5() {
        i++;
        //异常比例为0.333
        if (i % 3 == 0){
            throw new RuntimeException();
        }
        return "message5";
    }
}

 新增熔断

 和之前一样记得修改压测工具并且启动

 当压测工具已经结束运行的时候,再点击刷新就会报页面错误

自定义异常返回

新建一个config的包 直接复制进去

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

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

//异常处理页面
@Component
public class ExceptionHandlerPage implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        response.setContentType("application/json;charset=utf-8");
        ResponseData data = null;
        if (e instanceof FlowException) {
            data = new ResponseData(-1, "接口被限流了...");
        } else if (e instanceof DegradeException) {
            data = new ResponseData(-2, "接口被降级了...");
        }
        response.getWriter().write(JSON.toJSONString(data));
    }
}

@Data
@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
class ResponseData {
    private int code;
    private String message;
}

重新启动订单微服务,新增熔断,启动压测工具

 

 

举报

相关推荐

0 条评论