redis中使用java脚本实现分布式锁

转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/115.html?1455860390

edis被大量用在分布式的环境中,自然而然分布式环境下的锁如何解决,立马成为一个问题。例如我们当前的手游项目,服务器端是按业务模块划分服务器的,有应用服,战斗服等,但是这两个vm都有可能同时改变玩家的属性,这如果在同一个vm下面,就很容易加锁,但如果在分布式环境下就没那么容易了,当然利用redis现有的功能也有解决办法,比如redis的脚本。

redis在2.6以后的版本中增加了Lua脚本的功能,可以通过eval命令,直接在RedisServer环境中执行Lua脚本?,并且可以在Lua脚本中调用Redis命令。
使用脚本的好处:

1.减少网络开销:可以把一些要批量处理的功能,发在一个脚本里面执行,减少客户端和redis的交互次数
2.原子操作:这主要就是我们在这边主要利用的功能,在分布式环境下保证数据的原子性。
3.复用:客户端发送的脚本会永久的存储在redis中,这就意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。

下面先看一段lua脚本:

代码如下:

local food=redis.call(‘hget‘,KEYS[1],‘food‘);
food=food+ARGV[1];
redis.call(‘hset‘,KEYS[1],‘food‘,food);
local diamond=redis.call(‘hget‘,KEYS[1],‘diamond‘);
diamond=diamond+ARGV[2];
redis.call(‘hset‘,KEYS[1],‘diamond‘,diamond);

注:redis.call是我们在脚本中调用redis命令,KEYS和ARGV2个数组,分别是键和参数,下标都是从1开始的,不是0。 
这段脚本的功能是取出 KEYS指定的玩家food(粮草)和diamond(玉石),然后就行修改,最后保存在redis中,脚本的执行,保证了整个操作的原子性。

下面我们用java代码来看看具体的实现过程

代码如下:

Jedis jedis = new Jedis("192.168.128.128", 6379);
// 1.初始玩家数据到redis中
GamePlayer player = new GamePlayer();
player.setId(1001);
player.setName("ksfzhaohui");
player.setFood(100);
player.setDiamond(100);
 
Map<String, String> beanMap = BeanUtil.warp(player);// 将对象转换成map
String beanKey = getRedisBeanKey(player.getClass(), player.getId());
System.out.println("key:" + beanKey);
jedis.hmset(beanKey, beanMap);// 将玩家数据保存到redis中

首先模拟了一个玩家将玩家信息保存在redis中,这边的Id随便写了一个,正常的情况下都是通过redis的命令incr生成一个id
结果: 

代码如下:

String script = "local food=redis.call(‘hget‘,KEYS[1],‘food‘);"
                + "food=food+ARGV[1];"
                + "redis.call(‘hset‘,KEYS[1],‘food‘,food);"
                + "local diamond=redis.call(‘hget‘,KEYS[1],‘diamond‘);"
                + "diamond=diamond+ARGV[2];"
                + "redis.call(‘hset‘,KEYS[1],‘diamond‘,diamond);";
List<String> keys = new ArrayList<String>();
keys.add(beanKey);
List<String> args = new ArrayList<String>();
args.add("100");
args.add("100");
// 3.执行脚本
jedis.eval(script, keys, args);

指定键和参考,执行脚本,结果:

BeanUtil代码:

代码如下:

public class BeanUtil {
    private static Logger logger = Logger.getLogger(BeanUtil.class);
    private static final String CLASS = "class";
 
    /**
     * 将指定的对象数据封装成map
     * 
     * @param bean
     *            对象数据
     * @return
     */
    @SuppressWarnings("all")
    public static Map<String, String> warp(Object bean) {
        Map<String, String> propertyMap = new HashMap<String, String>();
        try {
            PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass())
                    .getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : ps) {
                String propertyName = propertyDescriptor.getName();
                if (propertyName != null && !propertyName.equals(CLASS)) {
                    Method getter = propertyDescriptor.getReadMethod();
                    if (getter != null) {
                        propertyMap.put(propertyName,
                                String.valueOf(getter.invoke(bean, null)));
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e);
        }
        return propertyMap;
    }
 
}

时间: 2024-10-10 09:34:02

redis中使用java脚本实现分布式锁的相关文章

