Code examples

e-mzungu edited this page Nov 26, 2012 · 20 revisions
Clone this wiki locally

Simple examples

There are few ways how you can use RJC:

        SingleRedisOperations redis = new RedisNode(new PoolableDataSource("redishost"));
        redis.set("foo", "hello world");
        String value = redis.get("foo");

Or

        SessionFactory factory = new SessionFactoryImpl(new SimpleDataSource("redishost"));
        Session session = factory.create();
        session.set("foo", "hello world");
        String value = session.get("foo");
        session.close();

Or

        RedisClient client = new RedisClientImpl(new SimpleDataSource("redishost").getConnection());
        client.getStatusReply(RedisCommand.SET, "foo", "hello world");
        String value = client.getBulkReply(RedisCommand.GET, "foo");
        client.close();

Or even

        RedisClient client = new RedisClientImpl(new SimpleDataSource("redishost").getConnection());
        client.getStatusReply(RedisCommand.SET, new byte[]{1,2,3}, new byte[]{4,5,6});
        byte[] value = client.getBinaryBulkReply(RedisCommand.GET, new byte[]{1,2,3});
        client.close();

In the first case each SingleRedisOperations method call use new connection to the Redis so we use PoolableConnectionFactory. This approach allows you to use Redis in multi-threaded environments (see example below) and you shouldn't worry about closing connections but it brings some overhead with getting connections from a pool (or creating new one if you use SimpleDataSource) and doesn't allow to use pipelines and Transactions commands.

Second example allows to use any Redis commands but Session use only one connection and it cannot be used in multi-treaded environment.

RedisClient is a litlle bit more native interface then previous one (Session implementation actually wraps RedisClient) and allows you to use Redis almost as is without additionally wrappers and data transformations. It can be useful especially if you need to get better performance and/or you prefer to use binary keys and value.

Using connection pool

        PoolableDataSource dataSource = new PoolableDataSource();
        dataSource.setHost("redishost");
        dataSource.setMaxActive(10);

        final SingleRedisOperations redis = new RedisNode(dataSource);

        // and small test for multithreading environment
        Thread[] ts = new Thread[100];
        for (int i = 0; i < ts.length; i++) {
            final int ii = i;
            Thread t = new Thread(new Runnable() {
                public void run() {
                    for (int n = 0; n < 1000; n++) {
                        String key = ii + "foo" + n;
                        redis.set(key, "bar" + n);
                        redis.get(key);
                    }
                }
            });
            ts[i] = t;
            t.start();
        }

        for (Thread t : ts) {
            t.join();
        }

        dataSource.close();

It just works :)

Transactions

Commands MULT and EXEC and commands between them should be executed on the one connection, but RedisNode class designed to get new connection for each command, so to use transaction functionality you need to create a Session:

        SessionFactory factory = new SessionFactoryImpl(new SimpleDataSource("redishost", 6379));
        Session session = factory.create();

and then use that session object:

        session.set("boo", "1");
        RedisClient redis = session.multi();
        redis.getStatusReply(RedisCommand.INCR, "boo");
        session.exec();

        System.out.println(session.get("boo"));
        session.close();

Note that Session instance should be used only in single thread enviroment and must be closed at the end. Also if you do not want to worry about session closing you may use RedisTemplate helper class:

        RedisTemplate template = new RedisTemplate(factory);
        String value = template.execute(new RedisCallback<String>() {
            public String doIt(Session session) {
                session.set("boo", "1");
                RedisClient redis = session.multi();
                redis.getStatusReply(RedisCommand.INCR, "boo");
                session.exec();

                return session.get("boo");
            }
        });

Sharding

First of all you need to create a list of shards:

        Collection<Shard<SingleRedisOperations>> shards = new ArrayList<Shard<SingleRedisOperations>>();
        shards.add(new ShardImpl<SingleRedisOperations>("shard1", new RedisNode(new SimpleDataSource("redishost", 6379))));
        shards.add(new ShardImpl<SingleRedisOperations>("shard2", new RedisNode(new SimpleDataSource("redishost", 6380))));

or

        ShardsFactoryImpl<SingleRedisOperations> shardsFactory = new ShardsFactoryImpl<SingleRedisOperations>();
        shardsFactory.setAddresses("redishost:6379, redishost:6380");
        shardsFactory.setDataSourceFactory(new SimpleDataSourceFactory());
        shardsFactory.setNodeFactory(new RedisNodeFactory());
        Collection<Shard<SingleRedisOperations>> shards = shardsFactory.create();

Then you may create a ShardedRedis instance:

        RedisOperations redis = new ShardedRedis(new HashNodeLocator<SingleRedisOperations>(shards));

        for (int i = 0; i < 1000; i++) {
            redis.set("key" + i, String.valueOf(i));
        }

