在Tomcat中运行时,大气广播不会到达客户端

时间:2014-06-19 16:57:10

标签: java tomcat jersey atmosphere

以下实质性更新(2014年10月15日)

==========原始发布===================

我有一个使用Atmosphere和Jersey的通知服务webapp。我试图在Linux上的Tomcat中运行它,并遇到了问题。 (请注意,这在Eclipse中嵌入Tomcat时运行正常,因此可能是Tomcat配置问题。)

问题是广播消息似乎已排队或缓冲,并且在我关闭Tomcat之前实际上并未传送到客户端。那时他们都被客户立刻收到了。

我尝试过在各种帖子中发现的一些事情,试图解决这个问题,但我的努力是分散的,因为我不知道是什么导致了这个问题:

  • 我尝试过明确使用Tomcat的NIO协议。
  • 我已经尝试过显式禁用文本和二进制缓冲,并且如果缓冲是原因,我还排队了足够的响应以触发刷新。
  • 我尝试在Tomcat 8中运行(但这有不同的无关问题。)
  • 我添加了氛围 - 运行时 - 本机,大气 - compat-jbossweb和大气 - compat-tomcat依赖项。

这些都没有改变服务的行为。而且,再次,它似乎在Eclipse中运行时工作正常。

环境:Ubuntu 14.04 LTS。 Tomcat 7.0.54。气氛2.1.5。对于客户来说,我现在只是使用telnet。

(这段代码比样本复杂一点,因为我想要支持多种不同的路由,并且我使用this回答提出的方法,这似乎有效我也回复了通知对象而不是字符串。这似乎也运行良好。为了清楚起见,我已经删除了大部分认证内容。)

的pom.xml:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.clearcaptial</groupId>
  <artifactId>notifications</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>notifications Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>org.atmosphere</groupId>
      <artifactId>atmosphere-jersey</artifactId>
      <version>2.1.5</version>
    </dependency>
    <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-json</artifactId>
      <version>1.17.1</version>
    </dependency>
    <dependency>
      <groupId>org.atmosphere</groupId>
      <artifactId>atmosphere-runtime-native</artifactId>
      <version>2.1.5</version>
    </dependency>
    <dependency>
      <groupId>org.atmosphere</groupId>
      <artifactId>atmosphere-compat-jbossweb</artifactId>
      <version>2.0.1</version>
    </dependency>
    <dependency>
      <groupId>org.atmosphere</groupId>
      <artifactId>atmosphere-compat-tomcat</artifactId>
      <version>2.0.1</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>notifications</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

的web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container, 
    see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:j2ee="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_3.0.xsd">

    <servlet>
    <servlet-name>AtmosphereServlet</servlet-name>
    <servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.clearcapital.notifications</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>org.atmosphere.useWebSocketAndServlet3</param-name>
      <param-value>true</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>AtmosphereServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>
来自ValidationsRouter.java的

@Path("/validations")
public class ValidationsRouter {

  @Context
  private AtmosphereResource atmosphereResource;

  @GET
  public SuspendResponse<ValidationNotification> subscribe(@Context UriInfo uri) throws URISyntaxException, NamingException, IOException {
    return new ValidationsResource().subscribe(uri, atmosphereResource);
  }

  @Broadcast
  @POST
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public Broadcastable broadcast(ValidationNotification message, @Context UriInfo uri) {
    return new ValidationsResource().broadcast(message, uri);
  }
}
来自ValidationsResource.java的

public class ValidationsResource extends ResourceBase<ValidationNotification>  {
  // a bunch of authentication stuff omitted for clarity.
}
来自ResourceBase.java的

public abstract class ResourceBase<T> {

  public SuspendResponse<T> subscribe(UriInfo uri, AtmosphereResource atmosphereResource) throws URISyntaxException, NamingException, IOException {

    Broadcaster myBroadcaster = BroadcasterFactory.getDefault().lookup(uri.getPath(), true);
    return new SuspendResponse.SuspendResponseBuilder<T>().broadcaster(myBroadcaster).outputComments(true)
                .addListener(new EventsLogger()).type(MediaType.APPLICATION_JSON_TYPE).build();
  }

  public Broadcastable broadcast(T message, UriInfo uri) {
    Broadcaster myBroadcaster = BroadcasterFactory.getDefault().lookup(uri.getPath(), true);
    return new Broadcastable(message, "", myBroadcaster);
  }
}

============原帖结束======================

2014年10月15日更新:

我在一个不同的项目上经过一段时间后才回到这个问题。使用新版本的Atmosphere(2.2.3)更新项目后,我发现行为没有区别。仍然如此,当在与客户端运行不同的计算机上运行服务时,在Tomcat关闭之前,客户端不会接收大气广播。在同一台计算机上运行客户端和服务时,一切都按预期工作。

因此,为了确定问题所在,我已经开始在测试中使用其中一个Atmosphere示例应用程序。我正在使用jersey-pubsub示例应用程序(有一些小的更新来编译它),因为那是最接近我试图做的那个。我创建了一个全新的服务器环境来运行示例应用程序,以最小化基于Linux配置的任何变量。以下是新步骤 - 也许有人可以复制行为。

获取安装了Tomcat7的新服务器(我使用Vagrant和VirtualBox进行这些测试):

host$ vagrant init ubuntu/trusty64
host$ nano Vagrantfile

将网络配置更改为已知IP:config.vm.network :private_network, ip: "10.1.1.2"

host$ vagrant box add https://vagrantcloud.com/ubuntu/boxes/trusty64/versions/1/providers/virtualbox.box
host$ vagrant up
------------------
vagrant$ sudo apt-get update
vagrant$ sudo apt-get upgrade
vagrant$ sudo apt-get install tomcat7

