思路:
-
我们可在定时任务拉取数据时,将最新的数据信息通过mq同步到主业务工程进行缓存处理,这样就避免了多用户从数据库反复加载股票数据导致数据库负载过高的问题,同样也提高了大屏终端服务的吞吐量;
-
使用CaffineCache本地缓存而非redis远程缓存,能提供更高效的响应速度,同时避免了与redis之间交换带来的网络I/O成本开销;
一、采集工程向MQ同步数据
第一步:导入amqp依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
第二步:在配置中配置rabbitmq(application-mq.yml)
spring:
rabbitmq:
host: # rabbitMQ的ip地址
port: 5672 # 端口
username: yourname
password: password
virtual-host: /
第三步:在配置类中定于需要的交换机、队列、消息转换资源bean:
@Configuration
public class MqConfig {
/**
* 重新定义消息序列化的方式,改为基于json格式序列化和反序列化
* @return
*/
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
/**
* 国内大盘信息队列
* @return
*/
@Bean
public Queue innerMarketQueue(){
return new Queue("innerMarketQueue",true);
}
/**
* 定义路由股票信息的交换机
* @return
*/
@Bean
public TopicExchange innerMarketTopicExchange(){
return new TopicExchange("stockExchange",true,false);
}
}
/**
* 绑定队列到指定交换机
* @return
*/
@Bean
public Binding bindingInnerMarketExchange(){
return BindingBuilder.bind(innerMarketQueue()).to(innerMarketTopicExchange())
.with("inner.market");
}
数据同步逻辑:
@Override
public void getInnerMarketInfo() {
//......
//解析的数据批量插入数据库
int count= stockMarketIndexInfoMapper.insertBatch(entities);
log.info("当前插入了:{}行数据",count);
//通知后台终端刷新本地缓存,发送的日期数据是告知对方当前更新的股票数据所在时间点
rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
}
二、CaffeineCache
第一步:注入依赖:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
第二步:CommonConfig中配置本地缓存bean:
/**
* 构建bean
* @return
*/
@Bean
public Cache<String,Object> caffineCache(){
Cache<String,Object> cache = Caffeine.
.new Builder()
.maximumSize(200) //设置缓存数量上限
.expireAfterAccess(1,TimeUnit.SECONDS) //访问1秒后删除
.expireAfterWrite(1,TimeUnit.SECONDS)//写入1秒后删除
.initialCapacity(100)// 初始的缓存空间大小
.recordStats()//开启统计
.build();
return cache;
}
第三步:监听消息,刷新缓存
/**
* 监听股票变化消息
*/
@Component
@Slf4j
public class MqListener {
@Autowired
private Cache<String,Object> caffeineCache;
@Autowired
private StockService stockService;
/**
*
* @param infos
* @throws Exception
*/
@RabbitListener(queues = "innerMarketQueue")
public void acceptInnerMarketInfo(Date date)throws Exception{
//获取时间毫秒差值
long diffTime= DateTime.now().getMillis()-new DateTime(date).getMillis();
//超过一分钟告警
if (diffTime>60000) {
log.error("采集国内大盘时间点:{},同步超时:{}ms",new DateTime(date).toString("yyyy-MM-dd HH:mm:ss"),diffTime);
}
//将缓存置为失效删除
caffeineCache.invalidate("innerMarketInfosKey");
//调用服务更新缓存
stockService.getNewestInnerMarketInfos();
}
}
第四步:在impl类中添加缓存查询:
@Autowired
private Cache<String,Object> caffeineCache;
/**
* 定义获取A股大盘最新数据
* @return
*/
@Override
public R<List<InnerMarketDomain>> getInnnerMarketInfos() {
//从缓存中加载数据,如果不存在,则走补偿策略获取数据,并存入本地缓存
R<List<InnerMarketDomain>> data= (R<List<InnerMarketDomain>>) caffeineCache.get("innerMarketInfos", key->{
//如果不存在,则从数据库查询
//1.获取最新的股票交易时间点
Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
//TODO 伪造数据,后续删除
lastDate=DateTime.parse("2022-01-03 09:47:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
//2.获取国内大盘编码集合
List<String> innerCodes = stockInfoConfig.getInner();
//3.调用mapper查询
List<InnerMarketDomain> infos= stockMarketIndexInfoMapper.getInnerIndexByTimeAndCodes(lastDate,innerCodes);
//4.响应
return R.ok(infos);
});
return data;
}