关于maven:无法在部署到Heroku的Java Tomcat服务器中执行工作进程

Worker process cannot be executed in a Java Tomcat server deployed to Heroku

我有1个Web和1个工作进程使用由2个通道/队列组成的CloudAMQP库相互通信(一个通道用于从web发送一条简单消息,另一个通道用于接收来自一个通道的消息) worker)。由于我正在以WAR文件格式部署Web应用程序,因此我必须通过创建Maven的本地存储库并包括将文件放置在WEB-INF/lib文件夹中的依赖项将worker类放入jar中,以便执行

使用从maven-war-plugin工件生成的worker sh脚本来处理工作进程。

worker sh脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
BASEDIR=`dirname $0`/..
BASEDIR=`(cd"$BASEDIR"; pwd)`



# OS specific support.  $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
case"`uname`" in
  CYGWIN*) cygwin=true ;;
  Darwin*) darwin=true
           if [ -z"$JAVA_VERSION" ] ; then
             JAVA_VERSION="CurrentJDK"
           else
             echo"Using Java version: $JAVA_VERSION"
           fi
           if [ -z"$JAVA_HOME" ] ; then
             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
           fi
           ;;
esac

if [ -z"$JAVA_HOME" ] ; then
  if [ -r /etc/gentoo-release ] ; then
    JAVA_HOME=`java-config --jre-home`
  fi
fi

# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
  [ -n"$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix"$JAVA_HOME"`
  [ -n"$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix"$CLASSPATH"`
fi

# If a specific java binary isn't specified search for the standard 'java' binary
if [ -z"$JAVACMD" ] ; then
  if [ -n"$JAVA_HOME"  ] ; then
    if [ -x"$JAVA_HOME/jre/sh/java" ] ; then
      # IBM'
s JDK on AIX uses strange locations for the executables
      JAVACMD="$JAVA_HOME/jre/sh/java"
    else
      JAVACMD="$JAVA_HOME/bin/java"
    fi
  else
    JAVACMD=`which java`
  fi
fi

if [ ! -x"$JAVACMD" ] ; then
  echo"Error: JAVA_HOME is not defined correctly."
  echo"  We cannot execute $JAVACMD"
  exit 1
fi

if [ -z"$REPO" ]
then
  REPO="$BASEDIR"/repo
fi

CLASSPATH=$CLASSPATH_PREFIX:"$BASEDIR"/etc:"$REPO"/com/rabbitmq/amqp-client/3.3.4/amqp-client-3.3.4.jar:"$REPO"/javax/servlet/jstl/1.2/jstl-1.2.jar:"$REPO"/com/example/worker/1.0/worker-1.0.jar:"$REPO"/cloudamqp/example/amqpexample/1.0-SNAPSHOT/amqpexample-1.0-SNAPSHOT.war
EXTRA_JVM_ARGUMENTS=""

# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
  [ -n"$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows"$CLASSPATH"`
  [ -n"$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows"$JAVA_HOME"`
  [ -n"$HOME" ] && HOME=`cygpath --path --windows"$HOME"`
  [ -n"$BASEDIR" ] && BASEDIR=`cygpath --path --windows"$BASEDIR"`
  [ -n"$REPO" ] && REPO=`cygpath --path --windows"$REPO"`
fi

exec"$JAVACMD" $JAVA_OPTS \\
  $EXTRA_JVM_ARGUMENTS \\
  -classpath"$CLASSPATH" \\
  -Dapp.name="worker" \\
  -Dapp.pid="$$" \\
  -Dapp.repo="$REPO" \\
  -Dbasedir="$BASEDIR" \\
  WorkerProcess \\
 "$@"

然后将worker sh脚本引用到Heroku Procfile中:

1
2
web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war
worker: sh target/bin/worker

我使用heroku local -f Procfile.windows在本地测试了该应用程序,并且一切正常。但是,当我部署到Heroku时(我也尝试同时使用Heroku CLI部署和git),只有web Procfile可以正确执行。当我检查日志时,工作进程会返回error 127,说它无法打开target\\bin\\worker脚本。我试图用Google搜索Heroku的error 127含义,但是到目前为止没有找到匹配的结果,而且我不知道导致远程部署的应用无法打开target\\bin\\worker的哪些原因,即使它在我的本地部署中也可以正常工作。任何帮助将不胜感激。
以下是分别来自Heroku,servlet类,worker类和POM的完整错误日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2016-10-31T14:27:32.629768+00:00 heroku[slug-compiler]: Slug compilation finished
2016-10-31T14:27:34.807925+00:00 heroku[web.1]: State changed from down to starting
2016-10-31T14:27:34.941663+00:00 heroku[worker.1]: State changed from down to starting
2016-10-31T14:27:37.334213+00:00 heroku[worker.1]: Starting process with command `sh target/bin/worker`
2016-10-31T14:27:37.825597+00:00 heroku[worker.1]: State changed from starting to up
2016-10-31T14:27:38.100208+00:00 app[worker.1]: sh: 0: Can't open target/bin/worker
2016-10-31T14:27:38.104979+00:00 heroku[worker.1]: State changed from up to crashed
2016-10-31T14:27:38.104979+00:00 heroku[worker.1]: State changed from crashed to starting
2016-10-31T14:27:38.291551+00:00 heroku[web.1]: Starting process with command `java $JAVA_OPTS -jar target/dependency/webapp-runner.jar $WEBAPP_RUNNER_OPTS --port 54481 target/amqpexample-1.0-SNAPSHOT.war`
2016-10-31T14:27:40.725610+00:00 app[web.1]: Setting JAVA_TOOL_OPTIONS defaults based on dyno size. Custom settings will override them.
2016-10-31T14:27:40.732741+00:00 app[web.1]: Picked up JAVA_TOOL_OPTIONS: -Xmx350m -Xss512k -Dfile.encoding=UTF-8
2016-10-31T14:27:41.385588+00:00 app[web.1]: Expanding amqpexample-1.0-SNAPSHOT.war into /app/target/tomcat.54481/webapps/expanded
2016-10-31T14:27:41.385722+00:00 app[web.1]: Adding Context  for /app/target/tomcat.54481/webapps/expanded
2016-10-31T14:27:41.817336+00:00 heroku[worker.1]: Starting process with command `sh target/bin/worker`
2016-10-31T14:27:42.172560+00:00 app[web.1]: INFO: Initializing ProtocolHandler ["http-nio-54481"]
2016-10-31T14:27:42.172549+00:00 app[web.1]: Oct 31, 2016 2:27:42 PM org.apache.coyote.AbstractProtocol init
2016-10-31T14:27:42.201060+00:00 app[web.1]: Oct 31, 2016 2:27:42 PM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
2016-10-31T14:27:42.201064+00:00 app[web.1]: INFO: Using a shared selector for servlet write/read
2016-10-31T14:27:42.205036+00:00 app[web.1]: Oct 31, 2016 2:27:42 PM org.apache.catalina.core.StandardService startInternal
2016-10-31T14:27:42.205038+00:00 app[web.1]: INFO: Starting service Tomcat
2016-10-31T14:27:42.206587+00:00 app[web.1]: Oct 31, 2016 2:27:42 PM org.apache.catalina.core.StandardEngine startInternal
2016-10-31T14:27:42.206588+00:00 app[web.1]: INFO: Starting Servlet Engine: Apache Tomcat/8.0.30
2016-10-31T14:27:42.378327+00:00 heroku[worker.1]: State changed from starting to up
2016-10-31T14:27:42.441771+00:00 app[web.1]: Oct 31, 2016 2:27:42 PM org.apache.catalina.startup.ContextConfig getDefaultWebXmlFragment
2016-10-31T14:27:42.441781+00:00 app[web.1]: INFO: No global web.xml found
2016-10-31T14:27:42.765321+00:00 heroku[web.1]: State changed from starting to up
2016-10-31T14:27:43.507545+00:00 app[worker.1]: sh: 0: Can'
t open target/bin/worker
2016-10-31T14:27:43.591348+00:00 heroku[worker.1]: Process exited with status 127
2016-10-31T14:27:43.603552+00:00 heroku[worker.1]: State changed from up to crashed
2016-10-31T14:27:44.410603+00:00 app[web.1]: Oct 31, 2016 2:27:44 PM org.apache.jasper.servlet.TldScanner scanJars
2016-10-31T14:27:44.410621+00:00 app[web.1]: INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
2016-10-31T14:27:44.498715+00:00 app[web.1]: Oct 31, 2016 2:27:44 PM org.apache.coyote.AbstractProtocol start
2016-10-31T14:27:44.498718+00:00 app[web.1]: INFO: Starting ProtocolHandler ["http-nio-54481"]
2016-10-31T14:27:47.410949+00:00 heroku[router]: at=info method=GET path="/" host=amqpexample.herokuapp.com request_id=334f7e05-d4e5-4f78-a444-1e6e7403e52c fwd="14.192.210.168" dyno=web.1 connect=1ms service=4305ms status=200 bytes=370
2016-10-31T14:27:50.003897+00:00 heroku[router]: at=info method=GET path="/" host=amqpexample.herokuapp.com request_id=51c9fac7-6763-4374-9f2b-672f0d7f7be7 fwd="14.192.210.168" dyno=web.1 connect=1ms service=21ms status=200 bytes=295

Servlet类(负责显示缓冲区消息并在刷新时等待从worker发送新消息):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package herokutest;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorld extends javax.servlet.http.HttpServlet {

    private final static String QUEUE_NAME ="hello";
    private final static String ANOTHER_QUEUE ="ANOTHER";

    public Channel channel;
    public Channel anotherChannel;
    public DefaultConsumer consumer;
    public  String message ="Temporary";

    public void init(final ServletConfig config) throws ServletException {
        try {
            createChannel();
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
                consumer = new DefaultConsumer(anotherChannel) {
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                        String innerMessage = new String(body,"UTF-8");
                        message = innerMessage;
                    }
                };
                try {
                    anotherChannel.basicConsume(ANOTHER_QUEUE, true, consumer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    public void createChannel() throws NoSuchAlgorithmException, KeyManagementException, URISyntaxException, IOException {
        String uri = System.getenv("CLOUDAMQP_URL");
        if (uri == null) uri ="amqp://guest:guest@localhost";

        ConnectionFactory factory = new ConnectionFactory();
        factory.setUri(uri);
        factory.setRequestedHeartbeat(30);
        factory.setConnectionTimeout(30);
        Connection connection = factory.newConnection();
         channel = connection.createChannel();
         anotherChannel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        anotherChannel.queueDeclare(ANOTHER_QUEUE, false, false, false, null);

    }


    protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {

        String test="test";
       channel.basicPublish("", QUEUE_NAME, null, test.getBytes());
        request.setAttribute("message", message);
        request.getRequestDispatcher("/index.jsp").forward(request, response);

    }
}

Worker类:??(负责将消息Another message发送回servlet)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import com.rabbitmq.client.*;

import java.io.IOException;

public class WorkerProcess {
    private final static String QUEUE_NAME ="hello";
    private final static String ANOTHER_QUEUE ="ANOTHER";
    static String message;



    public static void main(String[] argv) throws Exception {


        ConnectionFactory factory = new ConnectionFactory();

        String uri = System.getenv("CLOUDAMQP_URL");
            if (uri == null) uri ="amqp://guest:guest@localhost";
        factory.setUri(uri);
        factory.setRequestedHeartbeat(30);
        factory.setConnectionTimeout(30);

        Connection connection = factory.newConnection();

        Channel channel = connection.createChannel();
        final Channel anotherChannel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        anotherChannel.queueDeclare(ANOTHER_QUEUE, false, false, false, null);
        System.out.println(" [*] Connected to Worker");

        final Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                message = new String(body,"UTF-8");
                String anotherMessage="Another message";
                anotherChannel.basicPublish("", ANOTHER_QUEUE, null, anotherMessage.getBytes());
            }
        };
        channel.basicConsume(QUEUE_NAME, true, consumer);

    }

}

POM:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cloudamqp.example</groupId>
  amqpexample</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <dependencies>
    <dependency>
      <groupId>com.rabbitmq</groupId>
      amqp-client</artifactId>
      <version>3.3.4</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    jstl</artifactId>
    <version>1.2</version>
  </dependency>
      <dependency>
          <groupId>javax.servlet</groupId>
          javax.servlet-api</artifactId>
          <version>3.0.1</version>
          <scope>provided</scope>
      </dependency>

<!-- This is my worker jar included as dependency -->
    <dependency>
      <groupId>com.example</groupId>
      worker</artifactId>
      <version>1.0</version>
    </dependency>
  </dependencies>
 <!-- My local repository containing my worker .jar file-->
  <repositories>
    <repository>
      <id>project.local</id>
      <name>project</name>
      <url>file:${project.basedir}/localrepo</url>
    </repository>
  </repositories>


  <build>
    <plugins>
      <plugin>
        <groupId>com.heroku.sdk</groupId>
        heroku-maven-plugin</artifactId>
        <version>1.1.1</version>
      </plugin>


      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        maven-war-plugin</artifactId>
        <version>2.4</version>
        <configuration>
          <webResources>
            <resource>
              <directory>src/web</directory>
            </resource>
          </webResources>
            false</archiveClasses>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        appassembler-maven-plugin</artifactId>
        <version>1.1.1</version>
        <configuration>
          target</assembleDirectory>
          <programs>
            <program>
              <mainClass>WorkerProcess</mainClass>
              <name>worker</name>
            </program>
          </programs>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>assemble</goal>
            </goals>
          </execution>            
        </executions>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        maven-dependency-plugin</artifactId>
        <version>2.3</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals><goal>copy</goal></goals>
            <configuration>
             
               
                  <groupId>com.github.jsimone</groupId>
                  webapp-runner</artifactId>
                  <version>8.0.30.2</version>
                  <destFileName>webapp-runner.jar</destFileName>
                </artifactItem>
              </artifactItems>
            </configuration>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>  
</project>

这是日志中的重要消息:

1
2016-10-31T14:27:43.507545+00:00 app[worker.1]: sh: 0: Can't open target/bin/worker

您的Maven版本是否生成此target/bin/worker脚本?

我不希望CLI部署能够正常工作,因为默认情况下它不包含target目录。但是您可以将其包含在--include选项中。