diff --git a/opsli-common/src/main/java/org/opsli/common/annotation/HotDataDel.java b/opsli-common/src/main/java/org/opsli/common/annotation/HotDataDel.java new file mode 100644 index 00000000..7f99c449 --- /dev/null +++ b/opsli-common/src/main/java/org/opsli/common/annotation/HotDataDel.java @@ -0,0 +1,30 @@ +package org.opsli.common.annotation; + +import org.opsli.common.constants.CacheConstants; + +import java.lang.annotation.*; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.common.annotation + * @Author: Parker + * @CreateTime: 2020-09-16 16:36 + * @Description: 热数据 - Get + * + * 添加在 Service get 方法上 , 默认获得 传入对象 key为id的数据 + * + * 调用 热点数据 不论增加缓存 还是 删除缓存 + * 返回值 必须为 集成了 BaseEntity 的 类 + * + * 注意:不论是什么缓存,只要是缓存 就多少会有一致性的问题,针对不是那么重要的数据 且高频访问的数据可以缓存起来 + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface HotDataDel { + + /** 缓存源名字 */ + String name() default CacheConstants.HOT_DATA; + +} diff --git a/opsli-common/src/main/java/org/opsli/common/annotation/HotDataPut.java b/opsli-common/src/main/java/org/opsli/common/annotation/HotDataPut.java new file mode 100644 index 00000000..6179feda --- /dev/null +++ b/opsli-common/src/main/java/org/opsli/common/annotation/HotDataPut.java @@ -0,0 +1,30 @@ +package org.opsli.common.annotation; + +import org.opsli.common.constants.CacheConstants; + +import java.lang.annotation.*; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.common.annotation + * @Author: Parker + * @CreateTime: 2020-09-16 16:36 + * @Description: 热数据 - Get + * + * 添加在 Service get 方法上 , 默认获得 传入对象 key为id的数据 + * + * 调用 热点数据 不论增加缓存 还是 删除缓存 + * 返回值 必须为 集成了 BaseEntity 的 类 + * + * 注意:不论是什么缓存,只要是缓存 就多少会有一致性的问题,针对不是那么重要的数据 且高频访问的数据可以缓存起来 + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface HotDataPut { + + /** 缓存源名字 */ + String name() default CacheConstants.HOT_DATA; + +} diff --git a/opsli-common/src/main/java/org/opsli/common/api/ResultVo.java b/opsli-common/src/main/java/org/opsli/common/api/ResultVo.java index 4ea4ccd2..4c5f0f25 100644 --- a/opsli-common/src/main/java/org/opsli/common/api/ResultVo.java +++ b/opsli-common/src/main/java/org/opsli/common/api/ResultVo.java @@ -1,6 +1,8 @@ package org.opsli.common.api; import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import org.springframework.http.HttpStatus; import java.io.Serializable; @@ -15,6 +17,8 @@ import java.util.Map; * @author Parker * */ +@ApiModel(value="视图层返回Api对象", + description="视图层返回Api对象 success:成功状态 code:编号 msg:信息 datatime:时间戳") public class ResultVo extends HashMap implements Serializable { @@ -22,6 +26,7 @@ public class ResultVo extends HashMap implements Serializable { this.put("success", true); this.put("code", HttpStatus.OK.value()); this.put("msg", "操作成功"); + this.put("datatime", System.currentTimeMillis()); } /** get/set diff --git a/opsli-common/src/main/java/org/opsli/common/base/entity/BaseEntity.java b/opsli-common/src/main/java/org/opsli/common/base/entity/BaseEntity.java index d87e6a5e..1ee3dd80 100644 --- a/opsli-common/src/main/java/org/opsli/common/base/entity/BaseEntity.java +++ b/opsli-common/src/main/java/org/opsli/common/base/entity/BaseEntity.java @@ -1,18 +1,56 @@ package org.opsli.common.base.entity; -import com.baomidou.mybatisplus.annotation.Version; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; /** - * @BelongsProject: opsli-boot - * @BelongsPackage: org.opsli.common.base.entity - * @Author: Parker - * @CreateTime: 2020-09-14 17:29 - * @Description: Entity 基类 + * + * Entity 基础类 + * + * @author Parker + * @date 2019-05-11 + * */ -public class BaseEntity { +@Data +@EqualsAndHashCode(callSuper = false) +public abstract class BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** ID */ + @TableId(type = IdType.ASSIGN_ID) + @ApiModelProperty(value = "ID") + private String id; + + /** 创建人 */ + @ApiModelProperty(value = "创建人") + private String createBy; + + /** 创建时间 */ + @ApiModelProperty(value = "创建时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新人 */ + @ApiModelProperty(value = "修改人") + private String updateBy; + + /** 更新时间 */ + @ApiModelProperty(value = "修改时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; - /** 乐观锁 */ - @Version - private Integer version; + /** 乐观锁 版本 */ + private Integer version; } diff --git a/opsli-common/src/main/java/org/opsli/common/constants/CacheConstants.java b/opsli-common/src/main/java/org/opsli/common/constants/CacheConstants.java new file mode 100644 index 00000000..3d6526d3 --- /dev/null +++ b/opsli-common/src/main/java/org/opsli/common/constants/CacheConstants.java @@ -0,0 +1,18 @@ +package org.opsli.common.constants; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.common.constants + * @Author: Parker + * @CreateTime: 2020-09-16 17:42 + * @Description: 缓存 常量 + */ +public interface CacheConstants { + + /** 热点数据 */ + String HOT_DATA = "hotData"; + + /** 系统常量 */ + String SYSTEM_DATA = "systemData"; + +} diff --git a/opsli-common/src/main/java/org/opsli/common/constants/OrderConstants.java b/opsli-common/src/main/java/org/opsli/common/constants/OrderConstants.java new file mode 100644 index 00000000..d88ec65f --- /dev/null +++ b/opsli-common/src/main/java/org/opsli/common/constants/OrderConstants.java @@ -0,0 +1,19 @@ +package org.opsli.common.constants; + +/** + * Order + * @author parker + * @date 2020-09-16 + */ +public interface OrderConstants { + + /** 热点数据加载顺序 */ + int HOT_DATA_ORDER = 180; + + /** Controller异常拦截顺序 */ + int EXCEPTION_HANDLER_ORDER = 260; + + /** 参数非法验证顺序 */ + int PARAM_VALIDATE_AOP_SORT = 500; + +} diff --git a/opsli-core/pom.xml b/opsli-core/pom.xml index 480f7dd9..08441876 100644 --- a/opsli-core/pom.xml +++ b/opsli-core/pom.xml @@ -25,12 +25,21 @@ ${version} - + org.opsliframework.boot opsli-plugins-redis ${plugins.version} + + + + org.opsliframework.boot + opsli-plugins-ehcache + ${plugins.version} + + + \ No newline at end of file diff --git a/opsli-core/src/main/java/org/opsli/core/aspect/CacheDataAop.java b/opsli-core/src/main/java/org/opsli/core/aspect/CacheDataAop.java new file mode 100644 index 00000000..7e7e77db --- /dev/null +++ b/opsli-core/src/main/java/org/opsli/core/aspect/CacheDataAop.java @@ -0,0 +1,153 @@ +package org.opsli.core.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.opsli.common.annotation.HotDataDel; +import org.opsli.common.annotation.HotDataPut; +import org.opsli.common.base.entity.BaseEntity; +import org.opsli.common.constants.CacheConstants; +import org.opsli.core.cache.pushsub.enums.CacheType; +import org.opsli.core.cache.pushsub.enums.PushSubType; +import org.opsli.core.cache.pushsub.msgs.CacheDataMsgFactory; +import org.opsli.plugins.redis.RedisPlugin; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +import static org.opsli.common.constants.OrderConstants.HOT_DATA_ORDER; + +/** + * 热点数据 拦截处理 + * + * @author parker + * @date 2020-09-16 + */ +@Slf4j +@Order(HOT_DATA_ORDER) +@Aspect +@Component +public class CacheDataAop { + + /** 热点数据前缀 */ + public static final String PREFIX_NAME = "opsli:"; + + @Autowired + private RedisPlugin redisPlugin; + + @Pointcut("@annotation(org.opsli.common.annotation.HotDataPut)") + public void hotDataPut() { + } + + @Pointcut("@annotation(org.opsli.common.annotation.HotDataDel)") + public void hotDataDel() { + } + + @Around("hotDataPut()") + public Object hotDataPutProcess(ProceedingJoinPoint point) throws Throwable { + Object[] args= point.getArgs(); + Object returnValue = point.proceed(args); + // 将 + if(returnValue != null){ + String methodName= point.getSignature().getName(); + Class classTarget= point.getTarget().getClass(); + Class[] par=((MethodSignature) point.getSignature()).getParameterTypes(); + Method objMethod = classTarget.getMethod(methodName, par); + // 获取注解参数 + HotDataPut aCache= objMethod.getAnnotation(HotDataPut.class); + if(aCache != null){ + // 类型 + PushSubType type; + // key 前缀 + StringBuilder keyBuf = new StringBuilder(PREFIX_NAME); + // 热点数据 + if(CacheConstants.HOT_DATA.equals(aCache.name())){ + keyBuf.append(CacheConstants.HOT_DATA).append(":"); + type = PushSubType.HOT_DATA; + } + // 系统数据 + else if(CacheConstants.SYSTEM_DATA.equals(aCache.name())){ + keyBuf.append(CacheConstants.SYSTEM_DATA).append(":"); + type = PushSubType.SYSTEM_DATA; + } else { + // 如果都不是 则直接退出 不走缓存 + return returnValue; + } + + try { + // 这里 只对 继承了 BaseEntity 的类做处理 + BaseEntity baseEntity = (BaseEntity) returnValue; + + // key 存储ID + String key = keyBuf.append(baseEntity.getId()).toString(); + + // 广播数据 + redisPlugin.sendMessage( + CacheDataMsgFactory.createMsg(type, key, returnValue, CacheType.UPDATE) + ); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + } + } + return returnValue; + } + + + + @Around("hotDataDel()") + public Object hotDataDelProcess(ProceedingJoinPoint point) throws Throwable { + Object[] args= point.getArgs(); + Object returnValue = point.proceed(args); + // 将 + if(returnValue != null){ + String methodName= point.getSignature().getName(); + Class classTarget= point.getTarget().getClass(); + Class[] par=((MethodSignature) point.getSignature()).getParameterTypes(); + Method objMethod = classTarget.getMethod(methodName, par); + // 获取注解参数 + HotDataDel aCache= objMethod.getAnnotation(HotDataDel.class); + if(aCache != null){ + // 类型 + PushSubType type; + // key 前缀 + StringBuilder keyBuf = new StringBuilder(PREFIX_NAME); + // 热点数据 + if(CacheConstants.HOT_DATA.equals(aCache.name())){ + keyBuf.append(CacheConstants.HOT_DATA).append(":"); + type = PushSubType.HOT_DATA; + } + // 系统数据 + else if(CacheConstants.SYSTEM_DATA.equals(aCache.name())){ + keyBuf.append(CacheConstants.SYSTEM_DATA).append(":"); + type = PushSubType.SYSTEM_DATA; + } else { + // 如果都不是 则直接退出 不走缓存 + return returnValue; + } + + try { + // 这里 只对 继承了 BaseEntity 的类做处理 + BaseEntity baseEntity = (BaseEntity) returnValue; + + // key 存储ID + String key = keyBuf.append(baseEntity.getId()).toString(); + + // 广播数据 + redisPlugin.sendMessage( + CacheDataMsgFactory.createMsg(type, key, returnValue, CacheType.DELETE) + ); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + } + } + return returnValue; + } + +} diff --git a/opsli-core/src/main/java/org/opsli/core/cache/local/CacheUtil.java b/opsli-core/src/main/java/org/opsli/core/cache/local/CacheUtil.java new file mode 100644 index 00000000..a412ae75 --- /dev/null +++ b/opsli-core/src/main/java/org/opsli/core/cache/local/CacheUtil.java @@ -0,0 +1,76 @@ +package org.opsli.core.cache.local; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.opsli.common.constants.CacheConstants; +import org.opsli.core.aspect.CacheDataAop; +import org.opsli.plugins.cache.EhCachePlugin; +import org.opsli.plugins.redis.RedisPlugin; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.cache.local + * @Author: Parker + * @CreateTime: 2020-09-16 16:20 + * @Description: 本地 缓存接口 + */ +@Slf4j +@Component +public class CacheUtil { + + /** Redis插件 */ + private static RedisPlugin redisPlugin; + /** EhCache插件 */ + private static EhCachePlugin ehCachePlugin; + + public static V get(String key, Class vClass){ + return CacheUtil.get(CacheConstants.HOT_DATA,key,vClass,true); + } + + public static V getByKeyOriginal(String key, Class vClass){ + return CacheUtil.get(CacheConstants.HOT_DATA,key,vClass,false); + } + + + private static V get(String cacheName, String key, Class vClass,boolean keyFlag){ + // 自动处理 key + if(keyFlag){ + StringBuilder keyBuf = new StringBuilder(CacheDataAop.PREFIX_NAME); + keyBuf.append(cacheName).append(":"); + keyBuf.append(key); + key = keyBuf.toString(); + } + V v = null; + try { + JSONObject jsonObject; + jsonObject = ehCachePlugin.get(cacheName, key, JSONObject.class); + // 如果本地缓存为空 则去Redis中 再去取一次 + if(jsonObject != null){ + v = jsonObject.toJavaObject(vClass); + }else{ + jsonObject = (JSONObject) redisPlugin.get(key); + if(jsonObject != null){ + v = jsonObject.toJavaObject(vClass); + } + } + }catch (Exception e){ + log.error(e.getMessage(),e); + } + return v; + } + + + // ================================ + @Autowired + public void setRedisPlugin(RedisPlugin redisPlugin) { + CacheUtil.redisPlugin = redisPlugin; + } + + @Autowired + public void setEhCachePlugin(EhCachePlugin ehCachePlugin) { + CacheUtil.ehCachePlugin = ehCachePlugin; + } +} diff --git a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/CacheType.java b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/CacheType.java new file mode 100644 index 00000000..3be30773 --- /dev/null +++ b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/CacheType.java @@ -0,0 +1,20 @@ +package org.opsli.core.cache.pushsub.enums; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.cache.pushsub.enums + * @Author: Parker + * @CreateTime: 2020-09-16 22:28 + * @Description: 缓存操作类型 + */ +public enum CacheType { + + /** 更新 */ + UPDATE, + + /** 删除 */ + DELETE, + + ; + +} diff --git a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/MsgArgsType.java b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/MsgArgsType.java index 78dcf686..6a5251ca 100644 --- a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/MsgArgsType.java +++ b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/MsgArgsType.java @@ -15,7 +15,15 @@ public enum MsgArgsType { DICT_FIELD, /** 字典Value */ DICT_VALUE, + /** 字典Value */ + DICT_TYPE, + /** 缓存数据Key */ + CACHE_DATA_KEY, + /** 缓存数据Value */ + CACHE_DATA_VALUE, + /** 缓存数据Type */ + CACHE_DATA_TYPE, ; } diff --git a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/PushSubType.java b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/PushSubType.java index dd1707d7..eccd7820 100644 --- a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/PushSubType.java +++ b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/enums/PushSubType.java @@ -12,12 +12,13 @@ public enum PushSubType { /** 字典类型 */ DICT, - ; - + /** 热点数据 */ + HOT_DATA, + /** 系统数据 */ + SYSTEM_DATA - PushSubType(){ + ; - } } diff --git a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/HotDataHandler.java b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/HotDataHandler.java new file mode 100644 index 00000000..1d9ce851 --- /dev/null +++ b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/HotDataHandler.java @@ -0,0 +1,61 @@ +package org.opsli.core.cache.pushsub.handler; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.opsli.common.constants.CacheConstants; +import org.opsli.core.cache.pushsub.enums.CacheType; +import org.opsli.core.cache.pushsub.enums.MsgArgsType; +import org.opsli.core.cache.pushsub.enums.PushSubType; +import org.opsli.plugins.cache.EhCachePlugin; +import org.opsli.plugins.redis.RedisPlugin; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.cache.pushsub.handler + * @Author: Parker + * @CreateTime: 2020-09-15 16:24 + * @Description: 字典消息处理 + */ +@Slf4j +public class HotDataHandler implements RedisPushSubHandler{ + + /** 热点数据缓存时间 */ + @Value("${cache.ttl-hot-data}") + private int ttlHotData; + + @Autowired + private RedisPlugin redisPlugin; + @Autowired + private EhCachePlugin cachePlugin; + + @Override + public PushSubType getType() { + return PushSubType.HOT_DATA; + } + + @Override + public void handler(JSONObject msgJson) { + String key = (String) msgJson.get(MsgArgsType.CACHE_DATA_KEY.toString()); + Object value = msgJson.get(MsgArgsType.CACHE_DATA_VALUE.toString()); + CacheType type = CacheType.valueOf((String )msgJson.get(MsgArgsType.CACHE_DATA_TYPE.toString())); + + // 缓存更新 + if(CacheType.UPDATE == type){ + // 存入EhCache + cachePlugin.put(CacheConstants.HOT_DATA,key, value); + // 存入Redis + redisPlugin.put(key, value, ttlHotData); + } + // 缓存删除 + else if(CacheType.DELETE == type){ + // 存入EhCache + cachePlugin.delete(CacheConstants.HOT_DATA,key); + // 存入Redis + redisPlugin.del(key); + } + + } + +} diff --git a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/SystemDataHandler.java b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/SystemDataHandler.java new file mode 100644 index 00000000..d775abce --- /dev/null +++ b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/handler/SystemDataHandler.java @@ -0,0 +1,61 @@ +package org.opsli.core.cache.pushsub.handler; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.opsli.common.constants.CacheConstants; +import org.opsli.core.cache.pushsub.enums.CacheType; +import org.opsli.core.cache.pushsub.enums.MsgArgsType; +import org.opsli.core.cache.pushsub.enums.PushSubType; +import org.opsli.plugins.cache.EhCachePlugin; +import org.opsli.plugins.redis.RedisPlugin; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.cache.pushsub.handler + * @Author: Parker + * @CreateTime: 2020-09-15 16:24 + * @Description: 字典消息处理 + */ +@Slf4j +public class SystemDataHandler implements RedisPushSubHandler{ + + /** 热点数据缓存时间 */ + @Value("${cache.ttl-system-data}") + private int ttlSystemData; + + @Autowired + private RedisPlugin redisPlugin; + @Autowired + private EhCachePlugin cachePlugin; + + @Override + public PushSubType getType() { + return PushSubType.SYSTEM_DATA; + } + + @Override + public void handler(JSONObject msgJson) { + String key = (String) msgJson.get(MsgArgsType.CACHE_DATA_KEY.toString()); + Object value = msgJson.get(MsgArgsType.CACHE_DATA_VALUE.toString()); + CacheType type = CacheType.valueOf((String )msgJson.get(MsgArgsType.CACHE_DATA_TYPE.toString())); + + // 缓存更新 + if(CacheType.UPDATE == type){ + // 存入EhCache + cachePlugin.put(CacheConstants.SYSTEM_DATA,key, value); + // 存入Redis + redisPlugin.put(key, value, ttlSystemData); + } + // 缓存删除 + else if(CacheType.DELETE == type){ + // 存入EhCache + cachePlugin.delete(CacheConstants.SYSTEM_DATA,key); + // 存入Redis + redisPlugin.del(key); + } + + } + +} diff --git a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/CacheDataMsgFactory.java b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/CacheDataMsgFactory.java new file mode 100644 index 00000000..d180ab19 --- /dev/null +++ b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/CacheDataMsgFactory.java @@ -0,0 +1,45 @@ +package org.opsli.core.cache.pushsub.msgs; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; +import lombok.experimental.Accessors; +import org.opsli.core.cache.pushsub.enums.CacheType; +import org.opsli.core.cache.pushsub.enums.MsgArgsType; +import org.opsli.core.cache.pushsub.enums.PushSubType; +import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver; +import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.cache.pushsub.msgs + * @Author: Parker + * @CreateTime: 2020-09-15 16:50 + * @Description: 字典消息 + */ + +@Data +@Accessors(chain = true) +public final class CacheDataMsgFactory extends BaseSubMessage{ + + /** 通道 */ + private static final String CHANNEL = RedisPushSubReceiver.BASE_CHANNEL+RedisPushSubReceiver.CHANNEL; + + private CacheDataMsgFactory(){} + + /** + * 构建消息 + */ + public static BaseSubMessage createMsg(PushSubType py,String key, Object value, CacheType cacheType){ + BaseSubMessage baseSubMessage = new BaseSubMessage(); + // 数据 + JSONObject jsonObj = new JSONObject(); + jsonObj.put(MsgArgsType.CACHE_DATA_KEY.toString(),key); + jsonObj.put(MsgArgsType.CACHE_DATA_VALUE.toString(),value); + jsonObj.put(MsgArgsType.CACHE_DATA_TYPE.toString(),cacheType.toString()); + + // 热点数据 - 系统数据 + baseSubMessage.build(CHANNEL,py.toString(),jsonObj); + return baseSubMessage; + } + +} diff --git a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/DictMsgFactory.java b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/DictMsgFactory.java index 45815c76..60aa59ef 100644 --- a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/DictMsgFactory.java +++ b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/msgs/DictMsgFactory.java @@ -3,6 +3,7 @@ package org.opsli.core.cache.pushsub.msgs; import com.alibaba.fastjson.JSONObject; import lombok.Data; import lombok.experimental.Accessors; +import org.opsli.core.cache.pushsub.enums.CacheType; import org.opsli.core.cache.pushsub.enums.MsgArgsType; import org.opsli.core.cache.pushsub.enums.PushSubType; import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver; @@ -28,13 +29,14 @@ public final class DictMsgFactory extends BaseSubMessage{ /** * 构建消息 */ - public static BaseSubMessage createMsg(String key, String field, Object value){ + public static BaseSubMessage createMsg(String key, String field, Object value, CacheType cacheType){ BaseSubMessage baseSubMessage = new BaseSubMessage(); // 数据 JSONObject jsonObj = new JSONObject(); jsonObj.put(MsgArgsType.DICT_KEY.toString(),key); jsonObj.put(MsgArgsType.DICT_FIELD.toString(),field); jsonObj.put(MsgArgsType.DICT_VALUE.toString(),value); + jsonObj.put(MsgArgsType.DICT_TYPE.toString(),cacheType.toString()); // DICT 字典 baseSubMessage.build(CHANNEL,PushSubType.DICT.toString(),jsonObj); diff --git a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/receiver/RedisPushSubReceiver.java b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/receiver/RedisPushSubReceiver.java index 8ba53b5c..55b54447 100644 --- a/opsli-core/src/main/java/org/opsli/core/cache/pushsub/receiver/RedisPushSubReceiver.java +++ b/opsli-core/src/main/java/org/opsli/core/cache/pushsub/receiver/RedisPushSubReceiver.java @@ -8,6 +8,11 @@ import org.opsli.core.cache.pushsub.handler.RedisPushSubHandler; import org.opsli.core.msg.CoreMsg; import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage; import org.opsli.plugins.redis.pushsub.receiver.BaseReceiver; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.lang.reflect.Modifier; import java.util.Set; @@ -25,6 +30,7 @@ import java.util.concurrent.ConcurrentMap; * */ @Slf4j +@Configuration public class RedisPushSubReceiver extends BaseReceiver { /** 监听信道 */ @@ -33,13 +39,26 @@ public class RedisPushSubReceiver extends BaseReceiver { /** 处理方法集合 */ private static final ConcurrentMap HANDLER_MAP = new ConcurrentHashMap<>(); - static { + + @Autowired + private AutowireCapableBeanFactory beanFactory; + + @Autowired + DefaultListableBeanFactory defaultListableBeanFactory; + + public RedisPushSubReceiver() { + super(CHANNEL); + } + + @Bean + public void initRedisPushSubHandler(){ // 拿到state包下 实现了 SystemEventState 接口的,所有子类 Set> clazzSet = PackageUtil.listSubClazz(RedisPushSubHandler.class.getPackage().getName(), true, RedisPushSubHandler.class ); + int count = 0; for (Class aClass : clazzSet) { // 位运算 去除抽象类 if((aClass.getModifiers() & Modifier.ABSTRACT) != 0){ @@ -47,27 +66,33 @@ public class RedisPushSubReceiver extends BaseReceiver { } try { - RedisPushSubHandler handler = (RedisPushSubHandler) aClass.newInstance(); + Object obj = aClass.newInstance(); + + RedisPushSubHandler handler = (RedisPushSubHandler) obj; // 加入集合 HANDLER_MAP.put(handler.getType(),handler); + //将new出的对象放入Spring容器中 + defaultListableBeanFactory.registerSingleton("redisPushSubHandler"+count,obj); + + //自动注入依赖 + beanFactory.autowireBean(obj); + } catch (Exception e){ log.error(CoreMsg.REDIS_EXCEPTION_PUSH_SUB.getMessage()); } + count ++; } } - public RedisPushSubReceiver() { - super(CHANNEL); - } - @Override public void receiveMessage(String msg) { if(msg == null || "".equals(msg)){ return; } + long beginTime = System.currentTimeMillis(); // 替换 转意符 String replaceAll = msg.replaceAll("\\\\", ""); String substring = replaceAll.substring(1, replaceAll.length() - 1); @@ -79,6 +104,8 @@ public class RedisPushSubReceiver extends BaseReceiver { return; } redisPushSubHandler.handler(msgJson); + long endTime = System.currentTimeMillis(); + log.info("订阅节点更新缓存 耗时(毫秒):{}",(endTime-beginTime)); } } diff --git a/opsli-core/src/main/java/org/opsli/core/conf/RedisMessageListener.java b/opsli-core/src/main/java/org/opsli/core/conf/RedisMessageListenerConfig.java similarity index 98% rename from opsli-core/src/main/java/org/opsli/core/conf/RedisMessageListener.java rename to opsli-core/src/main/java/org/opsli/core/conf/RedisMessageListenerConfig.java index 6cd2171c..4bd237d5 100644 --- a/opsli-core/src/main/java/org/opsli/core/conf/RedisMessageListener.java +++ b/opsli-core/src/main/java/org/opsli/core/conf/RedisMessageListenerConfig.java @@ -23,7 +23,7 @@ import javax.annotation.Resource; @Slf4j @Configuration @ConditionalOnProperty(name = "spring.redis.pushsub.enable", havingValue = "true") -public class RedisMessageListener { +public class RedisMessageListenerConfig { @Resource private LettuceConnectionFactory factory; diff --git a/opsli-core/src/main/java/org/opsli/core/handler/GlobalExceptionHandler.java b/opsli-core/src/main/java/org/opsli/core/handler/GlobalExceptionHandler.java index 4a8c0eae..50ce0b34 100644 --- a/opsli-core/src/main/java/org/opsli/core/handler/GlobalExceptionHandler.java +++ b/opsli-core/src/main/java/org/opsli/core/handler/GlobalExceptionHandler.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; +import static org.opsli.common.constants.OrderConstants.EXCEPTION_HANDLER_ORDER; /** * @author parker @@ -17,11 +18,11 @@ import org.springframework.web.bind.annotation.ResponseStatus; */ @Slf4j @ControllerAdvice -@Order(-1) +@Order(EXCEPTION_HANDLER_ORDER) public class GlobalExceptionHandler { /** - * 拦截业务异常 + * 拦截 业务异常 */ @ExceptionHandler(ServiceException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @@ -34,12 +35,24 @@ public class GlobalExceptionHandler { } /** - * 拦截空异常 + * 拦截 自定义 空异常 */ @ExceptionHandler(EmptyException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + public ResultVo emptyException(EmptyException e) { + ResultVo errorR = ResultVo.error(e.getMessage()); + errorR.setCode(e.getCode()); + return errorR; + } + + /** + * 拦截 系统空指针异常 + */ + @ExceptionHandler(NullPointerException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody - public ResultVo bussinessException(EmptyException e) { + public ResultVo nullPointerException(EmptyException e) { ResultVo errorR = ResultVo.error(e.getMessage()); errorR.setCode(e.getCode()); return errorR; diff --git a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/entity/TestEntity.java b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/entity/TestEntity.java new file mode 100644 index 00000000..a8190b1c --- /dev/null +++ b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/entity/TestEntity.java @@ -0,0 +1,23 @@ +package org.opsli.modulars.test.entity; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.opsli.common.base.entity.BaseEntity; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.modulars.test.entity + * @Author: Parker + * @CreateTime: 2020-09-16 17:33 + * @Description: 测试类 + */ +@Data +public class TestEntity extends BaseEntity { + + @ApiModelProperty(value = "名称") + private String name; + + @ApiModelProperty(value = "备注") + private String remark; + +} diff --git a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/service/TestService.java b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/service/TestService.java new file mode 100644 index 00000000..7ff1d19b --- /dev/null +++ b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/service/TestService.java @@ -0,0 +1,45 @@ +package org.opsli.modulars.test.service; + +import org.opsli.common.annotation.HotDataDel; +import org.opsli.common.annotation.HotDataPut; +import org.opsli.modulars.test.entity.TestEntity; +import org.springframework.stereotype.Service; + + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.modulars.test.service + * @Author: Parker + * @CreateTime: 2020-09-16 17:34 + * @Description: 测试接口 + */ +@Service +public class TestService { + + /** + * 测试 存入热点数据 + * @param testEntity + * @return + */ + @HotDataPut + public TestEntity save(TestEntity testEntity){ + + + return testEntity; + } + + + /** + * 测试 存入热点数据 + * @param id + * @return + */ + @HotDataDel + public TestEntity del(String id){ + TestEntity t = new TestEntity(); + t.setId(id); + + return t; + } + +} diff --git a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/web/TestRestController.java b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/web/TestRestController.java index 4276c208..c5334e86 100644 --- a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/web/TestRestController.java +++ b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/web/TestRestController.java @@ -1,9 +1,15 @@ package org.opsli.modulars.test.web; import cn.hutool.core.thread.ThreadUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.opsli.common.api.ResultVo; import org.opsli.common.base.concroller.BaseController; +import org.opsli.core.cache.local.CacheUtil; +import org.opsli.core.cache.pushsub.enums.CacheType; import org.opsli.core.cache.pushsub.msgs.DictMsgFactory; +import org.opsli.modulars.test.entity.TestEntity; +import org.opsli.modulars.test.service.TestService; import org.opsli.plugins.mail.MailPlugin; import org.opsli.plugins.mail.model.MailModel; import org.opsli.plugins.redis.RedisLockPlugins; @@ -15,6 +21,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Random; import java.util.concurrent.TimeUnit; /** @@ -28,6 +35,9 @@ import java.util.concurrent.TimeUnit; @RequestMapping("/{opsli.prefix}/{opsli.version}/test") public class TestRestController extends BaseController { + + private Random random = new Random(); + @Autowired private MailPlugin mailPlugin; @@ -37,6 +47,10 @@ public class TestRestController extends BaseController { @Autowired private RedisLockPlugins redisLockPlugins; + @Autowired + private TestService testService; + + @GetMapping("/sendMail") public ResultVo sendMail(){ MailModel mailModel = new MailModel(); @@ -55,7 +69,7 @@ public class TestRestController extends BaseController { @GetMapping("/sendMsg") public ResultVo sendMsg(){ - BaseSubMessage msg = DictMsgFactory.createMsg("test", "aaa", 123213); + BaseSubMessage msg = DictMsgFactory.createMsg("test", "aaa", 123213, CacheType.UPDATE); boolean ret = redisPlugin.sendMessage(msg); if(ret){ @@ -112,6 +126,56 @@ public class TestRestController extends BaseController { return success; } + /** + * 发送 Redis 分布式锁 + * @return + */ + @GetMapping("/save") + public ResultVo save(String id){ + + if(StringUtils.isEmpty(id)){ + id = String.valueOf(random.nextLong()); + } + + TestEntity testEntity = new TestEntity(); + testEntity.setId(id); + testEntity.setName("测试名称"+random.nextInt()); + testEntity.setRemark("测试备注"+random.nextInt()); + + TestEntity saveObj = testService.save(testEntity); + if(saveObj == null){ + ResultVo success = ResultVo.error("保存对象失败!"); + success.put("object",testEntity); + return success; + } + + TestEntity o = CacheUtil.get(id, TestEntity.class); + ResultVo success = ResultVo.success("保存对象成功!"); + success.put("object",o); + + return success; + } + + + /** + * 发送 Redis 分布式锁 + * @return + */ + @GetMapping("/del") + public ResultVo del(String id){ + + TestEntity ret = testService.del(id); + if(ret == null){ + ResultVo success = ResultVo.error("删除对象失败!"); + success.put("object",ret); + return success; + } + + TestEntity o = CacheUtil.get(id, TestEntity.class); + ResultVo success = ResultVo.success("删除对象成功!"); + success.put("object",o); + return success; + } } diff --git a/opsli-web/src/main/resources/application-dev.yaml b/opsli-web/src/main/resources/application-dev.yaml index f2bf9bcc..f08be379 100644 --- a/opsli-web/src/main/resources/application-dev.yaml +++ b/opsli-web/src/main/resources/application-dev.yaml @@ -9,7 +9,10 @@ opsli: server: port: 8080 + spring: + + #redis 配置 redis: database: 0 @@ -49,11 +52,9 @@ spring: #password: 12345678 #driver-class-name: com.mysql.jdbc.Driver -#Mybatis输出sql日志 -#logging: -# level: -# org.opsli.modulars.system.mapper : debug # swagger 配置 swagger: - production: false \ No newline at end of file + production: false + + diff --git a/opsli-web/src/main/resources/application.yaml b/opsli-web/src/main/resources/application.yaml index 21ce2e4b..cd0aea1a 100644 --- a/opsli-web/src/main/resources/application.yaml +++ b/opsli-web/src/main/resources/application.yaml @@ -54,6 +54,10 @@ spring: # 默认编码 default-encoding: UTF-8 + # EhCache 配置 + cache: + jcache: + config: classpath:config/ehcache-opsli.xml #redis 配置 redis: # 开启消息订阅 @@ -127,4 +131,11 @@ mybatis-plus: cache-enabled: false call-setters-on-nulls: true # 打印SQL 开发测试使用 生产关闭 *** - #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl \ No newline at end of file + #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 缓存设置 +cache: + # 热点数据 缓存时间 600秒 10分钟 + ttl-hot-data: 600 + # 系统数据 缓存时间 60000秒 6小时 + ttl-system-data: 60000 \ No newline at end of file diff --git a/opsli-web/src/main/resources/config/ehcache-opsli.xml b/opsli-web/src/main/resources/config/ehcache-opsli.xml new file mode 100644 index 00000000..0409436d --- /dev/null +++ b/opsli-web/src/main/resources/config/ehcache-opsli.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + java.lang.String + java.lang.Object + + 2000 + 300 + + + + + + + + 10 + + + + + + + + 600 + + + + \ No newline at end of file diff --git a/opsli-web/src/main/resources/logback-spring.xml b/opsli-web/src/main/resources/config/logback-spring.xml similarity index 98% rename from opsli-web/src/main/resources/logback-spring.xml rename to opsli-web/src/main/resources/config/logback-spring.xml index 4c27a853..af90b7b2 100644 --- a/opsli-web/src/main/resources/logback-spring.xml +++ b/opsli-web/src/main/resources/config/logback-spring.xml @@ -10,7 +10,7 @@ - + ${CONSOLE_LOG_PATTERN} @@ -32,7 +32,7 @@ - + diff --git a/opsli-web/src/main/resources/config/message.properties b/opsli-web/src/main/resources/config/message.properties deleted file mode 100644 index c4bba294..00000000 --- a/opsli-web/src/main/resources/config/message.properties +++ /dev/null @@ -1,2 +0,0 @@ -## 统一异常类 -02001=请求数据不完整或格式错误! \ No newline at end of file diff --git a/pom.xml b/pom.xml index c0caff25..cec0ebbb 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ - + parker Parker Chou @@ -75,6 +75,7 @@ 2.8.0 29.0-jre 5.4.2 + 3.9.0