页面缓存是应对高并发的一个比较常见的方案,当请求页面的时候,会先查询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处理页面缓存的更多相关文章

  1. Django + Redis实现页面缓存

    目的:把从数据库读出的数据存入的redis 中既提高了效率,又减少了对数据库的读写,提高用户体验. 例如: 1,同一页面局部缓存,局部动态 from django.views import View ...

  2. PHP+Redis 实例 页面缓存

    前提分析! 上面的图,我分为了三个层级去做页面缓存,其实不一定要三个层面都实现的,如果你做了页面级的,项目初期是够了,作为接口级,基本可以解决很多吞吐量. 对于上面的三个层级,我用了同一个方法去做. ...

  3. Redis-基本概念、java操作redis、springboot整合redis,分布式缓存,分布式session管理等

    NoSQL的引言 Redis数据库相关指令 Redis持久化相关机制 SpringBoot操作Redis Redis分布式缓存实现 Resis中主从复制架构和哨兵机制 Redis集群搭建 Redis实 ...

  4. Springboot Mybatis Redis 实现二级缓存

    前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同 ...

  5. springboot+mybatis+redis实现分布式缓存

    大家都知道springboot项目都是微服务部署,A服务和B服务分开部署,那么它们如何更新或者获取共有模块的缓存数据,或者给A服务做分布式集群负载,如何确保A服务的所有集群都能同步公共模块的缓存数据, ...

  6. SpringBoot集成Redis来实现缓存技术方案

    概述 在我们的日常项目开发过程中缓存是无处不在的,因为它可以极大的提高系统的访问速度,关于缓存的框架也种类繁多,今天主要介绍的是使用现在非常流行的NoSQL数据库(Redis)来实现我们的缓存需求. ...

  7. spring-boot 整合redis作为数据缓存

    添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>sp ...

  8. springboot中redis的缓存穿透问题

    什么是缓存穿透问题?? 我们使用redis是为了减少数据库的压力,让尽量多的请求去承压能力比较大的redis,而不是数据库.但是高并发条件下,可能会在redis还没有缓存的时候,大量的请求同时进入,导 ...

  9. PHP+Redis 实例【二】页面缓存 新玩法

    今天算是认识到博客园里的审查团队多内幕了,哈哈,贴个图玩下. 气死宝宝了. 进入主题! 今天就不写什么功能性的了,换下口味说下关于页面级的缓存,应该怎么做. 相信有很多小伙伴查了百度,甚至google ...

随机推荐

  1. mybatis学习(四)——config全局配置文件解析

    在全集配置文件中引入dtd约束“http://mybatis.org/dtd/mybatis-3-config.dtd”,主要有以下几个标签,现在详细解释下这几个标签的使用 1.properties属 ...

  2. 创建mavenweb项目

    1.创建maven项目 2.创建maven-web项目 3.将webapp文件夹复制到maven项目下,src路径下

  3. R语言入门--画图(一)--ggplot2

    先写一些需要用到的知识点,比如包.函数 dplyr 很好用的包 经常与ggplot2连用 mutate:用于对数据框的列进行重新处理,或者用处理的结果添加新列 数据清洗: 1.na.omit()   ...

  4. Kail命令

    启动/关闭无线网卡 ifconfig wlan0 up  /  ifconfig wlan0 down 更改Mac地址: macchanger -A wlan0 启动监听 airmon-ng star ...

  5. 实现TTCP (检测TCP吞吐量)

    实现TTCP (检测TCP吞吐量) 应用层协议 为了解决TCP粘包问题以及客户端阻塞问题 设计的应用层协议如下: //告知要发送的数据包个数和长度 struct SessionMessage { in ...

  6. 2018 ICPC 沈阳网络预赛 Fantastic Graph (优先队列)

    [传送门]https://nanti.jisuanke.com/t/31447 [题目大意]:有一个二分图,问能不能找到它的一个子图,使得这个子图中所有点的度数在区间[L,R]之内. [题解]首先我们 ...

  7. 安装配置JDK+Eclipse+Maven、Eclipse里新建Maven Project以及HDFS命令和Java API-课堂内容

    步骤:1.安装JDK→2.安装Eclipse→3.安装Maven→4. Eclipse里配置Maven (下载Windows版本,在Windows里安装使用.) 1.安装配置JDK ①官网下载Java ...

  8. activiti实现的请假流程

    直接上图,还是有点复杂的

  9. RMA Sales Order – Stuck with “Awaiting Return Disposition”

    RMA Sales Order – Stuck with “Awaiting Return Disposition” Action : (Prod) ð  Login to Unix ð  su – ...

  10. 数据库的DDL、DML和DCL的区别与理解

    DML(data manipulation language): 它们是SELECT.UPDATE.INSERT.DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的语言 DDL ...