Construindo um sistema de notificação [fechado]

170

Estou no início da criação de um sistema de notificação no estilo do Facebook para nossa página (tipo de jogo social) e agora estou pesquisando qual seria a melhor maneira de projetar esse sistema. Não estou interessado em enviar notificações ao usuário ou algo assim (por enquanto, até mesmo). Estou pesquisando como criar o sistema no servidor (como armazenar notificações, onde armazená-las, como buscá-las, etc ...).

Então ... alguns requisitos que temos:

  • nos horários de pico, temos cerca de um mil usuários simultâneos conectados (e muitos outros convidados, mas eles não são importantes aqui, pois não terão notificações) que gerarão muitos eventos
  • haverá diferentes tipos de notificações (o usuário A adicionou você como amigo, o usuário B comentou seu perfil, o usuário C curtiu sua imagem, o usuário D venceu você no jogo X, ...)
  • a maioria dos eventos gera 1 notificação para 1 usuário (o usuário X gostou da sua imagem), mas haverá casos em que um evento gerará muitas notificações (é o aniversário do usuário Y, por exemplo)
  • as notificações devem ser agrupadas; se, por exemplo, quatro usuários diferentes, como uma imagem, o proprietário dessa imagem receber uma notificação informando que quatro usuários gostaram da imagem e não quatro notificações separadas (assim como o FB)

OK, então o que eu estava pensando é que eu deveria criar algum tipo de fila para armazenar eventos quando eles acontecerem. Então, eu teria um trabalho em segundo plano ( gearman ?) Que examinaria essa fila e geraria notificações com base nesses eventos. Esse trabalho armazenaria notificações no banco de dados para cada usuário (portanto, se um evento afetar 10 usuários, haveria 10 notificações separadas). Então, quando o usuário abria uma página com a lista de notificações, eu lia todas as notificações para ele (pensamos em limitar isso a 100 notificações mais recentes), agrupá-las e finalmente exibi-las.

Coisas que me preocupam com essa abordagem:

  • complexo como o inferno :)
  • é o banco de dados com o melhor armazenamento aqui (estamos usando MySQL) ou devo usar outra coisa (redis também parece ser um bom ajuste)
  • o que devo armazenar como notificação? ID do usuário, ID do usuário que iniciou o evento, tipo de evento (para que eu possa agrupá-los e exibir o texto apropriado), mas meio que não sei como armazenar os dados reais da notificação (por exemplo, URL e título da imagem que foi gostado). Devo apenas "assar" essas informações ao gerar a notificação ou armazenar o ID do registro (imagem, perfil, ...) afetado e puxar as informações para fora do banco de dados ao exibir a notificação.
  • o desempenho deve ser bom aqui, mesmo que eu precise processar 100 notificações em tempo real ao exibir a página de notificações
  • possível problema de desempenho em cada solicitação, porque eu precisaria exibir o número de notificações não lidas para o usuário (o que poderia ser um problema por si só, pois agruparia as notificações). Isso poderia ser evitado, se eu gerasse a exibição de notificações (onde elas estão agrupadas) em segundo plano e não em tempo real

Então, o que você acha da minha solução proposta e das minhas preocupações? Por favor, comente se você acha que devo mencionar qualquer outra coisa que seja relevante aqui.

Ah, estamos usando PHP para nossa página, mas acho que não deve ser um grande fator aqui.

Jan Hančič
fonte
Quanto tempo você levou para criar esse sistema de notificação como um homem se esforça. Eu só quero ter uma estimativa para fazer os cronogramas de acordo.
Shaharyar
@ Shaharyar Acho que depende da complexidade do sistema de notificação.
tyan
Eu usei o mesmo sistema com o MySQL para criar um sistema de notificação baseado em prioridade. O bom é que ele é dimensionado para alguns milhares de usuários; se for mais do que isso, explode, especialmente no Android e GCM. Gostaria de conhecer alternativas ao MySQL como redis, rabbitMQ, Kafka, que naturalmente exibem uma fila de mensagens, tipo de funcionalidade.
Ankit Marothi

Respostas:

168

Uma notificação é sobre algo (objeto = evento, amizade ..) sendo alterado (verbo = adicionado, solicitado ..) por alguém (ator) e relatado ao usuário (sujeito). Aqui está uma estrutura de dados normalizada (embora eu tenha usado o MongoDB). Você precisa notificar certos usuários sobre alterações. Portanto, são notificações por usuário. Ou seja, se houver 100 usuários envolvidos, você gera 100 notificações.

╔═════════════╗      ╔═══════════════════╗      ╔════════════════════╗
║notification ║      ║notification_object║      ║notification_change ║
╟─────────────╢      ╟───────────────────╢      ╟────────────────────╢
║ID           ║—1:n—→║ID                 ║—1:n—→║ID                  ║
║userID       ║      ║notificationID     ║      ║notificationObjectID║
╚═════════════╝      ║object             ║      ║verb                ║
                     ╚═══════════════════╝      ║actor               ║
                                                ╚════════════════════╝

(Adicione campos de tempo onde achar melhor)

Isso é basicamente para agrupar alterações por objeto, para que você possa dizer "Você tem 3 solicitações de amizade". E agrupar por ator é útil, para que você possa dizer "O usuário James Bond fez alterações em sua cama". Isso também oferece a capacidade de traduzir e contar notificações conforme você desejar.

Mas, como objeto é apenas um ID, você precisaria obter todas as informações extras sobre o objeto que deseja com chamadas separadas, a menos que o objeto realmente mude e deseje mostrar esse histórico (por exemplo, "usuário alterou o título do evento para ... ")

