Java实现分布式系统限流

为何使用分布式系统限流:

在分布式环境中,我们的系统都是集群化部署,那么使用了单机版的限流策略,比如我们对某一个接口的限流方案是每秒钟最多10次请求,那么因为各个实例都会自己维护一份请求次数,所以真实每秒的请求数是:
节点数 * 每秒最多请求数,这样的话就超出了我们的预期;

分布式限流解决方案:

● 可以基于redis,做分布式限流
● 可以基于nginx做分布式限流
● 可以使用阿里开源的 sentinel 中间件

本次介绍使用 redis 做分布式限流

实现思路:

设计思路:假设一个用户(用IP判断)每分钟访问某一个服务接口的次数不能超过10次,那么我们可以在Redis中根据该用户IP创建一个键,并此时我们就设置这个键的过期时间为60秒,当用户请求到来的时候,先去redis中根据用户ip获取这个用户当前分钟请求了多少次,如果获取不到,则说明这个用户当前分钟第一次访问,就创建这个健,并+1,如果获取到了就判断当前有没有超过我们限制的次数,如果到了我们限制的次数则禁止访问。

使用技术:使用redis提供的:incr命令 实现

先引入redis的依赖:

<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.70</version>
</dependency>

redis配置类:

package org.xhs.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @Author: hu.chen
 * @Description:
 **/
public class RedisConfig {

    // 服务器IP地址
    private static String ADDR = "127.0.0.1";
    // 端口
    private static int PORT = 6379;
    // 密码
    private static String AUTH = null;
    // 连接实例的最大连接数
    private static int MAX_ACTIVE = 1024;
    // 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
    private static int MAX_IDLE = 200;
    // 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException
    private static int MAX_WAIT = 10000;
    // 连接超时的时间
    private static int TIMEOUT = 10000;
    // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
    private static boolean TEST_ON_BORROW = true;

        private static JedisPool jedisPool = null;
    // 数据库模式是16个数据库 0~15
    public static final int DEFAULT_DATABASE = 0;

    /**
     * 初始化Redis连接池
     */

