前言:

1.前面基于Springboot的单体项目介绍已经完结了,至于项目中的其他功能实现我这里就不打算介绍了,因为涉及的知识点不难,而且都是简单的CRUD操作,假如有兴趣的话可以私信我我再看看要不要写几篇文章做个介绍。

2.完成上一阶段的学习,我就投入到了微服务的学习当中,所用教程为B站上面黑马的微服务教程。由于我的记性不是很好,所以对于新事物的学习我比较喜欢做笔记以加强理解,在这里我会将笔记的重点内容做个总结发布到“微服务学习”笔记栏目中。我是赵四,一名有追求的程序员,希望大家能多多支持,能给我点个关注就更好了。

一: 同步&异步

1.同步与异步的概念

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

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

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

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

2.1:同步方法调用

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

2.2:异步方法调用

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

二:问题引入

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);
    }
}
@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);
}

2.问题引出

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

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

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

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

3.问题剖析

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

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

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

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

三:问题解决

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

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

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

浅析SpringBoot微服务中异步调用数据提交数据库的问题的更多相关文章

  1. 在swift脚本中等待异步调用

    我正在编写一个swift脚本,在终端中运行,向后台线程调度几个操作.在完成所有调度之后,没有任何额外的努力,代码到达文件的末尾并退出,也杀死我的后台操作.在我的后台操作完成之前,保持swift脚本活着的最佳方法是什么?

  2. NT IIS下用ODBC连接数据库

    $connection=intodbc_connect建立数据库连接,$query_string="查询记录的条件"如:$query_string="select*fromtable"用$cur=intodbc_exec检索数据库,将记录集放入$cur变量中。再用while{$var1=odbc_result;$var2=odbc_result;...}读取odbc_exec()返回的数据集$cur。最后是odbc_close关闭数据库的连接。odbc_result()函数是取当前记录的指定字段值。

  3. Thinkphp5框架实现获取数据库数据到视图的方法

    这篇文章主要介绍了Thinkphp5框架实现获取数据库数据到视图的方法,涉及thinkPHP5数据库配置、读取、模型操作及视图调用相关操作技巧,需要的朋友可以参考下

  4. 如何在PHP环境中使用ProtoBuf数据格式

    这篇文章主要介绍了如何在PHP环境中使用ProtoBuf数据格式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

  5. Python爬取奶茶店数据分析哪家最好喝以及性价比

    这篇文章主要介绍了用Python告诉你奶茶哪家最好喝性价比最高,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

  6. Android本地存储方法浅析介绍

    这篇文章主要介绍了Android本地存储案例,方法简单可以实现存储并达到节省内存的效果,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

  7. 详解Python如何实现Excel数据读取和写入

    这篇文章主要为大家详细介绍了python如何实现对EXCEL数据进行读取和写入,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  8. Python自动化办公之Excel数据的写入

    这篇文章主要为大家详细介绍一下Python中excel的写入模块- xlsxwriter,并利用该模块实现Excel数据的写入,感兴趣的小伙伴可以了解一下

  9. SpringBoot本地磁盘映射问题

    这篇文章主要介绍了SpringBoot本地磁盘映射问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  10. java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    这篇文章主要介绍了java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

随机推荐

  1. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  4. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  5. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  6. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  7. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  8. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  9. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Spring JdbcTemplate执行数据库操作详解

    JdbcTemplate是Spring框架自带的对JDBC操作的封装,目的是提供统一的模板方法使对数据库的操作更加方便、友好,效率也不错,这篇文章主要介绍了Spring JdbcTemplate执行数据库操作,需要的朋友可以参考下

返回
顶部