03.服务限流实现方案

Sentinel概述

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来帮助您保障微服务的稳定性。

03.服务限流实现方案_第1张图片

gitee:https://gitee.com/rmlb/Sentinel/

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

1.服务保护策略有哪些?
2.服务限流框架有哪些
3.服务限流算法有哪些?
4.令牌桶、漏桶、滑动窗口
5.RateLimiter Api接口接口限流
6.Nginx实现接口限流
7.Getway实现API接口限流
8.纯手写封装自己的微服务限流框架

服务保护策略

服务限流、降级、熔断、隔离策略

服务限流

服务限流概念:限定固定请求数量 访问服务器端 保护服务接口

限流框架:1.nginx限流 2.谷歌 Guava限流 3.阿里巴巴 Sentinel限流 4.Redis+lua实现限流

本质限流框架底层 都是基于 漏桶、令牌桶、滑动窗口算法实现。

补充概念:qps为1 表达意思就是 每s最多只能够处理1个请求

限流算法

漏桶算法

令牌桶算法

03.服务限流实现方案_第2张图片

令牌桶的算法:

1.创建一个令牌桶 它的容量假设可以存放2个token;

2.以恒定的速度,生成令牌 如果令牌桶满了,则直接废弃 如果令牌桶

没有满 则存入到令牌桶中;

3.客户端请求过来,从令牌桶中获取令牌,如果能够获取到令牌就可以执行业务

代码,如果获取不到令牌就拒绝该请求。

滑动窗口法

Guava限流

Google的Guava工具包中就提供了一个限流工具类——RateLimiter,RateLimiter是基于“令牌通算法”来实现限流的。

RateLimiter实现接口限流

1.maven依赖

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

2.相关代码

   /**
     * 限流 qps 1  每s 只会向 令牌桶中 投递一个token
     */
    private RateLimiter rateLimiter = RateLimiter.create(1);

    /**
     * 根据RateLimiter 限流
     *
     * @return
     */
    @RequestMapping("/getMayikt")
    public String getMayikt() {
        if (!rateLimiter.tryAcquire()) {
            log.info("<接口被限流了>");
            return "您访问次数过多,请稍后重试!";
        }
        // 正常执行业务逻辑
        return "getMayikt";
    }

基于RateLimiter封装限流框架

1.自定义注解

package com.mayikt.service.ext;

import java.lang.annotation.*;

/**
 * @author 余胜军
 * @ClassName MayiktRateLimiter
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MayiktRateLimiter {
    /**
     * 默认QPS是1 每s 向 令牌桶投递 1s 令牌
     *
     * @return
     */
    double aqs() default 1;

    /**
     * 接口限流的名称
     *
     * @return
     */
    String name() default "";

    /**
     * 限流之后报错提示
     *
     * @return
     */
    String msg() default "当前访问人数过多,请稍后重试!";
}

2.aop拦截自定义注解

import com.google.common.util.concurrent.RateLimiter;
import com.mayikt.service.ext.MayiktRateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 余胜军
 * @ClassName AopRateLimiter
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
@Aspect
@Component
@Slf4j
public class AopRateLimiter {
    /**
     * 定义RateLimiter 限流集合容器
     */
    private static ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();

    /**
     * 环绕通知
     *
     * @param pjp
     * @return
     */
    @Around(value = "@annotation(com.mayikt.service.ext.MayiktRateLimiter)")
    public Object currentLimit(ProceedingJoinPoint pjp) throws Throwable {
        // 1.获取拦截到目标方法
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 2.获取目标方法
        Method method = methodSignature.getMethod();
        // 3.获取目标方法上的注解
        MayiktRateLimiter mayiktRateLimiter = method.getDeclaredAnnotation(MayiktRateLimiter.class);
        // 4.获取到请求方法名称
        String requestMethodName = method.getName();
        // 5.从容器查找该 请求方法 对应的 rateLimiter 是否有创建 如果没有创建 就创建
        RateLimiter rateLimiter = rateLimiters.get(requestMethodName);
        if (rateLimiter == null) {
            // 防止多个线程同时创建RateLimiter 使用双重检验锁 判断
            synchronized (this) {
                if (rateLimiter == null) {
                    // 获取注解上mayiktRateLimiter.aqs参数值
                    rateLimiter = RateLimiter.create(mayiktRateLimiter.aqs());
                    rateLimiters.put(requestMethodName, rateLimiter);
                }
            }
        }
        try {
            // 6.判断是否被限流 如果是被限流 则 获取mayiktRateLimiter注解中的 msg 返回
            if (!rateLimiter.tryAcquire()) {
                return mayiktRateLimiter.msg();
            }
        } catch (Exception e) {
            log.error("", e);
        }
        // 没有被限流 则直接放行
        return pjp.proceed();
    }
}

