spring Data Redis基本操作
spring-redis.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:redis/redis.properties" /> <!--注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive 而是maxTotal,而且没有maxWait属性 --> <!--redis连接池配置--> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!--最大空闲数--> <property name="maxIdle" value="${redis.maxIdle}"/> <!--连接池的最大数据库连接数--> <property name="maxTotal" value="${redis.maxTotal}"/> <!--建立连接的等待时间--> <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/> <!--去除连接的最小空闲时间,默认1800000毫秒(30分钟)--> <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/> <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 --> <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/> <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 --> <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/> <property name="testOnBorrow" value="true"></property> <property name="testOnReturn" value="true"></property> <property name="testWhileIdle" value="true"></property> </bean> <!--redis连接工厂--> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy"> <property name="poolConfig" ref="jedisPoolConfig"></property> <!--IP地址--> <property name="hostName" value="${redis.host.ip}"></property> <!--端口号--> <property name="port" value="${redis.port}"/> <!--如果Redis设置有密码 --> <property name="password" value="${redis.password}"/> <!--客户端超时时间单位是毫秒 --> <property name="timeout" value="${redis.timeout}"></property> <property name="usePool" value="true"/> </bean> <!-- 键值序列化器设置为String 类型 --> <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/> <bean id="jackson2JsonRedisSerializer" class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" /> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory" p:keySerializer-ref="stringRedisSerializer" p:valueSerializer-ref="stringRedisSerializer" p:hashKeySerializer-ref="stringRedisSerializer" p:hashValueSerializer-ref="jackson2JsonRedisSerializer"> </bean> </beans>
pom.xml依赖文件
<?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> <groupId>com.smart</groupId> <artifactId>RedisDemo</artifactId> <version>1.0-SNAPSHOT</version> <name>RedisDemo</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <spring.version>4.3.1.RELEASE</spring.version> <redis.version>2.9.0</redis.version> <spring.data.redis.version>2.0.10.RELEASE</spring.data.redis.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>${spring.data.redis.version}</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>${redis.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.10</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.1.0</version> </dependency> </dependencies> </project>
redisDemo
package com.smart; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.redis.core.RedisTemplate; public class RedisStringDemo { public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("classpath:redis/spring-redis.xml"); // redisTemplate.opsForValue()所返回的对象可以操作简单的键值对,可以是字符串,也可以是对象,具体依据所配置的序列化方案 // 在spring-redis-string.xml中key和value是指定的 stringRedisSerializer RedisTemplate<String,String> redisTemplate= (RedisTemplate<String, String>) context.getBean("redisTemplate"); //> set key1 value1 redisTemplate.opsForValue().set("key1","value1"); redisTemplate.opsForValue().set("key2","value2"); String value1 = redisTemplate.opsForValue().get("key1"); System.out.println(value1); Boolean success = redisTemplate.delete("key1"); System.out.println("删除key1是否成功"+success); Long size = redisTemplate.opsForValue().size("key2"); System.out.println("key2的长度"+size); //设置新值并返回旧值 String oldValue = redisTemplate.opsForValue().getAndSet("key2", "new_value2"); System.out.println("key2的旧值"+oldValue); String newValue = redisTemplate.opsForValue().get("key2"); System.out.println("key2的新值"+newValue); //获取子字符串 String subStr = redisTemplate.opsForValue().get("key2", 0, 3); System.out.println("subString :"+subStr); //将新的字符串value加入到原来key指向的字符串末尾 Integer value = redisTemplate.opsForValue().append("key2", "_app"); System.out.println("valie:"+value); String newValue2=redisTemplate.opsForValue().get("key2"); System.out.println("key2: "+newValue2); Object result = redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { //Delete all keys of the currently selected database. connection.flushDb(); return "flush db"; } }); ValueOperations<String, String> valueOperations = redisTemplate.opsForValue(); valueOperations.set("name","Jim"); valueOperations.setIfAbsent("name","Jack"); Map<String, String> map = new HashMap<>(); map.put("alias","Jim"); map.put("age","18"); valueOperations.multiSet(map); valueOperations.append("alias","_data"); valueOperations.increment("age",2); } }
实际工作中并不是那么用的,因为每一 个操作会尝试从连接池里获取 一 个新的 Redis 连接,多个命令应该使用SessionCallback 接口进行操作 。使用SessionCallBack这个接口,通过这个接口就可以把属于多个同一套命令放在同一个Redis连接中去执行
常见场景
缓存
Redis作为缓存层, 绝大部分请求的数据都是从Redis中获取。由于Redis具有支撑高并发的特性, 所以缓存通常能起到加速读写和降低后端压力的作用。

springboot+ redis实现token机制
user域
package com.smart.domain; import lombok.Data; @Data public class User { private Integer id; private String username; private String password; public User(Integer id, String username, String password) { this.id = id; this.username = username; this.password = password; } public User() { } }
Dto域
package com.smart.domain; public class Dto { private String token; private Long tokenCreatedDate; private Long tokenExpiryDate; private String isLogin; public String getToken() { return token; } public void setToken(String token) { this.token = token; } public Long getTokenCreatedDate() { return tokenCreatedDate; } public void setTokenCreatedDate(Long tokenCreatedDate) { this.tokenCreatedDate = tokenCreatedDate; } public Long getTokenExpiryDate() { return tokenExpiryDate; } public void setTokenExpiryDate(Long tokenExpiryDate) { this.tokenExpiryDate = tokenExpiryDate; } public String getIsLogin() { return isLogin; } public void setIsLogin(String isLogin) { this.isLogin = isLogin; } }
RedisConfig
package com.smart.config; 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; @Configuration public class RedisConfig { @Bean public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String,String> redisTemplate=new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; } }
RedisUtil
package com.smart.util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component; @Component public class RedisUtil { @Autowired private RedisTemplate<String,String> redisTemplate; public void set(String key,String value){ ValueOperations<String, String> valueOperations = redisTemplate.opsForValue(); valueOperations.set(key,value); } public void setex(String key,String value,int seconds){ ValueOperations<String, String> valueOperations = redisTemplate.opsForValue(); valueOperations.set(key,value,seconds); } }
application.yml
##指定使用redis数据库索引(默认为0) spring: redis: database: 0 host: 127.0.0.1 port: 6379 password:
UserService
package com.smart.service; import com.smart.domain.User; public interface UserService { User login(String username,String password); }
package com.smart.service.impl; import com.smart.domain.User; import com.smart.service.UserService; import org.springframework.stereotype.Service; @Service("userService") public class UserServiceImpl implements UserService { @Override public User login(String username, String password) { return new User(1,username,password); } }
TokenService
package com.smart.service; import com.smart.domain.User; public interface TokenService { String generateToken(String userAgentStr,String username); void save(String token,User user); }
package com.smart.service.impl; import com.alibaba.fastjson.JSONObject; import com.smart.domain.User; import com.smart.service.TokenService; import com.smart.util.RedisUtil; import nl.bitwalker.useragentutils.UserAgent; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; @Service("tokenService") public class TokenServiceImpl implements TokenService { @Autowired private RedisUtil redisUtil; @Override public String generateToken(String userAgentStr, String username) { //生成token(格式为token:设备-加密的用户名-时间-六位随机数) StringBuilder token = new StringBuilder("token:"); UserAgent userAgent = UserAgent.parseUserAgentString(userAgentStr); if(userAgent.getOperatingSystem().isMobileDevice()){ token.append("MOBILE-"); }else{ token.append("PC-"); } token.append(DigestUtils.md5Hex(username)+"-"); token.append(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())+"-"); token.append(String.format("%06d",new Random().nextInt(100000)+1)); return token.toString(); } @Override public void save(String token, User user) { if(token.startsWith("token:PC")){ redisUtil.setex(token, JSONObject.toJSONString(user),2*60*60); }else{ redisUtil.set(token,JSONObject.toJSONString(user)); } } }
UserController
package com.smart.controller; import com.alibaba.fastjson.JSONObject; import com.smart.domain.Dto; import com.smart.domain.User; import com.smart.service.TokenService; import com.smart.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @Autowired private TokenService tokenService; @GetMapping("/login") public String login(@RequestParam("username") String username,@RequestParam("password") String password, HttpServletRequest request){ Dto dto = new Dto(); User user = userService.login(username, password); if(user !=null){ String userAgent = request.getHeader("user-agent"); String token = tokenService.generateToken(userAgent, username); tokenService.save(token,user); dto.setIsLogin("true"); dto.setToken(token); dto.setTokenCreatedDate(System.currentTimeMillis()); dto.setTokenExpiryDate(System.currentTimeMillis()+2*60*60); }else{ dto.setIsLogin("false"); } return JSONObject.toJSONString(dto); } }
pom
<?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.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.smart</groupId> <artifactId>redistoken</artifactId> <version>0.0.1-SNAPSHOT</version> <name>redistoken</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-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.31</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>nl.bitwalker</groupId> <artifactId>UserAgentUtils</artifactId> <version>1.2.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
计数器
使用Redis作为计数的基础工具, 可以实现快速计数、查询缓存的功能, 同时数据可以异步落地到其他数据源。
限流
举个例子,对某个接口在1分钟内限制调用10次
session共享
使用Redis将用户的Session进行集中管理,在这种模式下只要保证Redis是高可用和扩展性的, 每次用户更新或者查询登录信息都直接从Redis中集中获取

Lettuce
Lettuce 和 Jedis 的都是连接Redis Server的客户端程序。Jedis在实现上是直连redis server,多线程环境下非线程安全,除非使用连接池,为每个Jedis实例增加物理连接。Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
在 pom.xml 中spring-boot-starter-data-redis的依赖,Spring Boot2.x 后底层不在是Jedis
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
由于Spring Boot2.x 的改动,连接池相关配置需要通过spring.redis.lettuce.pool或者 spring.redis.jedis.pool 进行配置
spring:
#指定配置环境
profiles:
active: test
devtools:
restart:
enabled: true
#添加需要restart目录文件
additional-paths: src/main/java
#排除不需要restart的目录文件
exclude: static/**
redis:
host: localhost
password:
database: 0
timeout: 10000ms
# Redis默认有16个分片,配置具体使用的分片,默认值为0
database: 0
# 连接池最大的连接数(使用负值表示没有限制)默认 8
lettuce:
pool:
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制)默认 -1
max-wait: -1ms
# 连接池中最大空闲连接 默认 8
max-idle: 8
# 连接池中最小空闲连接 默认 0
min-idle: 0
自定义Template
默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,开发中是不友好的,所以自定义模板是很有必要的,当自定义模板又想使用String存储这时候就可以使用StringRedisTemplate的方式,它们并不冲突
package com.smart.boot_mybatis.configuration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.io.Serializable; @Configuration @AutoConfigureAfter(RedisAutoConfiguration.class) public class RedisCacheAutoConfiguration { @Bean public RedisTemplate<String,Serializable> redisTemplate(LettuceConnectionFactory redisConnectionFactory){ RedisTemplate<String, Serializable> template = new RedisTemplate<>(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } }
测试:
package com.smart.boot_mybatis; import com.smart.boot_mybatis.domain.User; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.io.Serializable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.IntStream; @RunWith(SpringRunner.class) @SpringBootTest public class redisTest { private static final Logger log = LoggerFactory.getLogger(redisTest.class); @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private RedisTemplate<String, Serializable> redisCacheTemplate; @Test public void get(){ ExecutorService executorService = Executors.newFixedThreadPool(100); IntStream.range(0,1000).forEach(i->executorService.execute(()-> stringRedisTemplate.opsForValue().increment("aa",1))); stringRedisTemplate.opsForValue().set("k1","v1"); String v1=stringRedisTemplate.opsForValue().get("k1"); log.info("[字符缓存结果] - [{}]", v1); String key="user:1"; redisCacheTemplate.opsForValue().set(key,new User(1L,"u1","jing")); User user = (User) redisCacheTemplate.opsForValue().get(key); log.info("[对象缓存结果] - [{}]", user); } }
参考:
https://blog.csdn.net/yangshangwei/article/details/82251931#_238
浙公网安备 33010602011771号