Redis多容器高并发场景 , 设置缓存的时候,要考虑多容器加锁的场景。(incr计数和redis分布式锁区别)

1.设置缓存的时候,要考虑多容器加锁的场景。

(1)场景,短信回执场景,会有二次回执的情况,但是我们只处理一次回执的消息体,如何不处理二次回执呢?

// 队列中有数据且容量未达到100,可继续放入队列数据
	if (viberReceiptQueue.size() > 0 && (dlvViberReceiptMap.size() + fldViberReceiptMap.size()) <= 100) {
	while (viberReceiptQueue.size() > 0
	      && (dlvViberReceiptMap.size() + fldViberReceiptMap.size()) <= 100) {
	  ViberReceipt viberReceipt = viberReceiptQueue.take();
	  logger.info("HandlerRunnable viberReceipt take:{}", JSONObject.toJSONString(viberReceipt));
	  if (Objects.isNull(viberReceipt.getPartId())) {
	      MccContact mccContact = mccContactMapper.selectByPrimaryKey(viberReceipt.getMessageId());
	      viberReceipt.setPartId(Long.parseLong(mccContact.getPartId().toString()));
	      // 这里做一个二次回执业务过滤,判断contact表的状态是否已经被修改,如果已经被修改,则不处理
	      if (!CmsStateDef.CONTACT_STATE_CSD.equals(mccContact.getContactState())) {
	          continue;
	      }
	  }
	  //failed 发送失败  pending mmgw发送成功   success 发送成功  ceg一次成功回执pending 但一次失败回执failed 去重
	  if ("failed".equals(viberReceipt.getResult())) {
	      String key = viberReceipt.getMessageId() + "-" + viberReceipt.getResult();
	      String value = RedisUtil.getValue(RedisUtil.FUNTYPE1, key);
	      logger.info("qry FLD cache key:{}, is repeat flag:{}", key, value);
	
	      if (StringUtils.isEmpty(value)) {
	          fldViberReceiptMap.put(viberReceipt.getMessageId(), viberReceipt);
	          RedisUtil.setValue(RedisUtil.FUNTYPE1, key, "2", 3600);
	      }
	      else {
	          RedisUtil.delKey(RedisUtil.FUNTYPE1, key);
	      }
	  }
	  else if ("success".equals(viberReceipt.getResult())) {
	      dlvViberReceiptMap.put(viberReceipt.getMessageId(), viberReceipt);
	  }
	}

这里我们通过RedisUtil.setValue(RedisUtil.FUNTYPE1, key, “2”, 3600);方法。将一次回执之后的key设置到缓存中去,这样二次回执过来的时候,会去查看缓存中是否有值,有值,则一次回执已经处理,该消息体为二次回执。不处理。

(2)上面这种解决方法看起来好像没有什么问题,但是Redis一般都是分布在多容器的,如果两个容器的两个消息同时getValue,这样既取不到数据,并且两条消息都进行了处理,显然与我们要求的不符合。
解决方法:使用RedisUtil的单线程处理方法incr

	// 队列中有数据且容量未达到100,可继续放入队列数据
	if (viberReceiptQueue.size() > 0 && (dlvViberReceiptMap.size() + fldViberReceiptMap.size()) <= 100) {
	    while (viberReceiptQueue.size() > 0
	            && (dlvViberReceiptMap.size() + fldViberReceiptMap.size()) <= 100) {
	        ViberReceipt viberReceipt = viberReceiptQueue.take();
	        logger.info("HandlerRunnable viberReceipt take:{}", JSONObject.toJSONString(viberReceipt));
	        MccContact mccContact = mccContactMapper.selectByPrimaryKey(viberReceipt.getMessageId());
	        viberReceipt.setPartId(Long.parseLong(mccContact.getPartId().toString()));
	        // 这里做一个二次回执业务过滤,判断contact表的状态是否已经被修改,如果已经被修改,则不处理
	        if (!CmsStateDef.CONTACT_STATE_CSD.equals(mccContact.getContactState())) {
	            continue;
	        }
	
	        //failed 发送失败  pending mmgw发送成功   success 发送成功  ceg一次成功回执pending 但一次失败回执failed 去重
	        if ("failed".equals(viberReceipt.getResult())) {
	            String key = viberReceipt.getMessageId() + "-" + viberReceipt.getResult();
	            int incr = RedisUtil.incr(RedisUtil.FUNTYPE1, key, 1,  3600l);
	
	            logger.info("qry FLD cache key:{}, is repeat flag:{}", key, incr);
	
	            if (incrValue != incr) {
	                fldViberReceiptMap.put(viberReceipt.getMessageId(), viberReceipt);
	            }
	        }
	        else if ("success".equals(viberReceipt.getResult())) {
	            dlvViberReceiptMap.put(viberReceipt.getMessageId(), viberReceipt);
	        }
	    }
(3)为什么使用incr:(自增计数器)

1.Redis自增key 的好处

  1. 原子性(atomicity):一个事务是一个不可分割的最小工作单位,事务中包括的诸操作要么都做,要么都不做。
  2. Redis所有单个命令的执行都是原子性的,这与它的单线程机制有关;
  3. Redis命令的原子性使得我们不用考虑并发问题,可以方便的利用原子性自增操作
(4)为什么不使用加锁的方式(不选择Redis锁的原因:)
  1. 它获取锁的方式简单粗暴,获取不到锁直接不断尝试获取锁,比较消耗性能。
  2. 另外来说的话,Redis 的设计定位决定了它的数据并不是强一致性的,在某些极端情况下,可能会出现问题。锁的模型不够健壮。
  3. 即便使用 Redlock 算法来实现,在某些复杂场景下,也无法保证其实现 100% 没有问题,关于 Redlock 的讨论可以看 How to do distributed locking。
    Redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。
(5)其他使用场景:。

限流可以使用redis原子计数器incr.但是除了限流以外,很多系统会在一些节日的时候搞一些活动,当然,这些活动是有奖品的,并且奖品的数量也是有限的。为了防止在高并发的时候,出现多个人中奖的情况,那么可以使用分布式锁,比如redis的分布式锁,zookeeper的分布式锁。当然,我们也可以采用简单一点的方案,就是使用redis原子计数器incr来统计。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值