目录
三、SpringDataRedis(SpringBoot)的使用
一、介绍
在Redis官网,提供了多种编程语言的客户端,如Java、C++等,官网地址:Clients | Redis
而对于Java的客户端有很多,但是用的最多的就是下图的前两个:
二、Jedis的使用
创建一个普通javamaven项目:
在pom.xml文件中引入依赖:
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
建立连接:
创建测试类:
package com.jedis.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import redis.clients.jedis.Jedis;
@RunWith(JUnit4.class)
public class JedisTest {
private Jedis jedis;
@Test
public void run(){
jedis=new Jedis("127.0.0.1",6379);
jedis.select(0);
String jedisTest = jedis.set("jedisTest", "123");
System.out.println(jedisTest);
String s = jedis.get("jedisTest");
System.out.println(s);
if(jedis!=null){
jedis.close();
}
}
}
运行结果:
jedis提供的方法和指令名称是相同的,因此比较好上手。
通过上述操作,我们可以看出jedis的使用,还是非常方便的,只需要三步就可以实现:
1、导入依赖。
2、建立连接。
3、执行方法(和命令同名)。
jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此,我们使用jedis连接池来代替上述这种直连的方式。
package com.jedis.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@RunWith(JUnit4.class)
public class JedisTest {
private JedisPool jedisPool;
@Test
public void run2(){
JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
//最大连接
jedisPoolConfig.setMaxTotal(8);
//最大空闲连接
jedisPoolConfig.setMaxIdle(8);
//最小空闲连接数量
jedisPoolConfig.setMinIdle(0);
//设置最长等待时间
jedisPoolConfig.setMaxWaitMillis(200);
jedisPool=new JedisPool(jedisPoolConfig,"127.0.0.1",6379,1000);
//通过连接池获取连接
Jedis resource = jedisPool.getResource();
String myTest = resource.set("myTest", "1234");
System.out.println(myTest);
String s = resource.get("myTest");
System.out.println(s);
}
}
输出结果:
三、SpringDataRedis(SpringBoot)的使用
SpringData是Spring中数据操作的模块,官网地址:Spring Data Redis
包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis
提供了对不同Redis客户端的整合(Lettuce和Jedis)
提供了RedisTemplate统一API来操作Redis
支持Redis的发布订阅模型
支持Redis哨兵和Redis集群
支持基于Lettuce的响应式编程
支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化支持基于Redis的JDKCollection实现
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。RedisTemplate,是根据命令种类来分类的方法。并且将不同数据类型的操作API封装到了不同的类型中:
创建:
SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单。
首先,创建SpringBoot项目:
导入相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
在resource目录下创建application.yaml文件:
并填充内容:
spring:
redis:
host: 127.0.0.1
port: 6379
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 100
测试代码:
package com.example.springdataredistest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class SpringDataRedisTestApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("redisTemplate","success");
String o = (String) redisTemplate.opsForValue().get("redisTemplate");
System.out.println(o);
}
}
运行结果:
但是我们发现,Redis数据库中存入的并非我们所想的内容:
这是因为采用了SpringDataRedis默认的序列化方式。
RedisTemplate可以接收任意0bject作为值写入Redis,只不过写入前会把0bject序列化为字节形式,默认是采用IDK序列化,得到的结果是上面这样的。
这种方式有很大的缺点:1、可读性差。2、内存占用大。
因此,我们可以自定义序列化方式,来得到我们想要的结果。
我们创建一个配置类:
package com.example.springdataredistest.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
public class SpringDataRedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
//创建Template
RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
//设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer=new GenericJackson2JsonRedisSerializer();
//key和hashkey采用spring序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
//value和hashvalue采用json序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashKeySerializer(jsonRedisSerializer);
return redisTemplate;
}
}
导入依赖:
<!-- Jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
测试方法:
package com.example.springdataredistest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
public class Test2 {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Test
public void run(){
redisTemplate.opsForValue().set("redisTemplateNew","successNew");
String o = (String) redisTemplate.opsForValue().get("redisTemplateNew");
System.out.println(o);
}
}
执行结果:
数据库展示:
四、Redis使用小案例
我们现在可以用Redis来模拟一下“抢红包”的场景。
对于抢红包来说,我们需要从发红包的人和抢红包的人两个角度分别考虑。
对于发红包来说,我们发一个红包链接,可以指定红包总金额以及红包数量,但是每个红包金额不能为0,最低为0.01。同时,如果一个红包链接上面的红包未领完,我们不能再次用这个链接来发放红包。
对于抢红包来说,我们根据一个红包链接去抢红包,获得一个随机金额的红包,如果红包数量为0,就不能再抢了。
通常情况下,发红包的频率要原低于抢红包的频率,而且,为了同时多个用户抢红包时,避免较慢的查询,因此,我们将数据存入内存,可以采用Redis来存储,同时在红包发放后,便指定好每一个红包的随机金额,抢红包的直接取数据,而不再需要通过计算获得,这样更加快捷。
基于上述SpringDataRedis(SpringBoot)来实现,仍采用上述配置。
可以用web场景来更好的交互。
导入web场景依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
在启动类的同包或者自包下,创建controller:
package com.example.springdataredistest.controller;
import io.netty.util.internal.StringUtil;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.DoubleStream;
@Controller
public class MyController {
@Resource
private RedisTemplate redisTemplate;
/**
* 发放红包请求
* @param suid 发放链接的参数
* @param money 发放的金额
* @param count 发放的红包总额
* @return
*/
@RequestMapping("/sendRedPackage")
@ResponseBody
public String sendRedPackage(String suid,Double money,Integer count){
Object leftPop = redisTemplate.opsForList().leftPop(suid);
if(leftPop!=null){//判断此红包链接是否发放完
return "该红包发放连接,红包未领取完,不可以再次发送,请换一个连接发放!";
}
Random random=new Random();
List<Double> result=new LinkedList<>();
Double sum=0.0;
for(int i=1;i<count;i++){//循环红包数量-1
Double b=getRandomFloat(0.01,money);
System.out.println(b);
money-=b;
result.add(b);
}
//最后一个红包的金额,由剩余金额决定
money = Math.round(money * 100) / 100.0; // 保留两位小数,乘以100后进行四舍五入取整。
//每个红包的钱不能为0
if(money==0.0){
money=0.01;
for(int i=0;i<result.size();i++){//找一个红包中的钱大于0.01,从中扣除,补上金额为0的红包
if(result.get(i)>0.01){
result.set(i,result.get(i)-0.01);
break;
}
}
}
result.add(money);//加上最后一个红包的金额
System.out.println(result);
redisTemplate.opsForList().leftPushAll(suid,result);//存入redis中
return "发放成功!";
}
public Double getRandomFloat(Double min, Double max) {//得到浮点型的随机数
Random random = new Random();
Double result=min + random.nextDouble() * (max - min);
result = Math.round(result * 100) / 100.0; // 保留两位小数
return result;
}
/**
* 抢红包的链接方法
* @param suid 发放红包的链接参数
* @return
*/
@RequestMapping("/getRedPackage")
@ResponseBody
public String getRedPackage(String suid){
Double d = (Double) redisTemplate.opsForList().leftPop(suid);
if(d==null){
return "红包连接无效,或者该用户的红包已经被抢完了!";
}else{
return "您抢到了"+d+"元";
}
}
}
测试: