【SpringBoot】微服务学习笔记七:微服务中异步调用数据提交数据库的问题

简介: 【SpringBoot】微服务学习笔记七:微服务中异步调用数据提交数据库的问题

 一: 同步&异步

1.同步与异步的概念

       在进行问题探讨之前,我们有必要先了解一下什么是同步什么是异步。先来个官方点的说法:同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)。同步,就是调用某个东西是,调用方得等待这个调用返回结果才能继续往后执行。异步,和同步相反 调用方不会理解得到结果,而是在调用发出后调用者可用继续执行后续操作,被调用者通过状体来通知调用者,或者通过回掉函数来处理这个调用。

       可能你会就得有点懵?下面我们举个简单点的例子:就好像你去买水果,发现水果卖完了,这时候水果还在来的路上,你选择等待,直到水果到了你买完才离开,这就是同步;而你知道水果卖完了,你跟店家说你要买什么然后店家到时候给你送货上门,你只是跟店家说了一句之后便离开去干其他事情了,这就是异步。

2.同步方法调用&异步方法调用

       前面介绍完同步和异步的概念之后,我们要把它代入到我们的代码世界里面,在代码世界里面,同步和异步一般体现在方法调用和http请求(ajax发送异步请求)上面,这里主要介绍方法调用。

2.1:同步方法调用

       所谓同步方法调用,就是一个方法A调用方法B之后,方法A必须要等待方法B执行完才能继续执行,要是方法B没执行完方法A就必须一直等待,见下图:

image.gif编辑

2.2:异步方法调用

        异步方法调用指的是当方法A调用方法B之后,方法A不需要等待方法B执行完毕再去干别的事,方法A只需要发起调用请求之后便继续执行自己的程序,方法B在另外一个线程里面执行,两者互不干扰,见下图:

image.gif编辑

二:问题引入

1.功能需求

        程序中我要实现的功能是作者发布文章之后线程A完成文章的保存工作,且在线程A里面要开启异步调用线程B实现文章的审核功能。部分代码如下

@Override
@Async //表明这是一个异步方法
public void AutoScanTextAndImage(Integer id) throws TencentCloudSDKException {
    log.info("开始进行文章审核...");
    WmNews wmNews = wmNewsService.getById(id);
    if(wmNews == null) {
        throw new RuntimeException("WmAutoScanServiceImpl-文章信息不存在");
    }
    if(wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())) {
        //1.提取文章文本及图片
        Map<String,Object> map = getTextAndImages(wmNews);
        //2.检测文本
        //2.1提取文本
        String content = ((StringBuilder) map.get("content")).toString();
        //2.2调用腾讯云进行文本检测
        Boolean THandleResult = handleTextScan(content, wmNews);
        if(!THandleResult) return;
        //3.检测图片
        //3.1提取图片
        List<String> imageUrl = (List<String>) map.get("images");
        //3.2调用腾讯云对图片进行检测
        Boolean IHandleresult = handleImageScan(imageUrl, wmNews);
        if(!IHandleresult) return;
        //4,审核成功
        //4.1保存文章
        log.info("检测到文章无违规内容");
        ResponseResult responseResult = saveAppArticle(wmNews);
        if(!responseResult.getCode().equals(200)) {
            throw new RuntimeException("WmAutoScanServiceImpl-文章审核,保存文章失败");
        }
        //4.2回填article_id
        wmNews.setArticleId((Long) responseResult.getData());
        wmNews.setStatus(WmNews.Status.PUBLISHED.getCode());
        wmNews.setReason("审核成功");
        wmNewsService.updateById(wmNews);
    }
}

image.gif

@Autowired
private WmAutoScanService wmAutoScanService;
/**
 * 提交文章
 * @param dto
 * @return
 */
