版权声明:嘿嘿嘿 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里面的数据 进行了消费