文档章节

redis分布式锁应用场景一

纵宇七郎
 纵宇七郎
发布于 2017/08/31 23:16
字数 896
阅读 428
收藏 0

  在项目中有这样的一个场景,有两台服务器A和B,项目部署在A服务器和B服务器上,如果用户在两台电脑上登录,提交订单,这个时候可能会出现A服务器和B服务器同时执行订单提交的动作,导致C服务器上数据库出现相同的订单数据,如下图所示:

 

上面的问题属于线程并发导致数据产生了重复,使用redis分布式锁避免线程并发,有时候出现redis不能释放锁资源,需要将key转为字节:

public class RedisTest {
    private static Logger logger = LoggerFactory.getLogger(RedisTest.class);
    private static String serverIp = "54.222.147.130";
    private static  int port = 18000;
    private static String password = "QWer!@34%^911";
    private static JedisPool pool;
    private static int  maxTotal = 3000;
    private static int maxIdle = 10;
    private static int maxWaitMillis = 60000;//ms

    private static JedisPoolConfig jdsConfig;

    private static final int DEFAULT_CONN_TIME_OUT = 3000;//ms
    public static void main(String[] args) {
        init();
        boolean lock = tryLock("reidsAction",0);//获取锁资源,默认60秒后释放锁资源
        System.out.println(lock);//打印获取结果
    }


    private static void init() {
        JedisPoolConfig config = new JedisPoolConfig();
        // 控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;
        // 如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
        config.setMaxTotal(maxTotal);
        // 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
        config.setMaxIdle(maxIdle);
        //最小空闲连接数, 默认0
        config.setMinIdle(10);
        // 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
        //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted), 如果超时就抛异常, 小于零:阻塞不确定的时间, 默认 - 1
        config.setMaxWaitMillis(maxWaitMillis);
        // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
        config.setTestOnBorrow(true);
        config.setTestOnReturn(Boolean.FALSE);
        //在空闲时检查有效性, 默认false
        config.setTestWhileIdle(true);
        //逐出扫描的时间间隔(毫秒) 如果为负数, 则不运行逐出线程, 默认 - 1
        config.setTimeBetweenEvictionRunsMillis(60000);
        //对象空闲多久后逐出, 当空闲时间 > 该值 且 空闲连接>最大空闲数 时直接逐出, 不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
        config.setSoftMinEvictableIdleTimeMillis(1800000);
        //每次逐出检查时 逐出的最大数目 如果为负数就是: idleObjects.size / abs(n), 默认3
        config.setNumTestsPerEvictionRun(3);
        jdsConfig = config;
        pool = new JedisPool(config, serverIp, port, DEFAULT_CONN_TIME_OUT, password);
    }


    public static boolean tryLock(String key, long timeout) {
        try {
            //系统计时器的当前值,以毫微秒为单位。
            long startTime = System.currentTimeMillis();
            do {
                logger.debug("try lock key: " + key);
                Jedis jedis = pool.getResource();
//            //将 key 的值设为 value  返回值:1成功 0失败
            long res = jedis.setnx(key.getBytes(),SerializeUtils.serialize(key));//key转为字节,避免不能释放锁资源
            boolean isGetLock = false;
            if (res == 1) {
                if (jedis.exists(key)) {
                    jedis.expire(key.getBytes(), 60);//秒 默认占用锁的时间
                }
                isGetLock = true;
            }
            if (isGetLock) {
                //设置过期时间
                logger.debug("get lock, key: " + key + " , expire in " + 60+ " seconds.");
                //成功获取锁,返回true
                return Boolean.TRUE;
            }
            // 存在锁,循环等待锁
            logger.debug("key: " + key + " locked by another business:" + key);

            if (timeout <= 0) {
                //没有设置超时时间,直接退出等待
                break;
            }
            Thread.sleep(1000);//1000毫秒
        } while ((System.currentTimeMillis() - startTime) < timeout);
        } catch (JedisConnectionException je) {
            logger.error("redis 连接异常!", je);
        } catch (Exception e) {
            logger.error(String.format("获取分布式锁异常!key[%s]value[%s]",key,key), e);
        }
        return Boolean.FALSE;
    }


}

 

/**
 * 对象序列化 反序列化工具
 * Created by pengfeihu on 16/3/29.
 */
public class SerializeUtils {

    /**
     * 序列化
     *
     * @param object
     * @return
     * @throws IOException
     */
    public static byte[] serialize(Object object) throws IOException {
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        baos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(baos);
        oos.writeObject(object);
        byte[] bytes = baos.toByteArray();
        return bytes;
    }

    /**
     * 反序列化
     *
     * @param bytes
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Object unSerialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream bais = null;
        bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        return ois.readObject();
    }

}

© 著作权归作者所有

纵宇七郎
粉丝 4
博文 6
码字总数 4870
作品 0
朝阳
私信 提问
对比各类分布式锁缺陷,抓住Redis分布式锁实现命门

近两年来微服务变得越来越热门,越来越多的应用部署在分布式环境中,在分布式环境中,数据一致性是一直以来需要关注并且去解决的问题,分布式锁也就成为了一种广泛使用的技术。 常用的分布式...

张佳
02/26
0
0
Redis常见的应用场景解析

Redis是一个key-value存储系统,现在在各种系统中的使用越来越多,大部分情况下是因为其高性能的特性,被当做缓存使用,这里介绍下Redis经常遇到的使用场景。 Redis特性 一个产品的使用场景肯...

IT米粉
2017/09/25
0
0
ASP.NET Core中借助CSRedis实现安全高效的分布式锁

引言 最近回头看了看开发的.NET Core 2.1项目的复盘总结,其中在多处用到Redis实现的分布式锁,虽然在OnResultExecuting方法中做了防止死锁的处理,但在某些场景下还是会发生死锁的问题,下面...

dotNET跨平台
02/09
0
0
分布式锁与实现(一)基于Redis实现

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、...

rechardchensir
2018/10/08
979
9
基于Redis构建分布式锁进阶-RedLock(真分布)

前言 在之前的《基于redis的分布式锁设计实现》文章中,介绍并实现了两种常见的redis分布式锁。但这种方式仅能保证在一个单节点的、保证永不宕机的环境下没有任何问题。在redis集群中,若遇到...

谢随安
04/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

自建redis笔记

自建redis笔记 最近在linux安装了一下redis,特做一些笔记! 本文先单节点启动redis,然后再进行持久化配置,在次基础上,再分享搭建主从模式的配置以及Sentinel 哨兵模式及集群的搭建 单节点...

北极之北
14分钟前
1
0
vue+element之多表单验证

方法一:利用promise var p1=new Promise(function(resolve, reject) { this.$refs[form1].validate((valid) => { if(valid){ ......

沉迷代码我爱学习
16分钟前
1
0
golang 1.13 errors 包 新函数介绍

引 这次 errors 包算重量级更新。很有更能把以前的一些设计模式给推到。下面聊下用法。 error 装包 以前返回一个错误,想要保存 error 链,还要定义结构体保存以前的 error 信息。感兴趣看下...

guonaihong
25分钟前
41
0
并发编程之线程池

一、线程池 1、什么是线程池 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序 都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。 第一:降...

codeobj
27分钟前
3
0
知识点总结思维导图模板分享,良心安利,建议收藏

思维导图经常被用在学习中,对大脑思维进行发散,对知识进行记忆。使用思维导图可以让知识更加简单更有层次。下面是利用思维导图所绘制的几款知识点总结思维导图模板,大家可以进行进行参考使...

干货趣分享
30分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部