一 导入
在一开始通过数据库连接和在进行压力测试以后发现有以下问题
1)定时上架
2)超卖(后面分析)
3)重复下单(后面分析)
-----------------------------------------------------------------------------------
二 定时上架(秒杀列表)
1>问题分析
1)查询-->索引-->磁盘-->浪费io-->读取慢-->整体的qbs不高
2)秒杀商品的数据 基本不怎么变化 除了库存 没必要每次都查询库存
引入redis-->分布式缓存
在redis和数据库之间设置一个定时任务
3)考虑哪些内容可以放在redis中
>> 一些常用配置/用户的基本信息/评论/秒杀详情页
4)考虑秒杀列表中存在多个场次,如何设计key
>> 几个场次设计几个key (不用做场次过滤)
5)考虑如何设计value
>>因为秒杀商品信息多,用Hash数据类型来存储对象(field和value)是最合适的
6)定时任务做什么
>>查询当天需要参与秒杀的商品信息
>>并且将前一天的秒杀内容删除掉
-----------------------------------------------------------------------------------
2>具体应用
1)elasticJob的集成
2)配置一个初步的秒杀job(SeckillProductCacheJob)
>>实现SimpleJob接口(当当设计)
>>贴上@Component
/**
* 秒杀定时任务
*/
@Component
@Setter
@Getter
public class SeckillProductCacheJob implements SimpleJob {
@Value("${jobCron.initSeckillProduct}") //从配置中获取
private String cron;
@Override
public void execute(ShardingContext shardingContext) {
String time = shardingContext.getShardingParameter(); // 每个分片传进来的参数
}
}
3)配置bean
@Bean(initMethod = "init")
public SpringJobScheduler initSeckillProductCacheJob(CoordinatorRegistryCenter registryCenter, SeckillProductCacheJob job){
LiteJobConfiguration jobConfiguration = ElasticJobUtil.createJobConfiguration(job.getClass(), job.getCron(),3,"0=10,1=12,2=14",false);
SpringJobScheduler springJobScheduler = new SpringJobScheduler(job, registryCenter,jobConfiguration );
return springJobScheduler;
}
4)秒杀服务的远程调用的接口(SeckillProductFeignApi)
>>记得贴参数注解
@FeignClient(name="seckill-service")
public interface SeckillProductFeignApi {
Result<List<SeckillProductVo>> queryByTimeForJob(@RequestParam("time") Integer time);
}
5)在之前的秒杀服务的controller重新定义一个接口(queryByTimeForJob)
>>记得贴参数注解,和远程调用接口一致
//--秒杀定时任务列表-----------------------------------------------------------------------
@RequestMapping("/queryByTimeForJob") // 秒杀服务和商品服务关联的vo对象
public Result<List<SeckillProductVo>>queryByTimeForJob(@RequestParam("time")Integer time){
return Result.success(seckillProductService.queryByTime(time));
}
6)定义一个降级,实现兜底(SeckillProductFeign)
>>方法名和接口的一致
/**
* 降级兜底 返回null即可
*/
@Component
public class SeckillProductFeign implements SeckillProductFeignApi {
@Override
public Result<List<SeckillProductVo>> queryByTimeForJob(Integer time) {
return null;
}
}
7)秒杀服务远程调用的接口关联降级类
@FeignClient(name="seckill-service",fallback = SeckillProductFeignBack.class)
public interface SeckillProductFeignApi {
@RequestMapping("/seckillProduct/queryByTimeForJob")
Result<List<SeckillProductVo>> queryByTimeForJob(@RequestParam("time") Integer time);
}
8) 完善定时任务的步骤
>>业务逻辑
a.获取到分片参数
b.远程调用秒杀服务 根据场次查询当天的集合数据
c.删除前一天数据
d.将查询到的数据放到redis中
/**
* 秒杀定时任务
*/
@Component
@Setter
@Getter
public class SeckillProductCacheJob implements SimpleJob {
@Value("${jobCron.initSeckillProduct}") //从配置中获取
private String cron;
@Autowired
private SeckillProductFeignApi seckillProductFeignApi;
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public void execute(ShardingContext shardingContext) {
// 每个分片传进来的参数
String time = shardingContext.getShardingParameter();
// 远程调用秒杀服务 根据场次查询当天的商品的集合数据
Result<List<SeckillProductVo>>result = seckillProductFeignApi.queryByTimeForJob(Integer.parseInt(time));
if (result == null || result.hasError()){
// 通过短信或者邮件方式通知运维人员
return;
}
// 删除前一天的数据
String seckillProductKey = JobRedisKey.SECKILL_PRODUCT_HASH.getRealKey(time);
redisTemplate.delete(seckillProductKey);
//将查询到的数据放入到Redis中
List<SeckillProductVo> voList = result.getData();
for (SeckillProductVo vo : voList) {
redisTemplate.opsForHash().put(seckillProductKey,String.valueOf(vo.getId()),JSON.toJSONString(vo));
}
}
}