SpringBoot 2.7 .2实战基础 - 09 - 集成 Redis & 异步任务

1 集成Redis

《docker 安装 MySQL 和 Redis》一文已介绍如何在 Docker 中安装 Redis,本文就看看 SpringBoot 如何整合 Redis。SpringBoot 提供了整合 Redis 的 starter,使用非常简单。

1.1 添加依赖

在 pom.xml 中添加 redis 的 starter:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

1.2 配置 Redis

修改 application.yml 文件,添加 Redis 的配置:

spring:
redis:
host: 127.0.0.1
port: 6379
username:
password:
timeout: 5000
jedis:
pool:
max-active: 3
max-idle: 3
min-idle: 1
max-wait: -1

1.3 添加配置

在 com.yygnb.demo.config 中创建 RedisConfig,处理一些中文乱码问题。

com.yygnb.demo.config.RedisConfig:

@Configuration
public class RedisConfig { private final RedisTemplate redisTemplate; public RedisConfig(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
} /**
* 解决redis插入中文乱码
* @return
*/
@Bean
public RedisTemplate<Serializable, Object> redisTemplateInit() {
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer); GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return redisTemplate;
} }

1.4 封装 Redis 操作

可封装一些 Redis 的常见操作。

com.yygnb.demo.utils.RedisUtils:

@RequiredArgsConstructor
@Component
public class RedisUtils { private final RedisTemplate redisTemplate; /**
* 指定缓存失效时间
* @param key
* @param time 单位 秒
*/
public void expire(Serializable key, long time) {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
} /**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
} /**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
} /**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
} /**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
} /**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Serializable value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

操作太多,这里简单放了几个常见的方法。

1. 5 测试

新建 controller 测试 Redis 的操作。

com.yygnb.demo.controller.RedisDemoController:

@Tag(name = "Redis测试接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("/redis")
public class RedisDemoController { private final RedisUtils redisUtils; @Operation(summary = "存值")
@PostMapping()
public void save(@RequestBody Map<String, Object> map) {
Set<String> keys = map.keySet();
for (String key : keys) {
redisUtils.set(key, map.get(key));
}
} @Operation(summary = "取值")
@GetMapping()
public Object get(String key) {
return redisUtils.get(key);
}
}

2 异步任务

异步任务在后端开发中很常见,如生成报表,前端调用后端一个接口,如果数据量较大,后端生成报表会非常耗时,这时候如果是同步任务,等后端报表已经生成后,估计已经请求超时了。通常情况下,后端触发一个异步任务,成功触发任务后,后端就返回前端,无需等待报表生成成功。类似场景如同时给用户发送邮件和短信。

2.1 准备工作

(1) 两个 Service

分别编写模拟发送邮件和短信的 Service,这里只是演示使用,故 Service 省略了接口定义,直接编写实现类:

com.yygnb.demo.service.impl.EmailService:

@Slf4j
@Service
public class EmailService { public void sendEmail(String msg) {
log.info("开始发送邮件: {}", msg);
int i = new Random().nextInt(5);
try {
Thread.sleep(i * 1000);
log.info("邮件发送成功");
} catch (InterruptedException e) {
log.error("邮件发送失败");
e.printStackTrace();
}
}
}

发送短信的 Service 与邮件 service 类似。

com.yygnb.demo.service.impl.SmsService:

@Slf4j
@Service
public class SmsService { public void sendSms(String msg) {
log.info("开始发送短信: {}", msg);
int i = new Random().nextInt(5);
try {
Thread.sleep(i * 1000);
log.info("短信发送成功");
} catch (InterruptedException e) {
log.error("短信发送失败");
e.printStackTrace();
}
}
}

(2) 接口开发

创建 DemoService,在 DemoService 中调用上面两个 Service:

@Slf4j
@RequiredArgsConstructor
@Service
public class DemoServiceImpl implements DemoService { private final EmailService emailService;
private final SmsService smsService; @Override
public void send(String msg) {
log.info("发别发送短信和邮件");
smsService.sendSms(msg);
emailService.sendEmail(msg);
log.info("Demo Service 结束");
}
}

在 DemoController 中添加接口:

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("demo")
public class DemoController { private final DemoService demoService; @GetMapping("async")
public void asyncDemo(String msg) {
demoService.send(msg);
}
}

(3) 运行

请求该接口:

http://localhost:9099/demo/async?msg=hello

由于现在是同步执行,需要短信和邮件两个service都执行完后才会返回结果。而且输出的日志顺序是固定的:

2.2 异步任务

接下来进行异步任务的改造。

(1) @EnableAsync

首先在启动类上添加注解 @EnableAsync 开启异步任务。

@EnableAsync
@MapperScan("com.yygnb.demo.mapper")
@SpringBootApplication
public class DemoApplication {
...
}

(2)@Async

在需要异步执行的方法上添加注解 @Async。在 sendSms 和 sendEmail 两个方法上添加该注解:

...
@Async
public void sendEmail(String msg) {
...
}
...

(3)注意事项

调用异步任务的方法与异步任务的方法,不能在同一个 Service 中,即上面的 demo中,send 方法与 sendSms 不能在同一个 Service中。

2.3 自定义线程池

异步任务本质上是在子线程中运行的。可以自定义线程池。

(1)定义配置的实体类

创建线程池配置的实体类:

com.yygnb.demo.config.ThreadPoolInfo:

@Data
@Component
@ConfigurationProperties(prefix = "thread-pool")
public class ThreadPoolInfo { private int corePoolSize = 1; private int maxPoolSize = Integer.MAX_VALUE; private int keepAliveSeconds = 60; private int queueCapacity = Integer.MAX_VALUE; private String threadNamePrefix = "thread-";
}

(2)配置类

com.yygnb.demo.config.AsyncConfig:

@RequiredArgsConstructor
@Configuration
public class AsyncConfig { private final ThreadPoolInfo info; @Bean("asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(info.getCorePoolSize());
executor.setMaxPoolSize(info.getMaxPoolSize());
executor.setQueueCapacity(info.getQueueCapacity());
executor.setThreadNamePrefix(info.getThreadNamePrefix());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

(3)配置 YML

在 application.yml 中配置线程池:

thread-pool:
core-pool-size: 3
max-pool-size: 5
thread-name-prefix: yyg-async-

重启服务,访问上面的接口,日志如下:

感谢你花费宝贵的时间阅读本文,如果本文给了你一点点帮助或者启发,还请三连支持一下,点赞、关注、收藏,作者会持续与大家分享更多干货

集成 Redis & 异步任务 - SpringBoot 2.7 .2实战基础的更多相关文章

  1. 使用 Liquibase 管理数据库版本 - SpringBoot 2.7 .2 实战基础

    优雅哥 SpringBoot 2.7 .2 实战基础 - 05 -使用 Liquibase 管理数据库版本 在企业开发中,数据库版本管理好像是一个伪命题,大多项目都是通过 Power Designer ...

  2. 多环境配置 - SpringBoot 2.7.2 实战基础

    优雅哥 SpringBoot 2.7.2 实战基础 - 06 -多环境配置 在一个项目的开发过程中,通常伴随着多套环境:本地环境 local.开发环境 dev.集成测试环境 test.用户接受测试环境 ...

  3. 清晰梳理最全日志框架关系与日志配置-SpringBoot 2.7.2 实战基础

    优雅哥 SpringBoot 2.7.2 实战基础 - 07 - 日志配置 Java 中日志相关的 jar 包非常多,log4j.log4j2.commons-logging.logback.slf4 ...

  4. SpringBoot 如何集成 MyBatisPlus - SpringBoot 2.7.2实战基础

    SpringBoot 2.7.2 学习系列,本节通过实战内容讲解如何集成 MyBatisPlus 本文在前文的基础上集成 MyBatisPlus,并创建数据库表,实现一个实体简单的 CRUD 接口. ...

  5. Spring Boot从入门到精通(七)集成Redis实现Session共享

    单点登录(SSO)是指在多个应用系统中,登录用户只需要登录验证一次就可以访问所有相互信任的应用系统,Redis Session共享是实现单点登录的一种方式.本文是通过Spring Boot框架集成Re ...

  6. 集成 Spring Doc 接口文档和 knife4j-SpringBoot 2.7.2 实战基础

    优雅哥 SpringBoot 2.7.2 实战基础 - 04 -集成 Spring Doc 接口文档和 knife4j 前面已经集成 MyBatis Plus.Druid 数据源,开发了 5 个接口. ...

  7. 【springBoot】springBoot集成redis的key,value序列化的相关问题

    使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...

  8. SpringBoot集成redis的key,value序列化的相关问题

    使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...

  9. springboot集成redis(mybatis、分布式session)

    安装Redis请参考:<CentOS快速安装Redis> 一.springboot集成redis并实现DB与缓存同步 1.添加redis及数据库相关依赖(pom.xml) <depe ...

随机推荐

  1. 关于 GIN 的路由树

    GIN 是一个 golang 常用的 Web 框架,它对 API 比较友好,源码注释也很明确明确,使用起来快速灵活,还有极高的容错率.标题中的路由我们可以简单理解为在浏览器中输入的页面地址,而&quo ...

  2. UiPath录制器的介绍和使用

    一.录制器(Recording)的介绍 录制器是UiPath Studio的重要组成部分,可以帮助您在自动化业务流程时节省大量时间.此功能使您可以轻松地在屏幕上捕获用户的动作并将其转换为序列. 二.录 ...

  3. go-zero微服务实战系列(九、极致优化秒杀性能)

    上一篇文章中引入了消息队列对秒杀流量做削峰的处理,我们使用的是Kafka,看起来似乎工作的不错,但其实还是有很多隐患存在,如果这些隐患不优化处理掉,那么秒杀抢购活动开始后可能会出现消息堆积.消费延迟. ...

  4. SSRS筛选器的IN运算(即包含于)用法

    筛选器的IN运算,在Microsoft的官网上没像样儿的例子,不好设置,很容易错 Microsoft上的文档:https://docs.microsoft.com/zh-cn/sql/reportin ...

  5. [ARC096C] Everything on It 补题记录

    题目链接 题目大意: 对于集合 \(\{1,2,\dots,n\}\) ,求它的子集族中,有多少个满足: 任意两个子集互不相同: \(1,2,\dots,n\) 都在其中至少出现了 \(2\) 次. ...

  6. Ubuntu14.04.6配置阿里源

    Ubuntu14.04.6配置阿里源 这两天上手 Ubuntu 系统,因为公司用的是 14.04.6 版本,所以有了一些踩坑记录. 起因是安装完系统我需要安装一个搜狗输入法,过程得安装 fcitx,需 ...

  7. ubuntu20.04安装测试uhttpd

    uhttpd是openwrt上运行一个高效小型Http服务,支持cgi, lua等特性.可以直接通过snap方式安装,如果是16.04,18.04或者20.04,snap已经默认安装了:如果是其它版本 ...

  8. 记一道经典树上Nim游戏

    这道题首先是 Hanriver 提出来的,但是大家都不会做,今天看到了一道一模一样的题目 AT2667 题目大意是,每个人删掉一个不是整棵树的原树的子树,给定一个树问游戏状态. 首先,这是需要用到多个 ...

  9. 从零开始完整开发基于websocket的在线对弈游戏【五子棋】,只用几十行代码完成全部逻辑。

    五子棋是规则简单明了的策略型游戏,先形成五子连线者获胜.本课程习作采用两人在线对弈的方式进行比赛,拿着手机在上下班路上玩特别合适. 整个过程在众触低代码应用平台进行,使用表达式描述游戏逻辑(高度简化版 ...

  10. 如何手写一个js工具库?同时发布到npm上

    自从工作以来,写项目的时候经常需要手写一些方法和引入一些js库 JS基础又十分重要,于是就萌生出自己创建一个JS工具库并发布到npm上的想法 于是就创建了一个名为learnjts的项目,在空余时间也写 ...