Los eventos de juego, como ventanas emergentes, mensajes dentro del juego y tareas de personajes no jugables (NPC), deben llegar a cada jugador exactamente una vez. Ante millones de jugadores simultáneos, la pregunta fundamental que responde un filtro Bloom es: ¿Este jugador ya recibió esta notificación?
Esta guía muestra cómo conectarse a una instancia de Tair (Enterprise Edition) y usar filtros Bloom con Jedis para evitar notificaciones push duplicadas.
Funcionamiento
Un filtro Bloom es una estructura de datos probabilística que responde consultas de pertenencia con un uso mínimo de memoria. Devuelve uno de dos resultados:
Definitivamente no está en el conjunto: el jugador no ha recibido la notificación; envíela.
Posiblemente está en el conjunto: es probable que el jugador ya haya recibido la notificación; omítala.
La contrapartida es que pueden producirse falsos positivos, lo que significa que un pequeño número de jugadores podría perderse una notificación. En la mayoría de los escenarios de eventos de juego, este compromiso es aceptable dado el ahorro de memoria y la mejora en el rendimiento.
Por qué usar filtros Bloom en este caso de uso:
|
Ventaja |
Detalle |
|
Eficiencia de memoria |
Los filtros Bloom usan matrices de bits en lugar de almacenar ID de jugadores completos. Esto consume significativamente menos espacio que las estructuras de datos tradicionales, especialmente al registrar el estado de envío de millones de jugadores. |
|
Escalabilidad |
Los filtros Bloom funcionan de forma nativa en clústeres de Redis y escalan junto con su base de jugadores. |
Requisitos previos
Antes de empezar, asegúrese de contar con lo siguiente:
Una instancia de Tair (Enterprise Edition) con su endpoint y número de puerto
La contraseña de la instancia
Java 8 o posterior
Maven
Gestión de notificaciones con un filtro Bloom
El siguiente ejemplo usa tres operaciones para controlar la entrega de notificaciones:
|
Operación |
Descripción |
|
|
Crea el filtro Bloom con una capacidad objetivo y una tasa de falsos positivos determinada |
|
|
Comprueba si el ID de un jugador ya se encuentra en el filtro |
|
|
Registra que un jugador ha recibido la notificación |
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.
}
}
Salida esperada:
Push a notification to the player: player123
Player player123 has already been notified.
Selección de la tasa de falsos positivos
El parámetro error_rate en bfReserve controla el equilibrio entre precisión y memoria. Una tasa más baja mejora la precisión, pero incrementa el consumo de memoria.
|
Tasa |
Recomendado cuando |
|
1% (0.01) o superior |
La precisión no es crítica; por ejemplo, en precarga de caché o sistemas de recomendación. Consume menos memoria, aunque presenta una mayor tasa de falsos positivos. |
|
0.1% a 1% (0.001–0.01) |
Es adecuado para la mayoría de los escenarios, ya que ofrece un balance razonable entre eficiencia de memoria y precisión. |
|
0.01% (0.0001) o inferior |
Se requiere una tasa de falsos positivos extremadamente baja; por ejemplo, en sistemas de seguridad o aplicaciones financieras. Consume más memoria. |
En el ejemplo anterior, el filtro Bloom se crea con una tasa de falsos positivos del 1% y una capacidad para 50,000 ID de jugadores. Ajuste estos valores según el tamaño esperado de su base de jugadores y sus requisitos de precisión.
Falsos positivos y entrega de notificaciones
Un falso positivo ocurre cuando el filtro indica incorrectamente que un jugador ya recibió una notificación, cuando en realidad no fue así. El jugador afectado se pierde esa notificación. Para la mayoría de los eventos de juego, esto resulta aceptable. Si perderse una notificación no es admisible (por ejemplo, en recompensas sensibles al tiempo), considere complementar el filtro Bloom con un paso adicional de confirmación.
Compatibilidad con Tair (Enterprise Edition)
El filtro Bloom proporcionado por Tair (Enterprise Edition) es compatible con el filtro Bloom de Redis y usa los mismos comandos. Consulte Comandos de TairBloom para obtener la referencia completa de comandos.