springboot+redis实现⽂章浏览量记录
springboot+redis实现⽂章浏览量记录
之前,个⼈博客⽹站⾥的⽂章访问量都是存在MySQL中的。每次访问都需要通过⽂章id查数据库,获得浏览量后+1操作。之后在更新数据库,为了实时显⽰⽂章浏览量还要在读⼀遍数据库。访问量多了之后就带来了线程安全问题:两个线程同时获得数量,各⾃+1后更新,这个访问量就有问题了。对于这个情况,之前直接⽤synchronized粗暴的对操作加锁。这带来⼀个问题,多个线程只能有⼀个线程来获取⽂章信息,其他线程阻塞,。后来⼜想到另⼀个解决⽅案,浏览量的增加独⽴出来,不和读取⽂章信息绑定。每读取⼀个⽂章信息后返回调⽤⼀个接⼝来增加数量。这有⼀个缺陷就是显⽰的⽂章访问量不是实时的。
最后决定使⽤redis来存储⽂章的访问量,redis的关键语句是incr key。作⽤是key的value+1;key和⽂章id绑定。由于redis是单线程的,单个语句的执⾏是线程安全的。
理想很丰满,但操作的时候还是遇到了不少问题。
<dependency>
<groupId>org.springframework.boot</groupId>
如何用照片做抖音视频
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
设置redis配置
redis:
database:0
# Redis服务器地址写你的ip
host: XXX.XX.XX.XXX
# Redis服务器连接端⼝
port:你的端⼝号
# Redis服务器连接密码(默认为空)
password:你的密码
四川高考是全国几卷
timeout:6000
lettcue:
pool:
# 连接池中的最⼤空闲连接,默认值也是8。
max-idle:100
# 连接池中的最⼩空闲连接,默认值也是0。
min-idle:10
# 如果赋值为-1,则表⽰不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)
max-active:8
# 等待可⽤连接的最⼤时间,单位毫秒,默认值为-1,表⽰永不超时。如果超过等待时间,则直接抛出JedisConnectionException
max-wait:2000
如果不设置redis的连接池属性的话默认只有⼀个连接,多线程来使⽤redisTemplate会报”redis连接异常“。
设置redisTemplate类模板
@Bean
public RedisTemplate<String, Object>redisTemplate(LettuceConnectionFactory connectionFactory){
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate =new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));//value序列化
return redisTemplate;
}
JUnit测试类:
@Resource
RedisUtils redisUtils;
@Test
public void redisTest(){
try{
Runnable runnable =new Runnable(){
@SneakyThrows
@Override
public void run(){
for(int i=0;i<500;i++){
Thread.sleep(1);
System.out.println(Thread.currentThread().getName()+": "+addReadNums());
}
北京 自住型商品房>西安的二本大学}
};
ExecutorService executorService = wFixedThreadPool(5);
System.out.println("@Test线程执⾏完毕");
}catch(Exception e){
System.out.println(e);
}
}
public Long addReadNums(){
return redisUtils.incrBy("test",1);
}
进⾏多线程模拟测试的时候就报错了,lettuce线程池在多线程下有问题,查资料后都说⽤Jedis作为redis的线程池。修改后
修改后的依赖:在spring-boot-starter-data-redis去除lettuce线程池,导⼊jedis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apachemons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
酱香型白酒<version>3.3.0</version>
</dependency>
这⾥要注意Jedis的版本和spring-boot-starter-data-redis版本,版本不⼀致会导致redisTemplate创建失败。
参考:
还要修改redisTemplate类创建⽅式
@Bean
public RedisTemplate<String, Object>redisTemplate(JedisConnectionFactory connectionFactory){
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate =new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));//value序列化
return redisTemplate;
}
这时候测试发现,线程运⾏⼀会就打印:Shutting down ExecutorService ‘applicationTaskExecutor‘ 。之后线程就停⽌了。发现是JUnit单元测试的坑:它会在主线程结束后调⽤相关的it()⽅法,将JVM关闭,所以,⼦线程被动挂了。
参考:
修改后的测试类:
@Test
public void redisTest(){
try {
Runnable runnable = new Runnable(){
@SneakyThrows
@Override
public void run() {
for(int i=0;i<500;i++){
Thread.sleep(1);
System.out.println(Thread.currentThread().getName()+": "+addReadNums());
}
}
};
ExecutorService executorService = wFixedThreadPool(5);
try {
qq显示隐身TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("@Test线程执⾏完毕");
} catch (Exception e) {
System.out.println(e);
}
}
public Long addReadNums(){
return redisUtils.incrBy("test", 1);
}
看⼀下redis确实是1000。
最后对于⽂章浏览量,只要key传⼊的是⽂章的id即可。如果担⼼key与其他的内容重复,添加标识符区别即可。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论