玖叶教程网

前端编程开发入门

你的秒杀抢购,还可以这么做(秒杀抢购的图片)

之前跟大家提过,redis本身是支持事务性操作的,但是由于其结构原因,不支持回滚操作,即:在失败之前的指令操作无法回滚;其只能保证在一个client发起的事务指令中的命令可以连续执行;

本文将以乐观锁的方式来实现秒杀抢购功能,只是以我自己的方式实现的一个小Demo,当然实际实现过程中不一定这样做,码代码也是有讲究,所谓一通百通,如果有不足的地方,还请见谅;

在秒杀抢购的事件中,具体用到了一下三点:

1、使用watch,采用乐观锁 
2、不使用悲观锁,因为等待时间非常长,响应慢 
3、不使用队列,因为并发量会让队列内存瞬间升高

话不多说,直接开始撸代码;

config:

@Bean
public JedisPool redisPoolFactory(
 @Value("${spring.redis.host}") String redisHost,
 @Value("${spring.redis.port}") int redisPort,
 @Value("${spring.redis.password}") String redisPassword,
 @Value("${spring.redis.database}") int database ,
 @Value("${spring.redis.jedis.pool.max-wait}") int maxWaitMillis,
 @Value("${spring.redis.jedis.pool.max-idle}") int maxIdle,
 @Value("${spring.redis.jedis.pool.max-active}") int maxActive
){
 JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
 jedisPoolConfig.setMaxIdle(maxIdle);
 jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
 jedisPoolConfig.setMaxTotal(maxActive);
 jedisPoolConfig.setMinIdle(0);
 jedisPoolConfig.setMaxIdle(maxIdle);
 JedisPool jedisPool = new JedisPool(jedisPoolConfig,redisHost,redisPort,0,redisPassword);
 return jedisPool;
}
@Bean
public RedisWatchTest redisWatchTest(JedisPool jedisPool){
 return new RedisWatchTest(jedisPool);
}

新建类 RedisWatchTest,执行逻辑

package com.cookie.test;
import com.cookie.util.UUIDUtils;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import java.util.List;
/**
 * author : cxq
 * Date : 2019/8/22
 */
public class RedisWatchTest implements Runnable {
 private JedisPool jedisPool ;
 public RedisWatchTest(JedisPool jedisPool) {
 this.jedisPool = jedisPool ;
 }
 String watchkeys = "watchkeys";// 监视keys
 @Override
 public void run() {
 Jedis jedis = null ;
 try {
 jedis = jedisPool.getResource();
 jedis.watch(watchkeys);// watchkeys
 String val = jedis.get(watchkeys);
 int valint = Integer.valueOf(val);
 String userifo = UUIDUtils.getUUID();
 if (valint < 20) { // 20为总的商品数量,操作这个数量即抢购失败
 Transaction tx = jedis.multi();// 开启事务
 tx.incr("watchkeys");
 List<Object> list = tx.exec();// 提交事务,如果此时watchkeys被改动了,则返回null
 if (list != null) {
 System.out.println("用户:" + userifo + "抢购成功,当前抢购成功人数:"
 + (valint + 1));
 /* 抢购成功业务逻辑 */
 jedis.sadd("setsucc", userifo);
 } else {
 System.out.println("用户:" + userifo + "抢购失败");
 /* 抢购失败业务逻辑 */
 jedis.sadd("setfail", userifo);
 }
 } else {
 System.out.println("用户:" + userifo + "抢购失败");
 jedis.sadd("setfail", userifo);
 // Thread.sleep(500);
 return;
 }
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 jedis.close();
 }
 }
}

写一个调用方法:

@Autowired
private RedisWatchTest redisWatchTest ;
@Autowired
private JedisPool jedisPool ;
@GetMapping("redis")
public TResult<Integer> testRedisWatch(){
 String watchkeys = "watchkeys";
 ExecutorService executor = Executors.newFixedThreadPool(20);
 Jedis jedis = jedisPool.getResource();
 jedis.set(watchkeys, "0");// 重置watchkeys为0
 jedis.del("setsucc", "setfail");// 清空抢成功的,与没有成功的
 jedis.close();
 for (int i = 0; i < 10000; i++) {// 测试一万人同时访问
 executor.execute(redisWatchTest);
 }
 executor.shutdown();
 return new TResult<>(1);
}

控制台输出结果展示(由于10000次太大,这里用了20,分多次调用以便大家好看,展示结果为前两次调用和最后两次调用):

下面是redis控制台命令输出结果,也给大家截图展示下:

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言