分布式多级缓存(本地缓存,redis缓存)

结构包:

 

 

 使用案例:

 

 

 实现效果:

1、基本并发的本地缓存,基于分布式轻量级锁的redis缓存

2、热缓存(高频访问持续缓存)+快速过期(本地缓存2秒,redis缓存10秒)

3、方法级别缓存清理 (@HybridCache 与@HybridChange 绑定管理缓存 )

4、基于HybridType接口的可扩展式作用域,目前已实现:全局、token

5、基于HybridLevel接口的可扩展式缓存处理,目前已实现:本地缓存、redis缓存

核心代码包:

package com.*.server.live.core.hybridCache;

import com.*.server.live.core.hybridCache.impl.DepthLocal;
import com.*.server.live.core.hybridCache.impl.DepthRedis;

import java.lang.annotation.*;

/**
 * 功能描述:多重缓存
 * 作者:唐泽齐
 * @case @HybridCache(scope = ScopeGlobal.class)
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface HybridCache {

    /*缓存深度*/
    Class<? extends HybridLevel> depth() default DepthRedis.class;

    /*缓存广度*/
    Class<? extends HybridType> scope();

}
@HybridCache
package com.*.server.live.core.hybridCache;

import com.*.server.live.core.hybridCache.impl.DepthRedis;

import java.lang.annotation.*;

/**
 * 功能描述:多重缓存更新
 * 作者:唐泽齐
 * @case @HybridChange(clazz = LiveStudioController.class,method = "getStudio",args = {Long.class})
 * @case @HybridChange(clazz = LiveStudioController.class,method = "getStudio",args = {})
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface HybridChange {

    /*关联类*/
    Class<? extends Object> clazz();

    /*关联方法*/
    String method();

    /*关联方法入参类型*/
    Class<?>[] args();
}
@HybridChange
package com.*.server.live.core.hybridCache;

import org.aspectj.lang.ProceedingJoinPoint;

import javax.validation.constraints.NotNull;

/**
 * 功能描述:多重缓存级别
 * 作者:唐泽齐
 */
public interface HybridLevel {
    /**
     * 缓存
     * @param key 缓存key
     * @param id 混村ID
     * @param o 缓存值
     * @param self 是否执行节点
     * @param joinPoint 节点
     * @return
     * @throws Throwable
     */
    Object cache(@NotNull String key,@NotNull String id, Object o,@NotNull boolean self,@NotNull ProceedingJoinPoint joinPoint) throws Throwable;

    /**
     * 清缓存
     * @param key 缓存key
     * @param self 是否执行节点
     * @param joinPoint 节点
     * @return
     * @throws Throwable
     */
    Object del(@NotNull String key,@NotNull boolean self,@NotNull ProceedingJoinPoint joinPoint) throws Throwable;
}
HybridLevel
package com.*.server.live.core.hybridCache;

/**
 * 功能描述:多重缓存级别
 * 作者:唐泽齐
 */
public interface HybridType {
    String token();
}
HybridType
package com.*.server.live.core.hybridCache;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.util.Md5Utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * 功能描述:多重缓存接口
 * 作者:唐泽齐
 */
@Aspect
@Configuration
public class HybridCacheInterceptor {
    @Resource
    ApplicationContext applicationContext;
    final static String cache = "hybridCache:";

    @Around(value = "@annotation(com.*.server.live.core.hybridCache.HybridCache)")
    public Object cache(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        HybridCache cacheAnnotation = method.getAnnotation(HybridCache.class);
        String key = getCacheKey(method);
        String id = getCacheId(method,joinPoint.getArgs());
        HybridLevel hybridLevel = applicationContext.getBean(cacheAnnotation.depth());
        return hybridLevel.cache(key, id, null, true, joinPoint);
    }