    static {

        try {

            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_ACTIVE);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);
            jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH, DEFAULT_DATABASE);

        } catch (Exception e) {

            e.printStackTrace();
        }
    }

    /**
     * 获取Jedis实例
     */
    public static Jedis getJedis() {
        try {

            if (jedisPool != null) {
                Jedis resource = jedisPool.getResource();
                return resource;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

}

redis工具类:

package org.xhs.redis;

import redis.clients.jedis.Jedis;

/**
 * @Author: hu.chen
 * @Description:
 * @DateTime: 2022/1/21 1:06 PM
 **/
public class RedisUtils {

    /**
     * 将指定的key递增1(可用于乐观锁)
     *
     * @param key
     * @return
     */
    public static Long incr(final String key) {

        Jedis jedis = RedisConfig.getJedis();
        Long  incr = jedis.incr(key);

        returnJedis(jedis);
        return incr;
    }

    /**
     * 给指定key设置过期时间
     *
     * @param key
     * @param seconds
     * @author ruan 2013-4-11
     */
    public static void expire(String key, int seconds) {
        if (seconds <= 0) {
            return;
        }
        Jedis jedis = RedisConfig.getJedis();
        jedis.expire(key, seconds);
        // 将连接还回连接池
        returnJedis(jedis);
    }

    /**
     * 回收jedis
     *
     * @param jedis
     */
    private static void returnJedis(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}

实现:

package org.xhs.redis;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: hu.chen
 * @Description:
 **/
public class TestRedis {
    /**
     * 超时时间(单位秒)
     */
    private static int TIMEOUT = 30;

    /**
     * 每分钟的请求次数限制
     */
    private static int COUNT = 10;

    public static void main(String[] args) {

        List<UserRequest> tasks = new ArrayList();
        // 准备工作,先初始化 10个线程(用户),这10个用户同时访问一个接口
        for (int i = 1; i <= 12; i++) {
            String ip = "127.0.0." + i;
            String userName = "chenhu_";
            String interfaceName = "user/find_" + i;
            tasks.add(new UserRequest(ip, userName, interfaceName));
        }

        for (UserRequest request : tasks) {
            // 以用户名为键
            if (isAccess(request.getUserName(), COUNT)) {
                System.err.println("用户:"+request.getUserName()+" 当前时间访问次数还未达到上限,可以访问");
            } else {
                System.err.println("当前时间访问失败,"+request.getUserName()+"无法获取令牌");
            }

        }
    }

    /**
     * 是否可以访问
     *
     * @return
     */
    private static boolean isAccess(String userName, long count) {
        Long incr = RedisUtils.incr(userName);
        if (incr == 1) {
            RedisUtils.expire(userName, TIMEOUT);
        }
        if (count < incr) {
            return false;
        }
        return true;
    }

    /**
     * 实体对象
     */
    private static class UserRequest {
        /**
         * 请求用户ip
         */
        private String ip;
        /**
         * 用户名
         */
        private String userName;
        /**
         * 请求的接口名
         */
        private String interfaceName;

        public UserRequest(String ip, String userName, String interfaceName) {
            this.ip = ip;
            this.userName = userName;
            this.interfaceName = interfaceName;
        }

        public String getIp() {return ip;}

        public String getUserName() { return userName;}

        public String getInterfaceName() {return interfaceName;}
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • java单机接口限流处理方案详解

    对单机服务做接口限流的处理方案 简单说就是设定某个接口一定时间只接受固定次数的请求,比如/add接口1秒最多接收100次请求,多的直接拒绝,这个问题很常见,场景也好理解,直接上代码: /** * 单机限流 */ @Slf4j public class FlowLimit { //接口限流上限值和限流时间缓存 private static Cache<String, AtomicLong> localCache = CacheBuilder.newBuilder().maximumSize(10

  • Java实现接口限流方案

    本文实例为大家分享了Java实现接口限流方案的具体代码,供大家参考,具体内容如下 RateLimiter Google开源工具包Guava提供了限流工具类RateLimiter,基于令牌桶算法实现. 1.maven依赖: <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.1-jre</versio

  • Java中4种经典限流算法讲解

    目录 限流是什么? 常见的限流算法 固定窗口限流算法 滑动窗口限流算法 漏桶算法 令牌桶算法 最近,我们的业务系统引入了Guava的RateLimiter限流组件,它是基于令牌桶算法实现的,而令牌桶是非常经典的限流算法.本文将跟大家一起学习几种经典的限流算法. 限流是什么? 维基百科的概念如下: In computer networks, rate limiting is used to control the rate of requests sent or received by a net

  • Java使用Semaphore对单接口进行限流

    目录 一.实战说明 1.1 效果说明 1.2 核心知识点 二. 环境搭建 三.限流演示 3.1 并发请求工具 3.2 效果示例图 一.实战说明 1.1 效果说明 本篇主要讲如何使用Semaphore对单接口进行限流,例如有如下场景 a. A系统的有a接口主要给B系统调用,现在希望对B系统进行限流,例如处理峰值在100,超过100的请求快速失败 b. 接口作为总闸入口,希望限制所有外来访问,例如某个房间只能同时100个玩家在线,只有前面的处理完后面的才能继续请求 c. 其他类型场景,也就是资源固定

  • java实现单机限流

    何时使用限流: 比如你希望自己的应用程序 QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里 扔1000个令牌,RateLimiter经常用于限制对一些物理资源或者逻辑资源的访 问速率. 简介: 对于单机版的限流,可以使用Google 开源的 Guava项目,这个项目提供了Google在Java项目中使用一些核心库,包含集合(Collections),缓存(Caching),并发编程库(Concurrency),常用注解(Common annotations)

  • java限流算法详细

    目录 1.场景 2.算法详解 2.1 计数算法 2.1.1 说明 2.1.2 适用场景 2.1.3 代码 2.2 漏桶算法 2.2.1 说明 2.2.2 漏桶算法图示 2.2.3 适用场景 2.2.4 代码 2.3 令牌桶算法 2.3.1 说明 2.3.2 令牌桶算法图示 2.3.3 适用场景 2.3.4 代码 2.3.5 第三方工具类 1.场景 程序中经常需要对接口进行限流,防止访问量太大,导致程序崩溃. 常用的算法有:计数算法.漏桶算法.令牌桶算法,最常用的算法是后面两种. 2.算法详解 2

  • Java实现5种限流算法及7种限流方式

    目录 前言 1. 限流 2. 固定窗口算法 2.1. 代码实现 3. 滑动窗口算法 3.1. 代码实现 4. 滑动日志算法 4.1. 代码实现 5. 漏桶算法 6. 令牌桶算法 6.1. 代码实现 6.2. 思考 7. Redis 分布式限流 7.1. 固定窗口限流 7.3. 滑动窗口限流 8. 总结 参考 前言 最近几年,随着微服务的流行,服务和服务之间的依赖越来越强,调用关系越来越复杂,服务和服务之间的稳定性越来越重要.在遇到突发的请求量激增,恶意的用户访问,亦或请求频率过高给下游服务带来较

  • Java 实现限流器处理Rest接口请求详解流程

    Maven依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.0.1-jre</version> </dependency> 代码 上代码,不废话. 首先是限流器代码. package com.huyi.csdn.tools.rate; import com.google.c

  • Java实现分布式系统限流

    为何使用分布式系统限流: 在分布式环境中,我们的系统都是集群化部署,那么使用了单机版的限流策略,比如我们对某一个接口的限流方案是每秒钟最多10次请求,那么因为各个实例都会自己维护一份请求次数,所以真实每秒的请求数是:节点数 * 每秒最多请求数,这样的话就超出了我们的预期: 分布式限流解决方案: ● 可以基于redis,做分布式限流● 可以基于nginx做分布式限流● 可以使用阿里开源的 sentinel 中间件 本次介绍使用 redis 做分布式限流 实现思路: 设计思路:假设一个用户(用IP判

  • 详解Java分布式IP限流和防止恶意IP攻击方案

    前言 限流是分布式系统设计中经常提到的概念,在某些要求不严格的场景下,使用Guava RateLimiter就可以满足.但是Guava RateLimiter只能应用于单进程,多进程间协同控制便无能为力.本文介绍一种简单的处理方式,用于分布式环境下接口调用频次管控. 如何防止恶意IP攻击某些暴露的接口呢(比如某些场景下短信验证码服务)?本文介绍一种本地缓存和分布式缓存集成方式判断远程IP是否为恶意调用接口的IP. 分布式IP限流 思路是使用redis incr命令,完成一段时间内接口请求次数的统

  • 使用Java实现Redis限流的方法

    1.概述   限流的含义是在单位时间内确保发往某个模块的请求数量小于某个数值,比如在实现秒杀功能时,需要确保在10秒内发往支付模块的请求数量小于500个.限流的作用是防止某个段时间段内的请求数过多,造成模块因高并发而不可用. 2.zset有序集合相关命令与限流   zset也叫有序集合,是Redis的一种数据类型,在其中每个值(value)都会有一个对应的score参数,以此来描述该值的权重分值.可以通过如下形式的命令向zset有序集合里添加元素: zadd key score value   

  • Java 常见的限流算法详细分析并实现

    目录 为什么要限流 限流算法 计数器限流 漏桶限流 令牌桶限流 为什么要限流 在保证可用的情况下尽可能多增加进入的人数,其余的人在排队等待,或者返回友好提示,保证里面的进行系统的用户可以正常使用,防止系统雪崩. 限流算法 限流算法很多,常见的有三类,分别是 计数器算法 .漏桶算法.令牌桶算法 . (1)计数器:           在一段时间间隔内,处理请求的最大数量固定,超过部分不做处理. (2)漏桶:           漏桶大小固定,处理速度固定,但请求进入速度不固定(在突发情况请求过多时

  • 详解5种Java中常见限流算法

    目录 01固定窗口 02滑动窗口 03漏桶算法 04令牌桶 05滑动日志 06分布式限流 07总结 1.瞬时流量过高,服务被压垮? 2.恶意用户高频光顾,导致服务器宕机? 3.消息消费过快,导致数据库压力过大,性能下降甚至崩溃? ...... 在高并发系统中,出于系统保护角度考虑,通常会对流量进行限流:不但在工作中要频繁使用,而且也是面试中的高频考点. 今天我们将图文并茂地对常见的限流算法分别进行介绍,通过各个算法的特点,给出限流算法选型的一些建议,并给出Java语言实现的代码示例. 01固定窗

  • 基于.net的分布式系统限流组件示例详解

    前言 在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可以让整个系统的运行更加平稳.今天要与大家分享一下限流算法和C#版本的组件. 一.令牌桶算法: 令牌桶算法的基本过程如下: 假如用户配置的平均发送速率为r,则每隔1/r秒速率将一个令牌被加入到桶中: 假设桶最多可以存发b个令牌.当桶中的令牌达到上限后,丢弃令牌. 当一个有请求到达时,首先去令牌桶获取令牌,能够取到,则处理这个请求 如

  • Java RateLimiter的限流详解

    目录 限流背景 限流相关概念 服务熔断 服务降级 服务隔离 服务限流 比较 常见的限流方法 限流工具类RateLimiter 总结 限流背景 在早期的计算机领域,限流技术(time limiting)被用做控制网络接口收发通信数据的速率.可以用来优化性能,减少延迟和提高带宽等.现在在互联网领域,也借鉴了这个概念,用来为服务控制请求的速率,如双十一的限流,12306的抢票等.即使在细粒度的软件架构中,也有类似的概念. 系统在使用下游资源时,需要考虑下游对资源受限,处理能力,在下游资源无法或者短时间

随机推荐