@Override
public ResponseResult submitNews(WmNewsDto dto) throws TencentCloudSDKException {
    //1.参数校验
    if(dto == null || dto.getContent().length() == 0) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    //2.保存或修改文章
    //2.1属性拷贝
    WmNews wmNews = new WmNews();
    BeanUtils.copyProperties(dto,wmNews);
    //2.2设置封面图片
    if(dto.getImages() != null && dto.getImages().size() != 0) {
        String images = StringUtils.join(dto.getImages(), ",");
        wmNews.setImages(images);
    }
    //2.3封面类型为自动
    if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
        wmNews.setType(null);
    }
    saveOrUpdateWmNews(wmNews);
    //3.判断是否为草稿
    if(dto.getStatus().equals(WmNews.Status.NORMAL.getCode())) {
        //直接保存结束
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
    //4.不是草稿
    //4.1保存文章图片素材与文章关系
    //4.1.1提取图片素材列表
    List<String> imagesList = getImagesList(dto);
    //4.1.2保存
    saveRelatedImages(imagesList,wmNews.getId(),WemediaConstants.WM_CONTENT_REFERENCE);
    //4.2保存封面图片和文章关系
    saveRelatedCover(dto,imagesList,wmNews);
    //5.审核文章(异步调用)
    wmAutoScanService.AutoScanTextAndImage(wmNews.getId());
    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

image.gif

2.问题引出

代码看着没有什么问题,但是运行起来之后发现出现以下错误:

image.gif编辑

这是手动抛出的异常,抛出位置为:

WmNews wmNews = wmNewsService.getById(id);
if(wmNews == null) {
    throw new RuntimeException("WmAutoScanServiceImpl-文章信息不存在");
}

image.gif

可以看到查询出的文章对象为空。

3.问题剖析

       既然这里出现空对象,那么是不是因为没有进行数据插入呢?查看前面的日志信息,可以看到确实插入了一条数据:

image.gif编辑

接下来进行的操作时查询该条数据,查看MySQL日志信息:

image.gif编辑         可以看到查出的数据为空,那么为什么会出现这样的情况呢?要注意的是,这里开启了异步方法调用,这时候线程A是负责将数据保存的,而线程B是负责对文章进行审核的,而且线程A开启了事务支持,会不会是因为这两个方法都被认为是同一个事务所以事务还没结束线程B查询不到数据呢?这应该是不可能的,因为要想实现jdbc事务, 就必须是在同一个连接对象中操作,而我们可以看到,在进行查询操作时候是创建了一个新的连接的:

image.gif编辑

        那这时候只有一种可能,就是由于A开启了事务,这时候B线程是异步执行的,只要线程A还没有执行完毕,数据就不会被提交到数据库中,这时候线程B尝试去数据库中获取该数据显然是获取不到的。

三:问题解决

       有了以上假设,实践是检验真理的唯一标准,下面通过调试来检验,首先在线程A上加上一句日志打印信息,看看线程B执行时线程A是否执行完毕(关系到数据时候已经提交)。通过断点进行调试:

image.gif编辑

可以看到这时候是能够获取到数据的,那么假如我在查询之前让线程休眠0.5秒呢?

image.gif编辑

可以看到这时候成功获取到数据,说明就是跟数据提交时间有关。

相关文章
|
9月前
|
存储 JSON 关系型数据库
【干货满满】解密 API 数据解析:从 JSON 到数据库存储的完整流程
本文详解电商API开发中JSON数据解析与数据库存储的全流程,涵盖数据提取、清洗、转换及优化策略,结合Python实战代码与主流数据库方案,助开发者构建高效、可靠的数据处理管道。
|
12月前
|
存储 缓存 数据库
数据库数据删除策略:硬删除vs软删除的最佳实践指南
在项目开发中,“删除”操作常见但方式多样,主要分为硬删除与软删除。硬删除直接从数据库移除数据,操作简单、高效,但不可恢复;适用于临时或敏感数据。软删除通过标记字段保留数据,支持恢复和审计,但增加查询复杂度与数据量;适合需追踪历史或可恢复的场景。两者各有优劣,实际开发中常结合使用以满足不同需求。
1137 4
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
607 0
|
7月前
|
数据采集 关系型数据库 MySQL
python爬取数据存入数据库
Python爬虫结合Scrapy与SQLAlchemy,实现高效数据采集并存入MySQL/PostgreSQL/SQLite。通过ORM映射、连接池优化与批量提交,支持百万级数据高速写入,具备良好的可扩展性与稳定性。
|
安全 Java 数据安全/隐私保护
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 三大核心组件
本课程介绍如何在Spring Boot中集成Shiro框架,主要讲解Shiro的认证与授权功能。Shiro是一个简单易用的Java安全框架,用于认证、授权、加密和会话管理等。其核心组件包括Subject(认证主体)、SecurityManager(安全管理员)和Realm(域)。Subject负责身份认证,包含Principals(身份)和Credentials(凭证);SecurityManager是架构核心,协调内部组件运作;Realm则是连接Shiro与应用数据的桥梁,用于访问用户账户及权限信息。通过学习,您将掌握Shiro的基本原理及其在项目中的应用。
459 0
|
8月前
|
存储 数据管理 数据库
数据字典是什么?和数据库、数据仓库有什么关系?
在数据处理中,你是否常困惑于字段含义、指标计算或数据来源?数据字典正是解答这些问题的关键工具,它清晰定义数据的名称、类型、来源、计算方式等,服务于开发者、分析师和数据管理者。本文详解数据字典的定义、组成及其与数据库、数据仓库的关系,助你夯实数据基础。
数据字典是什么?和数据库、数据仓库有什么关系?
|
7月前
|
人工智能 Java 关系型数据库
使用数据连接池进行数据库操作
使用数据连接池进行数据库操作
193 11
|
7月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
1224 5
|
9月前
|
JSON Java 数据格式
Spring Boot返回Json数据及数据封装
在Spring Boot中,接口间及前后端的数据传输通常使用JSON格式。通过@RestController注解,可轻松实现Controller返回JSON数据。该注解是Spring Boot新增的组合注解,结合了@Controller和@ResponseBody的功能,默认将返回值转换为JSON格式。Spring Boot底层默认采用Jackson作为JSON解析框架,并通过spring-boot-starter-json依赖集成了相关库,包括jackson-databind、jackson-datatype-jdk8等常用模块,简化了开发者对依赖的手动管理。
795 3