springboot和redis处理页面缓存
页面缓存是应对高并发的一个比较常见的方案,当请求页面的时候,会先查询redis缓存中是否存在,若存在则直接从缓存中返回页面,否则会通过代码逻辑去渲染页面,并将渲染后的页面缓存到redis中,然后返回。下面通过简单的demo来描述这一过程:
一、准备工作:
1、新建一个springboot工程,命名为novel,添加如下依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dtouding</groupId>
<artifactId>novel</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>novel</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> <!--mysql connector-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency> <!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency> <!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency> <!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
pom.xml
2、创建一个novel表,并插入几条数据,如下:
DROP TABLE IF EXISTS `t_novel`;
CREATE TABLE `t_novel` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '小说ID',
`novel_name` varchar(16) DEFAULT NULL COMMENT '小说名称',
`novel_category` varchar(64) DEFAULT NULL COMMENT '小说类别',
`novel_img` varchar(64) DEFAULT NULL COMMENT '小说图片',
`novel_summary` longtext COMMENT '小说简介',
`novel_author` varchar(16) DEFAULT NULL COMMENT '小说作者',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
INSERT INTO `t_novel` VALUES ('', '诛仙', '仙侠', '/img/zhuxian.jpg', '该小说以“天地不仁,以万物为刍狗”为主题,讲述了青云山下的普通少年张小凡的成长经历以及与两位奇女子凄美的爱情故事,整部小说构思巧妙、气势恢宏,开启了一个独具魅力的东方仙侠传奇架空世界,情节跌宕起伏,人物性格鲜明,将爱情、亲情、友情与波澜壮阔的正邪搏斗、命运交战汇集在一起,文笔优美,故事生动。它与小说《飘邈之旅》、《小兵传奇》并称为“网络三大奇书”,又被称为“后金庸时代的武侠圣经”。', '萧鼎');
INSERT INTO `t_novel` VALUES ('', '英雄志', '武侠', '/img/yingxiongzhi.jpg', '《英雄志》为一虚构中国明朝历史的古典小说,借用明英宗土木堡之变为背景,以复辟为舞台,写尽了英雄们与时代间的相互激荡,造反与政变、背叛与殉道……书中无人不可以为英雄,贩夫走卒、市井小民、娼妇与公主、乞丐与皇帝,莫不可以为英雄。孙晓一次又一次建立英雄的面貌,又一次一次拆解英雄的形象。于穷途末路之时的回眸一笑,是孙晓笔下的安慰与沧桑。', '孙晓');
t_novel
3、在application.yml文件中配置数据库连接信息和redis连接信息:
spring:
##thymeleaf.#
thymeleaf:
##默认前缀
prefix: classpath:/templates/
##默认后缀
suffix: .html
cache: false
servlet:
content-type: text/html
enabled: true
encoding: UTF-8
mode: HTML5 ##datasource.#
datasource:
url: jdbc:mysql://localhost:3306/novel?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
tomcat:
max-active: 2
max-wait: 60000
initial-size: 1
min-idle: 1
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: select 'dtouding'
test-while-idle: true
test-on-borrow: false
test-on-return: false ##mybatis.#
mybatis:
type-aliases-package: com.dtouding.novel.domain
configuration:
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 30
mapper-locations: classpath:com/dtouding/novel/dao/*.xml redis:
host: 127.0.0.1
port: 6379
timeout: 3
password: 123456
poolMaxTotal: 10
poolMaxIdle: 10
poolMaxWait: 3
application.yml
4、做一个简单的查询小说列表的功能,编写dao、service、controller、html:
public class Novel { private Long id; private String novelName; private String novelCategory; private String novelImg; private String novelSummary; private String novelAuthor; public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getNovelName() {
return novelName;
} public void setNovelName(String novelName) {
this.novelName = novelName;
} public String getNovelCategory() {
return novelCategory;
} public void setNovelCategory(String novelCategory) {
this.novelCategory = novelCategory;
} public String getNovelImg() {
return novelImg;
} public void setNovelImg(String novelImg) {
this.novelImg = novelImg;
} public String getNovelSummary() {
return novelSummary;
} public void setNovelSummary(String novelSummary) {
this.novelSummary = novelSummary;
} public String getNovelAuthor() {
return novelAuthor;
} public void setNovelAuthor(String novelAuthor) {
this.novelAuthor = novelAuthor;
}
}
Novel
@Mapper
public interface NovelDao { @Select("select * from t_novel")
List<Novel> list(); }
NovelDao
@Service
public class NovelService { @Resource
private NovelDao novelDao; public List<Novel> list() {
return novelDao.list();
}
}
NovelService
@Controller
@RequestMapping(value = "/novel")
public class NovelController { @Autowired
private NovelService novelService; @RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(Model model) {
List<Novel> list = novelService.list();
model.addAttribute("novelList", list);
return "novel_list";
}
}
NovelController
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>小说列表</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- jquery -->
<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
<!-- bootstrap -->
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}" />
<script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script>
</head>
<body> <div class="panel panel-default">
<div class="panel-heading">小说列表</div>
<table class="table" id="goodslist">
<tr><td>小说名称</td><td>小说图片</td><td>小说类别</td><td>小说作者</td><td>小说简介</td>
<tr th:each="novel : ${novelList}">
<td th:text="${novel.novelName}"></td>
<td ><img th:src="@{${novel.novelImg}}" width="100" height="100" /></td>
<td th:text="${novel.novelCategory}"></td>
<td th:text="${novel.novelAuthor}"></td>
<td th:text="${novel.novelSummary}"></td>
</tr>
</table>
</div>
</body>
</html>
novel_list
5、通过http://localhost:8080/novel/list,可访问。
二、缓存novel_list页面
1、引入redis依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
2、编写redis配置类和通用的redis工具类:
@Component
@ConfigurationProperties(prefix = "redis")
public class RedisConfig { private String host;
private int port;
private int timeout;//秒
private String password;
private int poolMaxTotal;
private int poolMaxIdle;
private int poolMaxWait;//秒 public String getHost() {
return host;
} public void setHost(String host) {
this.host = host;
} public int getPort() {
return port;
} public void setPort(int port) {
this.port = port;
} public int getTimeout() {
return timeout;
} public void setTimeout(int timeout) {
this.timeout = timeout;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public int getPoolMaxTotal() {
return poolMaxTotal;
} public void setPoolMaxTotal(int poolMaxTotal) {
this.poolMaxTotal = poolMaxTotal;
} public int getPoolMaxIdle() {
return poolMaxIdle;
} public void setPoolMaxIdle(int poolMaxIdle) {
this.poolMaxIdle = poolMaxIdle;
} public int getPoolMaxWait() {
return poolMaxWait;
} public void setPoolMaxWait(int poolMaxWait) {
this.poolMaxWait = poolMaxWait;
} @Bean
public JedisPool jedisPoolFactory() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(poolMaxIdle);
jedisPoolConfig.setMaxTotal(poolMaxTotal);
jedisPoolConfig.setMaxWaitMillis(poolMaxWait * 1000);
JedisPool jedisPool = new JedisPool(jedisPoolConfig,
host,
port,
timeout * 1000,
password);
return jedisPool;
}
}
RedisConfig
@Service
public class RedisService { @Autowired
private JedisPool jedisPool; /**
* 获取存储对象
* @param key
* @param clazz
* @param <T>
* @return
*/
public <T> T get(String key, Class<T> clazz) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String str = jedis.get(key);
T t = stringToBean(str, clazz);
return t;
} finally {
returnToPool(jedis);
}
} /**
* 设置对象
* @param key
* @param expireSeconds
* @param value
* @param <T>
* @return
*/
public <T> boolean set(String key, int expireSeconds, T value) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String str = beanToString(value);
if (null == str) {
return false;
}
if (expireSeconds <= 0) {
jedis.set(key, str);
} else {
jedis.setex(key, expireSeconds, str);
}
return true;
} finally {
returnToPool(jedis);
}
} /**
* 判断key是否存在
* */
public <T> boolean exists(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return jedis.exists(key);
}finally {
returnToPool(jedis);
}
} /**
* 增加值
* */
public <T> Long incr(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return jedis.incr(key);
}finally {
returnToPool(jedis);
}
} /**
* 减少值
* */
public <T> Long decr(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
return jedis.decr(key);
}finally {
returnToPool(jedis);
}
} private <T> String beanToString(T value) {
if (null == value) {
return null;
}
if (value instanceof Integer || value instanceof Long) {
return "" + value;
} else if (value instanceof String) {
return (String) value;
} else {
return JSON.toJSONString(value);
}
} private <T> T stringToBean(String str, Class<T> clazz) {
if (StringUtils.isEmpty(str) || clazz==null) {
return null;
}
if (clazz==int.class || clazz==Integer.class) {
return (T) Integer.valueOf(str);
} else if (clazz == String.class) {
return (T) str;
} else if (clazz==long.class || clazz==Long.class) {
return (T)Long.valueOf(str);
} else {
return JSON.toJavaObject(JSON.parseObject(str), clazz);
}
} private void returnToPool(Jedis jedis) {
if (null != jedis) {
jedis.close();
}
}
}
RedisService
3、改写NovelController中的list方法,添加页面缓存逻辑,具体包括:
1)、在list方法上添加@ResponseBody注解,并修改返回类型为text/html,可以避免返回的html再次被渲染,因为缓存在redis中的页面是通过代码手工渲染的。
2)、判断redis中是否有novel_list的页面缓存,若有,则直接返回该缓存页面:
String html = redisService.get(NovelRedisKeys.NOVEL_LIST_PAGE, String.class);
if (!StringUtils.isEmpty(html)) {
return html;
}
3、若缓存中没有,则借助ThymeleafViewResolver去渲染html页面:
html = thymeleafViewResolver.getTemplateEngine().process("novel_list", webContext);
4、将渲染后的页面缓存到redis中:
if (!StringUtils.isEmpty(html)) {
//将渲染后的页面缓存到redis中
redisService.set(NovelRedisKeys.NOVEL_LIST_PAGE, 60, html);
}
4、修改后的完整代码如下:
@Controller
@RequestMapping(value = "/novel")
public class NovelController { @Autowired
private NovelService novelService; @Autowired
private RedisService redisService; @Autowired
private ThymeleafViewResolver thymeleafViewResolver; @RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public String list(HttpServletRequest request, HttpServletResponse response, Model model) {
//判断redis是否有缓存
String html = redisService.get(NovelRedisKeys.NOVEL_LIST_PAGE, String.class);
if (!StringUtils.isEmpty(html)) {
return html;
}
List<Novel> list = novelService.list();
model.addAttribute("novelList", list);
WebContext webContext = new WebContext(request,
response,
request.getServletContext(),
request.getLocale(),
model.asMap());
//渲染页面
html = thymeleafViewResolver.getTemplateEngine().process("novel_list", webContext);
if (!StringUtils.isEmpty(html)) {
//将渲染后的页面缓存到redis中
redisService.set(NovelRedisKeys.NOVEL_LIST_PAGE, 60, html);
}
return html;
}
}
springboot和redis处理页面缓存的更多相关文章
- Django + Redis实现页面缓存
目的:把从数据库读出的数据存入的redis 中既提高了效率,又减少了对数据库的读写,提高用户体验. 例如: 1,同一页面局部缓存,局部动态 from django.views import View ...
- PHP+Redis 实例 页面缓存
前提分析! 上面的图,我分为了三个层级去做页面缓存,其实不一定要三个层面都实现的,如果你做了页面级的,项目初期是够了,作为接口级,基本可以解决很多吞吐量. 对于上面的三个层级,我用了同一个方法去做. ...
- Redis-基本概念、java操作redis、springboot整合redis,分布式缓存,分布式session管理等
NoSQL的引言 Redis数据库相关指令 Redis持久化相关机制 SpringBoot操作Redis Redis分布式缓存实现 Resis中主从复制架构和哨兵机制 Redis集群搭建 Redis实 ...
- Springboot Mybatis Redis 实现二级缓存
前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同 ...
- springboot+mybatis+redis实现分布式缓存
大家都知道springboot项目都是微服务部署,A服务和B服务分开部署,那么它们如何更新或者获取共有模块的缓存数据,或者给A服务做分布式集群负载,如何确保A服务的所有集群都能同步公共模块的缓存数据, ...
- SpringBoot集成Redis来实现缓存技术方案
概述 在我们的日常项目开发过程中缓存是无处不在的,因为它可以极大的提高系统的访问速度,关于缓存的框架也种类繁多,今天主要介绍的是使用现在非常流行的NoSQL数据库(Redis)来实现我们的缓存需求. ...
- spring-boot 整合redis作为数据缓存
添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>sp ...
- springboot中redis的缓存穿透问题
什么是缓存穿透问题?? 我们使用redis是为了减少数据库的压力,让尽量多的请求去承压能力比较大的redis,而不是数据库.但是高并发条件下,可能会在redis还没有缓存的时候,大量的请求同时进入,导 ...
- PHP+Redis 实例【二】页面缓存 新玩法
今天算是认识到博客园里的审查团队多内幕了,哈哈,贴个图玩下. 气死宝宝了. 进入主题! 今天就不写什么功能性的了,换下口味说下关于页面级的缓存,应该怎么做. 相信有很多小伙伴查了百度,甚至google ...
随机推荐
- 关于流媒体(m3u8)的播放与下载
前一段时间做了一个视频播放下载应用,抓取的是优酷的视频,虽然优酷有自己的开发平台http://open.youku.com/,但未真正的实现.所以只能靠抓取视频源,Youku的视频采取了加密+动态的获 ...
- 【leetcode 字符串】466. Count The Repetitions
https://leetcode.com/problems/count-the-repetitions/description/ 找循环节 https://www.cnblogs.com/grandy ...
- maven配置中国下载源【转:http://www.cnblogs.com/libingbin/p/5949483.html】
修改 配置文件 maven 安装 路径 F:\apache-maven-3.3.9\conf 修改 settings.xml或者在.m2文件夹下新建一个settings.xml 阿里源 <mir ...
- Infinite monkey theorem(hdu 3689)
题意:问随机生成一个长度为m(m<=1000)长度的字符串,出现某个子串s的概率是多少. /* KMP+DP 设f[i][j]表示A生成到第i位,此时B串匹配到第j位的概率. 转移方程为f[i+ ...
- STL学习笔记(三) 关联容器
条款19:理解相等(equality)和等价(equivalence)的区别 相等的概念是基于 operator== 的,如果 operator== 的实现不正确,会导致并不实际相等等价关系是以&qu ...
- 洛谷 [P2216] 理想的正方形
二维单调队列 先横向跑一边单调队列,记录下每一行长度为n的区间的最值 在纵向跑一边单调队列,得出结果 注意,mi要初始化为一个足够大的数 #include <iostream> #incl ...
- 星球大战 BZOJ 1015
星球大战 [问题描述] 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过 ...
- Oracle常用操作【自己的练习】
Oracle查询的时候条件要用单引号包裹,不能用双引号;Oracle的in子查询里面的值最多有1000个........ 连接orcl数据库 C:\Windows\system32@orcl as s ...
- Python入门--13--爬虫一
URL的格式一般为(带方括号的是可选的): protocol://hostname[:port]/path/[;parameters][?query]#fragment URL由三部分组成: 第一部分 ...
- Python入门--8--现在需要先学习可视化--包:easygui
一.安装.了解easygui 下载地址:http://bbs.fishc.com/forum.php?mod=viewthread&tid=46069&extra=page%3D1%2 ...