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


    当前所做的项目有这样一个场景:新增数据的时候生成的流水号,是查询数据库表最大流水号加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");//释放锁
      }
    }

  • 相关阅读:
    自动打包脚本
    Tomcat内存溢出问题
    Nginx笔记总结二十:nginx索引目录配置
    Nginx笔记总结十九:nginx + fancy实现漂亮的索引目录
    Nginx笔记总结十八:nginx统计响应的http状态码信息(ngx-http-status-code-counter)
    Nginx笔记总结十七:nginx生成缩略图配置(http_image_filter_module)
    Nginx笔记总结十六:nginx优化指南
    Nginx笔记总结十五:nginx+keepalive+proxy_cache配置高可用nginx集群和高速缓存
    Nginx笔记总结十四: nginx反向代理,用内网域名转发
    Nginx笔记总结十三:nginx 正向代理
  • 原文地址:https://www.cnblogs.com/zhangdke/p/12510321.html
Copyright © 2020-2023  润新知