3.限流框架使用案例

    @RequestMapping("/getMeite")
    @MayiktRateLimiter(aqs = 2, msg = "当前接口已经被限流")
    public String getMeite() {
        return "getMayikt";
    }

Nginx限流

蚂蚁课堂 nginx 1个ip 过来 访问 nginx 1s 10次

根据ip来做限流。

Nginx接入层限流可以使用Nginx自带的两个模块:

1.连接数限流模块ngx_http_limit_conn_module

2.漏桶算法实现的请求限流模块ngx_http_limit_req_module

limit_req_zone b i n a r y r e m o t e a d d r z o n e = o n e : 10 m r a t e = 1 r / s ; 第 一 个 参 数 : binary_remote_addr zone=one:10m rate=1r/s; 第一个参数: binaryremoteaddrzone=one:10mrate=1r/s;binary_remote_addr 表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址。
第二个参数:zone=one:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息。
第三个参数:rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次
配置演示:

upstream backserver {
		server 127.0.0.1:22222;
	}
    limit_req_zone $binary_remote_addr zone=myRateLimit:10m rate=1r/s;
    server {
        listen       80;
        server_name  localhost;
        location /{
         limit_req zone=myRateLimit;		
		 proxy_pass http://backserver/;
		 proxy_set_header   Host             $host;
		 proxy_set_header   X-Real-IP        $remote_addr;						
		 proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
         index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

    }
  1. 在html 目录下创建一个

50x.html

<html>

<head>
    <meta charset="utf-8"/>

</head>
<body>
 您当前访问人数频率过高,请稍后重试!
</body>

03.服务限流实现方案_第3张图片

Getway限流

1.配置文件内容

server:
  port: 81
####服务网关名称
spring:
  application:
    name: mayikt-gateway
  cloud:
    gateway:
      discovery:
        locator:
          ####开启以服务id去注册中心上获取转发地址
          enabled: true
          ###路由策略
      routes:
        ###路由id
        - id: mayikt-member
          #### 基于lb负载均衡形式转发
          uri: lb://mayikt-member
          filters:
            - StripPrefix=1
          ###匹配规则
          predicates:
            - Path=/mayikt-member/**
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

2.定义过滤器

import com.google.common.util.concurrent.RateLimiter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @author 余胜军
 * @ClassName RateLimiter
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
@Component
public class RateLimiterFilter implements GlobalFilter {
    /**
     * 限流 qps 1  每s 只会向 令牌桶中 投递一个token
     */
    private RateLimiter rateLimiter = RateLimiter.create(1);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        boolean result = rateLimiter.tryAcquire();
        if (!result) {
            ServerHttpResponse response = exchange.getResponse();
            String msg = "Interface access frequency is too high, please try again later";
            DataBuffer buffer = response.bufferFactory().wrap(msg.getBytes());
            return response.writeWith(Mono.just(buffer));
        }
        // 放行请求
        return chain.filter(exchange.mutate().build());
    }
}

3.通过网关访问接口:

http://127.0.0.1:81/mayikt-member/getGatewayLimiter

Redis限流

Sentinel限流

服务降级

服务熔断

服务隔离

Sentinel环境搭建

你可能感兴趣的:(每特教育第十期,springboot,java,spring,boot)