还记得第一小节【SpingBoot项目之性能压测并提高并发访问量(一)】,我们文末提到mysql数据库压力问题,这一小节会引入redis做缓存,分散数据库的压力,再次提升我们项目的性能
redis作为我们的中间件缓存,其最好的优化思路便是距离我们用户请求最近的地方做缓存最为合适,因为当请求进来直接打在我们的controller上,查找缓存找到了相应的数据,便不用再次调用下游的service以及Dao,大大减轻了数据库的压力。下面我们就看一下如何一步步实现
1)项目集成redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
2)修改redis的默认配置
#配置springboot对redis的依赖
spring.redis.host=192.168.124.34
spring.redis.port=6379
spring.redis.database=0
spring.redis.lettuce.pool.max-active=10000
spring.redis.jedis.pool.max-idle=50
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.lettuce.pool.min-idle=0
spring.redis.password=
#设置jedis连接池
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.min-idle=20
3)找到我们需要引入缓存的获取商品详情的controller,并作出相应的修改
/**
*原逻辑直接调用Service
*ItemModel itemModel = itemService.getItemById(id);
*/
//引入redis
//根据商品的id到redis内获取
ItemModel itemModel = (ItemModel) redisTemplate.opsForValue().get("item_"+id);
//若redis内不存在对应的itemModel,则访问下游service
if(itemModel == null){
itemModel = itemService.getItemById(id);
//设置itemModel到redis内
redisTemplate.opsForValue().set("item_"+id,itemModel);
//设置缓存过期时间
redisTemplate.expire("item_"+id,10, TimeUnit.MINUTES);
}
这里注意:对应的object实体类一定要实现序列化接口,不然无法存储redis
然后启动项目进行访问:
查看我们的redis
已经存入了相应的数据。但是看着一堆的乱码似的东西,想要排查问题都看不懂,显然是不行的,我们需要自己来实现他的序列化方式,定义展示的格式
4)自定义一个配置类,我们去实现存入redis之后的格式信息的一个配置类【RedisConfig 】
@Component
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//key的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//value的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(DateTime.class,new JodaDateTimeJsonSerializer());
simpleModule.addDeserializer(DateTime.class,new JodaDateTimeJsonDeserializer());
//引入详细的类信息以及特殊字段类型信息
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.registerModule(simpleModule);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
其中自定义了时间格式的序列化与反序列化的两个类,分别是:
【JodaDateTimeJsonDeserializer 】
public class JodaDateTimeJsonDeserializer extends JsonDeserializer<DateTime> {
@Override
public DateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
String dateString =jsonParser.readValueAs(String.class);
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
return DateTime.parse(dateString,formatter);
}
}
【JodaDateTimeJsonSerializer 】
public class JodaDateTimeJsonSerializer extends JsonSerializer<DateTime> {
@Override
public void serialize(DateTime dateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(dateTime.toString("yyyy-MM-dd HH:mm:ss"));
}
}
5)再次重启去访问我们的项目,访问成功,查看我们的redis
格式化完毕
6)下面我们看一下压测结果(这里都是以单机测试的,没有加入水平扩容的集群)
压测条件:模拟1000个线程,10秒时间,循环访问10次,共10000次
6.1)下面是没有集成redis之前的压测结果:
6.2)下面是集成了redis之后的压测结果:
结果说明:通过前后的对比,很明显我们的性能得到了一个质的提升
没有集成redis之前:平均耗时777毫秒,吞吐量1024.4/sec,总耗时9秒
集成redis之后:平均耗时187毫秒,吞吐量2984.2/sec,总耗时3秒
平均耗时提高了将近4倍,吞吐量将近提升了3倍,总耗时也提升了3倍,性能大大得到一个优化