Redis lua简单应用

版权声明:嘿嘿嘿 https://blog.csdn.net/luzhensmart/article/details/86519402
package com.jd.gateway.controller;

import com.alibaba.fastjson.JSONObject;
import com.jd.gateway.logger.ILogger;
import com.jd.jim.cli.Cluster;
import com.jd.redis.queue.ObjectUtil;
import com.jd.redis.queue.RedisQueueUtil;
import com.jd.redis.vo.RedisMessage;
import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 测试类
 *
 * @date 2016/10/31
 */
@Controller
@RequestMapping("/testRedisLuaQueueController")
public class TestRedisLuaQueueController implements ILogger {

    ExecutorService pool = Executors.newFixedThreadPool(5);

    @Resource
    private RedisQueueUtil redisQueueUtil;

    @Resource
    private Cluster jimClient;

    private String redisKey = "taskListKey3";


    private void init() throws IOException {
        for (int i = 1; i <= 30; i++) {
            RedisMessage message = new RedisMessage(i, "这是第" + i + "个内容");
            try {
                redisQueueUtil.lpushForRedisQueueString(redisKey, JSONObject.toJSONString(message));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        webLogger.info("hhhhhhhh-----"+jimClient.lRange(redisKey,0,-1));

    }


    /**
     * 不用考虑并发情况,在接口的入口都是有防重表或redis缓存防重的 过了这段逻辑 才会向redis队列push
     */
    @RequestMapping(value = "/testBingFaRedisQueue", method = {RequestMethod.GET, RequestMethod.POST})
    public void testBingFaRedisQueue() {

//        final CountDownLatch down = new CountDownLatch(1);

//        for (int i = 0; i < 5; i++) {
//
//            new Thread(() -> {
//                try {
            try {
                init();
            }catch (Exception e){
                e.printStackTrace();
            }
//                    down.await();
//
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//
//            }).start();

        }
//        if(null != redisQueueUtil.lRangeList(redisKey)) {
//            webLogger.info("长度:"+redisQueueUtil.lRangeList(redisKey).size());
//        }
//        down.countDown();
//    }



}
package com.jd.gateway.init;

import com.alibaba.fastjson.JSONObject;
import com.jd.jim.cli.Cluster;
import com.jd.redis.queue.RedisQueueUtil;

import logger.ILogger;
import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collections;

/**
 * @Author: luzhen
 * @Date: 2019-01-11 14:51
 * @Version 1.0
 */
@Component
public class RedisLuaTaskQueueFactory implements InitializingBean, ILogger {

    @Resource
    private RedisQueueUtil redisQueueUtil;

    @Resource
    private Cluster jimClient;

    public String redisTaskListKey = "taskListKey3";

    //先判断taskListKey3这个list中的长度 是否大于0 如果list长度大于0 rop这个list最后一个元素(因为开始的时候 是lpush进来的) 如果list长度小于0 那么返回0
    private String luaRpopForList = "local tmp = redis.call('LLEN',KEYS[1]) if (tmp>0) then return redis.call('rpop',KEYS[1]) else return 0 end";


    @Override
    public void afterPropertiesSet() throws Exception {
        scheduleLogger.info("RedisLuaTaskQueueFactory-afterPropertiesSet()初始化启动成功!");
        String sha = jimClient.scriptLoad(luaRpopForList);
        new Thread(()->{
            scheduleLogger.info("RedisLuaTaskQueueFactory-创建线程成功!");
            while (true){
                try {
                    //evalsha第二个参数 args 不让空 所以随便传了
                    Object obj = jimClient.evalsha(sha, Collections.singletonList(redisTaskListKey), Collections.singletonList(redisTaskListKey), false);
                    Long len = 0L;
                    if(null != obj && !len.equals(obj)){
                        scheduleLogger.info("RedisLuaTaskQueueFactory消费任务队列数据为:" + StringEscapeUtils.unescapeJava(JSONObject.toJSONString(obj)));

                    }else{
                        Thread.sleep(300);
//                            webLogger.info("RedisLuaTaskQueueFactory-队列无数据!");
                    }
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
    }

}

1 单线程模型

Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会进入一个队列中,然后逐个被执行。并且多个客户端发送的命令的执行顺序是不确定的。但是可以确定的是不会有两条命令被同时执行,不会产生并发问题,这就是Redis的单线程基本模型。

因为redis服务器是单线程的,所以在rpop的时候,假如有10台服务器在并发 发出了evalsha执行该lua脚本的命令,redis服务器在执行lua脚本的时候,会让其他lua脚本或者redis命令等待,直到该lua脚本执行完后,才会执行下一个命令或者lua脚本,所以即使是10台服务器并发,但到了redis服务器就变成了串行。

经测试结果是:10台服务器 瓜分了list里面的数据 进行了消费

猜你喜欢

转载自blog.csdn.net/luzhensmart/article/details/86519402