按照这些步骤,我最终得到了Tomcat 7.0.52和Ubuntu 14.04

我有一个我使用

构建的jersey-pubsub项目(没有父项目等)的副本
host$ mvn clean install

然后按

部署
host$ cp target/jersey-pubsub.war ../../vagrants/trusty64/
vagrant$ sudo cp /vagrant/jersey-pubsub.war /var/lib/tomcat7/webapps

然后我可以使用telnet测试它,例如:

host$ telnet 10.1.1.2 8080 <return>
GET /jersey-pubsub/pubsub/foo HTTP/1.0 <return>
<return>

此时,telnet继续等待数据。在tomcat服务器日志中,我可以看到一个条目:

20:33:55.805 [http-bio-8080-exec-1] INFO  o.a.samples.pubsub.EventsLogger - onSuspend(): 10.1.1.1:59357

然后我向服务器发送POST请求。我为此使用了POSTman,但我认为这不重要。

POST http://10.1.1.2:8080/jersey-pubsub/pubsub/foo
Accept: text/html
Content-Type: application/x-www-form-urlencoded
message: <html><body><p>This is a test!</p></body></html>

在服务器日志中,我可以看到服务器认为它广播了消息:

20:34:06.990 [Atmosphere-Shared-AsyncOp-0] INFO  o.a.samples.pubsub.EventsLogger - onBroadcast(): <html><body><p>This is a test!</p></body></html>

但是telnet客户端中没有显示任何内容。我可以无限重复,看不到任何结果,直到我关闭Tomcat,此时Telnet立即收到所有广播消息。

相反,如果我在主机上运行的tomcat实例上部署服务,或者如果我在vagrant实例中运行telnet,那么一切都按预期工作:

vagrant$ telnet localhost 8080 <return>
GET /jersey-pubsub/pubsub/foo HTTP/1.0 <return>
<return>

如果我发送相同的POST请求,我会立即收到telnet中的广播消息。行为似乎是,如果服务和客户端不在同一台计算机上,那么当Tomcat运行时,网络流量就会被阻止。

这是我的jersey-pubsub示例副本的源代码,只需进行足够的更改即可将其编译并构建为独立项目。

EventsLogger.java: (除了在2个地方删除@Override以使Eclipse停止抱怨之外没有变化)

FileResource.java: (不变)

JerseyPubSub.java: (不变)

web.xml中: (不变)

pom.xml(修改为独立构建,不参考父pom):

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.atmosphere.samples</groupId>
    <artifactId>atmosphere-jersey-pubsub</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>atmosphere-jersey-pubsub</name>
    <url>http://maven.apache.org</url>
    <properties>
        <atmosphere-version>2.2.3</atmosphere-version>
        <client-version>2.2.3</client-version>
        <logback-version>1.0.13</logback-version>
    </properties>
    <repositories>
        <repository>
            <id>oss.sonatype.org</id>
            <url>http://oss.sonatype.org/content/repositories/releases</url>
        </repository>
        <repository>
            <id>oss.sonatype.org-snapshot</id>
            <url>http://oss.sonatype.org/content/repositories/snapshots</url>
        </repository>
        <!-- <repository> <id>scala-tools.org</id> <name>Scala-Tools Maven2 Repository</name> 
            <url>http://scala-tools.org/repo-releases</url> </repository> -->
        <repository>
            <id>jboss</id>
            <url>https://repository.jboss.org/nexus/content/groups/public/</url>
        </repository>
        <repository>
            <id>codehaus</id>
            <name>repository.codehaus.org</name>
            <url>http://repository.codehaus.org</url>
        </repository>
        <repository>
            <id>codehaus-snapshots</id>
            <url>http://snapshots.repository.codehaus.org</url>
        </repository>
        <repository>
            <id>maven.java.net</id>
            <url>https://maven.java.net/content/groups/public/</url>
        </repository>
    </repositories>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.atmosphere</groupId>
                <artifactId>atmosphere-runtime</artifactId>
                <version>${atmosphere-version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.geronimo.specs</groupId>
                <artifactId>geronimo-servlet_3.0_spec</artifactId>
                <version>1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>${logback-version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.atmosphere.client</groupId>
            <artifactId>javascript</artifactId>
            <version>${client-version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>org.atmosphere</groupId>
            <artifactId>atmosphere-jersey</artifactId>
            <version>${atmosphere-version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-servlet_3.0_spec</artifactId>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>${logback-version}</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>jersey-pubsub</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3 个答案:

答案 0 :(得分:1)

  1. 您是否在apache代理后面运行tomcat?
  2. apache是​​否正在运行HTTP 1.1?
  3. 要尝试here
  4. 的更多apache内容

答案 1 :(得分:1)

几个月后回到这个问题后,我能够弄清楚为什么我会看到这种行为:它特别是内置的Mac telnet客户端表现不佳。我确认在从Windows或Linux使用telnet时立即记录了响应,并且我还使用不同的Mac telnet客户端进行了测试,并且它也按预期运行。虽然我已经使用了一段时间,但我无法弄清楚是否有我可以在Mac上指定的telnet选项,这使得它的行为方式与我测试的Linux相似。

答案 2 :(得分:0)

查看this page

底部的快速依赖关系参考表

确保您的类路径中有正确的jar用于您正在使用的特定版本的tomcat。

请参阅我的pom.xml here,其中我有profile部分,其中包含几个容器选项。

尝试从命令行运行mvn dependency:tree。也许大气球衣带来了氛围 - 运行时作为传递依赖。在这种情况下,您希望将exclude用于tomcat 7。