食色性也,抗日电视剧-科创板:一小时带你了解投资新机遇,创新创业创投

高并发核心技能之 - 幂等性

1. 什么是幂等性

幂等性便是指:一个幂等操作任其履行屡次所发生的影响均与一次履行的影响相同。 用数学的概念表达是这样的: f(f(x)) = f(x). 就像 n食色性也,抗日电视剧-科创板:一小时带你了解出资新机遇,立异创业创投x1 = n 相同, x1 便是一个幂等操作。无论是乘以多少次成果都相同。

2. 常见的幂等性问题

幂等性问题常常会是由网络问题引起的,还有重复操作引起的。

场景一:比方点赞功用,一个用户只能对同一片文章点赞一次,重复点赞提示现已点过赞了。

示例代码:

 public void like(Article article,User user) {
//查看是否点过赞
if (ch看护香香公主eckIsLike(article,user)) {
//点过赞了
throw new Ap天菜是什么意思iException(CodeEnums.SYSTEM_ERR);
}
else {
//保存点赞
saveLike(article,user);
}
}

看上去如同没有什么问题,保存点赞之前现已查看过是否点赞了,理论上同一个人不会对同一篇文章重复点赞。但实践不是这样的。由于网络恳求不是排队进来的,而是一窝蜂涌进来的。

某些时分,用户网络欠好,或许很短的时刻内点击了屡次,由于网络传输问题,这些恳求或许会一起来到咱们的服务器。

  • 第一个恳求 checkIsLike() 回来 false , 正在履行 saveLike() 操作,还没来的及提交事务
  • 第二个恳求过来了 ,checkIsLike() 回来 也是 false , 并去 履行了 saveLike() 操作

这姿态,就形成了一个用户一起对一篇文章进行了屡次点赞操作。

这便是典型的幂等性问题, 操作了一次和操作了两次成果不相同,由于你多点了一次赞,依照幂等性准则 不论你点击了多少次成果都相同,只点了一茸毛币次赞。

许多场景都是这样形成的,比方用户重复下单,重复谈论,重复提交表单等。

那怎样处理呢? 假定网络的恳求是排队进来的就不会呈现这个问题了。

所以咱们能够改成这样:

public synchronized void like(Article article,User user) {
//查看是否点过赞
if (checkIsL食色性也,抗日电视剧-科创板:一小时带你了解出资新机遇,立异创业创投ike(article,user)) {
//点过赞了
throw new ApiException(CodeEnums.SYSTEM_ERR);
}
else {
//保存点赞
saveLike(article,user);
}
}

synchronized 同步锁食色性也,抗日电视剧-科创板:一小时带你了解出资新机遇,立异创业创投 这样咱们的恳求就会乖乖的排队进来了。

PS :这样做是功率比较低的做法,不主张sim卡这么做,仅仅举比方,synchronized 也不合适分布式集群场景。

场景二 : 第三方回调

咱们体系常常需求和第三方体系打交道,比方微信食色性也,抗日电视剧-科创板:一小时带你了解出资新机遇,立异创业创投充值,付出宝充值什么的,微信和付出宝常常会以回调你的接口告诉你付出成果。为了确保你能收到回调,往往食色性也,抗日电视剧-科创板:一小时带你了解出资新机遇,立异创业创投或许会回调屡次。

有时分咱们也为了确保数女生写真据的准确性会有个定时器去查询付出成果不知道的流水,并履行呼应的处理。 假如定时器的轮训和回调刚好是在一起进行,这或许又出BUG了,qq拼音输入法又进行了两次重复操作。

那么问题来了: 假邢金喜设我是一个充值操作, 回调回来的时分 ,会做事务处理,成功了给用户账户加钱。这是后就要确保幂等性了, 假定微信同一笔买卖给你回调了两次,假如你给用户充值了两次,这明显不合理(我是老板必定扣你薪酬),所以要确保 不论微信回调你多少次 ,同一笔买卖你只能给用户充一次钱。这就幂等性。

处理幂等性问题计划

  • synchronized 合适单机使用,不寻求功能 ,不寻求并发。
  • 分布式锁 可是往往咱们的使用是分布式的集群,而且很考究功能,并发,所以咱们需求用到 分布式锁 来处理这个食色性也,抗日电视剧-科创板:一小时带你了解出资新机遇,立异创业创投问题。

Redis 分布式锁:

/**
* setNx
*
* @param key
* @param value
* @return李小龙女儿李香凝
*/
public Boolean setNx(String key,Object val仲景艾宝ue) {
return redisTemplate.opsForValue().setIfAbsent(key,value);
}
/**
* @param key 锁
* @param waitTime 等候时刻 毫悍匪重生记秒
* @param expireTime 超时时刻 毫秒
* @return
*/
public Boolean lock(Str心雨ing key,Long waitTime,Long expireTime) {
String vlaue = UUIDUtil.mongoObjectId();
Boolean flag = setNx(key,vlaue);
//测验获取锁 成功回来
if (flag) {
redisTemplate.expire(key,expireTime,TimeUnit.MILLISECONDS);
return flag;
}
else {
//失利
//现在时刻
long newTime = System.currentTimeMillis();
//等候过期时刻
long鬼侵略 loseTime = newTime + waitTime;
//不断测验获取锁成功回来
while (System.currentTimeMillis() < loseTime) {
Boolean testFlag = setNx(key,vlaue);
if (testFlag) {
redisTemplate.expire(key,expireTime,TimeUnit.MILLISECONDS);
r我国外运eturn testFlag;
}
//休眠100毫秒
try {
Thread.sleep(100);
}
catch (InterruptedEx鼻血栓ception e) {
e.printStackTrace();
}
}}return false;}/**
* @param key
* @return
*/
public Boolean 天天撸影院lock(String key) {
return lock(key,1000L,60 * 1000L);
}
/**
* @param key
*/
public void unLock(String key) {
remove(key);
}

使用Redis 分布式锁 咱们的代粟耀莹码能够改成这样:

public void like(Article article,User user) {
String key = "key:like" + article.getId() + ":" + user.getUserId();
// 等候锁的时刻 0 , 过期时刻 一分钟避免死锁
boolean flag = redisService.lock(key,0,60 * 1000L);
if(!flag) {
//获取锁失利 阐明前面的恳求现已获取了锁
th狡猾王妃row new ApiException(CodeEnums.SYSTEM_ERR);
}
//查看是否点过赞
if (checkIsLike(article,user)) {
//点过赞了
throw new ApiException(CodeEnums.SYSTEM_ERR);
}
else {
//保存点赞
saveLike(article,user);
}
//删去锁
redisService.unLock(key);
}

key 的规划当废柴遭受桃花九也很考究: 数据不抵触的两个事务场景,key不能抵触,不同人的key也不相同,不同的文章Key也不相同。 依据场景事务设定。

一个准则: 尽或许的缩小key的规模。 这样才干增强咱们的并发。

首要咱们先获取锁,获取锁成功 履行完操作,保存数据 ,删去锁。获取不到锁回来失利。设置过期时爱努努间是为了避免‘死锁’,比方机器获取到了 锁,没有设置过期时刻,可是他死机了,没有删去开释锁。

  • 版本号操控 CAS 算法: CAS有3个操作数食色性也,抗日电视剧-科创板:一小时带你了解出资新机遇,立异创业创投,内存值V,旧的预期值A,要修正的新值B。当且仅当预期值A和内存值V相一起,将内存值V修正为B,不然什么都不做。这个比较冗杂,有爱好的同学能够去看看。

重视我:私信回复“555”获取往期Java高档架构材料、源码、笔记、视频Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构技能往期架构视频

 关键词: