使用Jedis客户端进行事务时出现Redis异常

3
为了避免在我的Redis频道中出现重复,我通过在Redis集合中保留索引来检查消息是否已经存在。以下是我的实现方式。然而,它会产生异常。
redis.clients.jedis.exceptions.JedisDataException: Please close pipeline or multi block before calling this method.
    at redis.clients.jedis.Response.get(Response.java:23)

这是实现代码。
          Jedis jedis = pool.getResource();

          String id = message.getId();
          Transaction transaction = jedis.multi();
          redis.clients.jedis.Response<java.lang.Boolean> response = transaction.sismember(ID_SET_REDIS_KEY, id);
          if (response != null && !response.get().booleanValue()) {
                //add it to the 
                transaction.sadd(ID_SET_REDIS_KEY, id);
                transaction.publish(redisChannelName, message);
            }
            transaction.exec();
            pool.returnResource(jedis);

我需要在事务中执行GET操作,因为有多个发布者可能会发布完全相同的消息。

2个回答

4
在事务结束之前,您无法获取到get的结果。
如果您使用Redis > 2.6.X,则可以使用Lua脚本创建一个带有逻辑的函数。参见Redis Lua
这正是我在我的项目中确保并发性所做的事情。 编辑:包含更完整的示例 您应该创建类似于PUBLISHNX脚本的东西(未经测试):
local shouldPublish = redis.call('SISMEMBER', KEYS[1], ARGV[1])

if shouldPublish == 0
    redis.call('SADD', KEYS[1], ARGV[1])
    redis.call('PUBLISH', ARGV[2], ARGV[3])
end

你需要传递所有必要的参数,包括channel、messageId、message和controlKey。

PS. 魏力是正确的,你可以使用WATCH和循环来实现并发时重试相同的结果,但我仍然更喜欢使用Lua脚本。


1
实际上,你可以通过使用"WATCH"在事务中获取结果。http://redis.io/topics/transactions。我认为在这里没有必要编写lua脚本。@Soumya Simanta - Wei Li

1

根据 @Axexandre 上面的评论,我使用以下代码执行操作。

import redis.clients.jedis.Jedis;

public class RedisLuaDemo {

    public static void main(String args[])
    {
        Jedis jedis = new Jedis("localhost");
        jedis.sadd("a", "b");
        int numberOfKeys = 1 //we are using only one Redis set 'setvar' 
        jedis.eval("if redis.call('sismember', KEYS[1], ARGV[1]) == 1 then return ARGV[2] else redis.call('sadd', KEYS[1], ARGV[1]); redis.call('publish', 'channel.mychannel', ARGV[2])  end", numberOfKeys, "setvar", "joe", "message from joe!");

    }
}

这里有关于脚本的更多信息。理解语法花费了一些时间。 if redis.call('sismember', KEYS[1], ARGV[1]) == 1 等同于 SISMEMBER setvar joe
redis.call('sadd', KEYS[1], ARGV[1]);

出于某种原因,如果我没有这行代码 jedis.sadd("a", "b");,我会得到一个异常(见下文)。

Exception in thread "main" java.lang.NullPointerException
    at redis.clients.jedis.Connection.setTimeoutInfinite(Connection.java:41)
    at redis.clients.jedis.Jedis.eval(Jedis.java:2763)
    at redis.RedisLuaDemo.main(RedisLuaDemo.java:13)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接