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

2023-10-27

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

image-20220905175810080

2.2 异步任务

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

(1) @EnableAsync

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

@EnableAsync
@MapperScan("com.yygnb.demo.mapper")
@SpringBootApplication
public 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
@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-

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 无法在 Android 10 中创建目录

    我无法在 android 10 中创建目录 它可以在 android Oreo 之前的设备上运行 我尝试了两种创建文件夹的方法 Using File mkdir File f new File Environment getExternal
  • 将 jar 作为 Linux 服务运行 - init.d 脚本在启动应用程序时卡住

    我目前正在致力于在 Linux VM 上实现一个可运行的 jar 作为后台服务 我已经使用了找到的例子here https gist github com shirish4you 5089019作为工作的基础 并将 start 方法修改为
  • “_加载小部件时出现问题”消息

    加载小部件时 如果找不到资源或其他内容 则会显示 加载小部件时出现问题 就这样 惊人的 此消息保留在主屏幕上 甚至没有说明加载时遇到问题的小部件 我通过反复试验弄清楚了这一点 但我想知道发生这种情况时是否有任何地方可以找到错误消息 Andr
  • Android 2.2 SDK - Droid X 相机活动无法正常完成

    我注意到我在 Droid X 上调用的默认相机活动与我的 Droid 和 Nexus One 上的默认相机活动看起来不同 在 Droid 和 Nexus One 上选择 确定 后 活动将完成 Droid X 有一个 完成 按钮 它将带您返回
  • 为什么 java 编译器不报告 Intellij 中多播表达式的未经检查的强制转换警告?

    为什么下面的代码没有报告 Intellij IDEA 的未经检查的警告jdk 1 8 0 121自从Supplier
  • Java:使用 HttpURLConnection 的 HTTP PUT

    如何执行 HTTP PUT 我正在使用的类似乎认为它正在执行 PUT 但端点将其视为我执行了 GET 我做错了什么吗 URL url new URL https HttpURLConnection conn HttpURLConnectio
  • 记录骆驼路线

    我的项目中有几个 Camel 上下文 如果可能的话 我想以逆向工程方式记录路线 因为我们希望保持与上下文相关的文档最新 最好的方法是什么 我们倾向于预先实际设计路线 并使用来自EIP book http www eaipatterns co
  • 如何让spring为JdbcMetadataStore创建相应的schema?

    我想使用此处描述的 jdbc 元数据存储 https docs spring io spring integration docs 5 2 0 BUILD SNAPSHOT reference html jdbc html jdbc met
  • 具有共享依赖项的多模块项目的 Gradle 配置

    使用 gradle 制作第一个项目 所以我研究了 spring gradle hibernate 项目如何组织 gradle 文件 并开始制作自己的项目 但是 找不到错误 为什么我的配置不起作用 子项目无法解决依赖关系 所以项目树 Root
  • 计算日期之间的天数差异

    在我的代码中 日期之间的差异是错误的 因为它应该是 38 天而不是 8 天 我该如何修复 package random04diferencadata import java text ParseException import java t
  • Espresso 和 Proguard 的 Java.lang.NoClassDefFoundError

    我对 Espresso 不太有经验 但我终于成功地运行了它 我有一个应用程序需要通过 Proguard 缩小才能处于 56K 方法之下 该应用程序以 3 秒的动画开始 因此我需要等到该动画结束才能继续 这就是我尝试用该方法做的事情waitF
  • 如何在 Eclipse Java 动态 Web 项目中使用 .properties 文件?

    我正在 Eclipse 中开发动态 Web 项目 我创建了一个 properties 文件来存储数据库详细信息 用户名 密码等 我通过右键单击项目和 New gt File 添加它 我使用了Java util包Properties类 但它不
  • 解析输入,除了 System.in.read() 之外不使用任何东西

    我很难找到具体的细节System in read 有效 也许有人可以帮助我 似乎扫描仪会更好 但我不允许使用它 我被分配了一个任务 我应该以 Boolean Operator Boolean 的形式读取控制台用户输入 例如T F 或 T T
  • 如何通过 Inno Setup for NetBeans 使用自定义 .iss 文件

    我将 Inno Setup 5 与 NetBeans 8 一起使用 并且我已经能够创建一个安装程序来安装该应用程序C users username local appname 但是我希望将其安装在C Programfiles 我如何在 Ne
  • JVM:是否可以操作帧堆栈?

    假设我需要执行N同一线程中的任务 这些任务有时可能需要来自外部存储的一些值 我事先不知道哪个任务可能需要这样的值以及何时 获取速度要快得多M价值观是一次性的而不是相同的M值在M查询外部存储 注意我不能指望任务本身进行合作 它们只不过是 ja
  • Java:拆箱整数时出现空指针异常?

    此代码导致空指针异常 我不知道为什么 private void setSiblings PhylogenyTree node Color color throws InvalidCellNumberException PhylogenyTr
  • Java:多线程内的 XA 事务传播

    我如何使用事务管理器 例如Bitronix http docs codehaus org display BTM Home JBoss TS http www jboss org jbosstm or Atomikos http www a
  • Hibernate 和可序列化实体

    有谁知道是否有一个框架能够从实体类中剥离 Hibernate 集合以使它们可序列化 我查看了 BeanLib 但它似乎只进行实体的深层复制 而不允许我为实体类中的集合类型指定实现映射 BeanLib 目前不适用于 Hibernate 3 5
  • Android AutoCompleteTextView 带芯片

    我不确定我是否使用了正确的词语来描述此 UI 功能 但我已附上我希望在我的应用程序中实现的目标的快照 它由 Go SMS 使用 用户在编辑文本中键入联系人 在用户从完成下拉列表中选择联系人后 该联系人将被插入到编辑文本中 如附图所示 编辑文
  • 启动Java项目时发生类冲突:ClassMetadataReadingVisitor将接口org.springframework.asm.ClassVisitor作为超类

    我正在使用最新的Spring框架版本 3 2 2 RELEASE 开发一个Java Web项目 但是现在项目启动时遇到了问题 详细错误是 java lang IncompleteClassChangeError 类 org springfr

