Многопоточный доступ к Twitter через Twitter4J

1

Я написал следующий код Java:

twitterStream.addListener(new StreamListener());

FilterQuery filterQuery = new FilterQuery();
filterQuery.follow(filteringUsers);
filterQuery.track(filteringWords);

twitterStream.filter(filterQuery);

для отслеживания некоторых пользователей и ключевых слов в Twitter (через Streaming API). Здесь StreamListener - это моя личная реализация слушателя.

Я отслеживаю множество ключевых слов, хэштегов и пользователей, и поэтому я накапливаю в памяти много твитов, ожидающих обработки. Фактически, я просто беру их через слушателя (в onStatus()) и смывая их в базе данных.

Тем не менее, факт, что они должны ждать в памяти, очевидно, насыщает память через несколько часов. За 20 минут я заработал в памяти 177000 LinkedBlockingQueue$Node и 1.272MB char[] (просматривается через профилирование).

Я бы хотел, чтобы конвейер работал непрерывно, и, очевидно, это невозможно в текущем состоянии.

Таким образом, я хотел бы знать, есть ли способ добавить несколько слушателей в многопоточность, чтобы они могли одновременно опустошать очередь твитов и ускорить обработку.

  1. В случае, если это возможно: эти слушатели одновременно опустошают очередь? Я имею в виду: может ли быть так, что они читали один и тот же твит?
  2. В случае, если это невозможно: как я могу решить свою проблему?

Заранее спасибо.

Теги:
multithreading
twitter
twitter4j

1 ответ

1
Лучший ответ

Хотя прямое многопоточное решение невозможно через Twitter4J, можно было бы решить имитировать обработку многопоточных запросов через класс слушателя.

Предположим, StreamListener - это ваша специализация слушателя StatusListener Twitter4J.

Мы копируем очередь внутри StreamListener как частный атрибут:

private LinkedBlockingQueue<String> tweets;

Очередь инициализируется в конструкторе:

tweets = new LinkedBlockingQueue<String>();

Более того, в конструкторе мы создаем пул потоков, предназначенный для чтения твитов из очереди (в партиях) и хранения их в базе данных:

    final ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
    Runnable tweetAnalyzer = defineMonitoringRunnable(tweetRepository);
    for (int i = 0; i < NUM_THREADS; i++) {
        executor.execute(tweetAnalyzer);
        try {
            Thread.sleep(THREADS_DELAY);
        } 
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

где объект Runnable может быть построен следующим образом:

private Runnable defineMonitoringRunnable(final TweetRepository tweetRepository) {
    return new Runnable() {

        @Override
        public void run() {
            List<String> tempTweets = new ArrayList<String>();

            while (true) {
                if (tweets.size() > 0) {
                    tempTweets.clear();
                    tweets.drainTo(tempTweets);

                    tweetRepository.insert(tempTweets);   
                }

                try {
                    Thread.sleep(TWEETS_SAVING_TIME);
                } 
                catch (InterruptedException ex) {
                    ex.printStackTrace();
                }

            }
        }
    };
}

(TWEETS_SAVING_TIME - время ожидания каждого объекта Thread между одним сохранением твита и другим)

Наконец, метод onStatus() сохраняет твиты в очереди, как только они приходят к слушателю:

@Override
public void onStatus(Status status) {   
    tweets.add(TwitterObjectFactory.getRawJSON(status));
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню