From fce088600866db5714aa092649cccb26a9ef2d06 Mon Sep 17 00:00:00 2001 From: Houpn Date: Tue, 18 Jul 2023 10:11:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E8=AE=A2=E5=8D=95=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hiver-admin/pom.xml | 18 ++ .../src/main/resources/application.yml | 49 +++-- hiver-admin/src/main/resources/logback.xml | 174 ++++++++++++++++++ hiver-core/pom.xml | 4 + .../config/cache/CustomCacheResolver.java | 62 +++++++ .../core/config/cache/RedisCacheConfig.java | 52 +++++- .../controller/manage/OrderController.java | 8 + .../cc/hiver/base/handler/OrderXdHandler.java | 41 +++++ pom.xml | 5 + 9 files changed, 391 insertions(+), 22 deletions(-) create mode 100644 hiver-admin/src/main/resources/logback.xml create mode 100644 hiver-core/src/main/java/cc/hiver/core/config/cache/CustomCacheResolver.java create mode 100644 hiver-modules/hiver-base/src/main/java/cc/hiver/base/handler/OrderXdHandler.java diff --git a/hiver-admin/pom.xml b/hiver-admin/pom.xml index fb8d5b75..5aea8b10 100644 --- a/hiver-admin/pom.xml +++ b/hiver-admin/pom.xml @@ -66,6 +66,24 @@ org.springframework.boot spring-boot-starter-actuator + + io.netty + netty-resolver-dns-native-macos + 4.1.77.Final + osx-aarch_64 + + + + org.bgee.log4jdbc-log4j2 + log4jdbc-log4j2-jdbc4.1 + 1.16 + + + top.javatool + canal-spring-boot-starter + 1.2.1-RELEASE + + diff --git a/hiver-admin/src/main/resources/application.yml b/hiver-admin/src/main/resources/application.yml index 89141df5..ab92fae3 100644 --- a/hiver-admin/src/main/resources/application.yml +++ b/hiver-admin/src/main/resources/application.yml @@ -23,12 +23,15 @@ spring: timeout-per-shutdown-phase: 10S # 数据源 datasource: - url: jdbc:mysql://154.8.162.157:3306/hiver_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + url: jdbc:log4jdbc:mysql://154.8.162.157:3306/hiver_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + #url: jdbc:mysql://154.8.162.157:3306/hiver_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true username: reddoor # Jasypt加密 可到common-utils中找到JasyptUtil加解密工具类生成加密结果 格式为ENC(加密结果) 以下解密结果为123456 password: ENC(Zla4U4+yRLPhicvuX2TmiEgxEpzP4dk8BHzFDEtiEhwLQIIaftZrrEUJZce6efoe) type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: com.mysql.jdbc.Driver + driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy + #driver-class-name: com.mysql.jdbc.Driver + # Druid StatViewServlet配置 druid: stat-view-servlet: @@ -58,10 +61,11 @@ spring: multi-statement-allow: true jpa: # 显示sql - show-sql: true + #show-sql: true # 自动生成表结构 关闭设为none hibernate: ddl-auto: update + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect #设置数据库方言 记住必须要使用 MySQL5InnoDBDialect 指定数据库类型对应InnoDB ;如果使用MySQLDialect 则对应的是MyISAM # Redis 若设有密码自行添加配置password redis: host: 154.8.162.157 @@ -70,6 +74,16 @@ spring: port: 6379 # 超时时间 Duration类型 3秒 timeout: 3S + jedis: + pool: + max-active: 8 + max-wait: -1 + max-idle: 8 + min-idle: 0 + #Canal服务用于数据同步 + canal: + server: 154.8.162.157:11111 + destination: example # Elasticsearch data: elasticsearch: @@ -97,7 +111,7 @@ spring: # 加锁调度 acquireTriggersWithinLock: true # “容忍”触发器经过下一次触发时间的毫秒数 - misfireThreshold: 10000 + misfireThreshold: 500000 servlet: multipart: max-file-size: -1 @@ -304,8 +318,10 @@ management: OUT_OF_SERVICE: 200 FATAL: 200 UNKNOWN: 200 + enabled: false endpoints: web: + enabled-by-default: false #关闭所有监控端点 base-path: /hiver/actuator/ exposure: include: '*' @@ -336,15 +352,16 @@ mybatis-plus: # 日志 logging: - # 输出级别 - level: - root: info - file: - # 指定路径 - path: hiver-logs - logback: - rollingpolicy: - # 最大保存天数 - max-history: 7 - # 每个文件最大大小 - max-file-size: 5MB \ No newline at end of file + config: classpath:logback.xml +# 输出级别 +# level: +# root: info +# file: +# # 指定路径 +# path: hiver-logs +# logback: +# rollingpolicy: +# # 最大保存天数 +# max-history: 7 +# # 每个文件最大大小 +# max-file-size: 5MB \ No newline at end of file diff --git a/hiver-admin/src/main/resources/logback.xml b/hiver-admin/src/main/resources/logback.xml new file mode 100644 index 00000000..c0480f43 --- /dev/null +++ b/hiver-admin/src/main/resources/logback.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + logback + + + + + + + + + + + + + + + + + + + + + + + + + + + + + debug + + + ${CONSOLE_LOG_PATTERN} + + UTF-8 + + + + + + + + ${OPEN_FILE_PATH}/log_debug.log + + ${OPEN_FILE_PATH}/debug/${FILE_PREFIX}-debug_%d{yyyy-MM-dd}-%i.log + 30 + + ${MAX_FILE_SIZE} + + + true + + %d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n + utf-8 + + + + + DEBUG + + ACCEPT + + DENY + + + + + + + ${OPEN_FILE_PATH}/log_info.log + + ${OPEN_FILE_PATH}/info/${FILE_PREFIX}_%d{yyyy-MM-dd}-%i.log + 30 + + ${MAX_FILE_SIZE} + + + true + + %d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n + utf-8 + + + + INFO + + ACCEPT + + DENY + + + + + + + ${OPEN_FILE_PATH}/log_warn.log + + ${OPEN_FILE_PATH}/warn/${FILE_PREFIX}_%d{yyyy-MM-dd}-%i.log + 30 + + ${MAX_FILE_SIZE} + + + true + + %d{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n + utf-8 + + + + WARN + + ACCEPT + + DENY + + + + + + + ${OPEN_FILE_PATH}/log_error.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n + UTF-8 + + + + ${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log + + 100MB + + + 15 + + + + ERROR + ACCEPT + DENY + + + + + + + + + + + + + + + + + + + + diff --git a/hiver-core/pom.xml b/hiver-core/pom.xml index f3039e4e..04253911 100644 --- a/hiver-core/pom.xml +++ b/hiver-core/pom.xml @@ -138,5 +138,9 @@ org.seleniumhq.selenium selenium-java + + com.github.ben-manes.caffeine + caffeine + \ No newline at end of file diff --git a/hiver-core/src/main/java/cc/hiver/core/config/cache/CustomCacheResolver.java b/hiver-core/src/main/java/cc/hiver/core/config/cache/CustomCacheResolver.java new file mode 100644 index 00000000..d090170f --- /dev/null +++ b/hiver-core/src/main/java/cc/hiver/core/config/cache/CustomCacheResolver.java @@ -0,0 +1,62 @@ +package cc.hiver.core.config.cache; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.interceptor.CacheOperationInvocationContext; +import org.springframework.cache.interceptor.CacheResolver; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class CustomCacheResolver implements CacheResolver, InitializingBean { + + @Nullable + private List cacheManagerList; + + public CustomCacheResolver(){ + } + public CustomCacheResolver(List cacheManagerList){ + this.cacheManagerList = cacheManagerList; + } + + public void setCacheManagerList(@Nullable List cacheManagerList) { + this.cacheManagerList = cacheManagerList; + } + public List getCacheManagerList() { + return cacheManagerList; + } + + @Override + public void afterPropertiesSet() { + Assert.notNull(this.cacheManagerList, "CacheManager is required"); + } + + @Override + public Collection resolveCaches(CacheOperationInvocationContext context) { + Collection cacheNames = getCacheNames(context); + if (cacheNames == null) { + return Collections.emptyList(); + } + Collection result = new ArrayList<>(); + for(CacheManager cacheManager : getCacheManagerList()){ + for (String cacheName : cacheNames) { + Cache cache = cacheManager.getCache(cacheName); + if (cache == null) { + throw new IllegalArgumentException("Cannot find cache named '" + + cacheName + "' for " + context.getOperation()); + } + result.add(cache); + } + } + return result; + } + + private Collection getCacheNames(CacheOperationInvocationContext context){ + return context.getOperation().getCacheNames(); + } +} diff --git a/hiver-core/src/main/java/cc/hiver/core/config/cache/RedisCacheConfig.java b/hiver-core/src/main/java/cc/hiver/core/config/cache/RedisCacheConfig.java index d7a207e0..74199ca0 100644 --- a/hiver-core/src/main/java/cc/hiver/core/config/cache/RedisCacheConfig.java +++ b/hiver-core/src/main/java/cc/hiver/core/config/cache/RedisCacheConfig.java @@ -4,22 +4,27 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.cache.interceptor.CacheErrorHandler; +import org.springframework.cache.interceptor.CacheResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.RedisSerializationContext; -import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.*; +import java.io.Serializable; import java.time.Duration; +import java.util.ArrayList; +import java.util.List; /** * Redis缓存 @@ -32,14 +37,48 @@ public class RedisCacheConfig extends CachingConfigurerSupport { @Value("${hiver.cache.timeToLive:-1}") private Duration timeToLive; + @Autowired + RedisConnectionFactory factory; + + + @Override + public CacheResolver cacheResolver() { + // 通过Guava实现的自定义堆内存缓存管理器 + CacheManager caffeineCacheManager = new CaffeineCacheManager(); + CacheManager redisCacheManager = cacheManager(); + List list = new ArrayList<>(); + // 优先读取堆内存缓存 + list.add(caffeineCacheManager); + // 堆内存缓存读取不到该key时再读取redis缓存 + list.add(redisCacheManager); + return new CustomCacheResolver(list); + } + + + @Bean + public RedisTemplate redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory){ + //初始化一个redis模板 + RedisTemplate template = new RedisTemplate<>(); + // key采用String的序列化方式 + template.setKeySerializer(new StringRedisSerializer()); + // value序列化方式采用jackson + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + // hash的key也采用String的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + // hash的value序列化方式采用jackson + template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); + template.setConnectionFactory(redisConnectionFactory); + return template; + } + /** * 自定义序列化方式 * - * @param factory + * @param * @return */ @Bean - public CacheManager cacheManager(RedisConnectionFactory factory) { + public CacheManager cacheManager() { RedisSerializer redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); @@ -92,4 +131,5 @@ public class RedisCacheConfig extends CachingConfigurerSupport { }; return cacheErrorHandler; } + } diff --git a/hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/OrderController.java b/hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/OrderController.java index d02bbad9..6dd0711e 100644 --- a/hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/OrderController.java +++ b/hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/OrderController.java @@ -41,6 +41,7 @@ import java.util.stream.Collectors; * * @author Yazhi Li */ + @Slf4j @RestController @Api(tags = "订单接口") @@ -117,6 +118,13 @@ public class OrderController { * 1.当前抢单工的抢单状态需进行查验,非正常无法进行抢单 * 2.正常状态下,判定当前抢单工押金余额是否充足,不足时需要将当前扛包工状态自动修正为不可接单状态,同时当前订单无法抢购。 * 3.正常抢单情况下,需要在redis中设置抢单后的剩余押金金额,要存在抢单锁机制,实时更新余额 + * + * 计划暂定使用延时双删策略,尽量保证最终一致性,但不是强一致性: + * 先进行缓存清除,再执行update,最后(延迟N秒)再执行缓存清除。 + * (延迟N秒)的时间要大于一次写操作的时间,一般为3-5秒。 + * 原因:如果延迟时间小于写入redis的时间,会导致请求1清除了缓存,但是请求2缓存还未写入的尴尬。。。 + * ps:一般写入的时间会远小于5秒 + * */ @RequestMapping(value = "/rush/order/{orderId}", method = RequestMethod.POST) @ApiOperation(value = "抢单接口", notes = "需要通过下单编号获取订单信息后进行绑定") diff --git a/hiver-modules/hiver-base/src/main/java/cc/hiver/base/handler/OrderXdHandler.java b/hiver-modules/hiver-base/src/main/java/cc/hiver/base/handler/OrderXdHandler.java new file mode 100644 index 00000000..a149c3e6 --- /dev/null +++ b/hiver-modules/hiver-base/src/main/java/cc/hiver/base/handler/OrderXdHandler.java @@ -0,0 +1,41 @@ +package cc.hiver.base.handler; + +import cc.hiver.core.entity.OrderXd; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import top.javatool.canal.client.annotation.CanalTable; +import top.javatool.canal.client.handler.EntryHandler; + +/** + * @author benjamin_5 + * @Description + * @date 2022/9/25 + */ +@CanalTable("bs_ecif") +@Component +@AllArgsConstructor +@Slf4j +public class OrderXdHandler implements EntryHandler { + + private final RedisTemplate redisTemplate; + + @Override + public void insert(OrderXd orderXd) { + log.info("[新增]"+orderXd.toString()); + redisTemplate.opsForValue().set("ORDER:"+orderXd.getOrderId(),orderXd); + } + + @Override + public void update(OrderXd before, OrderXd after) { + log.info("[更新]"+after.toString()); + redisTemplate.opsForValue().set("ORDER:"+after.getOrderId(),after); + } + + @Override + public void delete(OrderXd orderXd) { + log.info("[删除]"+orderXd); + redisTemplate.delete("ORDER:"+orderXd.getOrderId()); + } +} diff --git a/pom.xml b/pom.xml index bd08f5f7..61f94741 100644 --- a/pom.xml +++ b/pom.xml @@ -282,6 +282,11 @@ selenium-java ${selenuim.version} + + org.springframework.boot + spring-boot-starter-cache + ${spring.boot.version} +