    @Around(value = "@annotation(com.*.server.live.core.hybridCache.HybridChange)")
    public Object del(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        HybridChange cacheAnnotation = method.getAnnotation(HybridChange.class);
        try {
            Method method1 = cacheAnnotation.clazz().getMethod(cacheAnnotation.method(),cacheAnnotation.args());
            HybridCache cache = method1.getAnnotation(HybridCache.class);
            String key = getCacheKey(method1);
            HybridLevel hybridLevel = applicationContext.getBean(cache.depth());
            return hybridLevel.del(key,true, joinPoint);
        } catch (Exception e) {
            return joinPoint.proceed();
        }
    }

    /*获取缓存key*/
    private String getCacheKey(Method method) {
        return cache + method.getDeclaringClass().getSimpleName() + ":" + method.getName();
    }

    ;

    /*获取缓存id*/
    private String getCacheId(Method method,Object[] args) {
        HybridCache cacheAnnotation = method.getAnnotation(HybridCache.class);
        HybridType hybridType = applicationContext.getBean(cacheAnnotation.scope());
        return Md5Utils.getMD5((hybridType.token() + JSON.toJSONString(args)).getBytes());
    }

    ;

}
HybridCacheInterceptor

扩展代码包:

package com.*.server.live.core.hybridCache.impl;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.*.server.live.core.hybridCache.HybridLevel;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 功能描述:本地缓存
 * 作者:唐泽齐
 */
@Component
public class DepthLocal implements HybridLevel {
    /*自带读写锁*/
    public volatile static Cache<String, Object> localCache = CacheBuilder.newBuilder().initialCapacity(144).expireAfterAccess(2, TimeUnit.SECONDS).build();

    @Override
    public Object cache(String key, String id, Object o, boolean search, ProceedingJoinPoint joinPoint) throws Throwable {
        String keyId = key + ":" + id;
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        if (search && o == null && (o = localCache.getIfPresent(keyId)) == null) {
            synchronized (ms.getMethod()) {
                if ((o = localCache.getIfPresent(keyId)) == null) {
                    o = joinPoint.proceed();
                    localCache.put(keyId, o);
                }
            }
        }
        if (o == null) {
            o = localCache.getIfPresent(keyId);
        } else {
            localCache.put(keyId, o);
        }
        return o;
    }

    @Override
    public Object del(String key, boolean self, ProceedingJoinPoint joinPoint) throws Throwable {
        Object o = null;
        if (self) {
            o = joinPoint.proceed();
        }
        localCache.cleanUp();
        return o;
    }
}
DepthLocal
package com.*.server.live.core.hybridCache.impl;

import cn.hutool.core.util.RandomUtil;
import com.*.common.redis.service.RedisService;
import com.*.server.live.core.hybridCache.HybridLevel;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 功能描述:redis缓存
 * 作者:唐泽齐
 */
@Component
public class DepthRedis implements HybridLevel {

    @Resource
    private RedisService redisService;
    @Resource
    private HybridLevel depthLocal;

    @Override
    public synchronized Object cache(String key, String id, Object o,boolean search, ProceedingJoinPoint joinPoint) throws Throwable {
        o = depthLocal.cache(key, id, o,false,joinPoint);
        String lock = getLock(key, id);
        if(search && o== null && (o=redisService.hget(key , id)) == null ) {
            for (;;) {
                if(redisService.incr(lock,1l) <= 1l && (o=redisService.hget(key , id)) == null ) {
                    try {
                        o = joinPoint.proceed();
                        redisService.hset(key , id, o, 10l);
                    } catch (Throwable e) {
                        throw e;
                    }finally {
                        redisService.del(lock);
                    }
                } else {
                    o = redisService.hget(key , id);
                }
                if(o == null) {
                    Thread.sleep(1);
                } else {
                    break;
                }
            }
        }
        if (o == null) {
            o = redisService.hget(key , id);
        } else {
            depthLocal.cache(key, id, o,false,joinPoint);
            redisService.hset(key , id, o, 10l);
        }
        return o;
    }

    @Override
    public Object del(String key,boolean self,  ProceedingJoinPoint joinPoint) throws Throwable {
        Object o = null;
        if (self) {
            o = joinPoint.proceed();
        }
        redisService.del(key);
        depthLocal.del(key,false,joinPoint);
        return o;
    }

