热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

限流(三)Redis+lua分布式限流

一、简介1)分布式限流如果是单实例项目,我们使用Guava这样的轻便又高性能的堆缓存来处理限流。但是当项目发展为多实例了以后呢?这时候我们就需要采用分布式限流的方式,分布式限流可以

一、简介

1)分布式限流

如果是单实例项目,我们使用Guava这样的轻便又高性能的堆缓存来处理限流。但是当项目发展为多实例了以后呢?这时候我们就需要采用分布式限流的方式,分布式限流可以以redis + lua 或者 nignx + lua这样的组合来实现。。

分布式限流一般应用场景都是在业务上进行限流,所以本文不涉及niginx + lua,简单介绍redis + lua分布式限流的实现。如果是需要在接入层限流的话,应该直接采用nginx自带的连接数限流模块请求限流模块

 

2)redis

redis是一种键值对的单线程架构模型,所以它是线程安全的,也是分布式缓存常用的解决方案。(本文不涉及redis的分布式缓存,只是讲如何结合redis实现限流)

3)lua

lua是基于c语言的一种脚本语言,它可以很轻便地被使用在嵌入式方面。我们不会去重写redis,但是我们可以去使用lua来扩展redis的功能。而redis也内置了对lua支持的模块。

 

二、示例

示例图

限流(三)Redis + lua分布式限流

 

1)启动redis服务

我们得确保redis安装,并使用: ./redis-server 命令启动redis服务端

redis常用命令:

./redis-server 启动服务端
./redis-cli 启动客户端
./redis-cli shutdown 关闭服务
keys * 查看所有key
get 键 根据键获取值

2)编写lua脚本

创建limit.lua文件

local key = KEYS[1] --限流KEY(一秒一个)
local limit = tonumber(ARGV[1]) --限流大小
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then --如果超出限流大小
    return 0
else --请求数+1,并设置2秒过期
    redis.call("INCRBY", key,"1")
    redis.call("expire", key,"1")
    return 1
end

1)我们通过KEYS[1] 获取传入的key参数

2)通过ARGV[1]获取传入的limit参数

3)redis.call方法,从缓存中get和key相关的值,如果为nil那么就返回0

4)接着判断缓存中记录的数值是否会大于限制大小,如果超出表示该被限流,返回0

5)如果未超过,那么该key的缓存值+1,并设置过期时间为1秒钟以后,并返回1

 

3)Java调用

java采用jedis来操作redis

引入redis依赖和apache io的工具包(方便读取lua文件的内容)

 <dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
    <version>2.9.0version>
dependency>

<dependency>
    <groupId>commons-iogroupId>
    <artifactId>commons-ioartifactId>
    <version>2.4version>
dependency>

创建RedisLua.java文件

import org.apache.commons.io.FileUtils;
import redis.clients.jedis.Jedis;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author lay
 * @date 2018/5/22.
 * @time 17:48
 */
public class RedisLua {

    public static void main(String[] args) {
        final CountDownLatch latch = new CountDownLatch(1);

        for (int i = 0; i <7; i++) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        latch.await();
                        System.out.println(accquire());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();

        }

        latch.countDown();
    }

    public static boolean accquire() throws IOException, URISyntaxException {
        Jedis jedis = new Jedis("127.0.0.1");
        File luaFile = new File(RedisLua.class.getResource("/").toURI().getPath() + "limit.lua");
        String luaScript = FileUtils.readFileToString(luaFile);

        String key = "ip:" + System.currentTimeMillis()/1000; // 当前秒
        String limit = "5"; // 最大限制
        List keys = new ArrayList();
        keys.add(key);
        List args = new ArrayList();
        args.add(limit);
        Long result = (Long)(jedis.eval(luaScript, keys, args)); // 执行lua脚本,传入参数
        return result == 1;
    }
}

以上模拟了多线程下,使用jedis连接redis服务,并执行lua脚本

 

4)输出结果

true
true
false
true
false
true
true

我们看到,7个线程,其中2个线程被判断为false

 

参考文章:http://jinnianshilongnian.iteye.com/blog/2305117


推荐阅读
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • 31.项目部署
    目录1一些概念1.1项目部署1.2WSGI1.3uWSGI1.4Nginx2安装环境与迁移项目2.1项目内容2.2项目配置2.2.1DEBUG2.2.2STAT ... [详细]
  • 本文介绍了在Windows系统上使用C语言命令行参数启动程序并传递参数的方法,包括接收参数程序的代码和bat文件的编写方法,同时给出了程序运行的结果。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文讨论了为什么在main.js中写import不会全局生效的问题,并提供了解决方案。在每一个vue文件中都需要写import语句才能使其生效,而在main.js中写import语句则不会全局生效。本文还介绍了使用Swal和sweetalert2库的示例。 ... [详细]
  • svnWebUI:一款现代化的svn服务端管理软件
    svnWebUI是一款图形化管理服务端Subversion的配置工具,适用于非程序员使用。它解决了svn用户和权限配置繁琐且不便的问题,提供了现代化的web界面,让svn服务端管理变得轻松。演示地址:http://svn.nginxwebui.cn:6060。 ... [详细]
author-avatar
忆丨残年_686
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有