Spring 定时器在 Tomcat 上执行两次的问题

本贴最后更新于 2093 天前,其中的信息可能已经斗转星移

前言

  springboot 项目中用到了定时器,用的是 spring 的 @Scheduled 注解,每天早上九点执行,发送公众号消息提醒。今天接收到了提醒,发现提醒发送了两次,然后查看日志文件发现定时任务到点执行了两次。百度了一下发现有几种说法,总结一下。

正文

  1. 第一种说法,因为没有移除 springboot 项目的内置 tomcat,所以在使用外部 tomcat 运行时 springboot 项目启动了两次,移除内置的 tomcat 即可。这种方法对我没有用,因为我本来就已经移除了内置的 tomcat。
    <!--移除内置tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
  2. 第二种说法,tomcat 的 server.xml 配置错误,项目启动时会去找 appBase 目录下的项目加载一次,然后再去找 docBase 目录下的项目再加载一次。所以同一个项目被加载了两次,定时器也被加载了两次。将文件中的 <Host>修改为如下配置,appBase 置空,docBase 修改为项目的绝对路径即可。这种方法对我来说也没有用,因为我服务器上的 tomcat 本来就是这么配置的。
    <Host name="你的域名" appBase="" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="项目的绝对路径,如/opt/tomcat/webapps/abc" reloadable="true" privileged="true" debug="0"/> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" /> </Host>
      还有其他修改 server.xml 的方法,具体请参考 https://www.cnblogs.com/Syney/p/7678714.html 。这篇博客里的其他方法我没有尝试,因为我在一个 tomcat 上部署了多个项目,其他方法并不适用,所以我也没有尝试。关于 appBase 和 docBase 的区别可以参考 https://blog.csdn.net/sqiucheng/article/details/8510058
     
  3. 第三种说法,在 java 代码中手动阻止项目第一次启动加载。
      项目被部署在服务器 tomcat 的 webapps 目录里,导致项目被 tomcat 初始化了 2 次,部署成功了 2 次,一次访问路径是项目名,一次访问路径是/。实际中那个访问路径带项目名的是不需要的,所以直接阻止它启动,这样项目就只成功启动了一次,问题就可以解决。
      阻止具体方法,创建一个 bean,实现 ServletContextAware 接口,重写 setServletContext()方法。当这个 bean 创建时,会自动调用 setServletContext(),在方法里我们判断下当前的项目访问路径是否为空或者是否为"/",如果是,正常通过。如果不是,说明当前 tomcat 正在初始化访问路径为项目名的项目,所以我们要阻止它,这时候抛出个运行异常,当前 bean 就会创建失败,这时候这个项目就会启动失败了。
     
    这个接口在哪儿实现都可以,我直接在定时器的配置类中实现了这个接口。
    public class AsyncConfig implements ServletContextAware
    @Override public void setServletContext(ServletContext servletContext) { String contextPath = servletContext.getContextPath(); if ("".equals(contextPath) || "/".equals(contextPath)) { // 这句日志打印可以不要 logger.info("启动成功,本次启动映射路径为:" + contextPath); } else { throw new RuntimeException("为阻止tomcat初始化2次,导致spring定时器启动2次,阻止本次启动,本次启动映射路径为:" + contextPath); } }
    我用的是第三种方法,这种方法在项目第一次加载时会抛出大量重复的运行时异常,不止一次。第二次加载恢复正常,启动成功,然后测试一下,定时任务只会执行一次。
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    949 引用 • 1460 回帖
  • 定时器
    1 引用 • 1 回帖
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • wmy0436

    大佬又遇见你了

推荐标签 标签

  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    52 引用 • 190 回帖
  • Excel
    31 引用 • 28 回帖
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 486 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    212 引用 • 291 回帖 • 372 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 111 关注
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    176 引用 • 3860 回帖
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 636 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 448 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    6 引用 • 143 回帖
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 52 关注
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖 • 9 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    37 引用 • 157 回帖 • 2 关注
  • Office

    Office 现已更名为 Microsoft 365. Microsoft 365 将高级 Office 应用(如 Word、Excel 和 PowerPoint)与 1 TB 的 OneDrive 云存储空间、高级安全性等结合在一起,可帮助你在任何设备上完成操作。

    5 引用 • 34 回帖
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 2 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    182 引用 • 408 回帖 • 491 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    345 引用 • 749 回帖
  • 叶归
    9 引用 • 41 回帖 • 19 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    29 引用 • 202 回帖 • 29 关注
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 614 关注
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖
  • Anytype
    3 引用 • 31 回帖 • 17 关注
  • FFmpeg

    FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。

    23 引用 • 32 回帖
  • 反馈

    Communication channel for makers and users.

    121 引用 • 907 回帖 • 276 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 464 关注
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    210 引用 • 2040 回帖
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 648 关注