Como as notificações estão quase em tempo real para os usuários no site, eu as vincularia ao cliente nodejs + websockets com o php pressionando update para nodejs para todos os ouvintes à medida que as alterações fossem adicionadas.

Artjom Kurapov
fonte
1
notification_object.object tipo de mudança identifica, como uma corda "amizade" A referência real para alterado objeto com seus dados extras que eu falar é em notification_change.notificationObjectID
Artjom Kurapov
2
Esta pode ser uma pergunta idiota, mas com esta configuração, o que você faz quando o usuário vê ou age na notificação? Você acabou de removê-lo do banco de dados ou apenas usa datas para ver se o usuário efetuou login desde que a notificação foi criada?
Jeffery Mills
4
Sei que esse tópico já é bastante antigo, no entanto, estou um pouco intrigado com a primeira tabela, qual é exatamente o objetivo dessa tabela? qual é a vantagem de ter isso como uma tabela separada e colocar o ID do usuário na tabela notification_object? Em outras palavras, quando você criará uma nova entrada na notificação e quando apenas adicionará um objeto e mudará para uma notificação existente com essa estrutura?
Bas Goossen 27/02
3
@JefferyMills Você poderia ter um campo de status como is_notification_read na notificationtabela e marcá-lo adequadamente, se for unread, readou deleted.
Kevin
2
Também lutei para entender alguns aspectos dessa solução e fiz uma pergunta separada sobre isso: dba.stackexchange.com/questions/99401/…
user45623
27

Esta é realmente uma pergunta abstrata, então acho que teremos que discuti-la em vez de apontar o que você deve ou não fazer.

Aqui está o que penso sobre suas preocupações:

  • Sim, um sistema de notificação é complexo, mas não como o inferno. Você pode ter muitas abordagens diferentes na modelagem e implementação de tais sistemas, e elas podem ter uma complexidade média a alta;

  • Pesonally, eu sempre tento fazer coisas orientadas a banco de dados. Por quê? Como posso garantir o controle total de tudo o que está acontecendo - mas esse sou apenas eu, você pode ter controle sem uma abordagem orientada a banco de dados; confie em mim, você vai querer controlar esse caso;

  • Deixe-me exemplificar um caso real para você, para que você possa começar de algum lugar. No ano passado, modelei e implementei um sistema de notificação em algum tipo de rede social (não como o Facebook, é claro). O jeito que eu costumava armazenar notificações lá? Eu tinha uma notificationstabela em que mantinha o generator_user_id(o ID do usuário que está gerando a notificação), o target_user_id(meio óbvio, não é?), O notification_type_id(que se referia a uma tabela diferente com os tipos de notificação) e todos o material necessário que precisamos preencher nossas tabelas (timestamps, flags, etc). Minha notification_typestabela costumava ter uma relação com uma notification_templatestabela, que armazenava modelos específicos para cada tipo de notificação. Por exemplo, eu tinha um POST_REPLYtipo, que tinha um tipo de modelo {USER} HAS REPLIED ONE OF YOUR #POSTS. A partir daí, eu apenas tratei o{}como uma variável e #como um link de referência;

  • Sim desempenho deve e deve estar ok. Quando você pensa em notificações, pensa no servidor empurrando da cabeça aos pés. Se você fizer isso com solicitações de ajax ou o que for, precisará se preocupar com o desempenho. Mas acho que é uma segunda preocupação;

É claro que esse modelo que eu projetei não é o único que você pode seguir, nem o melhor. Espero que minha resposta, pelo menos, siga você na direção certa.

Daniel Ribeiro
fonte
Por que eu não teria controle com outro armazenamento de dados?
Jan Hančič
Bem, eu não disse isso. O que eu disse é que só posso garantir o controle de dados com uma abordagem orientada a banco de dados; mas isso sou só eu. Vou reformular isso.
Daniel Ribeiro
@DanielRibeiro, os marcadores ({...}) no modelo de notificação precisam substituir os dados dos marcadores dos diferentes conjuntos de tabelas no banco de dados para os diferentes tipos de notificações. Por exemplo, um modelo é "{usuário} curtiu sua foto.", Outro modelo é "Seu {nome da página} tem um novo gosto". Etc. {PageName} e {user} e outros espaços reservados serão mapeados a partir da tabela do banco de dados diferente; portanto, qual deve ser o esquema para obter o valor dos espaços reservados dinamicamente.
Ashish Shukla 27/03
DanielRibeiro como você substituído espaços reservados como solicitado por @Ashish Shukla,
Shantaram Tupe
@AshishShukla você usou ou substituiu espaços reservados e como?
Shantaram Tupe
8
╔════════════════════╗
║notification        ║
╟────────────────────╢
║Username            ║
║Object              ║
║verb                ║
║actor               ║
║isRead              ║
╚════════════════════╝

Parece uma boa resposta em vez de ter duas coleções. Você pode consultar por nome de usuário, objeto e isRead para obter novos eventos (como 3 solicitações de amizade pendentes, 4 perguntas, etc ...)

Deixe-me saber se houver algum problema com este esquema.

Kaphy
fonte
3
A resposta principal usou uma estrutura de dados normalizada, o que significa que não há redundâncias nas tabelas. Sua resposta faz isso?
Aaron Hall
4

Pessoalmente, não entendo muito bem o diagrama da resposta aceita; portanto, anexarei uma base de diagrama de banco de dados ao que aprendi com a resposta aceita e outras páginas.

insira a descrição da imagem aqui

Melhorias são bem recebidas.

Jason Glez
fonte
Parece que o message_template estaria na tabela NotificationType. Também parece que o main_url estaria na tabela de notificações, então você poderia eliminar a tabela Notification_Message. Você pode explicar o motivo de ter a tabela NotificationMessage por conta própria?
Jeff Ryan