springboot-how to connect to multiple redis server when using spring boot 1.x ?

Purpose

In this article , I would demo how to connect to multiple(for example 2) redis server using spring boot app. Just as following picture shows, if you don’t know how to start two redis server in your system, you can refer to this post.

image-20210305165358100

Environment

  • Spring boot 1.x
  • Jdk 8
  • Redis

The Project Layout

The layout of this demo project is as follows, all the example code can be found here:

  spring-boot-multiredis git:(master) tree .                                       
.
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── bswen
│   │   │           └── sbmr
│   │   │               ├── MultiRedisApplication.java
│   │   │               ├── config
│   │   │               │   ├── Redis1Configuration.java
│   │   │               │   ├── Redis1Property.java
│   │   │               │   ├── Redis2Configuration.java
│   │   │               │   ├── Redis2Property.java
│   │   │               │   └── RedisCommonProperty.java
│   │   │               └── service
│   │   │                   ├── MultiRedisTestRunner.java
│   │   │                   └── MyMessageReceiver.java
│   │   └── resources
│   │       └── application.properties

The Pom

We use maven to define the dependencies:

   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <start-class>Application</start-class>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- Import dependency management from Spring Boot -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.4.3.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <artifactId>spring-boot-multiredis</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
    </dependencies>

The code

1. Config file: src/main/resources/application.properties

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0

spring.redis2.host=localhost
spring.redis2.port=26379
spring.redis2.database=0

Here we define two redis connection, one is localhost:6379, the other is localhost:26379

2. Property config classes: src/main/java/com.bswen.sbmr/config/

To read the above properties file into spring boot, we must define configuration classes:

At first, we define a common class as the base class of redis configuration classes:

public class RedisCommonProperty {
    private String host;
    private int port;
    private int database;
    //getters setters
}

Then, we define Redis1Property and Redis2Property class to denote the different redis configurations

Redis1Property.java

@Configuration
@ConfigurationProperties(prefix = "spring.redis")
public class Redis1Property extends RedisCommonProperty {
}

Redis2Property.java

@Configuration
@ConfigurationProperties(prefix = "spring.redis2")
public class Redis2Property extends RedisCommonProperty {
}

Pay attention to the ConfigurationProperties annotation, we use it to load different properties from the application.properties file.

Thirdly, we should define how to get the RedisTemplate and RedisConnectionFactory from above properties:

For redis server1, we define Redis1Configuration:

@Configuration
public class Redis1Configuration {

    @Autowired
    private Redis1Property redis1Property;

    @Primary
    @Bean(name = "redis1ConnectionFactory")
    public RedisConnectionFactory userRedisConnectionFactory() {
        JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
        redisConnectionFactory.setHostName(redis1Property.getHost());
        redisConnectionFactory.setPort(redis1Property.getPort());
        redisConnectionFactory.setDatabase(redis1Property.getDatabase());
        redisConnectionFactory.setPoolConfig(getPoolConfig());
        return redisConnectionFactory;
    }

    private JedisPoolConfig getPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(8);
        jedisPoolConfig.setMinIdle(1);
        jedisPoolConfig.setMaxTotal(8);
        return jedisPoolConfig;
    }

    @Bean(name = "redis1StringRedisTemplate")
    public StringRedisTemplate userStringRedisTemplate(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory cf) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(cf);
        return stringRedisTemplate;
    }

    @Bean(name = "redis1RedisTemplate")
    public RedisTemplate userRedisTemplate(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory cf) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(cf);
        //setSerializer(stringRedisTemplate);
        return stringRedisTemplate;
    }

}

For redis server2, we define Redis2Configuration:


@Configuration
public class Redis2Configuration {
    @Autowired
    private Redis2Property redis2Property;


