Caché local y caché distribuido: implementan manualmente un sistema de caché

¿Qué es el caché y para qué sirve?

La caché se refiere al almacenamiento de los objetos de datos de uso común en el programa o sistema en un medio específico, como la memoria, para evitar la pérdida de rendimiento causada por la recreación u organización de datos cada vez que se llama al programa, mejorando así la velocidad de funcionamiento general del sistema.

Con la arquitectura del sistema actual, la solicitud del usuario generalmente pasa primero por el sistema de caché. Si no hay datos relevantes en el caché, los datos correspondientes se consultarán en otros sistemas y se almacenarán en el caché, y finalmente se devolverán al llamador.

Caché local

El componente de caché a nivel de programa se caracteriza por el hecho de que la caché local y la aplicación se ejecutarán en el mismo proceso, la operación de la caché local es extremadamente rápida y no habrá demoras de red ni sobrecarga en el mismo proceso.

La caché local es adecuada para escenarios de aplicaciones de un solo nodo que no son de clúster. Su ventaja es que es rápida. La desventaja es que varios programas no pueden compartir la caché . Por ejemplo, se guarda la información distribuida de la sesión del usuario. Dado que el servidor al que accede cada El usuario puede ser diferente, si no se puede compartir Almacenamiento en caché, significa que el sistema puede bloquear cada operación de solicitud, porque la información de la sesión solo se almacena en un servidor determinado. Cuando la solicitud no se reenvía a este servidor que almacena la información del usuario, se considerará no conforme. Operación de infracción de inicio de sesión.

Además, la imposibilidad de compartir la caché puede causar un desperdicio de recursos del sistema, debido a que cada sistema mantiene su propia caché por separado, y la misma caché puede ser almacenada por separado por varios sistemas, desperdiciando así los recursos del sistema.

Puede usar EhCache y Guava de Google para lograr

Análisis del uso y características de EhCache y Guayaba

EhCache admite caché de memoria y caché de disco, y admite múltiples algoritmos de eliminación como LRU (menos utilizado recientemente), LFU (menos utilizado) y FIFO (primero en entrar, primero en salir), y admite un sistema de caché distribuido.

EhCache era originalmente un componente de marco de caché local independiente. En el desarrollo posterior (a partir de la versión 1.2), es compatible con caché distribuida. La caché distribuida admite principalmente RMI, JGroups, EhCache Server y otros métodos.

El algoritmo LRU tiene una desventaja. Por ejemplo, si recientemente se ha accedido una vez a un valor de clave que no se ha utilizado durante mucho tiempo, incluso si es la caché menos utilizada, no se eliminará; mientras que el algoritmo LFU resuelve el problema de que ocasionalmente se accede una vez., Los datos no se eliminarán. Elimina los datos en función del número total de accesos. La idea central es "Si se ha accedido a los datos varias veces en el pasado, entonces se accederá más a menudo en el futuro ". Por tanto, LFU puede entenderse como un algoritmo de eliminación más razonable que LRU.

Configurar en el proyecto maven

<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->

<dependency>

    <groupId>org.ehcache</groupId>

    <artifactId>ehcache</artifactId>

    <version>3.8.1</version>

</dependency>

Uso de EhCache

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;

public class EhcacheLearn {
    
    
    public static void main(String[] args) {
    
    
        //创建缓存管理器
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
        //初始化 Ehcache
        cacheManager.init();
        //创建缓存(存储器)
        Cache<String, String> tangTangIsLearning = cacheManager.createCache("TangTangIsLearning",
                CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(10)));//设置缓存的最大容量
        //设置缓存
        tangTangIsLearning.put("key","value");
        //读取缓存
        String value = tangTangIsLearning.get("key");
        System.out.println(value);
        //关闭缓存
        cacheManager.close();


    }
}

CacheManager: Es un administrador de caché, que puede ser creado por instancias singleton o múltiples, y también es la clase de entrada de Ehcache;

Caché: cada CacheManager puede administrar múltiples Caches, y cada Cache puede almacenar múltiples elementos en un modo hash.

Su relación se muestra en la siguiente figura:

Inserte la descripción de la imagen aquí

