业务场景
? ? ? ? 业务方提出一个根据配置规则进行调查问卷信息推送的业务,有点类似QQ弹出的游戏调查问卷,问卷配置内容包括当日最大提醒次数、回答后不再提醒天数等一些配置参数信息。配置包括临时规则、默认规则。每天的订单数据平均有40W,实际符合规则的会在10W左右。数据会存在高峰期,大致为早上、晚上、凌晨三个时间段。
需求分析
? ?配置读取
? ? ? ? 1. 首先从数据量来看,当一次订单数据进来时,系统不能每次都去查数据库得到配置信息,然后去过滤当前订单是否满足推送规则。因此需要走缓存处理。
? ? ? ? 2.? 说到缓存,大多数人第一反应是Redis,但因为配置可能会动态调整,因此缓存不能永久不变,设置多久过期时间也是一个问题,那么Redis这时候真的符合业务要求吗。答案是不符合的,不管设置多久的过期时间都不会满足临时规则的添加,可能会有疑惑是我设置当新增时候就把Redis缓存清空不就行了,但是这个业务特殊地方就是配置和推送服务并不在一块,这也很好理解,配置是属于对内基础业务,推送服务是属于对外业务。
? ? ? ? 3. 不用Redis缓存,那怎么解决又想要缓存,这时能考虑的解决方案就是使用类变量去存储配置数据,加上定时刷新机制确保数据一致。
? 提醒数据信息
? ? ? ? 1. 当日提交数据这个可以利用Redis缓存去处理用户的提交数据信息,
? ? ? ? 2. 多少天内间隔,这个可以存入Redis数据,过期时间和配置中设置的间隔时间一致,只是Redis的存储的数据个数会比较多,但因为存的都是数值信息,且有过期时间并不会导致数据累加。也可以通过类变量去保存数据信息,但是考虑到数据库的日增长量,读取数据库的压力会越来越大,因此使用Redis缓存作为提醒数据信息缓存。
? ? 高峰期问题?
? ? ? ? ? ?因数据存在高峰期时间,因此配置数据和业务方约定好尽量提前设置好配置,同时用户提交的数据可以通过队列或者多线程异步处理,保证接口及时响应,因问卷信息并不影响实际业务,只是做统计使用,可以在提交中不设置事务以及防重复校验,重复提交校验可以放数据库层去校验,比如加上唯一索引
需求场景实现
? ?配置读取
? ? ? ? ? 1. 定时器实现
@Slf4j
@Component
public class MemberManager {
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
@Autowired
private MemberServiceManager memberServiceManager;
/**
* 使用@PostConstruct注解,确保Servlet实例化后就已经把数据加载完了
*/
@PostConstruct
public void loadMember() {
//设置定时执行间隔,这个假设是加载数据一
scheduledExecutorService.schedule(this::loadVipMember, 50, TimeUnit.MILLISECONDS);
//这里假设加载数据二
scheduledExecutorService.schedule(this::loadNormalMember, 50, TimeUnit.MILLISECONDS);
}
/**
* 加载VIP会员数据,这里是临时写的一个接口,理解一下就行
*/
private void loadVipMember(){
IMemberService memberService = memberServiceManager.getMemberService(MemberLevelServiceConstants.VIP_MEMBER);
memberService.loadMemberInfo();
}
/**
* 加载普通会员数据
*/
private void loadNormalMember(){
IMemberService memberService = memberServiceManager.getMemberService(MemberLevelServiceConstants.NORMAL_MEMBER);
memberService.loadMemberInfo();
}
}
2. 加载数据Service实现,Service实现是通过策略模式,因不同的规则对应的加载数据可能不一样
@Slf4j
@Component
public class MemberServiceManager {
@Autowired
private Map<String, IMemberService> handleMap;
public IMemberService getMemberService(String name) {
return handleMap.get(name);
}
}
public class MemberLevelServiceConstants {
public static final String REGISTER_MEMBER = "registerMemberService";
public static final String NORMAL_MEMBER = "normalMemberService";
public static final String VIP_MEMBER = "vipMemberService";
}
@Slf4j
@Service(MemberLevelServiceConstants.VIP_MEMBER)
public class MemberVipServiceImpl implements IMemberService {
private List<String> memberList = new ArrayList<>();
@Override
public void loadMemberInfo() {
System.out.println("当前Service" + MemberLevelEnum.VIP.getName());
memberList.add("VIP会员:张三");
}
@Override
public List<String> getMemberInfo() {
return memberList;
}
}
3. 测试实际效果
总结
? ? ? ? 缓存并不一定就得使用Redis,Redis并不是一定适合全部的业务场景,使用Redis还需要保证数据库和缓存的一致性,在分布式系统下,这个会增加系统的代码复杂度,使用类变量缓存,只需要保证服务读取的数据库是一致的,服务可以通过多数据源来控制所需要的数据库,使用Redis还有一个问题是如果规则结构类型发生改变,Redis的数据存储可能不支持这个结构类型,Redis理解应该只是作为一个旁路缓存使用。当然使用类变量去存数据,还有一个问题就是数据的刷新时间如何控制,这里需要结合实际的业务去设置,因此当一个实际业务到了手上时,不应该理所当然的去分析,而应结合实际场景分析。
|