2

私の 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 を実行する必要があります。

4

2 に答える 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。Wei Li の言うとおりです。WATCH と同時実行の場合に再試行するためのループを使用して同じ結果を得ることができますが、それでも私は Lua スクリプトを使用することを好みます。

于 2013-06-20T17:59:36.433 に答える
1

上記の@Axexandreのコメントに基づいて、次のコードを使用して操作を実行しました。

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) 
于 2013-06-21T02:11:31.200 に答える