Redis学习 Redis实现购物车核心功能实战(六)

基于Redis购物车需求分析

购物车业务需求

使用用户登录的Cookie记录用户浏览过的商品,即用户意向商品,用来挖掘意向用户

定期对浏览商品的旧记录进行剔除,保留最新的25个购物信息

购物车已存在同类商品,再次加入时商品数量大于0时,只更新购物车;

若用户加入购物车的商品数量为0,则从购物车删除

技术预热

签名Cookie:通常会存储用户名、可能还有用户ID、最后一次登录成功的时间以及网站觉得有用的其他任何信息,同时还包括一个签名,服务器通过验证这个签名来判断Cookie是否被改动过,但很难正确处理签名值,存在安全漏洞。

令牌Cookie:该Cookie里面存储一串随机字符串(token)作为令牌,服务器可以根据token在数据库中查找此令牌的拥有者,随着时间的推移,旧token会被新token取代,如果使用关系数据库来存储,载入与存储token代价很高。

购物车Cookie分解

Redis设计原则

设计原则:对于键的设计,可使用    功能模块名:ID值,用分隔符 : 分开                   比如用户购物车加了哪些商品:  cart:token_1

与时间及排序有关使用ZSET类型 :zadd key timescamp member

与数据库表二维数据相关使用HASH类型:  hset key filed1 value1  filed2 

购物车缓存设计实战

购物车方案设计

1,cookie与用户关系 HASH

2,用户浏览时间及商品ID记录 ZSET

3,商品购物车HASH

4,用户最后一次操作登录的时间记录ZSET

购物车核心业务操作语句

token与用户信息一一对应,故以token贯穿整个业务,假设为token_1:

1,记录token最后一次操作登录的时间      

redis->   zadd recent:info timestamp token;    

java ->   jedis.zadd("recent:in`fo", timestamp, token);

2,记录用户浏览过的商品    

redis->   zadd viewed:token_1 timestamp item;    

java ->   jedis.zadd("viewed:" + token_1, timestamp, item);

3,时间戳升序排列,保留用户最近浏览过的25个商品,删掉第25之后数据    

redis->   zremrangeByRank viewed:token_1 0 -26;    

java ->   jedis.zremrangeByRank("viewed:" + token, 0, -26);

4,将商品item加到购物车,加入的商品数量为count    

redis->   hmset cart:token_1 count item;  

 java ->   jedis.hset("cart:" + token, item, count);

5,从购物车移除商品    

redis->   hdel cart:token_1 item;    

java ->   jedis.hdel("cart:" + token, item);    

代码:实现类:

@Service
public class ShopServiceImpl implements ShopService{


	@Resource
	private JedisUtils jedis;
	/**
	 * 用户新登录后,更新用户的TOKEN值
	 */
	@Override
	public void updateToken(String token, String user, String itemCode) {
        long timestamp = System.currentTimeMillis() / 1000;	//获取当前时间戳1521434935812
        jedis.hset("login:info", token, user); //记录token与已登录用户之间的映射
        jedis.zadd("recent:info", timestamp, token);  //记录token最后一次出现的时间
        if (itemCode != null) { 
        	jedis.zadd("viewed:" + token, timestamp, itemCode);  //记录用户浏览过的商品
            jedis.zremrangeByRank("viewed:" + token, 0, -26); //分数升序排列,删除第0个与第-26——移除旧的记录,只保留用户最近浏览过的25个商品
        }
	}
	
	/**
	 * 尝试获取并返回令牌token对应的用户
	 */
	@Override
	public String checkToken(String token) {
		 return jedis.hget("login:info", token);
	}
	/**
	 * 将商品加入购物车
	 */
	@Override
	public Long addToCart(String token, String item, int count) {
		//count为用户订购此商品的数量,如果用户订购的数量为0,为无效,从购物车移除
        if (count <= 0) {
        	//从购物车移除商品 
            return 0L;
        } else {
        	//将指定的商品加到购物车, cart:token_1将item商品加入购物车,加入的数量为count
            return jedis.hset("cart:" + token, item, String.valueOf(count));
        }
	}

	@Override
	public long hlen(String key) {
		// TODO Auto-generated method stub
		return jedis.hlen(key);
	}

	@Override
	public Map<String, String> hgetAll(String key) {
		// TODO Auto-generated method stub
		return jedis.hgetAll(key);
	} 
	/**
	 * 删除用户旧token
	 */
	@Override
	public boolean removeOldTokens(long limit){
		long size = jedis.zcard("recent:info"); // 查找目前已有令牌token的个数
		if (size <= limit) { // 如果令牌没有超过limit限制个数 3
			return false;
		}

		long endIndex = size - limit; // 最多只保留10个旧令牌,剩余的删除
		Set<String> tokenSet = jedis.zrange("recent:info", 0L, endIndex - 1);// 获取需要移除的令牌ID
		String[] tokens = tokenSet.toArray(new String[tokenSet.size()]);// 将被移除的令牌转成String[]数组
       
		ArrayList<String> sessionKeys = new ArrayList<String>();
		for (String token : tokens) {
			sessionKeys.add("viewed:" + token); // 为即将被移除的令牌构建KEY键名
												// viewed:xxxxxxx01
		}

		jedis.del(sessionKeys.toArray(new String[sessionKeys.size()])); // 移除最旧令牌
		jedis.hdel("login:info", tokens); // 移除登录相关的令牌
		jedis.zrem("recent:info", tokens); // 移除最近令牌
		return true;
	}
} 

测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-mvc.xml")
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class CartShopTest {

	@Resource(name = "shopServiceImpl")
	private ShopService shopService;

	@Test
	public void asLoginTest() throws InterruptedException {
		
		 
		String username = "james";
		String itemCode = null; // 登录时未浏览商品
		System.out.println("登录成功---------");
		// 模拟服务器生成一个令牌token
		String token = UUID.randomUUID().toString();
		
		// 更新token
		shopService.updateToken(token, username, itemCode);
		System.out.println("刚刚更新的token令牌为: " + token+"; 当前操作的用户: "+username);
		
		String user = shopService.checkToken(token);// 尝试获取并返回令牌token对应的用户
		System.out.println(user);
		
		//考虑到redis的存储,使用线程对超出cookie个数的旧cookie从redis清除
		//如果token令牌的数量超过3了,只保留3个tokenid
		shopService.removeOldTokens(3);
		long s = shopService.hlen("login:info");
		System.out.println("查看当前的cookie剩余个数: " + s);
	}
	
	@Test
	public void putToCart() throws InterruptedException {
		//商品加入购物车及购物车商品列表查询
		String username = "james";
		String itemCode = "xmPhone"; // 浏览的商品为Vivo手机
		String token = UUID.randomUUID().toString();

		System.out.println("开始刷新session...");

		// 更新token,因为此用户登录进来,之前的旧token可能失效......
		shopService.updateToken(token, username, itemCode);
	    //假设将vivoPhone  数量为3, 加入购物车
		long i =shopService.addToCart(token, itemCode, 3);
		if(i >0 ){
			System.out.println("加入购物车成功");
		}else{
			System.out.println("加入购物车失败");
		}
		//查看用户james的购物车有哪些商品
		Map<String, String> r = shopService.hgetAll("cart:" + token);
		System.out.println("用户 " + username +" 的购物车里有如下商品:");
		for (Map.Entry<String, String> entry : r.entrySet()) {
			System.out.println("  " + entry.getKey() + ": " + entry.getValue());
		}
	}

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值