Redis项目应用场景与实例汇总
文章目录
- 一、背景
- 1.1 开发环境
- 1.2 项目配置
- 二、字符串的应用场景:封锁一个IP地址
- 三、Hash的应用场景:存储用户信息
- 四、List的应用场景:队列实现
- 五、Set的应用场景:自动去重
一、背景
- 本篇文章是将以前Redis实战的系列文章进行汇总,针对Redis中常用的一些数据结构,进行实战模拟。
strings | hashes | lists | sets | sorted sets |
---|---|---|---|---|
封锁一个IP地址 | 存储用户信息 | 模拟消息队列 | 自动排重 | 以某一个条件为权重,进行排序 |
1.1 开发环境
- JDK 1.8
- SpringBoot 2.2.5
- JPA
- Spring Security
- Mysql 8.0
- Redis Server 3.2.1
- Redis Desktop Manager
- Swagger2
1.2 项目配置
- SpringBoot集成Redis, 添加依赖
<!--pom.xl--><!--Redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
- 项目配置文件 添加redis连接配置
<!-- application.yml-->
server:port: 8000spring:freemarker:check-template-location: falseprofiles:active: devjackson:time-zone: GMT+8data:redis:repositories:enabled: false#配置 Jpajpa:properties:hibernate:dialect: org.hibernate.dialect.MySQL5InnoDBDialectopen-in-view: trueredis:database: 0host: 127.0.0.1port: 6379password:
- 增加RedisConfig配置类
/*** Redis配置类** @author zhuhuix*/
@Configuration
@EnableCaching
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig extends CachingConfigurerSupport {/***设置 redis 数据默认过期时间*/@Beanpublic RedisCacheConfiguration redisCacheConfiguration(){FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(Constant.CACHE_TIMEOUT_HOUR));return configuration;}@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);template.setValueSerializer(fastJsonRedisSerializer);template.setHashValueSerializer(fastJsonRedisSerializer);ParserConfig.getGlobalInstance().setAutoTypeSupport(true);template.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.afterPropertiesSet();return template;}/*** 参考:https://blog.csdn.net/qq_15071263/article/details/84335632* 自定义缓存key生成策略,默认将使用该策略*/@Bean@Overridepublic KeyGenerator keyGenerator() {return (target, method, params) -> {Map<String,Object> container = new HashMap<>(3);Class<?> targetClassClass = target.getClass();// 类地址container.put("class",targetClassClass.toGenericString());// 方法名称container.put("methodName",method.getName());// 包名称container.put("package",targetClassClass.getPackage());// 参数列表for (int i = 0; i < params.length; i++) {container.put(String.valueOf(i),params[i]);}// 转为JSON字符串String jsonString = JSON.toJSONString(container);// 做SHA256 Hash计算,得到一个SHA256摘要作为Keyreturn DigestUtils.sha256Hex(jsonString);};}
- 增加RedisUtils工具类:实现对各种数据结构的封装
/*** Redis工具类** @author zhuhuix*/
@Component
@AllArgsConstructor
public class RedisUtils {private RedisTemplate<Object, Object> redisTemplate;/*** HashGet根据键值得到对象** @param key 键值 @NotNull* @param item 项目 @NotNull* @return 对象*/public Object hashGet(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 根据键值向hash表中写入对象** @param key 键值 @NotNull* @param item 项目 @NotNull* @param value 对象 @NotNull* @return true 成功 false失败*/public boolean hashSet(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据键值向hash表中写入对象,并设置过期时间** @param key 键值 @NotNull* @param item 项目 @NotNull* @param value 对象 @NotNull* @param time 过期时间(秒) @NotNull* @return true 成功 false失败*/public boolean hashSet(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据键值对某一项目的进行累加计数** @param key 键值* @param l 累加数*/public long increment(String key, long l) {return redisTemplate.opsForValue().increment(key, l);}/*** 根据键值对某一项目的进行累加计数,并设置过期时间** @param key 键值* @param l 累加数* @param time 过期时间(秒)*/public long increment(String key, long l, long time) {long count = redisTemplate.opsForValue().increment(key, l);if (time > 0) {expire(key, time);}return count;}/*** 入队** @param key 队列键值* @param value 元素* @return 添加数量*/public long leftPush(String key, Object value) {return redisTemplate.opsForList().leftPush(key, value);}/*** 向队列头部添加全部集合元素** @param key 队列键值* @param list 集合* @return 返回添加的数量*/public long leftPushAll(String key, List<Object> list) {return redisTemplate.opsForList().leftPushAll(key, list);}/*** 统计队列中所有元素数量** @param key 队列键值* @return 队列中元素数量*/public long size(String key) {return redisTemplate.opsForList().size(key);}/*** 返回队列中从起始位置到结束位置的集合元素** @param key 队列键值* @param start 起始位置* @param end 结束位置* @return 返回集合*/public List<Object> range(String key, long start, long end) {return redisTemplate.opsForList().range(key, start, end);}/*** 出队** @param key 队列键值* @return 元素*/public Object rightPop(String key) {return redisTemplate.opsForList().rightPop(key);}/*** 弹出队列最新元素** @param key 队列键值* @return 元素*/public Object leftPop(String key) {return redisTemplate.opsForList().leftPop(key);}/*** 删除队列所有元素** @param key 队列键值*/public void deleteAll(String key) {redisTemplate.opsForList().trim(key, 0, 0);redisTemplate.opsForList().leftPop(key);}/*** 向集合中增加元素** @param key 集合键值* @param value 元素* @return 添加数量*/public long setAdd(String key, Object value) {return redisTemplate.opsForSet().add(key,value);}/*** 向集合中批量增加元素** @param key 集合键值* @param list 元素列表* @return 添加数量*/public long setAdd(String key, List<Object> list) {return redisTemplate.opsForSet().add(key,list);}/*** 集合删除指定元素** @param key 集合键值* @param value 指定元素* @return 删除数量*/public long setRemove(String key, Object value) {return redisTemplate.opsForSet().remove(key, value);}/*** 集合批量删除指定元素** @param key 集合键值* @param list 指定元素列表* @return 删除数量*/public long setRemove(String key, List<Object> list) {return redisTemplate.opsForSet().remove(key, list);}/*** 取出两信集合的交集** @param key1 集合1键值* @param key2 集合2键值* @return 交集*/public Set<Object> setInter(String key1, String key2) {return redisTemplate.opsForSet().intersect(key1, key2);}/*** 取出多个集合的交集** @param keys 键值列表* @return 交集*/public Set<Object> setInter(List<Object> keys) {return redisTemplate.opsForSet().intersect(keys);}/*** 取出两个集合的差集** @param key1 集合1键值* @param key2 集合2键值* @return 差集*/public Set<Object> setDifference(String key1,String key2){return redisTemplate.opsForSet().difference(key1,key2);}/*** 指定缓存的失效时间** @param key 键值 @NotNull* @param time 时间(秒) @NotNull*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}} catch (Exception e) {e.printStackTrace();return false;}return true;}}
二、字符串的应用场景:封锁一个IP地址
- 创建SpringBoot后台服务程序,实现用户登录及JWT认证;
- 通过Redis缓存限制在1分钟内同一IP请求登录不能超过5次。
- 登录实现类增加Redis计数判断
/*** 授权登录接口实现类** @author zhuhuix*/
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class AuthServiceImpl implements AuthService {@Value("${wxMini.appId}")private String appId;@Value("${wxMini.secret}")private String secret;private final JwtTokenUtils jwtTokenUtils;private final WxMiniApi wxMiniApi;private final UserService userService;private final JwtSecurityProperties properties;private final RedisUtils redisUtils;public AuthServiceImpl(JwtTokenUtils jwtTokenUtils, WxMiniApi wxMiniApi, UserService userService, JwtSecurityProperties properties, RedisUtils redisUtils) {this.jwtTokenUtils = jwtTokenUtils;this.wxMiniApi = wxMiniApi;this.userService = userService;this.properties = properties;this.redisUtils = redisUtils;}@Override@Transactional(rollbackFor = Exception.class)public Result<AuthUserDto> login(AuthUserDto authUserDto, HttpServletRequest request) {// 通过缓存判断同一IP某一时间段内的登录次数是否超出限定次数String ip = NetworkUtils.getIp(request);String requestLoginIp = "request_login_".concat(ip);long loginCount = redisUtils.increment(requestLoginIp, 1L);if (loginCount == 1) {redisUtils.expire(requestLoginIp, Constant.REQUEST_LOGIN_LIMIT_TIME);}if (loginCount > Constant.REQUEST_LOGIN_LIMIT_COUNT) {log.warn("IP:[".concat(ip).concat("]已超出限定次数"));throw new RuntimeException("时间段内已超出限定次数,请不要频繁登录!");}......}
}
- 测试与验证
三、Hash的应用场景:存储用户信息
– 创建SpringBoot后台服务程序,实现微信小程序登录及JWT认证;
– 通过Redis缓存记录该用户最后一次登录时间及登录累计次数。
/*** 授权登录接口实现类--增加redis缓存哈希表应用** @author zhuhuix*/
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class AuthServiceImpl implements AuthService {........// 将当前用户信息与登录时间写入Redis缓存的哈希表// 以微信登录用户的openId作为哈希键值String key = authUserDto.getUserInfo().getOpenId();redisUtils.hashSet(key, "id", authUserDto.getUserInfo().getId());redisUtils.hashSet(key, "nickName", authUserDto.getUserInfo().getNickName());redisUtils.hashSet(key, "getAvatarUrl", authUserDto.getUserInfo().getAvatarUrl());redisUtils.hashSet(key, "lastLoginTime", Timestamp.valueOf(LocalDateTime.now()));// 读取缓存中当前哈希值对应的用户的登录次数Long loginCount = 1L;Object obj = redisUtils.hashGet(key, "loginCount");if (obj != null) {loginCount += Long.valueOf(String.valueOf(obj));}// 累加后回写到哈希表中redisUtils.hashSet(key, "loginCount", loginCount);...}
- 测试与验证
四、List的应用场景:队列实现
- 创建SpringBoot上传文件WebApi服务接口;
- 通过Redis缓存队列记录最新10笔用户上传文件的信息。
- 图片上传服务源码中增加Redis队列
/*** 微信小程序CRM实现类:实现Redis队列** @author zhuhuix*/
@Slf4j
@AllArgsConstructor
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class WxMiniCrmImpl implements WxMiniCrm {...@Override@Transactional(rollbackFor = Exception.class)public Result<CrmIndex> uploadCrmIndex(String json, String openId, String realName, MultipartFile multipartFile) {try {JSONObject jsonObject = JSONObject.parseObject(json);String createTime = jsonObject.getString("create");String employeeCode = jsonObject.getString("employeeCode");String customerCode = jsonObject.getString("customerCode");String customerName = jsonObject.getString("customerName");String type = jsonObject.getString("type");if (StringUtils.isEmpty(createTime) || StringUtils.isEmpty(employeeCode) || StringUtils.isEmpty(customerCode)|| StringUtils.isEmpty(customerName) || StringUtils.isEmpty(type)) {throw new RuntimeException("上传信息中缺少关键资料");}UploadFile uploadFile = uploadFileTool.upload(openId, realName, multipartFile);if (uploadFile == null) {throw new RuntimeException("上传文件失败!");}CrmIndex crmIndex = new CrmIndex();DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");crmIndex.setCreateTime(Timestamp.valueOf(LocalDateTime.parse(createTime, dateTimeFormatter)));crmIndex.setEmployeeCode(employeeCode);crmIndex.setCustomerCode(customerCode);crmIndex.setCustomerName(customerName);crmIndex.setType(type);crmIndex.setJson(json);crmIndex.setOpenId(openId);crmIndex.setPath(uploadFile.getPath());// 将最新10条上传的信息放入redis缓存if (redisUtils.size(Constant.REDIS_UPLOAD_QUEUE_NAME) >= Constant.REDIS_UPLOAD_QUEUE_COUNT) {log.warn(Constant.REDIS_UPLOAD_QUEUE_NAME.concat("队列已满,移除最旧上传信息:") + redisUtils.rightPop(Constant.REDIS_UPLOAD_QUEUE_NAME));}log.info(Constant.REDIS_UPLOAD_QUEUE_NAME.concat("队列增加上传信息:").concat(crmIndex.toString()));redisUtils.leftPush(Constant.REDIS_UPLOAD_QUEUE_NAME, crmIndex);return new Result<CrmIndex>().ok(crmIndexRepository.save(crmIndex));} catch (JSONException ex) {throw new RuntimeException("json转换失败:" + ex.getMessage());}}...
}
文件上传的原理与实现可参考该文章《SpringBoot实现微信小程序文件上传的完整案例》
测试与验证
微信小程序端
– 前端将识别信息与图片上传至服务器
Redis缓存队列
– 队列中只保存最新10条(数量可自行调整)信息.:
五、Set的应用场景:自动去重
- 创建SpringBoot添加客户信息服务接口;
- 通过Redis集合缓存客户信息,要求自动去重,不得重复记录。
- 客户实体类
/*** CRM客户信息** @author zhuhuix*/
@Entity
@Getter
@Setter
@Table(name = "customer")
public class Customer implements Serializable {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@NotNull(groups = Update.class)private Long id;@Column(name = "open_id")private String openId;/*** 客户代码*/@Column(name = "customer_code")private String customerCode;/*** 客户名称*/@Column(name = "customer_name")private String customerName;/*** 首字母*/@Column(name = "first_letter")private String firstLetter;/*** 创建时间*/@Column(name = "create_time")@CreationTimestampprivate Timestamp createTime;/*** 更新时间*/@Column(name = "update_time")@UpdateTimestampprivate Timestamp updateTime;@Overridepublic String toString() {return "Customer{" +"customerCode='" + customerCode + '\'' +", customerName='" + customerName + '\'' +'}';}
}
- 客户信息WebApi
@ApiOperation(value = "通过扫一扫功能上传客户信息")@PostMapping(value = "/crmScan/{openId}")public ResponseEntity crmScan(@RequestBody WxScanDto wxScanDto, @PathVariable String openId) {return ResponseEntity.ok(wxMiniCrm.wxScan(wxScanDto, openId));}
- 记录并缓存客户信息实现类
/*** 微信小程序CRM实现类** @author zhuhuix*/
@Slf4j
@AllArgsConstructor
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class WxMiniCrmImpl implements WxMiniCrm {private final UploadFileTool uploadFileTool;private final CrmIndexRepository crmIndexRepository;private final CustomerRepository customerRepository;private final UserService userService;private final RedisUtils redisUtils;...@Override@Transactional(rollbackFor = Exception.class)public Result<WxScanDto> wxScan(WxScanDto wxScanDto, String openId) {//微信扫一扫保存客户信息if (Constant.SAVE_CUSTOMER_INFO.equals(wxScanDto.getScanType()) && wxScanDto.getJsonObject() != null) {try {Customer customer = JSONObject.parseObject(wxScanDto.getJsonObject().toJSONString(), Customer.class);Customer target = customerRepository.findByCustomerCodeAndOpenId(customer.getCustomerCode(), openId);if (target != null) {BeanUtils.copyProperties(customer, target, RepositoryUtil.getNullPropertyNames(customer));} else {target = customer;target.setOpenId(openId);}wxScanDto.setReturnObject(customerRepository.save(target));// 将用户增加的客户信息添加到redis集合中redisUtils.setAdd(openId.concat("_customer"),customer.toString());return new Result<WxScanDto>().ok(wxScanDto);} catch (JSONException ex) {throw new RuntimeException("json转换失败:" + ex.getMessage());}}return new Result<WxScanDto>().error("无法处理扫一扫功能");}}
- 测试与验证
- 相同信息自动去重:通过swagger2进行接口测试,多次提交相同的客户信息
- Redis Desktop Manager验证数据
– 查看集合中的数据,实现自动去重
- 不同集合之间的交集与差集:用户1通过接口添加4个客户
–用户2通过接口添加3个客户
– 获取用户1与用户2相同及不同的客户信息
/*** Redis测试* * @author zhuhuix*/
@SpringBootTest
@Slf4j
public class TestSet {@Testvoid test() {RedisUtils redisUtils = SpringContextHolder.getBean(RedisUtils.class);//获取交集:相同客户Set<Object> setInter=redisUtils.setInter("openId1_customer","openId2_customer");Iterator iterator = setInter.iterator();log.info("openId1_customer与openId2_customer相同的客户为:");while(iterator.hasNext()){log.info(iterator.next().toString());}//获取差集:不同客户Set<Object> setDiff=redisUtils.setDifference("openId1_customer","openId2_customer");iterator = setDiff.iterator();log.info("openId1_customer与openId2_customer不同的客户为:");while(iterator.hasNext()){log.warn(iterator.next().toString());}//获取差集:不同客户Set<Object> setDiff1=redisUtils.setDifference("openId2_customer","openId1_customer");iterator = setDiff1.iterator();log.info("openId2_customer与openId1_customer不同的客户为:");while(iterator.hasNext()){log.warn(iterator.next().toString());}}
}
- 测试结果
Redis项目应用场景与实例汇总相关推荐
- Redis项目应用场景与实例(三):队列(List)
文章目录 一.背景 二.项目需求 三.环境配置 四.项目代码 4.1 Redis工具类增加队列操作方法 4.2 图片上传服务增加Redis队列 五.测试与验证 六.源码 一.背景 在前两篇文章 < ...
- redis常见使用场景与实例
问题(需求): 项目中一些常见问题 1.多节点用户会话保存 2.订单大规模定时取消 3.产品服务访问慢 4.分布式任务重复执行 5.高并发提交:设备状态上报接口 接口高并发 /*** 接到上报* 1* ...
- redis应用场景及实例
redis应用场景及实例 前言 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.在这篇文章中,我们将阐述 Re ...
- Redis的应用场景汇总
Redis应用场景 Redis作为一个非关系型数据库,除了在访问速度上拥有显著优势外,其本身支持的多种数据类型也非常有用,能覆盖系统开发中的很多应用场景.下面列举的场景有的是从网上其他人的博客里看到的 ...
- 软件项目策划与管理知识点汇总
categories: [计算机通识,软件项目策划与管理] thumbnail: /images/fe/rjxmchhgl.jpg toc: true 软件项目策划与管知识点汇总 第一章:序言 关于软 ...
- java基础巩固-宇宙第一AiYWM:为了维持生计,Redis基础Part6(Redis的应用场景、Redis是单线程的速度还快、Redis线程模型:Reactor模式、事件、发布订阅、管道)~整起
PART1-1:为什么Redis是单线程的 Redis单线程是指: Redis的网络IO和键值对读写是由一个线程来完成的.这也是 Redis 对外提供键值存储服务的主要流程.Redis的其他功能,比如 ...
- 什么是Redis及其适用场景
什么是Redis? Redis全称(Remote Dictionary Server): Redis本质上是一个Key-Value类型的内存数据库,整个数据库统统加载在内存当中进行操作,定期通过异步操 ...
- 大容量类 Redis 存储的场景补充-pika
2019独角兽企业重金招聘Python工程师标准>>> 导读 我们在<大容量类 Redis 存储 - 有关 pika 的一切>里介绍过pika的诞生.pika的特点.pi ...
- python使用redis在实际场景使用_用python操作redis及redis的一些应用场景
redis安装 网上很多windows.Linux的安装教程,可根据自己的需要寻找对应教程安装 redis-py安装 pip install redis redis可视化工具 API的使用 redis ...
- Python 小白从零开始 PyQt5 项目实战(8)汇总篇(完整例程)
本系列面向 Python 小白,从零开始实战解说应用 QtDesigner 进行 PyQt5 的项目实战.不跳过一个细节,不漏掉一行代码,不省略一个例图. 本系列从软件安装.环境配置开始,介绍了基本应 ...
最新文章
- ubuntu g++编译32位应用
- 电子书下载:Moving to Microsoft Visual Studio 2010
- new ArrayList 报错
- Qt::WA_OpaquePaintEvent理解
- 【2018.3.10】模拟赛之四-ssl2133 腾讯大战360【SPAF,图论,最短路径】
- LeetCode 1073. 负二进制数相加(负数进制)
- oracle ko16mswin949,mysql字符集 - osc_wq7ij8li的个人空间 - OSCHINA - 中文开源技术交流社区...
- 原创力文档c语言程序设计第五章,C语言程序设计教程第五章练习题题目(7页)-原创力文档...
- pyqt5多进程 python_Python 多进程大全
- arduino雨滴传感器和舵机控制
- QQ登陆界面Resource Hacker制作
- Redis安装教程(vmware虚拟机上)
- [硬件选型] 工业相机之参数和选型
- Google map根据经纬度获取地址信息
- supp 和rdomain连接
- 设备树使用手册【转】
- matlab ode45例子,matlab的ode45
- ES7241D低功耗音频ADC芯片##小爱音响
- 11.selenium登录126邮箱出现定位问题解决
- 如何把PPT文件压缩变小