SpringCloud Netflix集成Sentinel限流

目录

一、摘要

1.1、sentinel介绍

1.2、sentinel特征

二、学习目标

三、实施步骤

3.1、新建项目product-server。

        3.1.1、New->File->Project->Spring Initializr

        3.1.2、选择依赖的jar(product-server选择lombok、spring-boot-starter-web、spring-cloud-starter-netflix-eureka-client):

        3.1.3、知识拓展

3.2、同样的步骤,新建order-server服务模块;其中order-server需要依赖openFeign。

3.3、实现产品查询接口

3.4、order-server使用feign调用product-server查询接口.

3.5、启动product-server和order-server服务。

3.6、下载sentinel并启动。

3.7、下载jemeter压测,下载地址:Apache JMeter - Apache JMeter™

3.8、在sentinel面板设置限流策略

3.9、QPS限流——我们设置query/{id}接口qps为8个请求/秒。

3.10、并发线程数限流——设置成每秒5个请求。

四、sentinel限流原理

4.1、sentinel如何拦截feign接口

4.2、四种限流原理

计数器限流算法

滑动窗口限流算法

漏桶限流算法

令牌桶限流算法

五、参考文章


一、摘要

1.1、sentinel介绍

Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。官网:Home · alibaba/Sentinel Wiki · GitHub

2012年,Sentinel诞生于阿里巴巴,其主要目标是流量控制。2013-2017年,Sentinel迅速发展,并成为阿里巴巴所有微服务的基本组成部分。 它已在6000多个应用程序中使用,涵盖了几乎所有核心电子商务场景。2018年,Sentinel演变为一个开源项目。2020年,Sentinel Golang发布。

1.2、sentinel特征

丰富的应用场景 :Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即
突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控 :Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机
器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态 :Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring
Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。

完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel的生态圈

二、学习目标

2.1、完成springcloud netflix框架搭建

2.2、完成springCloud与Sentinel的集成

2.3、使用Jemeter测试限流功能

2.4、总结Sentinel限流原理

3.5、服务有:一个集群的eureka服务(部署两个)、一个产品服务product-server、一个订单服务order-server、还有sentinel服务。其中eureka服务部署请看Eureka Server集群服务搭建_DayDayUp的博客-CSDN博客

3.6、各框架版本

框架版本注意
spring-boot2.5.5版本如果不兼容会出现意想不到的问题。
spring-cloud-starter-alibaba-sentinel2021.1
spring-cloud2020.0.4

三、实施步骤

3.1、新建项目product-server。

        3.1.1、New->File->Project->Spring Initializr

 输入项目名及选择配置。

        3.1.2、选择依赖的jar(product-server选择lombok、spring-boot-starter-web、spring-cloud-starter-netflix-eureka-client):

        3.1.3、知识拓展

  1. Developer tools->勾选Lombok, 作用:Lombok 提供了一些注解来帮助我们简化消除一些必须有但显得臃肿的 java 代码,如 gettingsetting、toString、equals等.
  2. Web->勾选Spring web,作用:Spring-boot-starter-web->提供servlet容器——tomcat、webmvc(restFul api)、spring核心jar、spring的bean装载jar、log日志jar等依赖(没有它,无法启动项目).
  3. Spring Cloud Discovery->勾选Eureka Discovery client: 注册中心eureka的客户端的依赖jar.
  4. Spring Cloud Routing->勾选OpenFeign,作用:Feign接口依赖的jar(feign自动依赖ribbon和hystrix相关jar).

3.2、同样的步骤,新建order-server服务模块;其中order-server需要依赖openFeign。

3.3、实现产品查询接口

        3.3.1、新建基本类请求对象ProductResponse和响应对象Response。

package com.example.product.response;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.math.BigDecimal;

@Data
@AllArgsConstructor
public class ProductResponse {


    private Integer id;

    private String name;

    private Integer num;

