diff --git a/src/main/java/com/redis/cluster/config/RedisCacheConfig.java b/src/main/java/com/redis/cluster/config/RedisCacheConfig.java index 1c11297..73f1335 100644 --- a/src/main/java/com/redis/cluster/config/RedisCacheConfig.java +++ b/src/main/java/com/redis/cluster/config/RedisCacheConfig.java @@ -8,6 +8,9 @@ import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; @@ -36,4 +39,20 @@ public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory).cacheDefaults(configuration) .withInitialCacheConfigurations(cacheConfigurations).build(); } + + @Bean + public RedisMessageListenerContainer RedisMessageListener(RedisConnectionFactory connectionFactory) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + return container; + } + + @Bean + public RedisTemplate redisTemplateForObject(RedisConnectionFactory connectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(connectionFactory); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + return redisTemplate; + } } \ No newline at end of file diff --git a/src/main/java/com/redis/cluster/controller/PubSubController.java b/src/main/java/com/redis/cluster/controller/PubSubController.java new file mode 100644 index 0000000..bfd16b0 --- /dev/null +++ b/src/main/java/com/redis/cluster/controller/PubSubController.java @@ -0,0 +1,63 @@ +package com.redis.cluster.controller; + +import com.redis.cluster.pubsub.RedisPublisher; +import com.redis.cluster.pubsub.RedisSubscriber; +import com.redis.cluster.pubsub.RoomMessage; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.PostConstruct; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@RequiredArgsConstructor +@RequestMapping("/pubsub") +@RestController +public class PubSubController { + // topic에 발행되는 액션을 처리할 Listner + private final RedisMessageListenerContainer redisMessageListener; + // 발행자 + private final RedisPublisher redisPublisher; + // 구독자 + private final RedisSubscriber redisSubscriber; + // 특정 topic에 메시지를 발송할 수 있도록 topic정보를 Map에 저장 + private Map channels; + + @PostConstruct + public void init() { + // 실행될때 topic정보를 담을 Map을 초기화 + channels = new HashMap<>(); + } + + // 유효한 Topic 리스트 반환 + @GetMapping("/room") + public Set findAllRoom() { + return channels.keySet(); + } + + // Topic 생성하여 Listener에 등록후 Topic Map에 저장 + @PutMapping("/room/{roomId}") + public void createRoom(@PathVariable String roomId) { + ChannelTopic channel = new ChannelTopic(roomId); + redisMessageListener.addMessageListener(redisSubscriber, channel); + channels.put(roomId, channel); + } + + // 특정 Topic에 메시지 발송 + @PostMapping("/room/{roomId}") + public void pushMessage(@PathVariable String roomId, @RequestParam String name, @RequestParam String message) { + ChannelTopic channel = channels.get(roomId); + redisPublisher.publish(channel, RoomMessage.builder().name(name).roomId(roomId).message(message).build()); + } + + // 특정 Topic 삭제 후 Listener 해제 + @DeleteMapping("/room/{roomId}") + public void deleteRoom(@PathVariable String roomId) { + ChannelTopic channel = channels.get(roomId); + redisMessageListener.removeMessageListener(redisSubscriber, channel); + channels.remove(roomId); + } +} \ No newline at end of file diff --git a/src/main/java/com/redis/cluster/controller/RedisController.java b/src/main/java/com/redis/cluster/controller/RedisController.java index acafc8a..c564f58 100644 --- a/src/main/java/com/redis/cluster/controller/RedisController.java +++ b/src/main/java/com/redis/cluster/controller/RedisController.java @@ -23,21 +23,18 @@ public User findOne(@PathVariable long msrl) { } @PostMapping("/user") - @ResponseBody public User postUser(@RequestBody User user) { return userJpaRepo.save(user); } @CachePut(value = CacheKey.USER, key = "#user.msrl") @PutMapping("/user") - @ResponseBody public User putUser(@RequestBody User user) { return userJpaRepo.save(user); } @CacheEvict(value = CacheKey.USER, key = "#msrl") @DeleteMapping("/user/{msrl}") - @ResponseBody public boolean deleteUser(@PathVariable long msrl) { userJpaRepo.deleteById(msrl); return true; diff --git a/src/main/java/com/redis/cluster/pubsub/RedisPublisher.java b/src/main/java/com/redis/cluster/pubsub/RedisPublisher.java new file mode 100644 index 0000000..cf59994 --- /dev/null +++ b/src/main/java/com/redis/cluster/pubsub/RedisPublisher.java @@ -0,0 +1,17 @@ +package com.redis.cluster.pubsub; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class RedisPublisher { + + private final RedisTemplate redisTemplate; + + public void publish(ChannelTopic topic, RoomMessage message) { + redisTemplate.convertAndSend(topic.getTopic(), message); + } +} diff --git a/src/main/java/com/redis/cluster/pubsub/RedisSubscriber.java b/src/main/java/com/redis/cluster/pubsub/RedisSubscriber.java new file mode 100644 index 0000000..12954c4 --- /dev/null +++ b/src/main/java/com/redis/cluster/pubsub/RedisSubscriber.java @@ -0,0 +1,29 @@ +package com.redis.cluster.pubsub; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +@Slf4j +@RequiredArgsConstructor +@Service +public class RedisSubscriber implements MessageListener { + + private final ObjectMapper objectMapper; + private final RedisTemplate redisTemplate; + + @Override + public void onMessage(Message message, byte[] pattern) { + try { + String body = (String) redisTemplate.getStringSerializer().deserialize(message.getBody()); + RoomMessage roomMessage = objectMapper.readValue(body, RoomMessage.class); + log.info("Room - Message : {}", roomMessage.toString()); + } catch (Exception e) { + log.error(e.getMessage()); + } + } +} diff --git a/src/main/java/com/redis/cluster/pubsub/RoomMessage.java b/src/main/java/com/redis/cluster/pubsub/RoomMessage.java new file mode 100644 index 0000000..d77ef1e --- /dev/null +++ b/src/main/java/com/redis/cluster/pubsub/RoomMessage.java @@ -0,0 +1,17 @@ +package com.redis.cluster.pubsub; + +import lombok.*; + +import java.io.Serializable; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class RoomMessage implements Serializable { + private static final long serialVersionUID = 2082503192322391880L; + private String roomId; + private String name; + private String message; +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8fd0652..7917e7c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,7 +3,7 @@ server: logging: level: - root: warn + root: info com.rest.api: debug spring: @@ -28,4 +28,4 @@ spring: - 15.164.98.87:6401 - 15.164.98.87:6402 max-redirects: 3 - password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX