Expired Redisson Keys still visible in Redis Cli

2019-08-22 01:38发布

I just learnt about Redis and Redisson as well. Basically I am trying to use Redis for storing AcessTokens/RefreshTokens used for authorization in my app. So I want to store the tokens with an expiration time. I used Spring Data Redis to store the token but there is no Api to expire each entry in a Map. I came across this post Spring Data Redis Expire Key and hence looked up Redisson. I tried a simple maven java project to test the expiration. Here is pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bridgelabz</groupId>
    <artifactId>redissonApp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>redissonApp</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.3.0</version>
        </dependency>

    </dependencies>
</project>

Below is Token class

package com.bridgelabz.redissonApp;

public class Token {

    private String accessToken;
    private int id;

    public Token() { }


    public Token(String accessToken, int id) {

        this.accessToken = accessToken;
        this.id = id;
    }

    public String getAccessToken() {
        return accessToken;
    }

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Token [accessToken=" + accessToken + ", id=" + id + "]";
    }   

}

And here is my demo App:

package com.bridgelabz.redissonApp;

import java.util.concurrent.TimeUnit;

import org.redisson.Redisson;
import org.redisson.api.LocalCachedMapOptions;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.api.LocalCachedMapOptions.EvictionPolicy;
import org.redisson.config.Config;


public class App {
    public static void main(String[] args) {
        Config config = new Config();

        config.useSingleServer().setAddress("127.0.0.1:6379");

        // LocalCachedMapOptions localCachedMapOptions =
        // LocalCachedMapOptions.defaults()
        // .evictionPolicy(EvictionPolicy.LFU);

        RedissonClient redisson = Redisson.create(config);

        try {

            RMapCache<Integer, Token> map = redisson.getMapCache("TestMap");

            Token myToken = new Token("abc", 1);

            map.put(1, myToken, 10, TimeUnit.SECONDS);

            System.out.println("Stored value with key 1 is: " + map.get(1));

        }

        finally {

            redisson.shutdown();

        }

    }
}

After running App.java I get the output I get the output as:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Stored value with key 1 is: Token [accessToken=abc, id=1]

And just commenting the put code and running the app after 10 seconds gives me the partly desired result:

LF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Stored value with key 1 is: null

But when I run redis-cli I'm still getting the value in the output:

127.0.0.1:6379> hget TestMap 1
"\x00\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x00\x00\x00\x00{\"@class\":\"com.bridgelabz.redissonApp.Token\",\"accessToken\":\"abc\",\"id\":1}"

Why isn't the key removed from redis also? FYI: Everything is being tested on my local machine only including redis.

2条回答
何必那么认真
2楼-- · 2019-08-22 02:19

I don't know the reasoning but the key's expired when I used the same code in Spring Web App but didn't work in a simple Java Application. Here is code for TokenDaoImpl.java:

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.log4j.Logger;
import org.redisson.Redisson;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.stereotype.Repository;

import com.bridgelabz.restApiDemo.entity.Token;

@Repository
public class TokenDaoImpl implements TokenDao {

    Logger logger = Logger.getLogger(TokenDaoImpl.class);

    RedissonClient redissonClient;

    RMapCache<String, Token> tokenMap;

    @PostConstruct
    public void initRedisson() {

        Config config = null;
        try {
            config = Config.fromJSON("{\n" + "   \"singleServerConfig\":{\n"
                    + "      \"idleConnectionTimeout\":10000,\n" + "      \"pingTimeout\":1000,\n"
                    + "      \"connectTimeout\":10000,\n" + "      \"timeout\":3000,\n" + "      \"retryAttempts\":3,\n"
                    + "      \"retryInterval\":1500,\n" + "      \"reconnectionTimeout\":3000,\n"
                    + "      \"failedAttempts\":3,\n" + "      \"password\":null,\n"
                    + "      \"subscriptionsPerConnection\":5,\n" + "      \"clientName\":null,\n"
                    + "      \"address\": \"redis://127.0.0.1:6379\",\n"
                    + "      \"subscriptionConnectionMinimumIdleSize\":1,\n"
                    + "      \"subscriptionConnectionPoolSize\":50,\n" + "      \"connectionMinimumIdleSize\":10,\n"
                    + "      \"connectionPoolSize\":64,\n" + "      \"database\":0,\n"
                    + "      \"dnsMonitoring\":false,\n" + "      \"dnsMonitoringInterval\":5000\n" + "   },\n"
                    + "   \"threads\":0,\n" + "   \"nettyThreads\":0,\n" + "   \"codec\":null,\n"
                    + "   \"useLinuxNativeEpoll\":false\n" + "}");
        } catch (IOException e1) {
            logger.info("******Inside Config Catch");
            e1.printStackTrace();
        }

        logger.info("**************Config Object" + config);

        redissonClient = Redisson.create(config);

        tokenMap = redissonClient.getMapCache("tokenMap");
    }

    @PreDestroy
    public void redissonShutdown() {
        redissonClient.shutdown();
    }

    public TokenDaoImpl() {

    }

    @Override
    public Token generateToken(String tokenType, int uid) {

        // first generate the token
        String tokenValue = UUID.randomUUID().toString().replaceAll("-", "");
        logger.info("******Generated access token is " + tokenValue);

        Token token = new Token(tokenType, tokenValue, uid);

        // save the token in redis cache with expiration depending upon tokenType
        switch (tokenType) {
        case "accessToken":
            tokenMap.put(tokenValue, token, 15, TimeUnit.MINUTES);
            break;

        case "refreshToken":
            tokenMap.put(tokenValue, token, 30, TimeUnit.MINUTES);
            break;

        case "forgotToken":
            tokenMap.put(tokenValue, token, 30, TimeUnit.MINUTES);
            break;

        default:
            logger.info("**********Please specify correct token type!");
            break;
        }

        return token;
    }

    @Override
    public boolean verifyToken(String tokenValue) {

        Token token = tokenMap.get(tokenValue);

        logger.info("******Token from redis for verification" + token);

        if (token != null) {
            return true;
        }

        return false;
    }

}

Previously, when running Java App, I had two Maps in redis- one which I created i.e. TokenMap and and new map which has MapName_session_timeout and the keys would remain even after the specified expiration. Now all keys are removed correctly.

查看更多
欢心
3楼-- · 2019-08-22 02:36

Redis does not support individual element eviction out of the box for hashes. So Redisson has built its own solution and named it MapCache.

So with MapCache, you now have two levels expiry control: Key level which offered by Redis, and field level which offered by Redisson.

In your test code:

RMapCache<Integer, Token> map = redisson.getMapCache("TestMap");

Token myToken = new Token("abc", 1);

map.put(1, myToken, 10, TimeUnit.SECONDS);

You have set the expiry on the field 1 of the hash TestMap. That means the hash does not have an expiry set against it, rather that an expiry is set against one of the field it has. So when you look it up in using redis-cli, the hash still exists. It eventually will disappear when the Redisson expiration process kicks in.

查看更多
登录 后发表回答