文章目录

  • 一、背景
    • 1.1 开发环境
    • 1.2 项目配置
  • 二、字符串的应用场景:封锁一个IP地址
  • 三、Hash的应用场景:存储用户信息
  • 四、List的应用场景:队列实现
  • 五、Set的应用场景:自动去重

一、背景

  • 本篇文章是将以前Redis实战的系列文章进行汇总,针对Redis中常用的一些数据结构,进行实战模拟。
strings hashes lists sets sorted sets
封锁一个IP地址 存储用户信息 模拟消息队列 自动排重 以某一个条件为权重,进行排序

1.1 开发环境

  1. JDK 1.8
  2. SpringBoot 2.2.5
  3. JPA
  4. Spring Security
  5. Mysql 8.0
  6. Redis Server 3.2.1
  7. Redis Desktop Manager
  8. 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("无法处理扫一扫功能");}}
  • 测试与验证
  1. 相同信息自动去重:通过swagger2进行接口测试,多次提交相同的客户信息
  • Redis Desktop Manager验证数据
    – 查看集合中的数据,实现自动去重
  1. 不同集合之间的交集与差集:用户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项目应用场景与实例汇总相关推荐

  1. Redis项目应用场景与实例(三):队列(List)

    文章目录 一.背景 二.项目需求 三.环境配置 四.项目代码 4.1 Redis工具类增加队列操作方法 4.2 图片上传服务增加Redis队列 五.测试与验证 六.源码 一.背景 在前两篇文章 < ...

  2. redis常见使用场景与实例

    问题(需求): 项目中一些常见问题 1.多节点用户会话保存 2.订单大规模定时取消 3.产品服务访问慢 4.分布式任务重复执行 5.高并发提交:设备状态上报接口 接口高并发 /*** 接到上报* 1* ...

  3. redis应用场景及实例

    redis应用场景及实例 前言 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.在这篇文章中,我们将阐述 Re ...

  4. Redis的应用场景汇总

    Redis应用场景 Redis作为一个非关系型数据库,除了在访问速度上拥有显著优势外,其本身支持的多种数据类型也非常有用,能覆盖系统开发中的很多应用场景.下面列举的场景有的是从网上其他人的博客里看到的 ...

  5. 软件项目策划与管理知识点汇总

    categories: [计算机通识,软件项目策划与管理] thumbnail: /images/fe/rjxmchhgl.jpg toc: true 软件项目策划与管知识点汇总 第一章:序言 关于软 ...

  6. java基础巩固-宇宙第一AiYWM:为了维持生计,Redis基础Part6(Redis的应用场景、Redis是单线程的速度还快、Redis线程模型:Reactor模式、事件、发布订阅、管道)~整起

    PART1-1:为什么Redis是单线程的 Redis单线程是指: Redis的网络IO和键值对读写是由一个线程来完成的.这也是 Redis 对外提供键值存储服务的主要流程.Redis的其他功能,比如 ...

  7. 什么是Redis及其适用场景

    什么是Redis? Redis全称(Remote Dictionary Server): Redis本质上是一个Key-Value类型的内存数据库,整个数据库统统加载在内存当中进行操作,定期通过异步操 ...

  8. 大容量类 Redis 存储的场景补充-pika

    2019独角兽企业重金招聘Python工程师标准>>> 导读 我们在<大容量类 Redis 存储 - 有关 pika 的一切>里介绍过pika的诞生.pika的特点.pi ...

  9. python使用redis在实际场景使用_用python操作redis及redis的一些应用场景

    redis安装 网上很多windows.Linux的安装教程,可根据自己的需要寻找对应教程安装 redis-py安装 pip install redis redis可视化工具 API的使用 redis ...

  10. Python 小白从零开始 PyQt5 项目实战(8)汇总篇(完整例程)

    本系列面向 Python 小白,从零开始实战解说应用 QtDesigner 进行 PyQt5 的项目实战.不跳过一个细节,不漏掉一行代码,不省略一个例图. 本系列从软件安装.环境配置开始,介绍了基本应 ...

最新文章

  1. ubuntu g++编译32位应用
  2. 电子书下载:Moving to Microsoft Visual Studio 2010
  3. new ArrayList 报错
  4. Qt::WA_OpaquePaintEvent理解
  5. 【2018.3.10】模拟赛之四-ssl2133 腾讯大战360【SPAF,图论,最短路径】
  6. LeetCode 1073. 负二进制数相加(负数进制)
  7. oracle ko16mswin949,mysql字符集 - osc_wq7ij8li的个人空间 - OSCHINA - 中文开源技术交流社区...
  8. 原创力文档c语言程序设计第五章,C语言程序设计教程第五章练习题题目(7页)-原创力文档...
  9. pyqt5多进程 python_Python 多进程大全
  10. arduino雨滴传感器和舵机控制
  11. QQ登陆界面Resource Hacker制作
  12. Redis安装教程(vmware虚拟机上)
  13. [硬件选型] 工业相机之参数和选型
  14. Google map根据经纬度获取地址信息
  15. supp 和rdomain连接
  16. 设备树使用手册【转】
  17. matlab ode45例子,matlab的ode45
  18. ES7241D低功耗音频ADC芯片##小爱音响
  19. 11.selenium登录126邮箱出现定位问题解决
  20. 如何把PPT文件压缩变小

热门文章

  1. BT下载软件开发笔记——种子解析模块的设计与实现
  2. Windows官方镜像大全---【内附网盘链接】
  3. 本科毕业论文降低查重率小技巧
  4. word转pdf公式乱码_求助:Word转pdf时公式会公式变成乱码
  5. 前端实现序列帧_Html5 序列帧动画
  6. 进制转化(北理乐学编程题目)
  7. java web 邮件_Java Web(十二) JavaMail发送邮件
  8. 前端之HTML视频、音频
  9. Spring定时器corn表达式
  10. HFSS天线设计实例_不同介质的BLE S11曲线