天天看點

部署一個不依賴tomcat容器的應用

 一個task項目,應用裡邊都是一些定時任務。我和新入職的高開商定程式部署不依賴于tomcat。

計劃趕不上變化,任務開發完成還沒等上線呢,哥們要離職了。工作交接時大概說了一下上線怎麼部署。

結果呢,當我在linux測試伺服器上部署時,可費了一些周折。之前都是把應用部署到tomcat下面的。那位高開說過,不依賴tomcat容器的部署方式已經不是新概念了。漫長的解決過程中,有同僚建議我放棄,沿用tomcat吧。我覺得有必要堅持下來,最終也堅持下來了。

一.先介紹一下項目

工程如下圖。assembly/bin下有一個emax-paycenter-task.sh檔案,主要是通過nohup指令來運作LauncherMain。高開說了,部署時将該shell檔案放到應用的根目錄下,通過執行它來啟動程式。

部署一個不依賴tomcat容器的應用

emax-paycenter-task.sh檔案裡是如下指令,start用的是nohup指令:

部署一個不依賴tomcat容器的應用

LauncherMain.java裡是一個main方法,用來初始化環境:

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.support.XmlWebApplicationContext;

import java.net.URL;
import java.util.concurrent.Semaphore;

/**
 * Description 啟動方法
 * Date 2018/2/8 上午10:01
 */
public class LauncherMain {

    private static Logger logger= LoggerFactory.getLogger(LauncherMain.class);
    public static void main(String[] args) throws Exception {

        logger.info("init @Prop");
        Semaphore sp = new Semaphore(0);
        XmlWebApplicationContext xmlWeb = new XmlWebApplicationContext();
        xmlWeb.setConfigLocation("classpath*:*spring/spring-applicationContext.xml");
        String logbackCfg = "logback.xml";
        URL logURL = new ClassPathResource(logbackCfg).getURL();

        ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
        LoggerContext loggerContext = (LoggerContext) loggerFactory;

        JoranConfigurator configurator = new JoranConfigurator();
        configurator.setContext(loggerContext);
        configurator.doConfigure(logURL);

        xmlWeb.refresh();
        xmlWeb.start();
        sp.acquire();
    }

}      

 項目中涉及到的定時任務采用Spring的@Scheduled來實作:

@Component
public class AgentPayTask {

    @Autowired
    private LimitConfigDataHolder limitConfigDataHolder;

    @Autowired
    private AgentPayTaskService agentPayTaskService;

    private static Logger logger = LoggerFactory.getLogger(AgentPayTask.class);

    @Scheduled(cron ="0/5 * * * * ?")
    public void process() {
        System.out.println("AgentPayTask111");
        logger.info("AgentPayTask");
        Map<String, RateLimiterConfig> LimiterMap = limitConfigDataHolder.getAgentPayLimiter();
        agentPayTaskService.distributeTask(LimiterMap);
    }

}      

二.linux部署

我在本地直接運作LauncherMain#main方法,程式是沒問題的,裡面的定時任務都可以正常跑。

将maven打包後的emax-paycenter-task-1.0.0-SNAPSHOT-assembly.tar.gz解壓部署到測試伺服器上。最終執行指令

./emax-paycenter-task.sh start &      

檢視生成的nohup.out檔案,發現總是找不到class com.emax.paycenter.LauncherMain。

後來咨詢運維,發現是沒有給目錄配置設定權限所緻。

技能娴熟的運維又幫忙把emax-paycenter-task.sh裡的指令修正了一下。最終如下:

部署一個不依賴tomcat容器的應用

再次按照上面指令啟動程式,檢視日志檔案發現隻有如下一條日志:

2018-03-21 15:11:45.381 [main] INFO  [com.emax.paycenter.LauncherMain] - init @Prop      

多次測試發現,程式執行到sp.acquire();這條語句時,就不動了;我在這條語句後加列印日志結果也沒列印出來;定時任務也沒執行。

了解了一下Semaphore信号量,把permits改成0或1或其他數字,都不行。

。。。。。一個小時。。。。

。。。。。兩個小時。。。。

找同僚幫助,無果。

。。。。。若幹小時。。。。

靈感咋現!我将懷疑的焦點放到了spring context的設定這裡,是不是壓根都沒有設定成功呢?

xmlWeb.setConfigLocation("classpath*:*spring/spring-applicationContext.xml");      

莫非是各jar包裡有同名的配置檔案? 将路徑參數"classpath*:*spring/spring-applicationContext.xml"改成"classpath:spring/spring-applicationContext.xml"後,檢視nohup.out發現出現了新的問題:

[root@localhost emax-paycenter-task]# tail -f nohup.out 
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:531)
  at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:295)
  ... 11 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.emax.paycenter.api.service.IPayCenterFacade] 
found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. 
Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:997)
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:867)
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:779)
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:503)
  ... 13 more      

 同時,logback日志檔案裡也發現類似問題:spring context初始化時出現了異常。項目中一個類“AgentPayTaskQueryServiceImpl”通過@Autowired依賴注入了另一個type為“IPayCenterFacade”的bean,而在spring context裡找不到這個類型為“IPayCenterFacade”的bean(NoSuchBeanDefinitionException),進而導緻spring context建立bean “agentPayTaskQueryServiceImpl”失敗(BeanCreationException異常)。

部署一個不依賴tomcat容器的應用
部署一個不依賴tomcat容器的應用
[root@localhost emax-paycenter-task]# tail -n200 -f logback-task/task_info.log 
2018-03-21 16:16:37.174 [main] INFO  [com.emax.paycenter.LauncherMain] - init @Prop
2018-03-21 16:16:39.397 [main] WARN  [org.springframework.web.context.support.XmlWebApplicationContext] - Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'agentPayTaskQueryServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.emax.paycenter.api.service.IPayCenterFacade com.emax.paycenter.service.impl.AgentPayTaskQueryServiceImpl.payCenterFacade; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.emax.paycenter.api.service.IPayCenterFacade] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:298) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1148) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:636) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:934) ~[spring-context-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479) ~[spring-context-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at com.emax.paycenter.LauncherMain.main(LauncherMain.java:38) [emax-paycenter-task-1.0.0-SNAPSHOT.jar:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.emax.paycenter.api.service.IPayCenterFacade com.emax.paycenter.service.impl.AgentPayTaskQueryServiceImpl.payCenterFacade; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.emax.paycenter.api.service.IPayCenterFacade] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:531) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:295) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  ... 11 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.emax.paycenter.api.service.IPayCenterFacade] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:997) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:867) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:779) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:503) ~[spring-beans-3.2.13.RELEASE.jar:3.2.13.RELEASE]
  ... 13 common frames omitted      

View Code

IPayCenterFacade定義在dubbo配置檔案裡。是以,将spring applicationContext裡對dubbo配置檔案的import由

<import resource="classpath*:*spring/dubbo-applicationContext.xml"/>      

改成

<import resource="classpath:spring/dubbo-applicationContext.xml"/>      

就可以了。