La característica de EhCache es que es relativamente simple de usar, y su paquete jar no es pequeño y se puede usar normalmente después de una configuración simple. EhCache es más flexible de usar. Admite la configuración de múltiples estrategias de almacenamiento en caché. Es compatible con el almacenamiento en memoria caché y en disco. Después de EhCache 1.2, también admite el almacenamiento en caché distribuido.

Guava Cache es una subfunción del código abierto Guava de Google. Es una solución de implementación de caché local basada en memoria que proporciona un mecanismo de operación de caché seguro para subprocesos.

El diseño arquitectónico de Guava Cache está inspirado en ConcurrentHashMap, que utiliza bloqueos de grano fino basados ​​en múltiples segmentos para garantizar la seguridad de los subprocesos y, al mismo tiempo, admite escenarios de uso de alta concurrencia. Guava Cache opera en pares clave-valor de manera similar a las colecciones de mapas, pero con una lógica de procesamiento adicional, como la caducidad y la eliminación.

Caché distribuida

La caché distribuida se refiere a un mecanismo de almacenamiento en caché que separa las aplicaciones y los componentes de la caché, de modo que varios sistemas de aplicaciones pueden compartir un conjunto de datos almacenados en caché. Se caracteriza por los servicios de caché compartidos y la implementación de clústeres, lo que proporciona alta disponibilidad para el entorno operativo y el programa del sistema de caché. mecanismo operativo para compartir caché.

Utilice Redis o Memcached. Dado que Redis en sí es un sistema de almacenamiento en caché independiente, se puede utilizar como un tercero para proporcionar almacenamiento en caché de datos compartidos, y la distribución de Redis admite los modos maestro-esclavo, centinela y clúster, por lo que puede admitir el almacenamiento en caché distribuido. En el caso de Memcached, es similar.

Implementar manualmente un sistema de almacenamiento en caché

Actualmente existen tres estrategias de vencimiento comunes

Eliminación programada

Significa crear un evento cronometrado al establecer el tiempo de expiración del valor de la clave. Cuando se alcanza el tiempo de expiración, el manejador de eventos ejecutará la operación de borrar la clave expirada. Su ventaja es que puede liberar el espacio de la memoria a tiempo, pero la desventaja es que es necesario abrir varios eventos de ejecución retardada para procesar la tarea de limpieza, lo que provocará que una gran cantidad de eventos de tareas se acumulen y ocupen una gran cantidad de recursos del sistema. .

Eliminación perezosa

La clave caducada no se eliminará activamente, pero se juzgará si el valor caduca cada vez que se solicita. Si caduca, el valor de la clave se eliminará, de lo contrario se devolverá el valor normal. Su ventaja es que solo ocupa una pequeña cantidad de recursos del sistema, pero la desventaja es que no se borra a tiempo y provocará una cierta cantidad de espacio desperdiciado.

Eliminar regularmente

Significa verificar la base de datos a intervalos regulares y eliminar aleatoriamente algunos valores clave caducados.

Redis utiliza dos estrategias: eliminación diferida y eliminación periódica.

Primero, debe definir una clase de entidad que almacene los valores de la caché. Esta clase contiene información relacionada con la caché, como la clave y el valor de la caché, el tiempo de almacenamiento de la caché, la hora del último uso y las horas de visita (campos reservados para admitir la eliminación de la caché LFU), luego use ConcurrentHashMap para guardar la clave en caché y los objetos de valor (la clase de entidad del valor en caché), y luego agregue una clase de herramienta para operaciones de almacenamiento en caché para agregar y eliminar el caché, y finalmente, cuando se inicie el caché, inicie un hilo para Para detectar y eliminar la caché caducada, el código de implementación es el siguiente.

Código de clase de entidad:

import lombok.Getter;
import lombok.Setter;

import java.util.Comparator;

@Setter
@Getter
public class MyCacheEntity implements Comparable<MyCacheEntity> {
    
    
    private Object key;
    private Object value;
    private long lastTime;
    private long writeTime;
    private long  expireTime;
    private Integer hitCount;


    @Override
    public int compareTo(MyCacheEntity o) {
    
    
        return hitCount.compareTo(o.hitCount);
    }
}

Definir el objeto de caché