Note that each shard must have unique id ("shard1" and "shard2" in example above) If you need to store certain keys in the same shard you can use "keytags" feature. To work with keytags you just need to set a pattern when you instance NodeLocator. For example:

        NodeLocator locator = new HashNodeLocator<RedisOperations>(shards, HashNodeLocator.DEFAULT_KEY_TAG_PATTERN);
        RedisOperations redis = new ShardedRedis(locator);

You can create your own pattern if you want. The default pattern is {}, this means that whatever goes inside curly brackets will be used to determine the shard.

So for example:

        redis.set("foo:{1}", "1");

and

       redis.set("boo:{1}", "1");

will go to the same shard

Spring configuration:

    <bean id="ds1" class="org.idevlab.rjc.ds.SimpleDataSource">
        <property name="host" value="192.168.56.101"/>
        <property name="port" value="6379"/>
    </bean>

    <bean id="ds2" class="org.idevlab.rjc.ds.SimpleDataSource">
        <property name="host" value="192.168.56.101"/>
        <property name="port" value="6380"/>
    </bean>

    <bean id="node1" class="org.idevlab.rjc.RedisNode">
        <property name="dataSource" ref="ds1"/>
    </bean>

    <bean id="node2" class="org.idevlab.rjc.RedisNode">
        <property name="dataSource" ref="ds2"/>
    </bean>

    <bean id="locator" class="org.idevlab.rjc.sharding.HashNodeLocator">
        <property name="shards">
            <list>
                <bean class="org.idevlab.rjc.ShardImpl">
                    <property name="shardId" value="shard1"/>
                    <property name="node" ref="node1"/>
                </bean>
                <bean class="org.idevlab.rjc.ShardImpl">
                    <property name="shardId" value="shard2"/>
                    <property name="node" ref="node2"/>
                </bean>
            </list>
        </property>
    </bean>

    <bean id="redis" class="org.idevlab.rjc.sharding.ShardedRedis">
        <property name="locator" ref="locator"/>
    </bean>

or

    <bean id="shardsFactory" class="org.idevlab.rjc.sharding.ShardsFactoryImpl">
        <property name="addresses" value="redishost:6379, redishost:6380"/>
        <property name="dataSourceFactory">
            <bean class="org.idevlab.rjc.ds.SimpleDataSourceFactory"/>
        </property>
        <property name="nodeFactory">
            <bean class="org.idevlab.rjc.RedisNodeFactory"/>
        </property>
    </bean>

    <bean id="locator" class="org.idevlab.rjc.sharding.HashNodeLocator">
        <property name="shards">
            <bean class="java.util.Collection" factory-bean="shardsFactory" factory-method="create"/>
        </property>
    </bean>

    <bean id="redis" class="org.idevlab.rjc.sharding.ShardedRedis">
        <property name="locator" ref="locator"/>
    </bean>

Pipeline

        SingleRedisOperations redis = new RedisNode(new SimpleDataSource("redishost"));

        List<Object> result = redis.pipeline(new Pipeline() {
            public void execute(RedisClient redis) {
                redis.noReply(RedisCommand.SET, "key", "1");
                redis.noReply(RedisCommand.INCR, "key");
                redis.noReply(RedisCommand.INCR, "key");
            }
        });

        System.out.println(result.get(2));

Messaging

To listen for messages published on certain node first of all need to create a RedisNodeSubscriber instance:

        RedisNodeSubscriber subscriber = new RedisNodeSubscriber();
        subscriber.setDataSource(new SimpleDataSource(hnp.host, hnp.port));

If you need to handle response of SUBSCRIBE/UNSUBSCRIBE commands you can do the following:

        subscriber.setSubscribeListener(new SubscribeListener() {
            public void onSubscribe(String channel, long subscribedChannels) {
                System.out.println("s: " + channel + ":" + subscribedChannels);
            }

            public void onUnsubscribe(String channel, long subscribedChannels) {
                System.out.println("us: " + channel + ":" + subscribedChannels);
            }

            public void onPSubscribe(String pattern, long subscribedChannels) {
                System.out.println("ps: " + pattern + ":" + subscribedChannels);
            }

            public void onPUnsubscribe(String pattern, long subscribedChannels) {
                System.out.println("pus: " + pattern + ":" + subscribedChannels);
            }
        });

Subscribe to the specific channel:

        subscriber.subscribe("c1");
        subscriber.setMessageListener(new MessageListener() {
            public void onMessage(String channel, String message) {
                System.out.println(message);
            }
        });

Subscribe to the specific pattern:

        subscriber.psubscribe("c*");
        subscriber.setPMessageListener(new PMessageListener() {
            public void onMessage(String pattern, String channel, String message) {
                System.out.println(pattern + " : " + channel + " : " + message);
            }
        });

And after that you need to run message listening:

       subscriber.runSubscription();

Note that the runSubscription method use invocation thread to listen messages.

To publish message just use implementations of RedisOperations interface:

        RedisOperations redis = new RedisNode(new SimpleDataSource("192.168.56.101"););
        redis.publish("c1", "message");

Also see unit test as example