learn.lianglianglee.com/专栏/案例上手 Spring Boot WebFlux(完)/07 WebFlux 整合 Redis.md.html
2022-08-14 03:40:33 +08:00

371 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>07 WebFlux 整合 Redis.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/01 导读:课程概要.md.html">01 导读:课程概要</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/02 WebFlux 快速入门实践.md.html">02 WebFlux 快速入门实践</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/03 WebFlux Web CRUD 实践.md.html">03 WebFlux Web CRUD 实践</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/04 WebFlux 整合 MongoDB.md.html">04 WebFlux 整合 MongoDB</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/05 WebFlux 整合 Thymeleaf.md.html">05 WebFlux 整合 Thymeleaf</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/06 WebFlux 中 Thymeleaf 和 MongoDB 实践.md.html">06 WebFlux 中 Thymeleaf 和 MongoDB 实践</a>
</li>
<li>
<a class="current-tab" href="/专栏/案例上手 Spring Boot WebFlux/07 WebFlux 整合 Redis.md.html">07 WebFlux 整合 Redis</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/08 WebFlux 中 Redis 实现缓存.md.html">08 WebFlux 中 Redis 实现缓存</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/09 WebFlux 中 WebSocket 实现通信.md.html">09 WebFlux 中 WebSocket 实现通信</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/10 WebFlux 集成测试及部署.md.html">10 WebFlux 集成测试及部署</a>
</li>
<li>
<a href="/专栏/案例上手 Spring Boot WebFlux/11 WebFlux 实战图书管理系统.md.html">11 WebFlux 实战图书管理系统</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>07 WebFlux 整合 Redis</h1>
<h3>前言</h3>
<p>上一篇内容讲了如何整合 MongoDB这里继续讲如何操作 Redis 这个数据源,那什么是 Reids</p>
<p>Redis 是一个高性能的 key-value 数据库,<a href="https://github.com/antirez/redis">GitHub 地址详见这里</a>。GitHub 是这么描述的:</p>
<p>Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, HyperLogLogs, Bitmaps.</p>
<p>Redis 是内存式数据库存储在磁盘支持的数据类型很多Strings、Lists、Sets、Sorted Sets、Hashes、HyperLogLogs、Bitmaps 等。</p>
<h4>安装简易教程(适用 Mac/Linux</h4>
<p>下载并解压:</p>
<pre><code>下载安装包 redis-x.x.x.tar.gz
## 解压
tar zxvf redis-2.8.17.tar.gz
</code></pre>
<p>编译安装:</p>
<pre><code>cd redis-x.x.x/
make ## 编译
</code></pre>
<p>启动 Redis</p>
<pre><code>cd src/
redis-server
</code></pre>
<p>如果需要运行在守护进程,设置 daemonize 从 no 修改成 yes并指定运行redis-server redis.conf。</p>
<p><img src="assets/f9abbce7c1585de46bf88b7a6af427691524817.png" alt="file" /></p>
<h3>结构</h3>
<p>类似上面讲的工程搭建,新建一个工程编写此案例,工程如图:</p>
<p><img src="assets/42ed19c43dfc8031b5ab260e6b3446111524816.png" alt="file" /></p>
<p>目录核心如下:</p>
<ul>
<li>pom.xml maven 配置</li>
<li>application.properties 配置文件</li>
<li>domain 实体类</li>
<li>controller 控制层,本文要点</li>
</ul>
<h3>新增 POM 依赖与配置</h3>
<p>在 pom.xml 配置新的依赖:</p>
<pre><code> &lt;!-- Spring Boot 响应式 Redis 依赖 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-data-redis-reactive&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre>
<p>类似 MongoDB 配置,在 application.properties 配置连接 Redis</p>
<pre><code>## Redis 配置
## Redis服务器地址
spring.redis.host=127.0.0.1
## Redis服务器连接端口
spring.redis.port=6379
## Redis服务器连接密码默认为空
spring.redis.password=
# 连接超时时间(毫秒)
spring.redis.timeout=5000
</code></pre>
<p>默认 密码为空,这里注意的是连接超时时间不能太少或者为 0不然会引起异常 RedisCommandTimeoutException: Command timed out。</p>
<h3>对象</h3>
<p>修改 org.spring.springboot.domain 包里面的城市实体对象类城市City对象 City代码如下</p>
<pre><code>import org.springframework.data.annotation.Id;
import java.io.Serializable;
/**
* 城市实体类
*
*/
public class City implements Serializable {
private static final long serialVersionUID = -2081742442561524068L;
/**
* 城市编号
*/
@Id
private Long id;
/**
* 省份编号
*/
private Long provinceId;
/**
* 城市名称
*/
private String cityName;
/**
* 描述
*/
private String description;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getProvinceId() {
return provinceId;
}
public void setProvinceId(Long provinceId) {
this.provinceId = provinceId;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
</code></pre>
<p>值得注意点:</p>
<ul>
<li>@Id 注解标记对应库表的主键或者唯一标识符。因为这个是我们的 DO数据访问对象一一映射到数据存储。</li>
<li>City 必须实现序列化,因为需要将对象序列化后存储到 Redis。如果没实现 Serializable会引出异常java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type。</li>
<li>如果不是用默认的序列化,需要自定义序列化实现,只要实现 RedisSerializer 接口去实现即可,然后在使用 RedisTemplate.setValueSerializer 方法去设置你实现的序列化实现,支持 JSON、XML 等。</li>
</ul>
<h3>控制层 CityWebFluxController</h3>
<p>代码如下:</p>
<pre><code>import org.spring.springboot.domain.City;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping(value = &quot;/city&quot;)
public class CityWebFluxController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping(value = &quot;/{id}&quot;)
public Mono&lt;City&gt; findCityById(@PathVariable(&quot;id&quot;) Long id) {
String key = &quot;city_&quot; + id;
ValueOperations&lt;String, City&gt; operations = redisTemplate.opsForValue();
boolean hasKey = redisTemplate.hasKey(key);
City city = operations.get(key);
if (!hasKey) {
return Mono.create(monoSink -&gt; monoSink.success(null));
}
return Mono.create(monoSink -&gt; monoSink.success(city));
}
@PostMapping()
public Mono&lt;City&gt; saveCity(@RequestBody City city) {
String key = &quot;city_&quot; + city.getId();
ValueOperations&lt;String, City&gt; operations = redisTemplate.opsForValue();
operations.set(key, city, 60, TimeUnit.SECONDS);
return Mono.create(monoSink -&gt; monoSink.success(city));
}
@DeleteMapping(value = &quot;/{id}&quot;)
public Mono&lt;Long&gt; deleteCity(@PathVariable(&quot;id&quot;) Long id) {
String key = &quot;city_&quot; + id;
boolean hasKey = redisTemplate.hasKey(key);
if (hasKey) {
redisTemplate.delete(key);
}
return Mono.create(monoSink -&gt; monoSink.success(id));
}
}
</code></pre>
<p>代码详解:</p>
<ul>
<li>使用 @Autowired 注入 RedisTemplate 对象,这个对象和 Spring 的 JdbcTemplate 功能十分相似RedisTemplate 封装了 RedisConnection具有连接管理、序列化和各个操作等还有针对 String 的支持对象 StringRedisTemplate。</li>
<li>删除 Redis 某对象,直接通过 key 值调用 delete(key)。</li>
<li>Redis 操作视图接口类用的是 ValueOperations对应的是 Redis String/Value 操作get 是获取数据set 是插入数据,可以设置失效时间,这里设置的失效时间是 60 s。</li>
<li>还有其他的操作视图ListOperations、SetOperations、ZSetOperations 和 HashOperations。</li>
</ul>
<h3>运行工程</h3>
<p>一个操作 Redis 工程就开发完毕了,下面运行工程验证一下,使用 IDEA 右侧工具栏,单击 Maven Project Tab单击使用下 Maven 插件的 install 命令。或者使用命令行的形式,在工程根目录下,执行 Maven 清理和安装工程的指令:</p>
<pre><code>cd springboot-webflux-6-redis
mvn clean install
</code></pre>
<p>在控制台中看到成功的输出:</p>
<pre><code>... 省略
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:30 min
[INFO] Finished at: 2018-10-15T10:00:54+08:00
[INFO] Final Memory: 31M/174M
[INFO] ------------------------------------------------------------------------
</code></pre>
<p>在 IDEA 中执行 Application 类启动,任意正常模式或者 Debug 模式,可以在控制台看到成功运行的输出:</p>
<pre><code>... 省略
2018-04-10 08:43:39.932 INFO 2052 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext : Started HttpServer on /0:0:0:0:0:0:0:0:8080
2018-04-10 08:43:39.935 INFO 2052 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
2018-04-10 08:43:39.960 INFO 2052 --- [ main] org.spring.springboot.Application : Started Application in 6.547 seconds (JVM running for 9.851)
</code></pre>
<p>打开 POST MAN 工具,开发必备。进行下面操作:</p>
<h4>新增城市信息 POST http://127.0.0.1:8080/city</h4>
<p><img src="assets/f69fa5b09730de686ef4837824da48e71523883-1608971256856.png" alt="file" /></p>
<h4>获取城市信息 GET http://127.0.0.1:8080/city/2</h4>
<p><img src="assets/2e129648b8e574a54ff7940f00f1dc591524819.png" alt="file" /></p>
<p>如果等待 60s 以后,再次则会获取为空,因为保存的时候设置了失效时间是 60 s。</p>
<h3>总结</h3>
<p>这里探讨了 Spring WebFlux 的如何整合 Redis介绍了如何通过 RedisTemplate 去操作 Redis。因为 Redis 在获取资源性能极佳,常用 Redis 作为缓存存储对象,下面我们利用 Reids 实现缓存操作。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/案例上手 Spring Boot WebFlux/06 WebFlux 中 Thymeleaf 和 MongoDB 实践.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/案例上手 Spring Boot WebFlux/08 WebFlux 中 Redis 实现缓存.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997939ff1d3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>