在redis中使用lua脚本

在实际工作过程中,可以使用lua脚本来解决一些需要保证原子性的问题,而且lua脚本可以缓存在redis服务器上,势必会增加性能. 不过lua也会有很多限制,在使用的时候要注意. 在Redis中执行Lua脚本有两种方法:eval和evalsha eval EVAL script numkeys key [key ...] arg [arg ...] 其中: <1> script:你的lua脚本 <2> numkeys:  key的个数 <3> key:redis中各种数据

【JSP】JSP中的Java脚本

前言 现代Web开发中,在JSP中嵌入Java脚本不是推荐的做法,因为这样 不利于代码的维护.有很多好的,替代的方法避免在JSP中写Java脚本.本文仅做为JSP体系技术的一个了解. 类成员定义 1.将定义的代码放在<%! %>之间即可. 定义的成员将被转换为servlet类的成员. 2.可以定义任何成员方法和成员字段,static 成员 或者 实例成员都可以. 3.注意JSP/Servlet是工作在多线程环境下的,定义的成员变量要注意线程安全问题.一般建议使用_jspService方法中 的

Java使用Redisson分布式锁实现原理

本篇文章摘自:https://www.jb51.net/article/149353.htm 由于时间有限,暂未验证 仅先做记录.有大家注意下哈(会尽快抽时间进行验证) 1. 基本用法 添加依赖 <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.2</version> </depend

利用consul在spring boot中实现最简单的分布式锁

因为在项目实际过程中所采用的是微服务架构,考虑到承载量基本每个相同业务的服务都是多节点部署,所以针对某些资源的访问就不得不用到用到分布式锁了. 这里列举一个最简单的场景,假如有一个智能售货机,由于机器本身的原因不能同一台机器不能同时出两个商品,这就要求在在出货流程前针对同一台机器在同一时刻出现并发 创建订单时只能有一笔订单创建成功,但是订单服务是多节点部署的,所以就不得不用到分布式锁了. 以上只是一种简单的业务场景,在各种大型互联网实际应用中,需要分布式锁的业务场景会更多,综合比较了业界基于各种

【分布式锁】Redis实现可重入的分布式锁

一.前言 之前写的一篇文章<细说分布式锁>介绍了分布式锁的三种实现方式,但是Redis实现分布式锁关于Lua脚本实现.自定义分布式锁注解以及需要注意的问题都没描述.本文就是详细说明如何利用Redis实现重入的分布式锁. 二.方案 死锁问题 当一个客户端获取锁成功之后,假如它崩溃了导致它再也无法和 Redis 节点通信,那么它就会一直持有这个锁,导致其它客户端永远无法获得锁了,因此锁必须要有一个自动释放的时间.   我们需要保证setnx命令和expire命令以原子的方式执行,否则如果客户端执行

Redis官网——如何利用Redis做服务器集群的分布式锁

链接:http://redis.io/topics/distlock 原理很简单,一段时间内轮询加锁的key 重点,不同语言的开源实现 Before describing the algorithm, here are a few links to implementations already available that can be used for reference. Redlock-rb (Ruby 实现). There is also a fork of Redlock-rb th

Java实现基于Redis的分布式锁

我这里不实现JDK的java.util.concurrent.locks.Lock接口,而是自定义一个,因为JDK的有个newCondition()方法我这里暂时没实现.这个Lock提供了5个lock方法的变体,可以自行选择使用哪一个来获取锁,我的想法是 最好用带超时返回的那几个方法,因为不这样的话,假如redis挂了,线程永远都在那死循环了(关于这里,应该还可以进一步优化,如果redis挂了,Jedis的操作肯定会抛异常之类的,可以定义个机制让redis挂了的时候通知使用这个lock的用户,或

如何优雅地用Redis实现分布式锁

https://mp.weixin.qq.com/s?__biz=MzAxNjM2MTk0Ng==&mid=2247484976&idx=2&sn=a0b6771f0b4e471c710f8cd51c243971&chksm=9bf4b685ac833f936f3722a795ae202a3be37a3fb57332393e2eec3bbf8b34c4705d5b14a964&mpshare=1&scene=1&srcid=0919f7t2duWiu

基于Redis的分布式锁到底安全吗(上)?

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多. 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论.由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争