Eventos de jogos — janelas pop-up, mensagens in-game e tarefas de personagens não jogáveis (NPCs) — devem chegar a cada jogador exatamente uma vez. Com milhões de jogadores simultâneos, a principal questão que um filtro de Bloom responde é: este jogador já recebeu esta notificação?
Este guia demonstra como conectar-se a uma instância Tair (Enterprise Edition) e usar filtros de Bloom com Jedis para evitar notificações push duplicadas.
Como funciona
O filtro de Bloom é uma estrutura de dados probabilística que responde a consultas de pertinência com uso mínimo de memória. Ele retorna um dos dois resultados seguintes:
Definitivamente não está no conjunto — o jogador não recebeu a notificação; envie-a.
Possivelmente está no conjunto — o jogador pode ter recebido a notificação; ignore o envio.
Existe uma contrapartida: falsos positivos são possíveis, ou seja, um pequeno número de jogadores pode deixar de receber uma notificação. Para a maioria dos cenários de eventos em jogos, essa troca é aceitável diante dos ganhos de memória e desempenho.
Por que usar filtros de Bloom neste caso de uso:
|
Vantagem |
Detalhe |
|
Eficiência de memória |
Filtros de Bloom usam arrays de bits em vez de armazenar IDs completos de jogadores. Isso ocupa significativamente menos espaço do que estruturas de dados tradicionais, especialmente ao registrar o status de push de milhões de usuários. |
|
Escalabilidade |
Essa estrutura opera nativamente em clusters Redis e escala conforme sua base de jogadores aumenta. |
Pré-requisitos
Antes de começar, verifique se você tem:
Uma instância Tair (Enterprise Edition) com o endpoint e o número da porta
A senha da instância
Java 8 ou superior
Maven
Gerencie notificações com um filtro de Bloom
O exemplo a seguir usa três operações para controlar a entrega de notificações:
|
Operação |
Descrição |
|
|
Cria o filtro de Bloom com uma capacidade alvo e taxa de falso positivo |
|
|
Verifica se um ID de jogador já existe no filtro |
|
|
Registra que um jogador recebeu a notificação |
import redis.clients.jedis.*;
import redis.clients.jedis.UnifiedJedis;
public class TairBloomFilterDemo {
// Replace with your instance endpoint, port number, and password.
static HostAndPort hostAndPort = new HostAndPort("r-bp1y****svonly41srpd.redis.rds.aliyuncs.com", 6379);
static JedisClientConfig config = DefaultJedisClientConfig.builder().password("tw:Da***3").build();
static UnifiedJedis unifiedJedis = new UnifiedJedis(hostAndPort, config);
private static final String BLOOM_KEY = "activity_popup";
/**
* Creates the Bloom filter with a 1% false positive rate and capacity for 50,000 player IDs.
*/
public static void createBloom() {
try {
unifiedJedis.bfReserve(BLOOM_KEY, 0.01, 50000);
} catch (Exception e) {
e.printStackTrace(); // Handle connection timeouts and other errors.
}
}
/**
* Returns true if the player has not yet received the notification.
*/
public static boolean shouldShowPopup(String playerId) {
try {
return !unifiedJedis.bfExists(BLOOM_KEY, playerId);
} catch (Exception e) {
e.printStackTrace(); // Handle connection timeouts and other errors.
return true;
}
}
/**
* Records that the player has received the notification.
*/
public static void updatePopupState(String playerId) {
try {
unifiedJedis.bfAdd(BLOOM_KEY, playerId);
} catch (Exception e) {
e.printStackTrace(); // Handle connection timeouts and other errors.
}
}
/**
* Sends a notification to the player if they haven't received one yet.
*/
public static void handlePopup(String playerId) {
if (shouldShowPopup(playerId)) {
System.out.println("Push a notification to the player: " + playerId);
updatePopupState(playerId);
} else {
System.out.println("Player " + playerId + " has already been notified.");
}
}
public static void main(String[] args) {
createBloom();
String playerId = "player123";
handlePopup(playerId); // Sends the notification on first login.
handlePopup(playerId); // Skips the notification on subsequent logins.
}
}
Saída esperada:
Push a notification to the player: player123
Player player123 has already been notified.
Escolha uma taxa de falso positivo
O parâmetro error_rate em bfReserve controla o equilíbrio entre precisão e memória. Uma taxa menor melhora a precisão, mas aumenta o consumo de memória.
|
Taxa |
Quando usar |
|
1% (0,01) ou superior |
A precisão não é crítica — por exemplo, em pré-busca de cache ou sistemas de recomendação. Consome menos memória, mas apresenta maior taxa de falsos positivos. |
|
0,1% a 1% (0,001–0,01) |
Adequado para a maioria dos cenários — oferece um equilíbrio razoável entre eficiência de memória e precisão. |
|
0,01% (0,0001) ou inferior |
Recomendado quando uma baixa taxa de falsos positivos é essencial — como em sistemas de segurança ou aplicações financeiras. Requer mais memória. |
No exemplo, o filtro de Bloom é criado com uma taxa de falso positivo de 1% e capacidade para 50.000 IDs de jogadores. Ajuste esses valores com base na sua base esperada de jogadores e nos requisitos de precisão.
Falsos positivos e entrega de notificações
Um falso positivo ocorre quando o filtro informa incorretamente que um jogador já recebeu uma notificação, embora isso não tenha acontecido. O jogador afetado deixa de receber uma única notificação. Na maioria dos cenários de eventos de jogos, isso é aceitável. Caso a perda de uma notificação seja inaceitável — por exemplo, para recompensas sensíveis ao tempo — considere complementar o filtro de Bloom com uma etapa secundária de confirmação.
Compatibilidade com Tair (Enterprise Edition)
O filtro de Bloom fornecido pelo Tair (Enterprise Edition) é compatível com o filtro de Bloom do Redis e usa os mesmos comandos. Consulte Comandos TairBloom para obter a referência completa de comandos.