草庐IT

java - Spring Scheduled Jobs 在不同的池中执行 3 次

coder 2024-03-21 原文

我有一个每小时运行一次的 Spring @Scheduled 作业,但我发现它实际上每小时运行 3 次。这是显示此问题的日志输出:

2013-05-06 12:00:27,656 [pool-2-thread-1] INFO  src.jobs.NotifyUsersWhenVideoAvailableJob - Emails sent from NotifyUsersWhenVideoAvailableJob: 1
2013-05-06 12:00:27,750 [pool-1-thread-1] INFO  src.jobs.NotifyUsersWhenVideoAvailableJob - Emails sent from NotifyUsersWhenVideoAvailableJob: 1
2013-05-06 12:00:27,796 [pool-4-thread-1] INFO  src.jobs.NotifyUsersWhenVideoAvailableJob - Emails sent from NotifyUsersWhenVideoAvailableJob: 1

这显然很烦人,因为每次运行此作业时都会发出同一封电子邮件的三份副本。

我正在使用 Spring 3.1

这是我的配置:

WEB.XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  version="2.5">
  <display-name>site2</display-name>
  <description>Roo generated site2 application</description>
  <context-param>
    <param-name>defaultHtmlEscape</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
  </context-param>
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter>
    <filter-name>HttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
  <filter>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>HttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>site2</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>WEB-INF/spring/webmvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>site2</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>120</session-timeout>
  </session-config>
  <error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/error</location>
  </error-page>
</web-app>

ApplicationContext.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
  xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:security="http://www.springframework.org/schema/security"
  xmlns:task="http://www.springframework.org/schema/task"
  xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
           http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

  <context:property-placeholder
    location="classpath*:META-INF/spring/*.properties" />

  <context:spring-configured />

  <context:component-scan base-package="src">
    <context:exclude-filter expression=".*_Roo_.*"
      type="regex" />
    <context:exclude-filter expression="org.springframework.stereotype.Controller"
      type="annotation" />
  </context:component-scan>

  <task:annotation-driven/>

  <bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="src.domain" />
    <property name="mappingDirectoryLocations">
      <list>
        <value>classpath*:**/src.domain</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="format_sql">true</prop>
        <prop key="hibernate.use_sql_comments">true</prop>
      </props>
    </property>
  </bean>

  <bean id="webexpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />
  <security:http pattern="/index.html" security="none" />
  <security:http pattern="/about.html" security="none" />
  <security:http pattern="/pricing.html" security="none" />
  <security:http pattern="/signup.html" security="none" />
  <security:http pattern="/forgotPassword.htm" security="none" />
  <security:http pattern="/**.json" security="none" />

  <security:http auto-config="true">
    <security:intercept-url pattern="/**.htm"
      access="ROLE_FREE" />
      <security:intercept-url pattern="/test/**.htm"
      access="ROLE_FREE" />
      <security:intercept-url pattern="/admin.htm"
      access="ROLE_SUPERUSER" />
      <security:intercept-url pattern="/exerciseFiles/**.zip"
      access="ROLE_RECOMMENDED" />
    <security:form-login login-page="/login.html"
      authentication-failure-handler-ref="failedLoginService"
      authentication-success-handler-ref="successfulLoginService" />
      <security:logout logout-success-url="/index.html"/>
  </security:http>

  <security:authentication-manager>
    <security:authentication-provider
      user-service-ref="userDetailsService" />
  </security:authentication-manager>
</beans>

webmvc-config.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd                 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd                 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <context:component-scan base-package="src" use-default-filters="false">
        <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    </context:component-scan>

    <mvc:annotation-driven conversion-service="applicationConversionService"/>

    <mvc:resources location="/, classpath:/META-INF/web-resources/" mapping="/resources/**"/>

    <mvc:default-servlet-handler/>

    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
    </mvc:interceptors>

    <mvc:view-controller path="/" view-name="index"/>
    <mvc:view-controller path="/uncaughtException"/>
    <mvc:view-controller path="/resourceNotFound"/>
    <mvc:view-controller path="/dataAccessFailure"/>

    <bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource" id="messageSource" p:basenames="WEB-INF/i18n/messages,WEB-INF/i18n/application" p:fallbackToSystemLocale="false"/>

    <bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver" id="localeResolver" p:cookieName="locale"/>

    <bean class="org.springframework.ui.context.support.ResourceBundleThemeSource" id="themeSource"/>

    <bean class="org.springframework.web.servlet.theme.CookieThemeResolver" id="themeResolver" p:cookieName="theme" p:defaultThemeName="standard"/>

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" p:defaultErrorView="uncaughtException">
        <property name="exceptionMappings">
            <props>
                <prop key=".DataAccessException">dataAccessFailure</prop>
                <prop key=".NoSuchRequestHandlingMethodException">resourceNotFound</prop>
                <prop key=".TypeMismatchException">resourceNotFound</prop>
                <prop key=".MissingServletRequestParameterException">resourceNotFound</prop>
            </props>
        </property>
    </bean>

    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>

    <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="viewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean class="src.web.ApplicationConversionServiceFactoryBean" id="applicationConversionService"/>