import java.util.concurrent.ConcurrentHashMap;

public class CacheGlobal {
    
    
    //全局缓存对象
    public static ConcurrentHashMap<String,MyCacheEntity> concurrentHashMap=new ConcurrentHashMap<>();
}

Clase de herramienta de caché, con funciones de eliminación y actualización

import org.apache.commons.lang3.StringUtils;

public class CacheUtils {
    
    
    /**
     * 添加缓存
     * @param key
     * @param value
     * @param expire
     */
    public void put(String key, Object value, long expire) {
    
    
        // 非空判断,借助 commons-lang3
        if (StringUtils.isBlank(key)) return;
        // 当缓存存在时,更新缓存
        if (CacheGlobal.concurrentHashMap.containsKey(key)) {
    
    
            MyCacheEntity cache = CacheGlobal.concurrentHashMap.get(key);
            cache.setHitCount(cache.getHitCount() + 1);
            cache.setWriteTime(System.currentTimeMillis());
            cache.setLastTime(System.currentTimeMillis());
            cache.setExpireTime(expire);
            cache.setValue(value);
            return;
        }
        // 创建缓存
        MyCacheEntity cache = new MyCacheEntity();
        cache.setKey(key);
        cache.setValue(value);
        cache.setWriteTime(System.currentTimeMillis());
        cache.setLastTime(System.currentTimeMillis());
        cache.setHitCount(1);
        cache.setExpireTime(expire);
        CacheGlobal.concurrentHashMap.put(key, cache);
    }
    /**
     * 获取缓存
     * @param key
     * @return
     */
    public Object get(String key) {
    
    
        // 非空判断
        if (StringUtils.isBlank(key)) return null;
        // 字典中不存在
        if (CacheGlobal.concurrentHashMap.isEmpty()) return null;
        if (!CacheGlobal.concurrentHashMap.containsKey(key)) return null;
        MyCacheEntity cache = CacheGlobal.concurrentHashMap.get(key);
        if (cache == null) return null;
        // 惰性删除,判断缓存是否过期
        long timoutTime = System.currentTimeMillis() - cache.getWriteTime();
        // 缓存过期
        if (cache.getExpireTime() <= timoutTime) {
    
    
            // 清除过期缓存
            CacheGlobal.concurrentHashMap.remove(key);
            return null;
        }
        cache.setHitCount(cache.getHitCount() + 1);
        cache.setLastTime(System.currentTimeMillis());
        return cache.getValue();
    }
}

Verifique si la caché expira en un intervalo de 10 segundos y límpiela después de la expiración

import java.util.concurrent.TimeUnit;

public class ExpireThread implements Runnable {
    
    


    @Override
    public void run() {
    
    
        int i=0;
        while (i<2)
            try {
    
    
                //每10s检查一次
                TimeUnit.SECONDS.sleep(10);
                expireCache();
            }catch (Exception e){
    
    
                e.printStackTrace();
            }finally {
    
    
                i++;
            }
    }

    /*缓存检测和清除方法*/
    private void expireCache()
    {
    
    
        System.out.println("检测缓存是否过期");
        for (String key : CacheGlobal.concurrentHashMap.keySet()) {
    
    
            MyCacheEntity cache = CacheGlobal.concurrentHashMap.get(key);
            //当前时间减去写入时间
            long timOutTime= System.currentTimeMillis() - cache.getWriteTime();
            if(cache.getExpireTime()>timOutTime)
            {
    
    
                //没有过期
                continue;
            }
            //清除过期缓存
            CacheGlobal.concurrentHashMap.remove(key);
        }

    }
}

Clase de prueba

import java.util.concurrent.TimeUnit;

public class MyCacheTest {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        CacheUtils cache=new CacheUtils();
        cache.put("name","TangTang",10);
        String name = (String) cache.get("name");
        System.out.println(name);
        String sex = (String) cache.get("Sex");
        System.out.println(sex);
        new ExpireThread().run();
        TimeUnit.SECONDS.sleep(13);
        String name2 = (String) cache.get("name");
        System.out.println(name2);
    }
}

Supongo que te gusta

Origin blog.csdn.net/tangshuai96/article/details/111396598
Recomendado
Clasificación