    private BigDecimal price;

}
package com.example.product.response;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class Response<T>{

    private Integer errorCode;

    private String errorMsg;

    private T data;

    public Response(Integer errorCode, String errorMsg, T data) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
        this.data = data;
    }

    public static <T> Response<T> success(T data){
        return new Response<>(null, null, data);
    }


}

           3.3.2、product-server的application.yml配置。

spring:
  application:
    name: product-server
server:
  port: 8081
  servlet:
    context-path: /product
#配置注册eureka地址(带用户名和密码)
eureka:
  client:
    serviceUrl:
      defaultZone: http://eureka:eureka@localhost:8671/eureka,http://eureka:eureka@localhost:8672/eureka

        3.3.3、 product-server的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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>product-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>product-server</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.4</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

        3.3.4、产品查询接口简单实现

package com.example.product.controller;

import com.example.product.response.ProductResponse;
import com.example.product.response.Response;
import com.example.product.service.ProductService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class ProductController {

    @Resource
    private ProductService productService;

    @RequestMapping(value = "/select/{id}")
    public Response<ProductResponse> selectById(@PathVariable Integer id){
        return Response.success(productService.queryById(id));
    }

}
package com.example.product.service;

import com.example.product.response.ProductResponse;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Service
public class ProductService {

    private static Map<Integer, ProductResponse> productHashMap = new HashMap<>();
    static {
        productHashMap.put(1, new ProductResponse(1, "冰箱", 5, new BigDecimal(20000)));
        productHashMap.put(2, new ProductResponse(2, "空调", 9, new BigDecimal(30000)));
        productHashMap.put(3, new ProductResponse(3, "洗衣机", 8, new BigDecimal(5000)));
    }

    public ProductResponse queryById(Integer id){
        return productHashMap.get(id);
    }
}

3.4、order-server使用feign调用product-server查询接口.

        3.4.1、新建基本类请求对象ProductResponse和响应对象Response(可以做成jar依赖order)。

package com.example.order.response;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.math.BigDecimal;

@Data
@AllArgsConstructor
public class ProductResponse{

    private Integer id;

    private String name;

    private Integer num;

    private BigDecimal price;

}
package com.example.order.response;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class Response<T>{

    private Integer errorCode;

    private String errorMsg;

    private T data;

    public Response(Integer errorCode, String errorMsg, T data) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
        this.data = data;
    }

    public static <T> Response<T> success(T data){
        return new Response<>(null, null, data);
    }


}

        3.4.2、feign接口实现

package com.example.order.feign;


import com.example.order.response.ProductResponse;
import com.example.order.response.Response;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "product-server", path = "/product", fallbackFactory = ProductMicroServerFallbackFactory.class)
public interface ProductMicroServer {

    @GetMapping(value = "/select/{id}")
    Response<ProductResponse> selectById(@PathVariable Integer id);
}
package com.example.order.feign;

import com.example.order.response.ProductResponse;
import com.example.order.response.Response;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

@Service
public class ProductMicroServerFallback implements ProductMicroServer{
    @Override
    public Response<ProductResponse> selectById(Integer id) {
        return Response.success(new ProductResponse(0, "棒棒糖(兜底商品)", 1, new BigDecimal(0.5)));
    }
}
package com.example.order.feign;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Slf4j
@Service
public class ProductMicroServerFallbackFactory implements FallbackFactory<ProductMicroServer> {

    @Resource
    private ProductMicroServerFallback productMicroServerFallback;

    @Override
    public ProductMicroServer create(Throwable cause) {
        log.error("ProductMicroServerFallback->selectById(Integer id) exception:", cause);
        return productMicroServerFallback;
    }
}

 启动类添加激活feign接口注解并指定扫描包。

package com.example.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients(value = "com.example.order.feign")
@SpringBootApplication
public class OrderServerApplication {

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

}

    3.4.3、application.yml和pom.xml配置

        application.yml添加sentinel配置及支持。

spring:
  application:
    name: order-server
  cloud:
    sentinel:
      transport:
        #指定控制台交流的端口,随意指定一个未使用的端口即可
        port: 8719
        #sentinel dashboard 地址
        dashboard: localhost:8080
