项目中遇到的分布式高并发情况及解决方案

当前所做的项目有这样一个场景:新增数据的时候生成的流水号,是查询数据库表最大流水号加1,并发情况下流水号有可能会重复,这时候我们首先想到的是方法上加synchronized,一个单词搞定,但是如果项目是做了集群部署,就相当于一个项目部署到了多台服务器上,还是会出现并发的情况的,因为synchronized是jvm层面的它只对单机服务有作用。

解决方案:使用分布式锁,有基于redis的有基于zookper的,这里采用了redis的框架Redisson

直接上代码

1、引入Jar

 <dependency>

    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>2.7.0</version>
  </dependency>
2、配置Redisson

import jxl.demo.Demo;

import org.redisson.Redisson;
import org.redisson.config.Config;

import com.yonyou.me.utils.PropertiesUtils;
import com.yonyou.me.utils.StringUtils;

public class RedissonManager {
  private static Config config = new Config();
  // 声明redisso对象
  private static Redisson redisson = null;

  private static String realPath = Demo.class.getClassLoader()
  .getResource("application.properties").getPath();
  // 实例化redisson
  static {
    String path = PropertiesUtils.readValue(realPath, "redis.url");
    path = StringUtils.subString(path, "//", "?");
    config.useSingleServer().setAddress(path)
    .setPassword("yonyou123");
    // 得到redisson对象
    redisson = (Redisson) Redisson.create(config);
  }

  // 获取redisson对象的方法
  public static Redisson getRedisson() {
  return redisson;
  }

 }

这里是RedissonManager用到的读取配置文件properties的路径类

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

public class PropertiesUtils {

// 根据key读取value
public static String readValue(String filePath, String key) {
  Properties props = new Properties();
  try {
    InputStream in = new BufferedInputStream(new FileInputStream(
    filePath));
    props.load(in);
    String value = props.getProperty(key);
    return value;
  } catch (Exception e) {
    e.printStackTrace();
    return null;
  }
  }

}

这里是RedissonManager用到的截取字符串的公共类

public class StringUtils {
  /**
  * 判断是否为null 或空值
  * @param str
  * @return
  */
  public static boolean isBlank(String str){
    return str == null || str.trim().equals("");
  }
  /**
  * 判断是否非null 或非空值
  * @param str
  * @return
  */
  public static boolean isNotBlank(String str){
    return str != null && !str.trim().equals("");
  }

  public static boolean equals(String a, String b) {
    if (a == null) {
      return b == null;
    }
    return a.equals(b);
  }


  public static boolean equalsIgnoreCase(String a, String b) {
    if (a == null) {
      return b == null;
    }
    return a.equalsIgnoreCase(b);
   }

  /**
  * 截取字符串str中指定字符 strStart、strEnd之间的字符串
  *
  * @param string
  * @param str1
  * @param str2
  * @return
  */
  public static String subString(String str, String strStart, String strEnd) {

    /* 找出指定的2个字符在 该字符串里面的 位置 */
    int strStartIndex = str.indexOf(strStart);
    int strEndIndex = str.indexOf(strEnd);

    /* index 为负数 即表示该字符串中 没有该字符 */
    if (strStartIndex < 0) {
      return "字符串 :---->" + str + "<---- 中不存在 " + strStart + ", 无法截取目标字符串";
    }
    if (strEndIndex < 0) {
      return "字符串 :---->" + str + "<---- 中不存在 " + strEnd + ", 无法截取目标字符串";
    }
    /* 开始截取 */
    String result = str.substring(strStartIndex, strEndIndex).substring(strStart.length());
    return result;
   }
}

3、锁的获取和释放

import java.util.concurrent.TimeUnit;

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Component;

@Component
public class DistributedRedisLock {
  //从配置类中获取redisson对象
  private static Redisson redisson = RedissonManager.getRedisson();
  private static final String LOCK_TITLE = "redisLock_";
  //加锁
  public static boolean acquire(String lockName){
    //声明key对象
    String key = LOCK_TITLE + lockName;
    //获取锁对象
    RLock mylock = redisson.getLock(key);
    //加锁,并且设置锁过期时间,防止死锁的产生
    mylock.lock(2, TimeUnit.MINUTES);
    //加锁成功
    return true;
  }
  //锁的释放
  public static void release(String lockName){
    //必须是和加锁时的同一个key
    String key = LOCK_TITLE + lockName;
    //获取所对象
    RLock mylock = redisson.getLock(key);
    //释放锁(解锁)
    mylock.unlock();
  }
}

4,业务逻辑中使用分布式锁

/**
* 模拟获取最大流水号+1生成新的流水号,分布式高并发处理
*/
@RequestMapping(value = "/test", method = RequestMethod.GET)
public void test() {
  DistributedRedisLock.acquire("mylock");//加锁
  String sql = "select * from (select * from qcjs_erpinspecinter where dr=0 order by netweight desc) where rownum=1";
  ErpInspecInterVO vo;
  try {
    vo = iBaseQueryBS.queryVOsBySql(sql, ErpInspecInterVO.class).get(0);
    ErpInspecInterVO newVo = new ErpInspecInterVO();
    BeanUtils.copyProperties(vo, newVo);
    newVo.setId(null);
    newVo.setTs(null);
    newVo.setStatus(2);
    newVo.setNetweight(vo.getNetweight().add(new BigDecimal(1)));
    ErpInspecInterBillVO billVO = new ErpInspecInterBillVO();
    billVO.setHead(newVo);
    erpInspecInterService.save(billVO);
    } catch (Exception e) {
    // TODO 自动生成的 catch 块
    e.printStackTrace();
  }finally{
    DistributedRedisLock.release("mylock");//释放锁
  }
}

猜你喜欢

转载自www.cnblogs.com/zhangdke/p/12510321.html