實現商品秒殺之——Redis Watch機制

Watch命令

       watch命令可以監控一個或多個鍵,一旦其中有一個鍵被修改或刪除,之後的事務就不會執行。監控一直持續到exec命令,事務中的命令是在exec之後才執行的,所以在multi命令後可以修改watch監控的鍵值。假設我們通過watch命令在事務執行之前監控了多個Key,這時在watch之後有任何Key的值發生了變化,exec命令執行的事務都將被放棄。

在業務中的應用

1.創建Redis操作工具類

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
 
@Service
public class RedisService {
	
	@Autowired
	private JedisPool jedisPool;
	
	/**
	 * 獲取對象
	 */
	public <T> T get(String key,Class<T> clazz){
	    Jedis jedis = null;
	    try {
		jedis = jedisPool.getResource();
		String sval = jedis.get(key);
		//將String轉換爲Bean
		T t = stringToBean(sval,clazz);
		return t;
	    }finally {
		if(jedis != null) {	
		    jedis.close();
		}
	    }
        }
	
	/**
	 * 設置對象
	 */					
	public <T> boolean set(String key,T value){
	    Jedis jedis = null;
	    try {
	    	jedis = jedisPool.getResource();
		//將Bean轉換爲String
		String s = beanToString(value);
		if(s == null || s.length() <= 0) {
		    return false;
		}
		jedis.set(key, s);
		return true;
	    }finally {
	        if(jedis != null) {	
	    	    jedis.close();
	        }
	    }
        }
	
	/**
	 * 減少值
	 */
	public <T> Long decr(String key){
	    Jedis jedis = null;
	    try {
	    	jedis = jedisPool.getResource();
	    	//返回value減1後的值
	    	return jedis.decr(key);
	    }finally {
	    	if(jedis != null) {	
	    	    jedis.close();
	    	}
	    }
        }
	
	/**
	 * 將字符串轉換爲Bean對象
	 */
	@SuppressWarnings("unchecked")
	public static <T> T stringToBean(String str,Class<T> clazz) {
	    if(str == null || str.length() == 0 || clazz == null) {
	    	return null;
	    }		
	    if(clazz == int.class || clazz == Integer.class) {
	    	return ((T) Integer.valueOf(str));
	    }else if(clazz == String.class) {
	    	return (T) str;
	    }else if(clazz == long.class || clazz == Long.class) {
	    	return (T) Long.valueOf(str);
	    }else if(clazz == List.class) {
	    	return JSON.toJavaObject(JSONArray.parseArray(str), clazz);
	    }else {
	    	return JSON.toJavaObject(JSON.parseObject(str), clazz);
	    }		
        }
	
	/**
	 * 將Bean對象轉換爲字符串類型
	 */
	public static <T> String beanToString(T value) {
	    if(value == null){
	    	return null;
	    }
	    Class<?> clazz = value.getClass();
	    if(clazz == int.class || clazz == Integer.class) {
	    	return ""+value;
	    }else if(clazz == String.class) {
	    	return (String)value;
	    }else if(clazz == long.class || clazz == Long.class) {
	    	return ""+value;
	    }else {
	    	return JSON.toJSONString(value);
	    }		
	}
	
}

2.秒殺業務調用

import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Response;
import redis.clients.jedis.Transaction;
import cn.com.app.redis.RedisService;

@Controller
public class RedisWatchController implements InitializingBean {
	
    @Autowired
    private RedisService redisService;
    @Autowired
    private JedisPool jedisPool;
	
    //內存標記,減少redis訪問
    private HashMap<String, Boolean> localOverMap =  new HashMap<String, Boolean>();
    
	/**
	 * 系統初始化的時把商品庫存加入到緩存中
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
	    //設置默認商品庫存
	    redisService.set("goodsStock", "10");
            //添加內存標記
	    localOverMap.put("goodsStock", false);
	}
    
	/**
         * 請求秒殺,redis watch事務方式
         */
	@RequestMapping(value="/miaoshawatch")
	@ResponseBody
        public String miaoshawatch(HttpServletRequest request,@RequestParam("userid")String userid){
    	    boolean over = localOverMap.get("goodsStock");
    	    if(over) {
    		System.out.println("秒殺結束");
    		return "秒殺結束";
    	    }
    	    Jedis jedis = null;
	    try {
	        jedis = jedisPool.getResource();
		//監視商品庫存key和訂單key
		jedis.watch("goodsStock","order"+userid);
	    	int count = redisService.get("goodsStock",Integer.class);
	    	if(count <= 0){
	    	    localOverMap.put("goodsStock", true);
	    	    System.out.println("庫存不足");
	    	    return "庫存不足";
	    	}
	    	String orderInfo = redisService.get("order"+userid,String.class);
		if(orderInfo != null){
		    System.out.println("重複下單");
		    return "重複下單";
		}
		//開啓事務    
		Transaction transaction = jedis.multi();
		//保存訂單
		transaction.set("order"+userid, userid+"_"+UUID.randomUUID().toString());
	        //扣減庫存
		Response<Long> v = transaction.decrBy("goodsStock",1);
	        List<Object> result = transaction.exec();
		if(result == null || result.isEmpty()){
	            System.out.println("秒殺失敗");
	            return "秒殺失敗";
	        }
	        System.out.println("秒殺成功,剩餘:" + v.get().intValue());
	        /**
        	 * 數據庫操作減少庫存,下訂單,在一個事務中
        	 */        
	}finally {
	    if(jedis != null) {	
		jedis.close();
	    }
	}
	return "秒殺成功";
    }
}

 

發佈了95 篇原創文章 · 獲贊 132 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章