    private JedisPoolConfig getPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(8);
        jedisPoolConfig.setMinIdle(1);
        jedisPoolConfig.setMaxTotal(8);
        return jedisPoolConfig;
    }


    @Bean(name = "redis2ConnectionFactory")
    public RedisConnectionFactory roleRedisConnectionFactory() {
        JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
        redisConnectionFactory.setHostName(redis2Property.getHost());
        redisConnectionFactory.setPort(redis2Property.getPort());
        redisConnectionFactory.setDatabase(redis2Property.getDatabase());
        redisConnectionFactory.setPoolConfig(getPoolConfig());
        return redisConnectionFactory;
    }

    @Bean(name = "redis2StringRedisTemplate")
    public StringRedisTemplate roleStringRedisTemplate(@Qualifier("redis2ConnectionFactory") RedisConnectionFactory cf) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(cf);
        return stringRedisTemplate;
    }

    @Bean(name = "redis2RedisTemplate")
    public RedisTemplate roleRedisTemplate(@Qualifier("redis2ConnectionFactory") RedisConnectionFactory cf) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(cf);
        //setSerializer(stringRedisTemplate);
        return stringRedisTemplate;
    }

}

Now we have setup the properties and the configurations , we have beans of RedisTemplate , we can test it as follows:


@Component
public class MultiRedisTestRunner implements CommandLineRunner {
    private final static Logger logger = LoggerFactory.getLogger(MultiRedisApplication.class);

    @Autowired
    @Qualifier("redis1StringRedisTemplate")
    private StringRedisTemplate userStringRedisTemplate;
    @Autowired
    @Qualifier("redis2StringRedisTemplate")
    private StringRedisTemplate roleStringRedisTemplate;

    @Override
    public void run(String... strings) throws Exception {
        try {
            for (int i = 0; i < 10; i++) {
                logger.info("=====================================================================");
                logger.info("start loop " + i);
                String key = "key" + i;
                userStringRedisTemplate.opsForValue().set(key, "value" + i);
                roleStringRedisTemplate.opsForValue().set(key, "value" + (i + 1));

                String primaryKeyValue = userStringRedisTemplate.opsForValue().get(key);
                String secondaryKeyValue = roleStringRedisTemplate.opsForValue().get(key);

                logger.info("=====================================================================");
                logger.info(String.format("read from the redis1, key %s value is %s", key, primaryKeyValue));
                logger.info(String.format("read from the redis2, key %s value is %s", key, secondaryKeyValue));
            }
        }catch(Exception ex) {
          throw ex;
        }
    }
}

Run it, we get this result:

3-05 17:26:49.352  INFO 13130 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 2147483647
2021-03-05 17:26:49.481  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.482  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 0
2021-03-05 17:26:49.507  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.507  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key0 value is value0
2021-03-05 17:26:49.507  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key0 value is value1
2021-03-05 17:26:49.507  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.507  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 1
2021-03-05 17:26:49.511  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.511  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key1 value is value1
2021-03-05 17:26:49.511  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key1 value is value2
2021-03-05 17:26:49.511  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.512  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 2
2021-03-05 17:26:49.515  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.516  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key2 value is value2
2021-03-05 17:26:49.516  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key2 value is value3
2021-03-05 17:26:49.516  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.516  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 3
2021-03-05 17:26:49.520  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.520  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key3 value is value3
2021-03-05 17:26:49.520  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key3 value is value4
2021-03-05 17:26:49.520  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.520  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 4
2021-03-05 17:26:49.524  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.524  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key4 value is value4
2021-03-05 17:26:49.524  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key4 value is value5
2021-03-05 17:26:49.524  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.524  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 5
2021-03-05 17:26:49.528  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.529  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key5 value is value5
2021-03-05 17:26:49.529  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key5 value is value6
2021-03-05 17:26:49.529  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.529  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 6
2021-03-05 17:26:49.533  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.533  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key6 value is value6
2021-03-05 17:26:49.533  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key6 value is value7
2021-03-05 17:26:49.533  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.533  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 7
2021-03-05 17:26:49.538  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.538  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key7 value is value7
2021-03-05 17:26:49.538  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key7 value is value8
2021-03-05 17:26:49.538  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.538  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 8
2021-03-05 17:26:49.542  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.542  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key8 value is value8
2021-03-05 17:26:49.542  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key8 value is value9
2021-03-05 17:26:49.542  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.542  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : start loop 9
2021-03-05 17:26:49.546  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : =====================================================================
2021-03-05 17:26:49.546  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis1, key key9 value is value9
2021-03-05 17:26:49.546  INFO 13130 --- [           main] com.bswen.sbmr.MultiRedisApplication     : read from the redis2, key key9 value is value10

