I have clustered redis and trying to insert data in there using redisTemplate. I am getting error below when it reached to line that is trying to put data. "redis.clients.jedis.exceptions.JedisMovedDataException: MOVED"
org.springframework.data.redis.ClusterRedirectException: Redirect: slot 7319 to IP addr:6379.; nested exception is redis.clients.jedis.exceptions.JedisMovedDataException: MOVED 7319 IP addr:6379
Any idea? The hostName in the redisConnectionFactory bean is the cluster's Configuration endpoint.
return items -> {
HashOperations<String, String, String> hashOps = redisTemplate.opsForHash();
items.forEach(item -> {
hashOps.put((item.getProgramName()), item.getProgramName(), item.toJson().toString());
});
};
#Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName(hostName);
redisConnectionFactory.setPort(port);
return redisConnectionFactory;
}
#Bean(name = "redisTemplate")
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setDefaultSerializer(new StringRedisSerializer());
return redisTemplate;
}
You are using a Redis cluster but your configuration is for standalone Jedis connection factory. You should provide RedisClusterConfiguration to create JedisConnectionFactory.
The following posts will help:
http://stackoverflow.com.mevn.net/questions/46667584/springboot-elasticache-jedismoveddataexception-moved
How to config redis-cluster when use spring-data-redis 1.7.0.M1
Related
I have been trying to use RabbitMq instead of using Kafka. I am a beginner for using RabbitMq. How can I change this KafkaListener event to RabbitMQListener?
I have been trying to find out, but I could not. I need to change this listener for RabbitMQ.
PRODUCER
private final KafkaTemplate<String, Object> kafkaTemplate;
public AccountEventProducer(KafkaTemplate<String, Object> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
#Override
public void produce(String topic, BaseEvent event) {
this.kafkaTemplate.send(topic, event);
}
CONSUMER
#KafkaListener(topics = "AccountOpenedEvent", groupId = "${spring.kafka.consumer.group-id}")
#Override
public void consume(AccountOpenedEvent event, Acknowledgment ack) {
eventHandler.on(event);
ack.acknowledge();
}
Can anyone help me?
To use rabbitmq on the producer side, follow the steps below:
1. The first step is to add dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.5.5</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-clenter code hereoud-starter-stream-rabbit</artifactId>
<version>3.1.3</version>
</dependency>
2. The second step is to configure the connection to rabbitmq in the yml file:
spring:
rabbitmq:
host: localhost
password: guest
port: 5672
username: guest
exchange: user.exchange
queue: user.queue
routingkey: user.routingkey
3. In the third step, create a class to configure the required beans :
#Configuration
public class ProducerConfig {
#Bean
public Queue queue(){
return new Queue("user.queue", false);
}
#Bean
public TopicExchange topicExchange(){
return new TopicExchange ( "user.exchange" );
}
#Bean
public Binding binding(Queue queue, TopicExchange topicExchange){
return BindingBuilder.bind( queue).to( topicExchange).with( "user.routingkey" );
}
#Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
#Bean
public AmqpTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate( connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
}
5. The last step is to create a service on the producer side to submit a request to rabbitmq
#Autowired
AmqpTemplate amqpTemplate;
public void send(Object requestEvent){
amqpTemplate.convertAndSend( "user.exchange","user.routingkey",requestEvent );
System.out.println("Send messages successfully.");
}
}
To use rabbitmq on the consumer side, follow the steps below:
1. first do steps 1 and 2 of the producer side, after that create a service to read the message:
#RabbitListener(queues = "user.queue")
public void getMessage(Object requestEvent){
System.out.println(requestEvent.toString());
}
2. The second step is create a class to configure the required beans
#Configuration
public class ConsumerConfig {
#Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
On redis I have added key S000, and I want load hash object with that key.
I always get NULL from even I passed the correct key.
How can I do that, please find screenshot below for more detail.
Thank!
#Configuration
#EnableRedisRepositories
public class RedisConfig {
#Bean
public JedisConnectionFactory connectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName("localhost");
configuration.setPort(9000);
return new JedisConnectionFactory(configuration);
}
#Bean
public RedisTemplate<String, User> redisTemplate() {
RedisTemplate<String, User> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setEnableTransactionSupport(true);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
#Repository
public class UserDaoImpl implements UserDao {
#Autowire
private final RedisTemplate redisTemplate;
public Object fetchUserById(String key) {
return redisTemplate.opsForHash().entries(key);
}
}
I have the below Spring boot code to receive values whenever a Redis stream is appended with new record. The problem is receiver never receives any message, also, the subscriber, when checked with subscriber.isActive(), is always inactive. Whats wrong in this code? What did I miss? Doc for reference.
On spring boot start, initialize the necessary redis resources
Lettuce connection factory
#Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory("127.0.0.1", 6379);
}
RedisTemplate from the connection factory
#Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
Rest controller to append data to redis stream
#PutMapping("/{name}")
public String post(#PathVariable String name) {
return redisTemplate.opsForStream().add(StreamRecords.newRecord().in("streamx").ofObject(name)).getValue();
}
JMS style imperative message listener
#Component
public class MyStreamListener implements StreamListener<String, MapRecord<String, String, String>> {
#Override
public void onMessage(MapRecord<String, String, String> message) {
System.out.println("message received: " + message.getValue());
}
}
Initialize the listener
#Bean
public Subscription listener(MyStreamListener streamListener, RedisConnectionFactory redisConnectionFactory) throws InterruptedException {
StreamMessageListenerContainer<String, MapRecord<String, String, String>> container = StreamMessageListenerContainer
.create(redisConnectionFactory);
Subscription subscription = container.receive(Consumer.from("my-group-1", "consumer-1"),
StreamOffset.create("streamx", ReadOffset.latest())), streamListener);
System.out.println(subscription.isActive()); // always false
return subscription;
}
Though, I am able to append to the stream through api.
The important step is, start the StreamMessageListenerContainer after the subscription is done.
container.start();
I been working on implementing a PUB/SUB service using spring-data-Redis.
I have been researching and following the web and got something to work fine.
my problem is that I need absolute reliability when a message is not processed ( either an Exception is thrown or a logic error occurs ).
In which case I need the message to return to the topic for a retry ( by another subscriber or even the same ).
I have looked at several questions, particularly the following:
Redis Pub/Sub with Reliability
and
How to implement Redis Multi-Exec by using Spring-data-Redis
I have understood that I should use multi, exec for managing a transaction, but I couldn't get it to work.
Here is a simplified version of my code
#Configuration
#PropertySource(value = { "classpath:application.properties" })
public class RedisConfig {
#Autowired
Environment env;
#Bean
public MessageListenerAdapter messageListener() {
MyMessageListenerAdapter messageListeneradapter = new MyMessageListenerAdapter(new RedisMessageSubscriber());
messageListeneradapter.afterPropertiesSet();
return messageListeneradapter;
}
#Bean(name="RedisMessagePublisherBean")
public RedisMessagePublisher messagePublisher() {
return new RedisMessagePublisher();
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String , Object> template = new RedisTemplate<>();
template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));
template.setEnableTransactionSupport(true);
template.setConnectionFactory(lettuceConnectionFactory());
return template;
}
#Bean
public RedisMessageListenerContainer redisContainer() {
RedisMessageListenerContainer container
= new RedisMessageListenerContainer();
container.setConnectionFactory(lettuceConnectionFactory());
container.addMessageListener(messageListener(), topic());
return container;
}
#Bean
public LettuceConnectionFactory lettuceConnectionFactory() {
LettuceConnectionFactory factory = new LettuceConnectionFactory();
factory.setValidateConnection(true);
factory.setDatabase(1);
factory.afterPropertiesSet();
return factory;
}
#Bean
public ChannelTopic topic() {
return new ChannelTopic("MQ_TOPIC");
}
public class MyMessageListenerAdapter extends MessageListenerAdapter{
public MyMessageListenerAdapter(RedisMessageSubscriber redisMessageSubscriber) {
super(redisMessageSubscriber);
}
#Override
public void onMessage(Message message, byte[] pattern) {
RedisTemplate<?, ?> template = redisTemplate();
template.execute(new SessionCallback<String>() {
#Override
public <K, V> String execute(RedisOperations<K, V> operations) throws DataAccessException {
operations.multi();
System.out.println("got message");
String result = doSomeLogic(message);
if (result == null)
operations.discard();
else
operations.exec();
return null;
}
}) ;
}
}
}
My requirements are that if a message failed to process ( I can leave without runtime exceptions etc.. strictly logical error would suffice for now ), It will return to the topic.
Any help is appreciated, Thanks!
I have a client/server test that uses a custom CacheStore implementation. I want the custom CacheStore on the server nodes but not on the client nodes, but Ignite is trying to load the custom implementation on the client. Is there a way to avoid this?
Server code:
IgniteConfiguration igniteCfg = new IgniteConfiguration();
Ignite ignite = Ignition.start(igniteCfg);
CacheConfiguration<String, String> cacheCfg = new CacheConfiguration<>("test");
cacheCfg.setReadThrough(true);
cacheCfg.setCacheStoreFactory(new CacheStoreFactory());
IgniteCache<String, String> cache = ignite.getOrCreateCache(cacheCfg);
CacheStore:
public class CacheStoreFactory implements Factory<CacheStore<? super String, ? super String>> {
#Override
public CacheStore<? super String, ? super String> create() {
return new CacheStoreAdapter<String, String>() {
#Override
public String load(String key) throws CacheLoaderException {
System.out.println("load: key=" + key);
return key;
}
#Override
public void write(Entry<? extends String, ? extends String> entry) throws CacheWriterException {
}
#Override
public void delete(Object key) throws CacheWriterException {
}
};
}
}
Client:
IgniteConfiguration igniteCfg = new IgniteConfiguration();
igniteCfg.setClientMode(true);
Ignite ignite = Ignition.start(igniteCfg);
CacheConfiguration<String, String> cacheCfg = new CacheConfiguration<>("test");
IgniteCache<String, String> cache = ignite.getOrCreateCache(cacheCfg);
String value = cache.get("someKey");
The CacheStore is called correctly on the server, and the client appears to work, but the client logs this error:
Caused by: java.lang.ClassNotFoundException: org.dalsing.ignite.server.test.CacheStoreFactory
The cache store on the client is required for TRANSACTIONAL caches. For ATOMIC caches (this is the default mode), it's not needed, so you can safely ignore the exception.