    private String getLock(String key, String id) {
        return this.getClass().getSimpleName()+":"+key+":"+id;
    }

}
DepthRedis
package com.*.server.live.core.hybridCache.impl;

import com.*.server.live.core.hybridCache.HybridType;
import org.springframework.stereotype.Component;

/**
 * 功能描述:全局级别
 * 作者:唐泽齐
 */
@Component
public class ScopeGlobal implements HybridType {
    @Override
    public String token() {
        return "";
    }
}
ScopeGlobal
package com.*.server.live.core.hybridCache.impl;

import com.*.common.core.utils.*Util;
import com.*.server.live.core.hybridCache.HybridType;
import org.springframework.stereotype.Component;

/**
 * 功能描述:token级别
 * 作者:唐泽齐
 */
@Component
public class ScopeToken implements HybridType {
    @Override
    public String token() {
        return *Util.getCurrentTokenValue();
    }
}
ScopeToken

问题修复:
1.json问题: 类HybridCacheInterceptor调整使用JSON.toJSON(args).toString()

2.死循环问题:类DepthRedis调整使用

  

@Override
public synchronized Object cache(String key, String id, Object o,boolean search, ProceedingJoinPoint joinPoint) throws Throwable {
o = depthLocal.cache(key, id, o,false,joinPoint);
final String lock = getLock(key, id);
if(search && o== null && (o=redisService.hget(key , id)) == null ) {
if(redisService.incr(lock,1l) <= 1l && (o=redisService.hget(key , id)) == null ) {
try {
o = joinPoint.proceed();
redisService.hset(key , id, o, RandomUtil.randomLong(10l,20l));
} catch (Throwable e) {
throw e;
}finally {
redisService.del(lock);
}
} else {
Thread.sleep(10);
o = redisService.hget(key , id);
}
}
if (o == null) {
Thread.sleep(10);
o = redisService.hget(key , id);
} else {
depthLocal.cache(key, id, o,false,joinPoint);
redisService.hset(key , id, o, 10l);
}
return o;
}

 

问题修复:
1.取消清除缓存时的常规重试机制:

HybridCacheInterceptor -->
public Object del(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Method method = ms.getMethod();
String updateKey = getCacheKey(method);
HybridChange cacheAnnotation = method.getAnnotation(HybridChange.class);
Method method1 = cacheAnnotation.clazz().getMethod(cacheAnnotation.method(),cacheAnnotation.args());
HybridCache cache = method1.getAnnotation(HybridCache.class);
String key = getCacheKey(method1);
try {
HybridLevel hybridLevel = applicationContext.getBean(cache.depth());
Object o = hybridLevel.del(key, true, joinPoint);
log.info("多级缓存 清除 {}-->{}",updateKey,key);
return o;
} catch (Exception e) {
log.info("多级缓存 清除失败 {}-->{}:{}",updateKey,key,e);
throw e;
}
}
问题修复:
1、分布式情况下缓存死锁问题:
@Override
public synchronized Object cache(String key, String id, Object o,boolean search, ProceedingJoinPoint joinPoint) throws Throwable {
o = depthLocal.cache(key, id, o,false,joinPoint);
final String lock = getLock(key, id);
if(search && o== null && (o=redisService.hget(key , id)) == null ) {
if(redisService.increx(lock,1l,1l) <= 1l && (o=redisService.hget(key , id)) == null ) {
try {
o = joinPoint.proceed();
redisService.hset(key , id, o, RandomUtil.randomLong(10l,20l));
} catch (Throwable e) {
throw e;
}finally {
redisService.del(lock);
}
} else {
Thread.sleep(10);
o = redisService.hget(key , id);
}
}
if (o == null) {
Thread.sleep(10);
o = redisService.hget(key , id);
} else {
depthLocal.cache(key, id, o,false,joinPoint);
redisService.hset(key , id, o, 10l);
}
return o;
}

posted on 2022-07-11 16:35  instr  阅读(1290)  评论(0编辑  收藏  举报

导航