博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
redis+lua实现高并发商品秒杀案例
阅读量:4029 次
发布时间:2019-05-24

本文共 6312 字,大约阅读时间需要 21 分钟。

如果多redis服务节点不建议使用此方法。多节点可以用redLock实现分布式锁实现

用redis+lua不会出现商品卖超,减库存问题,不用加锁,只需要在lua脚本中把业务写好,一切都是这么简单。redis的好处就是多路io复用,基于内存。存储快速。redis+Lua脚本1、减少网络开销,如果一个功能需要多次请求redis,使用脚本完成同样的操作只需要请求一次,减少了网络往返2、原子操作,redis会将lua脚本作为一个整体执行,中间不会被其他命令插入,无需担心竞态条件,无需使用事务3、复用,客户端发送的脚本会永久存储在redis中,其他客户端可以复用这一脚本我用的是单体服务进行跑批。999个商品一万个线程。用了一分钟(redis+lua执行时间+异步订单写入数据库)tomcat服务拒绝3500次(单体),tomcat服务吐吞量在400/s,如果真正意义上的秒杀,单体服务肯定是不行的,服务肯定要考虑负载和集群,分布等,此次模拟只是测试。由于高并发的特点是瞬间用户量大,对服务配置要求要高点。我的电脑配置是算差的了。所以真正线上商品秒杀要根据用户量去选型配置。好了今天就说到这里。看下面的代码吧。有问题可以随时撩我。只要我在线1、定义lua脚本local productId = tostring(KEYS[1])local uid = tostring(ARGV[1])-- 成功函数local function successFun(success, msg, data)    success = success or 1    msg = msg or ""    data = data or {}    return cjson.encode({success = success, msg = msg, data = data})end-- 错误函数local function response(errno, msg, data)    errno = errno or 0    msg = msg or ""    data = data or {}    return cjson.encode({errno = errno, msg = msg, data = data})end-- 判断用户没有抢过该商品local log_key = "LOG_{" .. productId .. "}"-- return log_keylocal has_fetched = redis.call("sIsMember", log_key, uid)if (has_fetched ~= 0) then    return response(-1, "已经抢过该商品了")endlocal result = false-- 遍历商品所有批次local quan_key = "QUAN_{" .. productId .. "}"local param = productId.."@";local product = redis.call("hgetall",param)if product==nil then   return response(-1, "商品数据不存在")endlocal nums = redis.call("hget",param,"num");local n = tonumber(nums);if (n<=0)then  return response(-1, "暂无库存")endredis.call("sAdd", log_key, uid)local num = n-1;local json = {};json["id"] = productId;json["num"] = n;result = {uid = uid, pid = productId,  product = json}--把人员订单信息写入redisredis.call("rPush", "DB_QUEUE", cjson.encode(result))---修改库存redis.call("hset", param, "num",(num))redis.call('rPush',"user",cjson.encode(result))if (result == false) then    return response(-1, "商品已抢完")else    return successFun(1, "秒杀成功", result)end

 

2、封装redis工具

package com.bus.utils;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.core.io.ClassPathResource;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;import org.springframework.scripting.support.ResourceScriptSource;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;import javax.annotation.Resource;import java.io.Serializable;import java.util.Collections;import java.util.List;/** * @author wwz * @date 2020-03-06 * @descrption: */@Componentpublic class RedisUtils {    private final  Logger log = LoggerFactory.getLogger(this.getClass());    private final  String expireTime = "50000";    @SuppressWarnings("rawtypes")    @Autowired    private StringRedisTemplate stringRedisTemplateDemo;    private DefaultRedisScript
getLockRedisScript; private DefaultRedisScript
releaseLockRedisScript; private DefaultRedisScript
realRedisScript; private StringRedisSerializer argsStringSerializer = new StringRedisSerializer(); private StringRedisSerializer resultStringSerializer = new StringRedisSerializer(); private StringRedisSerializer realStringSerializer = new StringRedisSerializer(); private final String EXEC_RESULT = "1"; @SuppressWarnings("unchecked") @PostConstruct private void init() { getLockRedisScript = new DefaultRedisScript
(); getLockRedisScript.setResultType(String.class); releaseLockRedisScript = new DefaultRedisScript
(); realRedisScript = new DefaultRedisScript
(); releaseLockRedisScript.setResultType(String.class); realRedisScript.setResultType(String.class); // 初始化装载 lua 脚本 getLockRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/getLock.lua"))); releaseLockRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/releaseLock.lua"))); realRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/real.lua"))); } /** * 原子操作 * @param key * @param requestId * @param retryTimes * @return */ public JSONObject set(String key, String requestId, int retryTimes) { try { int count = 0; while (true) { String result = stringRedisTemplateDemo.execute(realRedisScript, argsStringSerializer, realStringSerializer, Collections.singletonList(key), requestId); JSONObject object = JSON.parseObject(result); log.debug("result:{},type:{}", result, result.getClass().getName()); return object; } } catch (Exception e) { e.printStackTrace(); } return null; } public boolean get(String key, String requestId) { String result = stringRedisTemplateDemo.execute(releaseLockRedisScript, argsStringSerializer, resultStringSerializer, Collections.singletonList(key), requestId); if (EXEC_RESULT.equals(result)) { return true; } return false; }}

 

控制层调用

@RequestMapping("buy")@ResponseBodypublic Object orderBy(String productId){    String requestId = UUID.randomUUID().toString();    try{        //Executors.newFixedThreadPool()        JSONObject object = redisUtils.set(productId,requestId,0);        if(object == null){            return JsonResult.Fail("服务中断,请稍后重试!");        }        String success = object.getString("success");        if("1".equals(success)){            taskExecutor.execute(new Runnable() {                @Override                public void run() {                    orderService.createOrder(object.getJSONObject("data"),requestId);                }            });            return JsonResult.OK("恭喜你抢到了");        }        return JsonResult.Fail(object.getString("msg"));    }catch (Exception e){        e.printStackTrace();        return JsonResult.Fail("服务中断,请稍后重试!");    }}

转载地址:http://ylvbi.baihongyu.com/

你可能感兴趣的文章
linux 驱动开发 头文件
查看>>
/etc/resolv.conf
查看>>
container_of()传入结构体中的成员,返回该结构体的首地址
查看>>
linux sfdisk partition
查看>>
ipconfig,ifconfig,iwconfig
查看>>
opensuse12.2 PL2303 minicom
查看>>
电平触发方式和边沿触发的区别
查看>>
网络视频服务器移植
查看>>
Encoding Schemes
查看>>
移植QT
查看>>
如此调用
查看>>
计算机的发展史
查看>>
带WiringPi库的交叉编译如何处理一
查看>>
带WiringPi库的交叉笔译如何处理二之软链接概念
查看>>
Spring事务的七种传播行为
查看>>
ES写入找不到主节点问题排查
查看>>
Java8 HashMap集合解析
查看>>
ArrayList集合解析
查看>>
欢迎使用CSDN-markdown编辑器
查看>>
Android计算器实现源码分析
查看>>