Как реализовать ленту активности в социальной сети

Насколько я знаю, Java не имеет никакой аналогичной структуры к WaitHandle.WaitAny метод.

мне кажется, что это могло быть достигнуто через декоратора "WaitableFuture":

public WaitableFuture<T>
    extends Future<T>
{
    private CountDownLatch countDownLatch;

    WaitableFuture(CountDownLatch countDownLatch)
    {
        super();

        this.countDownLatch = countDownLatch;
    }

    void doTask()
    {
        super.doTask();

        this.countDownLatch.countDown();
    }
}

, Хотя это только работало бы, если бы это может быть вставлено перед кодом выполнения, с тех пор иначе, код выполнения не имел бы нового doTask() метод. Но я действительно не вижу способа сделать это, не опрашивая, если Вы не можете так или иначе получить контроль над будущим объектом перед выполнением.

Или если будущее всегда работает в его собственном потоке, и можно так или иначе получить тот поток. Тогда Вы могли породить новый поток для присоединения друг к другу поток, затем обработать механизм ожидания после возвратов соединения... Это было бы действительно ужасно и вызовет много издержек все же. И если некоторые будущие объекты не заканчиваются, у Вас могло бы быть много заблокированных потоков в зависимости от мертвых потоков. Если Вы не осторожны, это могло бы пропустить ресурсы памяти и системные ресурсы.

/**
 * Extremely ugly way of implementing WaitHandle.WaitAny for Thread.Join().
 */
public static joinAny(Collection<Thread> threads, int numberToWaitFor)
{
    CountDownLatch countDownLatch = new CountDownLatch(numberToWaitFor);

    foreach(Thread thread in threads)
    {
        (new Thread(new JoinThreadHelper(thread, countDownLatch))).start();
    }

    countDownLatch.await();
}

class JoinThreadHelper
    implements Runnable
{
    Thread thread;
    CountDownLatch countDownLatch;

    JoinThreadHelper(Thread thread, CountDownLatch countDownLatch)
    {
        this.thread = thread;
        this.countDownLatch = countDownLatch;
    }

    void run()
    {
        this.thread.join();
        this.countDownLatch.countDown();
    }
}
135
задан dreftymac 30 September 2018 в 11:23
поделиться

3 ответа

Резюме : Для примерно 1 миллиона активных пользователей и 150 миллионов сохраненных действий я стараюсь упростить:

  • Используйте реляционную базу данных для хранения уникальных действий (1 запись на действие / "то, что произошло") Сделайте записи как можно более компактными. Структура так, чтобы вы могли быстро получить пакет действий по идентификатору действия или с помощью набора идентификаторов друзей с ограничениями по времени.
  • Публикуйте идентификаторы действий в Redis всякий раз, когда создается запись активности, добавляя идентификатор в "поток действий" "список для каждого пользователя, который является другом / подписчиком, который должен видеть активность.

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


Я использую простую старую таблицу MySQL для работы примерно с 15 миллионами действий.

Это выглядит примерно так. это:

id             
user_id       (int)
activity_type (tinyint)
source_id     (int)  
parent_id     (int)
parent_type   (tinyint)
time          (datetime but a smaller type like int would be better) 

activity_type сообщает мне тип активности, source_id сообщает мне запись, с которой связано действие. Итак, если тип действия означает «добавленный в избранное», тогда я знаю, что source_id относится к идентификатору избранной записи.

parent_id / parent_type полезны для моего приложения - они говорят мне, с чем связана эта деятельность. Если книга была добавлена ​​в избранное, то parent_id / parent_type сообщит мне, что действие относится к книге (типу) с заданным первичным ключом (id)

Я индексирую на (user_id, time) и запрос действий, которые имеют user_id IN (... friends ...) AND time> some-cutoff-point . Отказ от идентификатора и выбор другого кластерного индекса может быть хорошей идеей - я не экспериментировал с этим.

Довольно простые вещи, но они работают, они просты, и с ними легко работать, когда ваши потребности меняются. Кроме того, если вы не используете MySQL, вы можете лучше работать с индексами.


Для более быстрого доступа к самым последним действиям я экспериментировал с Redis . Redis хранит все свои данные в памяти, поэтому вы не можете поместить туда все свои действия, но вы можете сохранить достаточно для большинства часто посещаемых экранов на вашем сайте. Последние 100 для каждого пользователя или что-то в этом роде. С Redis в миксе это может работать так:

  • Создайте свою запись активности MySQL.
  • Для каждого друга пользователя, создавшего действие, поместите идентификатор в их список действий в Redis.
  • Обрезать каждый список до последних X элементов.

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