It works!

By the way: How to subscribe to two redis server simultaneously?

Now we can connect two redis server , send command (get/set/…) to them and get response, but how can we use the pub/sub functions to listen to redis channels?

image-20210306105708059

As the above picture shown, the process is as follows:

  • You should have two redis server running
  • Your spring boot application connects to the two redis servers
  • Then you can subscribe to topics on the two redis servers by using spring boot RedisMessageListener
  • At last, the messages would be forwarded to a RedisReceiver to process

So the key point is how to setup two redis servers’ RedisMessageListener, let’s check the code:

@Component
public class MultiRedisListenerTester {
    @Bean
    RedisMessageListenerContainer container1(@Qualifier("redis1ConnectionFactory") RedisConnectionFactory connectionFactory,

                                            MessageListenerAdapter listenerAdapter) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic("tasks1"));

        return container;
    }

    @Bean
    RedisMessageListenerContainer container2(@Qualifier("redis2ConnectionFactory") RedisConnectionFactory connectionFactory,

                                             MessageListenerAdapter listenerAdapter) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic("tasks2"));

        return container;
    }

    @Bean
    MessageListenerAdapter listenerAdapter(MyMessageReceiver receiver) {
        return new MessageListenerAdapter(receiver, "receiveMessage");
    }
}

Let’s analyze the above code:

  • We create a class named MultiRedisListenerTester with the annotation @Component, which indicate that the class is a spring bean.
  • The method named container1 and container2 has two parameters, one is connectionFactory, which is injected with specified redisConnectionFactory with @Qualifer annotation, the redisConnectionFactory beans are defined in Redis1Configuration and Redis2Configuration, the other is a listenerAdapter, which is supplied by listenerAdapter method defined below.
  • We subscribed with topic name tasks1 on redis server1 and tasks2 on redis server2.
  • The method named listenerAdapter indicates that when redis get a message, it would push the message the the receiver’s receiveMessage method.

Now let’s check the reciever’s code:

@Component
public class MyMessageReceiver {
    private static Log log = LogFactory.getLog(MyMessageReceiver.class);

    public void receiveMessage(String message) {
        log.info("got message "+message);
    }
}

Let test the multiple redis server’s pub/sub feature on redis server1:

//publish on redis server1 with redis-cli console
localhost-6379:0>publish tasks1 hello1
1
//got message from spring boot message receiver:
2021-03-06 11:22:31.465  INFO 17414 --- [   container1-2] c.bswen.sbmr.service.MyMessageReceiver   : got message hello1

Let test the multiple redis server’s pub/sub feature on redis server2:

//publish on redis server2 with redis-cli console
localhost-6379:0>publish tasks2 hello2
1
//got message from spring boot message receiver:
2021-03-06 11:22:32.465  INFO 17414 --- [   container1-2] c.bswen.sbmr.service.MyMessageReceiver   : got message hello2

If you want to tell from which redis server pushed the message, e.g. the source of the message, you can inject different named MessageListenerAdapter instances by using @Qualifier injection in method container1 and container2.

For example:

    @Bean
    RedisMessageListenerContainer container2(@Qualifier("redis2ConnectionFactory") RedisConnectionFactory connectionFactory,

                                             @Qualifier("redis2Listener") MessageListenerAdapter listenerAdapter) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic("tasks2"));

        return container;
    }

    @Bean(name="redis2Listener")
    MessageListenerAdapter listenerAdapter(MyMessageReceiver receiver) {
        return new MessageListenerAdapter(receiver, "receiveMessage");
    }

Summary

In this post, I demonstrated how to connect to multiple redis server instances with spring boot, the key point is that you should setup specific configuration for each redis server connection. And I also showed how to use the pub/sub function of springboot in the multiple redis server environment, thanks for your reading. All code of the project is uploaded to github.com, you can download at this link.