随机推荐

  • 剑指Offer - 面试题47:礼物的最大价值

    题目 在一个m n的棋盘的每一格都放有一个礼物 每个礼物都有一定的价值 价值大于0 你可以从棋盘的左上角开始拿格子里的礼物 并每次向左向下移动一格 直到到达棋盘的右下角 给定一个棋盘及其上面的礼物 请计算你最多能拿到多少价值的礼物 例如 在
  • 关于vue开发时,项目结构布局总结---(经验篇)

    最近结束了一个项目 总结下这个项目结构 先看下这个项目大致布局结构 首页 图1 个人中心 图2 先来看图1 分为头部 内容 底部 三部分 代码结构如下 然后这个配置好的文件 当然在配置路由里使用啦 这些路由配置好后 开始整合 在main j
  • 根据三角形三边长度,判断为直角三角形、钝角三角形还是锐角三角形

    1 首先输入三角形三边 2 判断是否构成三角形 任意两边和大于第三边 如果不是则输出不是三角形 3 如若是三角形判断为何种三角形 任意一边平方等于其余两边的平方和为直角三角形 任意一边平方大于其余两边的平方和则为钝角三角形 否则为锐角三角形
  • 安全运维-如何在Kubernetes中使用注释对ingress-nginx及后端应用进行安全加固配置实践...

    关注 WeiyiGeek 公众号 设为 特别关注 每天带你玩转网络安全运维 应用开发 物联网IOT学习 本章目录 0x08 Kubernetes中ingress nginx安全配置 1 配置指定的 Ingress Class 2 安全配置之
  • OkGo二次封装工具类

    public class OkGoUtils 必须在Application中初始化 param context Application对象 author LH created at 2019 9 25 10 23 public static
  • el-checkbox的基本使用,避坑指南

    el checkbox的基本使用 避坑指南 使用el checkbox勾选出现的问题 全选不生效 全选后底下的勾选不能回显 有问题的代码
  • Linux下并行时间增加,Linux下Castep并行计算时节点配置

    1 并行所使用的节点及核心配置文件 PATH ms42 share data machines LINUX 格式为 节点名称 核心数 每cpu核心数 cpu数 node1 8 Intel R Xeon R CPU E5430 2 66GHz
  • 【python+selenium】UI自动化测试环境搭建及使用(建议收藏)

    一 什么是Selenium Selenium 是一个浏览器自动化测试框架 它主要用于web应用程序的自动化测试 其主要特点如下 开源 免费 多平台 浏览器 多语言支持 对web页面有良好的支持 API简单灵活易于使用 支持分布式测试用例执行
  • python基础笔记

    https www bilibili com video BV1qW4y1a7fU p 12 spm id from pageDriver vd source db3d134c564b091aeb95550baf2fa5b0 第一阶段 01
  • linux和windows的对比

    Windows下可以运行绝大部分的游戏 硬件厂商近乎100 的支持 linux下可直接运行的软件数量 和win比起来就是1和99的区别 选择linux的人基本不会考虑玩游戏 同时linux正期待更多硬件厂商的支持 linux安全性高 win
  • Ubuntu 20.04下docker安装和添加阿里云服务器镜像和加速镜像

    官方的docker安装文档 https docs docker com engine install ubuntu 先卸载旧版的docker sudo apt get remove docker docker engine docker i
  • C/C++程序员何去何从

    滚滚长江东逝水 浪花淘尽英雄 虽说是个人英雄的时代已经成为过去 但我们仍然不能对这样的榜样们有所忘怀 他们是WPS求伯君 CCDOS严援朝 2 13吴晓军 四通利方王志东 CCED朱崇君 UCDOS鲍岳桥等 因为他们不仅是成名的优秀程序员
  • element组件:点击侧边栏,切换右侧内容

    侧边栏 远程加载了页面的头部
  • pip工具使用之版本变更及恢复

    1 版本变更 降低版本 pycharm安装第三方库 Try to run this command from the system terminal Make sure that you use the问题 亲测已解决 爱编程的小新的博客
  • STL-String容器

    string本质上是一个类 string 类内部封装了很多成员方法 例如 查找find 拷贝copy 删除delete 替换replace 插入insert string管理char 所分配的内存 不用担心复制越界和取值越界等 由类内部进行
  • Android通用的筛选栏实现

    最近事情比较多 不管是生活还是工作 感觉节奏都在赶着走 对于之前相对比较喜欢自由的我 也要慢慢适应之后的节奏了 不管怎样 生活还在继续 加油 今天来写一个通用的筛选栏的实现 也是因为之前项目中要好多地方用到筛选栏这么个东西 所以为了之后用起
  • C++基础知识 - 类模板函数写在类的外部

    1 所有的类模板函数写在类的外部 在一个cpp中 include
  • Could not initialize class sun.awt.X11GraphicsEnvironment

    当你的代码出现这一段你不慌吗 其实解决方法很简单 这一段代码很重要 用了它就能在linux环境下没有桌面的情况下运行验证码 在静态代码块中添加这一段表示启用headless模式就不用下载桌面环境了 static System setProp
  • 2014年2月21日星期五(DEMO7-6战区漫步)

    这是本章最后一个例子 与上个例子不同的是 1 加载的物体是2个 2 用了欧拉相机 视野120度 这个DEMO是个综合 应该没有新的函数了 稍微简单些 首先设定个宇宙空间 以及 各物体参数 define UNIVERSE RADIUS 400
  • 集成 Redis & 异步任务-SpringBoot 2.7 .2实战基础

    学SpringBoot 2 7 2实战基础 09 集成 Redis 异步任务 1 集成Redis docker 安装 MySQL 和 Redis 一文已介绍如何在 Docker 中安装 Redis 本文就看看 SpringBoot 如何整合