Для более подробного объяснения того, что я я говорю, см. пример Twitter Redis: http://redis.io/topics/twitter-clone

Обновление от февраля 2011 г. У меня 50 миллионов активных действий на данный момент, и я не знаю » т ничего не изменил. В том, чтобы делать что-то подобное, хорошо то, что они используют компактные, маленькие строки. Я планирую внести некоторые изменения, которые повлекут за собой гораздо больше действий и больше запросов об этих действиях, и я определенно буду использовать Redis для ускорения работы. Я использую Redis в других областях, и он действительно хорошо работает для определенных типов проблем.

Обновление, июль 2014 г. У нас около 700 000 активных пользователей в месяц. Последние пару лет я использовал Redis (как описано в маркированном списке) для хранения последних 1000 идентификаторов действий для каждого пользователя. Обычно в системе около 100 миллионов записей активности, и они по-прежнему хранятся в MySQL и имеют тот же формат. Эти записи позволяют нам обойтись меньшим объемом памяти Redis, они служат в качестве записи данных об активности, и мы используем их, если пользователям нужно перейти на более раннюю страницу во времени, чтобы что-то найти.

Это не было '

236
ответ дан 23 November 2019 в 23:44
поделиться

Я думаю, что объяснение того, как работает система уведомлений на больших веб-сайтах, можно найти в вопросе о переполнении стека как веб-сайты социальных сетей вычисляют обновления друзей? в ответе Джереми Уолла . Он предлагает использовать Message Qeue и указывает два программного обеспечения с открытым исходным кодом, которые его реализуют:

  1. RabbitMQ
  2. Apache QPid

См. Также вопрос Как лучше всего реализовать поток социальной активности?

13
ответ дан 23 November 2019 в 23:44
поделиться

Это моя реализация потока активности с использованием mysql. Существует три класса: Activity, ActivityFeed, Subscriber.

Activity представляет запись действия, и ее таблица выглядит следующим образом:

id
subject_id
object_id
type
verb
data
time

Subject_id - это идентификатор объекта, выполняющего действие, object_id ] идентификатор объекта, который получает действие. тип и глагол описывает само действие (например, если пользователь добавляет комментарий к статье, они будут «комментировать» и «создать» соответственно), данные содержат дополнительные данные в чтобы избежать объединений (например, он может содержать имя и фамилию субъекта, заголовок и URL-адрес статьи, текст комментария и т. д.).

Каждое действие принадлежит одному или нескольким фидам действий, и они связаны таблицей, которая выглядит так:

feed_name
activity_id

В моем приложении у меня есть один канал для каждого пользователя и один канал для каждого элемента (обычно статьи в блогах), но они могут быть какими угодно.

Подписчик обычно является пользователем вашего сайта, но также может быть любым объектом в вашей объектной модели (например, статья может быть подписана на feed_action его создателя).

Каждый подписчик принадлежит одному или нескольким ActivityFeed, и, как и выше, они связаны таблицей ссылок такого типа :

feed_name
subscriber_id
reason

Поле причина здесь объясняет, почему подписчик подписался на канал. Например, если пользователь добавляет в закладки сообщение в блоге, причина в «закладке». Это помогает мне позже фильтровать действия для уведомлений пользователей.

Чтобы получить активность для подписчика, я выполняю простое объединение трех таблиц. Соединение выполняется быстро, потому что я выбираю несколько действий благодаря условию WHERE , которое выглядит как сейчас - время> несколько часов . Я избегаю других объединений благодаря полю данных в таблице действий.

Дополнительные пояснения к полю причина . Если, например, я хочу отфильтровать действия для уведомлений пользователя по электронной почте, и пользователь добавил в закладки сообщение в блоге (и поэтому он подписывается на канал сообщений с причиной «закладка»), я не хочу, чтобы пользователь получал уведомления по электронной почте о действиях над этим элементом, в то время как, если он комментирует сообщение (и поэтому он подписывается на канал сообщений с причиной «комментарий»), я хочу, чтобы он получал уведомление, когда другие пользователи добавляют комментарии к тому же сообщению. Поле причины помогает мне в этом различении (я реализовал его через класс ActivityFilter),

21
ответ дан 23 November 2019 в 23:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: