优化持续部署(通过责任链取消)
Optimize continuous deployment (cancel throu chain-of-responsibility)
我想改进持续交付。我正在使用 Tomcat 8 和 maven。
我使用 mvn tomcat:redeploy
来部署 webapp。
在tomcat documentation中我找到了这部分:
Deploy A New Application Archive (WAR) Remotely
If installation and startup is successful, you will receive(...)
Otherwise, the response will start with FAIL and include an error message.
作为FAIL
的可能原因,有一点值得关注:
An exception was encountered trying to start the new web application.
所以我想在启动时抛出异常,所以在 web.xml
我写:
<load-on-startup>1</load-on-startup>
到 org.springframework.web.servlet.DispatcherServlet
-Servlet。然后我写一个单例:
@Service
public class AvoidStartupOnMissingDatabase implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
throw new RuntimeException("Do not deploy this app!");
}
}
这会导致此 Stacktrace:
java.lang.RuntimeException: Do not deploy this app!
at xxx.AvoidStartupOnMissingDatabase.afterSingletonsInstantiated(AvoidStartupOnMissingDatabase.java:11)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:775)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:664)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:630)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:678)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:549)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:490)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1144)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1091)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:983)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4962)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5274)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3823)
at org.apache.catalina.startup.HostConfig.reload(HostConfig.java:1410)
at org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1320)
at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1648)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1525)
at org.apache.catalina.manager.ManagerServlet.deploy(ManagerServlet.java:773)
at org.apache.catalina.manager.ManagerServlet.doPut(ManagerServlet.java:443)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:664)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:613)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:486)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
问题
应用程序不幸部署!
问题
我初始化失败。我说的是启动时加载。 Tomcat 表示不启动则失败。 Tomcat 说(重新)部署是否可以开始。
因此异常必须使部署失败。对吧?
编辑
我测试了 tomcat-8.5.30 和 tomcat-8.5.37,不幸的是都成功了。
Tomcat 有两个部署 war 文件的选项:
- 复制 war 到 webapps
- http tomcat 端点 /manager/text/deploy
更多详细信息和配置在此 answer
此外,tomcat:redeploy 不用于回滚目的,仅用于部署现有的 war,无需重新打包。
我确定 mvn tomcat:deploy
插件使用 /manager/text/deploy tomcat 端点来部署 war 文件。
假设您正在使用一些持续集成服务器(jenkins、travis 等)或只是一个简单的 shell 脚本来调用前面的步骤,我建议您使用这些方法来执行回滚:
回滚参数
在您的流程中添加一个新的输入参数,称为:rollback_tag 或 previous_version。
使用 Maven 回滚
Maven 是编译和生成 war 或 jar 文件的最佳选择。但出于部署目的,我认为这不是最佳选择。我不想在我的 pom.xml 中共享凭据或配置(有或没有变量):
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.1</version>
<configuration>
<url>http://localhost:8080/manager/text</url>
<server>TomcatServer</server>
<path>/javaCodeGeeks</path>
</configuration>
</plugin>
无论如何,如果你想使用maven,你需要:
- 搜索maven插件源码
- 对其进行扩展或添加新功能,例如:
- 从 tomcat 部署中捕获错误
- 使用一些标签版本下载源代码
- 出错时重新部署
再次回滚下载
步骤可以是:
- (1) 使用svn下载源码
- (2) 执行mvn clean package
- (3) 执行mvn tomcat:deploy
- 此命令必须 return 退出代码
- (3.1) 在 exit_code == 0 的情况下,表示您的部署成功。
- (3.2) 在 exit_code != 0 的情况下,表示部署错误。继续 (4) 步骤
- (4) 使用 svn 下载源代码,但在这种情况下,您必须使用 rollback tag 参数并重复步骤 (2) 和 (3)
有一些工件存储库
有了这个工具,我们需要用svn下载源码,一次就可以。编译并成功部署后,您必须 上传 war 或特定版本的 jar 文件,例如 1.0.0 流程中的下一步只需要下载 war/jar文件。
如果 1.0.1 部署出错,您只需要从您的工件存储库下载以前的稳定版本 1.0.0。
步骤可以是:
- (1)检测开发者的svn commit动作,使用你的持续集成服务器或你的编译测试上传war文件到工件仓库shell 脚本。
- (2) 从工件存储库下载 war 文件
- (3)执行 curl 命令上传和部署 war 文件。更多细节和配置在这里:
- How to deploy war to tomcat using jenkins pipeline?
- Tomcat manager remote deploy
- (4) 如果出现错误,请从工件存储库下载 war 文件,但在这种情况下,您必须使用 previous_version 参数和重复步骤 (3)
部署成功。当 Spring 尝试加载 AvoidStartupOnMissingDatabase
bean 时,您看到的堆栈跟踪是由应用程序运行时错误引起的。此时,应用程序已经部署并且 运行.
如果你想让 Maven redeploy
目标失败,你需要在部署期间导致错误,而不是在应用程序 运行 时。例如,您可以通过在 Maven Tomcat 插件配置中使用 来完成此操作。
我创建了一个 bug in Tomcat。开发人员指导我找到解决方案
类似于 Alexandru Cojocaru。
我从 Servlet 转移到 SerlvetContextListener,我成功收到了部署的 "FAIL"。
在此无效部署之后,很遗憾,旧应用程序未 重新启动。但是对 svn 的提交被成功取消并且没有添加到 svn-change-log!
我想改进持续交付。我正在使用 Tomcat 8 和 maven。
我使用 mvn tomcat:redeploy
来部署 webapp。
在tomcat documentation中我找到了这部分:
Deploy A New Application Archive (WAR) Remotely
If installation and startup is successful, you will receive(...)
Otherwise, the response will start with FAIL and include an error message.
作为FAIL
的可能原因,有一点值得关注:
An exception was encountered trying to start the new web application.
所以我想在启动时抛出异常,所以在 web.xml
我写:
<load-on-startup>1</load-on-startup>
到 org.springframework.web.servlet.DispatcherServlet
-Servlet。然后我写一个单例:
@Service
public class AvoidStartupOnMissingDatabase implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
throw new RuntimeException("Do not deploy this app!");
}
}
这会导致此 Stacktrace:
java.lang.RuntimeException: Do not deploy this app!
at xxx.AvoidStartupOnMissingDatabase.afterSingletonsInstantiated(AvoidStartupOnMissingDatabase.java:11)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:775)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:664)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:630)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:678)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:549)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:490)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1144)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1091)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:983)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4962)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5274)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3823)
at org.apache.catalina.startup.HostConfig.reload(HostConfig.java:1410)
at org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1320)
at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1648)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:300)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1525)
at org.apache.catalina.manager.ManagerServlet.deploy(ManagerServlet.java:773)
at org.apache.catalina.manager.ManagerServlet.doPut(ManagerServlet.java:443)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:664)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:613)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:486)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
问题
应用程序不幸部署!
问题
我初始化失败。我说的是启动时加载。 Tomcat 表示不启动则失败。 Tomcat 说(重新)部署是否可以开始。
因此异常必须使部署失败。对吧?
编辑
我测试了 tomcat-8.5.30 和 tomcat-8.5.37,不幸的是都成功了。
Tomcat 有两个部署 war 文件的选项:
- 复制 war 到 webapps
- http tomcat 端点 /manager/text/deploy
更多详细信息和配置在此 answer
此外,tomcat:redeploy 不用于回滚目的,仅用于部署现有的 war,无需重新打包。
我确定 mvn tomcat:deploy
插件使用 /manager/text/deploy tomcat 端点来部署 war 文件。
假设您正在使用一些持续集成服务器(jenkins、travis 等)或只是一个简单的 shell 脚本来调用前面的步骤,我建议您使用这些方法来执行回滚:
回滚参数
在您的流程中添加一个新的输入参数,称为:rollback_tag 或 previous_version。
使用 Maven 回滚
Maven 是编译和生成 war 或 jar 文件的最佳选择。但出于部署目的,我认为这不是最佳选择。我不想在我的 pom.xml 中共享凭据或配置(有或没有变量):
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.1</version>
<configuration>
<url>http://localhost:8080/manager/text</url>
<server>TomcatServer</server>
<path>/javaCodeGeeks</path>
</configuration>
</plugin>
无论如何,如果你想使用maven,你需要:
- 搜索maven插件源码
- 对其进行扩展或添加新功能,例如:
- 从 tomcat 部署中捕获错误
- 使用一些标签版本下载源代码
- 出错时重新部署
再次回滚下载
步骤可以是:
- (1) 使用svn下载源码
- (2) 执行mvn clean package
- (3) 执行mvn tomcat:deploy
- 此命令必须 return 退出代码
- (3.1) 在 exit_code == 0 的情况下,表示您的部署成功。
- (3.2) 在 exit_code != 0 的情况下,表示部署错误。继续 (4) 步骤
- (4) 使用 svn 下载源代码,但在这种情况下,您必须使用 rollback tag 参数并重复步骤 (2) 和 (3)
有一些工件存储库
有了这个工具,我们需要用svn下载源码,一次就可以。编译并成功部署后,您必须 上传 war 或特定版本的 jar 文件,例如 1.0.0 流程中的下一步只需要下载 war/jar文件。
如果 1.0.1 部署出错,您只需要从您的工件存储库下载以前的稳定版本 1.0.0。 步骤可以是:
- (1)检测开发者的svn commit动作,使用你的持续集成服务器或你的编译测试上传war文件到工件仓库shell 脚本。
- (2) 从工件存储库下载 war 文件
- (3)执行 curl 命令上传和部署 war 文件。更多细节和配置在这里:
- How to deploy war to tomcat using jenkins pipeline?
- Tomcat manager remote deploy
- (4) 如果出现错误,请从工件存储库下载 war 文件,但在这种情况下,您必须使用 previous_version 参数和重复步骤 (3)
部署成功。当 Spring 尝试加载 AvoidStartupOnMissingDatabase
bean 时,您看到的堆栈跟踪是由应用程序运行时错误引起的。此时,应用程序已经部署并且 运行.
如果你想让 Maven redeploy
目标失败,你需要在部署期间导致错误,而不是在应用程序 运行 时。例如,您可以通过在 Maven Tomcat 插件配置中使用
我创建了一个 bug in Tomcat。开发人员指导我找到解决方案 类似于 Alexandru Cojocaru。
我从 Servlet 转移到 SerlvetContextListener,我成功收到了部署的 "FAIL"。
在此无效部署之后,很遗憾,旧应用程序未 重新启动。但是对 svn 的提交被成功取消并且没有添加到 svn-change-log!