集成Redis&异步任务-SpringBoot2.7.2实战基础

博客 分享
0 182
张三
张三 2022-09-06 16:04:09
悬赏:0 积分 收藏

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

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

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

@Configurationpublic 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@Componentpublic 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@Servicepublic 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@Servicepublic 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@Servicepublic 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都执行完后才会返回结果。而且输出的日志顺序是固定的:

image-20220905175810080

2.2 异步任务

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

(1) @EnableAsync

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

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

(2)@Async

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

...    @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@Configurationpublic 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-

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

image-20220905182201881

image

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

posted @ 2022-09-06 15:22 程序员优雅哥(\/同) 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员