</beans>

这是执行作业的类文件:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import src.jobs.NotifyUsersWhenVideoAvailableJob;
import src.jobs.PayAsYouGoReminderJob;
import src.jobs.RemindUsersToActivateJob;

@Service
public class ScheduledJobsService
{
  @Autowired
  @Qualifier("videoJob")
  private NotifyUsersWhenVideoAvailableJob videoJob;
  @Autowired
  @Qualifier("activateJob")
  private RemindUsersToActivateJob activateJob; 
  @Autowired
  private PayAsYouGoReminderJob payAsYouGoReminderJob;

  //This cron just should be set to 1 second past the hour
  // as the videoJob has dates set to be ON the hour exactly
  // example of good setting: @Scheduled(cron="1 0 * * * *")
  @Scheduled(cron="1 0 * * * *")
  public void doHourlyJobs() 
  {
    videoJob.run();
  }

  @Scheduled(cron="0 0 12 * * *")
  public void doDailyJobs() 
  {
    try
    {
        activateJob.run();
    }
    catch (Exception e)
    {
      EmailService.sendError(e, null);
    }

    try
    {
      payAsYouGoReminderJob.run();
    }
    catch (Exception e)
    {
      EmailService.sendError(e, null);
    }
  }
}

* 编辑 *

在进行了更多探索之后,我缩小了(更多)可能发生问题的范围。我无法在我的 DEV 环境中重现此问题,因此我的 PROD 盒上必须有某种配置。

我的 PROD box 在 webapps 文件夹中有 5 个不同的 Web 应用程序:

  • Tomcat 6.0
    • 网络应用程序
      • 站点 1
      • 站点 2
      • 站点 3
      • 网站4
      • 站点 5

我对我的 server.xml 文件做了一些更改,现在它似乎只执行作业两次而不是三次。这是新配置:

server.xml

<Server port="8005" shutdown="SHUTDOWN">

  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JasperListener" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

  <Service name="Catalina">
    <Connector port="80" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
             resourceName="UserDatabase"/>
      <Host name="site1.net"  appBase="webapps"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
        <Alias>www.site1.net</Alias>
      </Host>

        <Host name="site2.net"  appBase="webapps"
            unpackWARs="true" autoDeploy="false" deployOnStartup="false"
            xmlValidation="false" xmlNamespaceAware="false">
            <Alias>www.site2.net</Alias>
            <Context path="" docBase="./site2"/>
        </Host>

    </Engine>
  </Service>
</Server>

最佳答案

如果您在 server.xml 中定义了两个 Web 应用程序,并且您不小心(见下文),那么您将拥有在 servlet 容器中运行的所有内容的两个完全独立的实例。

context.xml 文件为您提供了一种区分共享内容和应用程序特定内容的方法。可共享事物的最常见示例是数据源或连接池。不过,这并不能真正帮助您使用 Spring bean。

幸运的是,Spring 为您提供了一种定义共享父应用程序上下文的方法,以便在同一容器中运行的 WAR 之间共享 bean。这是pretty well documented在线。

不幸的是,我不认为这适用于 <Host>元素。虚拟主机允许您隔离同一台物理机器上的资源,特别是让资源可以独立管理。因此,如果两个应用程序都通过 <task:annotation-driven/> 定义任务,那么您最终会得到两个单独的实例。

因此,总而言之,如果您希望每个 servlet 容器的每个任务都有一个实例,那么您需要一个共享的应用程序上下文。你能:

  • 将应用程序合并为一个 <Host>
  • 使用<Alias>带有单个 <Host> 的标签支持两个独立的 URL
  • 使用上述技术确保存在共享的应用程序上下文

关于java - Spring Scheduled Jobs 在不同的池中执行 3 次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16405848/

有关java - Spring Scheduled Jobs 在不同的池中执行 3 次的更多相关文章

  1. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  2. ruby - Chef 执行非顺序配方 - 2

    我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

  3. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  4. ruby - 为什么 Ruby 的 each 迭代器先执行? - 2

    我在用Ruby执行简单任务时遇到了一件奇怪的事情。我只想用每个方法迭代字母表,但迭代在执行中先进行:alfawit=("a".."z")puts"That'sanalphabet:\n\n#{alfawit.each{|litera|putslitera}}"这段代码的结果是:(缩写)abc⋮xyzThat'sanalphabet:a..z知道为什么它会这样工作或者我做错了什么吗?提前致谢。 最佳答案 因为您的each调用被插入到在固定字符串之前执行的字符串文字中。此外,each返回一个Enumerable,实际上您甚至打印它。试试

  5. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  6. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  7. ruby - 检查是否通过 require 执行或导入了 Ruby 程序 - 2

    如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby​​文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否

  8. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  9. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  10. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

随机推荐