2016-11-24 9 views
4

В моем приложении есть список publisherPostListenerList, который получает сообщения пользователя реального времени из очереди RabbitMQ, которые будут отправлены подписчикам/потребителям. Список является свойством класса ApplicationListener, который прослушивает события очереди pubsub. Следующий метод контроллера извлекает элементы списка с помощью метода getter & на основе логики, толкает сообщения к подписчикам.Создание списка для кластерной среды

поток выглядит следующим образом

Пользователь пишет пост -> сообщение попадет в БД + Очередь -> Сообщения от очереди добавляются в списке, который publisherPostListenerList для толкания абонентов пользователя.

Как мы видим, publisherPostListenerList является общим списком для n одновременных запросов из-за того, что ApplicationListener является синглом. Для одного экземпляра настройка работает нормально, но не будет работать в кластерной среде, так как каждый узел будет иметь свой собственный список publisherPostListenerList.

Как я могу справиться с этой ситуацией? Я не могу сделать ApplicationListener class stateless Мне нужен список для хранения элементов сообщения, полученных из очереди. Я помещаю список в распределенный в кеш памяти? Или есть какой-то другой обычный способ?

ApplicationListener.java

@Component 
public class ApplicationEventListener { 

    private List<Post> publisherPostListenerList = new CopyOnWriteArrayList<Post>(); 

    private static final Logger logger = Logger.getLogger(ApplicationEventListener.class); 

    @EventListener 
    public void postSubmissionEventHandler(PostSubmissionEvent event) throws IOException { 
     Post post = event.getPost(); 
     logger.debug("application published user post received " + post); 
     publisherPostListenerList.add(post); 
    } 

    public List<Post> getPublisherPostListenerList() { 
     return publisherPostListenerList; 
    } 

    public void setPublisherPostListenerList(List<Post> publisherPostListenerList) { 
     this.publisherPostListenerList = publisherPostListenerList; 
    } 
} 

метод контроллера для толкания сообщения абоненту

@RequestMapping(value="/getRealTimeServerPushUserPosts") 
    public SseEmitter getRealTimeServerPushUserPosts(@RequestParam("userId") int userId){ 
     SseEmitter sseEmitter = new SseEmitter(); 
     CustomUserDetail myUserDetails = currentUserAccessor.getCurrentLoggedInUser(); 
     User loggedInUser=myUserDetails.getUser(); 

     List<Integer> userPublisherIDList = this.userService.loadUserPublisherIdListWhichLoggedInUserFollows(loggedInUser); 
     List<Post> postList =eventListener.getPublisherPostListenerList(); 


     for(Integer userPublisherId : userPublisherIDList){ 
      for(Post post:postList){ 
        if((userPublisherId.intValue()) == (post.getUser().getUserId().intValue())){ 
         try { 
         sseEmitter.send(post); 
         postList.remove(post); //removes the post for all the subscribers as the list acts as a global list. 
        } catch (IOException e) { 
         logger.error(e); 
        } 
       } 
      } 
     } 
     return sseEmitter; 
    } 
+0

кто будет вызывать метод контроллера? – developer

+0

Я использую события, отправленные сервером, чтобы отправить сообщение клиенту/loggedInUser – underdog

+0

, почему вы не можете использовать базу данных вместо этого? – developer

ответ

2

Вы можете использовать Hazelcast IList. Это следует из семантики j.u.List и подходит для распределенных/кластерных сред.

Вы можете найти документацию here и примеры here. Другой вариант - использовать распределенную карту aka IMap.

Дайте мне знать, если у вас есть конкретные вопросы относительно деталей реализации.

Спасибо

-1

ApplicationListener предназначен для использования для обработки событий в пределах контекста приложения. Для решения вашей проблемы вам может потребоваться развернуть некоторые технологии сообщений (тема JMS). Вместо добавления PostSubmmittion в список ваш postSubmissionEventHandler() создаст и отправит сообщение для представления события в теме JMS. Теперь в вашем методе контроллера вы можете читать сообщения из темы, а затем публиковать их для подключенных пользователей.

Надеется, что это помогает

1

Вводу списка в кэшированной памяти в приложении может привести к ряду проблем (например, низкой масштабируемость ...). Почему бы вам не использовать базу данных в памяти, такую ​​как Redis? Таким образом, вы можете масштабировать свое приложение, и все экземпляры могут совместно использовать одну и ту же базу данных. Вы также гарантируете целостность данных.