server:
  port: 8082
  servlet:
    context-path: /order

#Sentinel 对 Feign 的支持
feign:
  sentinel:
    enabled: true

#配置注册eureka地址(带用户名和密码)
eureka:
  client:
    serviceUrl:
      defaultZone: http://eureka:eureka@localhost:8671/eureka

 pom.xml添加sentinel的jar依赖。

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>order-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order-server</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.4</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2021.1</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.5、启动product-server和order-server服务。

        3.5.1、启动后并查看是否注册到eureka。

         3.5.2、访问order-server查询接口,检测feign服务是调用成功:http://localhost:8082/order/query/1

 

3.6、下载sentinel并启动。

        3.6.1、下载地址:控制台 · alibaba/Sentinel Wiki · GitHub

        3.6.2、启动sentinel

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

启动日志:

 浏览器输入sentinel启动时指定端口号8080,http://localhost:8080, 如下(默认登录密码为sentinel)。

成功访问一次product查询商品接口后再次刷新sentinel面板,出现order-server表示集成生效。

3.7、下载jemeter压测,下载地址:Apache JMeter - Apache JMeter™

        3.7.1、运行bin\jemeter.bat文件启动jemeter(可以选择中文操作界面)。

         3.7.2、添加线程组测试。

        3.7.3、添加http请求。

        3.7.4、添加结果数查看。

         3.7.5、添加http接口配置

        3.7.6、设置线程并发数。

3.8、在sentinel面板设置限流策略

        3.8.1、在sentinel面板找到指定的接口并设置限流策略。

3.9、QPS限流——我们设置query/{id}接口qps为8个请求/秒。

 启动jemeter压测。  

 启动后查看结果数,发现sentinel的qps限流生效。

 查看sentinel的限流数据及监控。

3.10、并发线程数限流——设置成每秒5个请求。

sentinel并发线程数限流设置。

 jemeter压测线程数设置成每秒300个线程(我电脑是4核)。

 压测结果:

 

四、sentinel限流原理

4.1、sentinel如何拦截feign接口

sentinel跟 hystrix 一样实现自定义的java.lang.reflect.InvocationHandler 接口 SentinelInvocationHandler 用来处理方法的调用。

4.2、四种限流原理

  • 计数器限流算法

        计数器是限流中最简单的,规定为:在指定周期内累加访问次数,当访问次数达到设定的阈值时,出发限流策略,当进入下一个时间周期时会将访问次数清零。

        优点:实现简单
        临界问题:如图所示,当在8-10秒和10-12秒内分别并发500,虽然没有超过阈值,但如果算8-12秒,则并发数高达1000,已经超过了原先定义的10秒内不超过500的并发量。

  • 滑动窗口限流算法

        为了避免计数器中的临界问题,让限制更加平滑,将固定窗口中分割出多个小时间窗口,分别在每个小的时间窗口中记录访问次数,然后根据时间将窗口往前滑动并删除过期的小时间窗口。

        优点:实现相对简单,且没有计数器算法的临界问题
        缺点:无法应对短时间高并发(突刺现象)

  • 漏桶限流算法

        漏桶限流算法的核心就是, 不管上面的水流速度有多块, 漏桶水滴的流出速度始终保持不变

        实际应用:消息中间件采用的就是漏桶限流的思想

        主要作用:

  1. 控制数据注入网络的速度
  2. 平滑网络上的突发流量(类似于电容整流)

        不足:无法应对突发的并发流量,因为流出速率一直都是恒定的

  • 令牌桶限流算法

        令牌桶是网络流量整形(Traffic Shaping)速率限制(Rate Limiting)中最常使用的一种算法。速度恒定、令牌桶大小固定,如果令牌桶被填满,则会丢弃生成的令牌,如果桶内没有令牌则出现限流策略。

        优点:可以像漏桶那样匀速,也可以像计数器那样突发处理。

五、参考文章

1、控制台 · alibaba/Sentinel Wiki · GitHub

2、常用的四种限流算法图解_July的博客-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值