700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > RedisTemplate 常用方法 序列化方式 基于 Redis 实现分布式锁

RedisTemplate 常用方法 序列化方式 基于 Redis 实现分布式锁

时间:2023-07-03 21:16:15

相关推荐

RedisTemplate 常用方法 序列化方式 基于 Redis 实现分布式锁

目录

客户端 Jedis、Redisson、Lettuce 对比

RedisTemplate 常用方法

RedisTemplate 序列化方式

分布式锁需求分析 与 主流实现方式

基于 Redis 实现分布式锁

其它常用操作与方法

客户端 Jedis、Redisson、Lettuce 对比

1、三个都提供了基于 Redis 操作的 Java API,只是封装程度,具体实现稍有不同。

RedisTemplate 常用方法

org.springframework.data.redis.core.RedisTemplate 常用方法(本文环境 Spring Boot 2.1.3):

import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.data.redis.core.types.RedisClientInfo;import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;import java.util.Date;import java.util.List;import java.util.Set;import java.util.concurrent.TimeUnit;@RunWith(SpringRunner.class)@SpringBootTestpublic class RedisStuWebApplicationTests {//注入 RedisTemplate 或者 StringRedisTemplate 其中一个即可,前者是后者的父类。它们默认已经全部在容器种了.//org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 中已经自动将 RedisTemplate 添加到了容器中,直接获取使用即可.@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Testpublic void test1() throws InterruptedException {ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();opsForValue.set("wmx_name", "Zhang San");//设置字符串值System.out.println(opsForValue.get("wmx_name"));//Zhang SanstringRedisTemplate.expire("wmx_name", 60, TimeUnit.SECONDS);//设置 key 失效时间// for (int i = 0; i < 15; i++) {// System.out.println(stringRedisTemplate.getExpire("wmx_name"));// System.out.println(stringRedisTemplate.getExpire("wmx_name", TimeUnit.SECONDS));// Thread.sleep(1000);// }//判断是否含有指定的 keySystem.out.println(stringRedisTemplate.hasKey("wmx_name") + ", " + stringRedisTemplate.hasKey("wmx_name_2"));//true, falseopsForValue.set("age", "33");opsForValue.set("address", "长沙");stringRedisTemplate.delete("age");//删除 keyDate stopDate = new Date();stopDate.setTime(System.currentTimeMillis() + (60 * 1000));stringRedisTemplate.expireAt("address", stopDate);//设置 key 1 分钟后失效List<RedisClientInfo> clientList = stringRedisTemplate.getClientList();System.out.println(clientList);System.out.println(redisTemplate.getDefaultSerializer());//获取默认序列化方式Set<String> keys = stringRedisTemplate.keys("*");//获取当前库下所有的 keySystem.out.println("keys=" + keys);Boolean move = stringRedisTemplate.move("address", 2);//将 address 移动 2 号数据库System.out.println("move=" + move);opsForValue.set("info", "描述");System.out.println(opsForValue.get("info"));stringRedisTemplate.rename("info", "summary");//对 key 进行重命名}}

演示源码:src/test/java/com/wmx/wmxredis/WmxRedisApplicationTests.java · 汪少棠/wmx-redis -

RedisTemplate 序列化方式

1、StringRedisTemplate 继承 RedisTemplate,主要区别就是前者默认使用 StringRedisSerializer 序列化 String,后者默认使用 JdkSerializationRedisSerializer 序列化对象。

2、RedisTemplate<K, V> 可以用来存储对象,如 Map、List 、Set、POJO 等,但对象需要实现 Serializable 序列化接口。此种方式序列化时,以二进制数组方式存储,内容没有可读性。

Map<String, Object> dataMap = new HashMap<>();dataMap.put("id", 1001);dataMap.put("name", "张三");HashOperations opsForHash = redisTemplate.opsForHash();opsForHash.putAll("map_1",dataMap);redisTemplate.expire("map_1",60,TimeUnit.SECONDS);Map map_11 = opsForHash.entries("map_1");System.out.println(map_11);//{name=张三, id=1001}

3、StringRedisTemplate 专门用来存储字符串,StringRedisTemplate extends RedisTemplate<String, String>。序列化接口 org.springframework.data.redis.serializer.RedisSerializer 的实现类如下:

4、本文环境 Spring Boot 2.1.3 + Java JDK 1.8,下面以指定 Jackson2JsonRedisSerializer 序列化方式为例:

import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;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.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;@Configurationpublic class RedisConfig {//自定义 RedisTemplate 序列化方式@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();//创建 RedisTemplate,key 和 value 都采用了 Object 类型redisTemplate.setConnectionFactory(redisConnectionFactory);//绑定 RedisConnectionFactory//创建 Jackson2JsonRedisSerializer 序列方式,对象类型使用 Object 类型,Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);//设置一下 jackJson 的 ObjectMapper 对象参数// 设置 RedisTemplate 序列化规则。因为 key 通常是普通的字符串,所以使用 StringRedisSerializer 即可。// 而 value 是对象时,才需要使用序列化与反序列化redisTemplate.setKeySerializer(new StringRedisSerializer());// key 序列化规则redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value 序列化规则redisTemplate.setHashKeySerializer(new StringRedisSerializer());// hash key 序列化规则redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// hash value 序列化规则redisTemplate.afterPropertiesSet();//属性设置后操作return redisTemplate;//返回设置好的 RedisTemplate}}

在线演示源码:src/main/java/com/wmx/wmxredis/config/RedisConfig.java · 汪少棠/wmx-redis -

5、RedisTemplate 使用就很简单了,保存数据与读取数据时,直接操作对象即可,会自动进行序列化与反序列化:

import com.wmx.wmxredis.beans.Person;import org.springframework.data.redis.core.HashOperations;import org.springframework.data.redis.core.ListOperations;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;import java.util.Date;import java.util.List;import java.util.concurrent.TimeUnit;/*** RedisTemplate 操作 redis** @author wangMaoXiong*/@RestControllerpublic class RedisController {/*** 从容器中获取 RedisTemplate 实例*/@Resourceprivate RedisTemplate redisTemplate;/*** 保存数据,设置缓存:http://localhost:8080/redis/save?id=1000&name=张三** @param person* @return*/@GetMapping("redis/save")public String redisCache(Person person) {ValueOperations opsForValue = redisTemplate.opsForValue();ListOperations opsForList = redisTemplate.opsForList();HashOperations opsForHash = redisTemplate.opsForHash();person.setBirthday(new Date());//设置缓存。演示三种数据类型:字符串、列表、hashopsForValue.set(RedisController.class.getName() + "_string" + person.getId(), person);opsForList.rightPushAll(RedisController.class.getName() + "_list" + person.getId(), person, person);opsForHash.put(RedisController.class.getName() + "_map", "person" + person.getId(), person);//设置 key 失效时间redisTemplate.expire(RedisController.class.getName() + "_string" + person.getId(), 60, TimeUnit.SECONDS);redisTemplate.expire(RedisController.class.getName() + "_list" + person.getId(), 60, TimeUnit.SECONDS);redisTemplate.expire(RedisController.class.getName() + "_map", 60, TimeUnit.SECONDS);return "缓存成功.";}/*** 查询缓存:http://localhost:8080/redis/get?personId=1000** @param personId* @return*/@GetMapping("redis/get")public List<Person> getRedisCache(@RequestParam Integer personId) {//1、演示三种数据类型:字符串、列表、hashValueOperations opsForValue = redisTemplate.opsForValue();ListOperations opsForList = redisTemplate.opsForList();HashOperations opsForHash = redisTemplate.opsForHash();//2、读取缓存,如果 key 不存在,则返回为 null.Person person = (Person) opsForValue.get(RedisController.class.getName() + "_string" + personId);List<Person> personList = opsForList.range(RedisController.class.getName() + "_list" + personId, 0, -1);Person person1 = (Person) opsForHash.get(RedisController.class.getName() + "_map", "person" + personId);System.out.println("person=" + person);System.out.println("personList=" + personList);System.out.println("person1=" + person1);return personList;}}

分布式锁需求分析 与 主流实现方式

分布式锁需求分析 与 主流实现方式

基于 Redis 实现分布式锁

1、分布式索最常见的一种方案就是使用 Redis 做分布式锁,使用 Redis 做分布式锁的思路是:在 redis 中设置一个值表示加了锁,然后释放锁的时候就把这个 key 删除。

// 获取锁// NX是指如果key不存在就成功,key存在返回false,PX可以指定过期时间SET anyLock unique_value NX PX 30000// 释放锁:通过执行一段lua脚本// 释放锁涉及到两条指令,这两条指令不是原子性的// 需要用到redis的lua脚本支持特性,redis执行lua脚本是原子性的if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])elsereturn 0end

2、一定要用 "SET key value NX PX milliseconds" 命令:否则先设置了值,再设置过期时间,这个不是原子性操作,有可能在设置过期时间之

前宕机,会造成死锁(key永久存在)

3、value 要具有唯一性:在解锁的时候,需要验证 value 是和加锁的一致才删除 key。

这是避免了一种情况:假设A获取了锁,过期时间30s,此时35s之后,锁已经自动释放了,A

去释放锁,但是此时可能B获取了锁。A客户端就不能删除B的锁了。

4、下面通过 RedisTemplate 进行实现:

/*** http://localhost:8080/redis/execute?key=wwww&value=1rui* <p>* Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit)* Boolean setIfAbsent(K key, V value, Duration timeout)* * 1、key 不存在时进行设值,返回 true; 否则 key 存在时,不进行设值,返回 false.* * 2、此方法相当于先设置 key,然后设置 key 的过期时间,它的操作是原子性的,是事务安全的。* * 3、相当于:SET anyLock unique_value NX PX 30000,NX是指如果key不存在就成功,key存在返回false,PX可以指定过期时间* T execute(RedisScript<T> script, List<K> keys, Object... args):执行给定的脚本。* * 1、多个操作使用 lau 脚本统一执行是事务安全的,具有原子性* * 2、脚本中 KEYS[x] 是对 keys 进去取值,ARGV[x] 是对 args 进行取值,索引从1开始.* * 3、返回脚本执行的结果,类型与 RedisScript 的类型一致。** @param key* @param value* @return* @throws InterruptedException*/@GetMapping("redis/execute")public Map<String, Object> execute(@RequestParam String key, @RequestParam String value) throws InterruptedException {Map<String, Object> returnMap = new HashMap<>();Boolean ifAbsent = false;try {ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofSeconds(60));if (!ifAbsent) {returnMap.put("code", 500);returnMap.put("msg", "程序正在处理中,请稍后再试!");return returnMap;}TimeUnit.SECONDS.sleep(10);//休眠 10 秒,模拟执行业务代码System.out.println("执行业务代码.");} catch (Exception e) {e.printStackTrace();} finally {if (ifAbsent){//接口执行完毕后删除 key,key 不存在时 execute 方法返回 0//此种脚本删除的方式在 redis 集群部署时会报错,实际上直接使用 redisTemplate.delete(cacheKey) 也是可以的String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";RedisScript<Long> redisScript = RedisScript.of(script, Long.class);// 返回删除key的个数,未删除成功时,返回 0Object execute = redisTemplate.execute(redisScript, Arrays.asList(key), value);returnMap.put("data", execute);}}returnMap.put("code", 200);returnMap.put("msg", "seccess");return returnMap;}

在线演示源码:src/main/java/com/wmx/wmxredis/controller/RedisController.java · 汪少棠/wmx-redis -

并发压测:src/main/java/com/wmx/hb/controller/FlowConfigController.java · 汪少棠/hb -

其它常用操作与方法

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。