0

I am now using java to design the redis pub/sub system and got a problem. I will show you the details:

The publisher here :

public class RedisMessagePublisher implements MessagePublisher {

public RedisMessagePublisher(StringRedisTemplate redisTemplate,ChannelTopic topic)
{
    this.redisTemplate = redisTemplate;
    this.topic = topic;
}

private StringRedisTemplate redisTemplate;

private ChannelTopic topic;

@Override
public void publish(String message) {
    redisTemplate.convertAndSend(topic.getTopic(), message);
    }
}

The publisher is correct and can work correctly.

Then let's move to subscriber class:

public class RedisMessageSubscriber implements MessageListener {

//action inspect here
private Action2<Message, byte[]> action;

public void setAction(Action2<Message, byte[]> action) {
    logger.info("action set");
    this.action = action;
}

private static Logger logger = LogManager.getLogger(RedisMessageSubscriber.class);

@Override
public void onMessage(Message message, byte[] bytes) {

    logger.info("===> redis subscribe message in <===");

    if (action != null)
        action.call(message, bytes);
    else
        logger.info("===> action is null <===");
    }
}

In subscriber class, I used the RxJava to inject the Action so that I can use it much more easily.

But the question is here, after I published the message from publisher, I can c that the message can be transferred to onMessage method, the log print was not what I expected:

===> redis subscribe message in <===
===> action is null <===

What I expected is that when I published a new message, the subscriber got it and ran the Action that I created.

The service I used to trigger the publisher and subscriber below:

@RestController("redispubsubcontroller")
@RequestMapping(value = "/redis")
public class redispubsubcontroller {

@Autowired
private RedisMessagePublisher redisMessagePublisher;

@Autowired
private RedisMessageSubscriber redisMessageSubscriber;

private static Logger logger = LogManager.getLogger(redispubsubcontroller.class);

@RequestMapping(value = "/publisher", method = {RequestMethod.GET})
public ApiResponse getConfig(String message,HttpServletRequest request,
                                            HttpServletResponse response) {

    redisMessageSubscriber.setAction(new Action2<Message, byte[]>() {
        @Override
        public void call(Message message, byte[] bytes) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                String result = objectMapper.readValue(message.getBody(), String.class);
                logger.info("receive:"+result);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });

    redisMessagePublisher.publish(message);

    return new ApiResponse("success","message sent");
    }
}

From above code, you can c that I subscribed the topic and set a new action to the subscriber:

 redisMessageSubscriber.setAction(new Action2<Message, byte[]>() {
    @Override
    public void call(Message message, byte[] bytes) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String result = objectMapper.readValue(message.getBody(), String.class);
            logger.info("receive:"+result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});

But I don't know why, after triggering the publisher, the subscriber can get the message but hold NULL Action still, the Action I created didn't pass to it.

Anyone can help? Is there any problem with this mechanism?

====EDIT=====

RedisMessageConfig code below:

@Configuration
public class RedisMessageConfig {

@Bean
ChannelTopic topic() {
    return new ChannelTopic("useraddresspubsub:queue");
}

@Bean
MessageListenerAdapter messageListener() {
    return new MessageListenerAdapter(new RedisMessageSubscriber());
}

@Autowired
private RedisConnectionFactory JedisConnectionFactory;

@Bean
RedisMessageListenerContainer redisContainer() {
    final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(JedisConnectionFactory);
    container.addMessageListener(messageListener(), topic());
    return container;
    }
}

====Solved====

Finally I got this solved per mp's idea, slightly changed myredismessagesubscriber to myredismessageconfig because the flow is from redismessageconfig to redismessagesubscriber, so in redismessageconfig, I need to first inject the action to it, then redismessageconfig will create new redismessagesubscriber and hold the new created action. The code below:

@Component
public class MyRedisMessageConfig extends RedisMessageConfig {

private static Logger logger =LogManager.getLogger(MyRedisMessageConfig.class);

public MyRedisMessageConfig() {
    super.action = new Action2<Message, byte[]>() {
        @Override
        public void call(Message message, byte[] bytes) {
            String result = new String(message.getBody());
                logger.info("received:" + result);
            }
        };
    }
}

Screenshot below: enter image description here

CharlieShi
  • 888
  • 3
  • 17
  • 43

1 Answers1

1

That's not the way MessageListener is intended to work. Additionally, you create shared mutable state. Two concurrent invocations change concurrently the state of RedisMessageSubscriber.

I assume you run into visibility issues as you set action in one thread and message receiving happens on a different thread.

If you require different behavior per MessageListener, then create multiple listeners that implement that behavior.

mp911de
  • 17,546
  • 2
  • 55
  • 95
  • No. Multiple threads make it just visible to you. The problem is caused by shared mutable state. – mp911de Dec 09 '16 at 09:53
  • Ok, would you pls share me some runnable ideas to impove this scenario? – CharlieShi Dec 09 '16 at 09:54
  • Sure. Take a look at https://gist.github.com/mp911de/3d5f1d9fa1efa105044e51f2ceb9648f which illustrates the approach. – mp911de Dec 09 '16 at 09:59
  • Sorry, I can't get the code run per your link url. Because If I use ur scenario, then the redis config will change, I post the code for redisMessageConfig above. – CharlieShi Dec 09 '16 at 10:51