【问题标题】:Grizzly Http Server - accepting only one connection at a timeGrizzly Http Server - 一次只接受一个连接
【发布时间】:2014-09-27 18:48:27
【问题描述】:

我有一个添加了异步处理的 Grizzly Http 服务器。尽管添加了异步支持,但它仍在排队我的请求并一次只处理一个请求。

HttpHandler 绑定的路径是:“/” 端口号:7777

当我同时从两个浏览器点击http://localhost:7777 时观察到的行为是: 第二个调用等待第一个调用完成。我希望我的第二个 http 调用也与第一个 http 调用同时工作。

编辑 Github link of my project

以下是类
GrizzlyMain.java

package com.grizzly;

import java.io.IOException;
import java.net.URI;

import javax.ws.rs.core.UriBuilder;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.strategies.WorkerThreadIOStrategy;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;

import com.grizzly.http.IHttpHandler;
import com.grizzly.http.IHttpServerFactory;

public class GrizzlyMain {

    private static HttpServer httpServer;

    private static void startHttpServer(int port) throws IOException {
        URI uri = getBaseURI(port);

        httpServer = IHttpServerFactory.createHttpServer(uri,
            new IHttpHandler(null));

        TCPNIOTransport transport = getListener(httpServer).getTransport();

        ThreadPoolConfig config = ThreadPoolConfig.defaultConfig()
                .setPoolName("worker-thread-").setCorePoolSize(6).setMaxPoolSize(6)
                .setQueueLimit(-1)/* same as default */;

        transport.configureBlocking(false);
        transport.setSelectorRunnersCount(3);
        transport.setWorkerThreadPoolConfig(config);
        transport.setIOStrategy(WorkerThreadIOStrategy.getInstance());
        transport.setTcpNoDelay(true);

        System.out.println("Blocking Transport(T/F): " + transport.isBlocking());
        System.out.println("Num SelectorRunners: "
            + transport.getSelectorRunnersCount());
        System.out.println("Num WorkerThreads: "
            + transport.getWorkerThreadPoolConfig().getCorePoolSize());

        httpServer.start();
        System.out.println("Server Started @" + uri.toString());
    }

    public static void main(String[] args) throws InterruptedException,
        IOException, InstantiationException, IllegalAccessException,
        ClassNotFoundException {
        startHttpServer(7777);

        System.out.println("Press any key to stop the server...");
        System.in.read();
    }

    private static NetworkListener getListener(HttpServer httpServer) {
        return httpServer.getListeners().iterator().next();
    }

    private static URI getBaseURI(int port) {
        return UriBuilder.fromUri("https://0.0.0.0/").port(port).build();
    }

}

HttpHandler(内置异步支持)

package com.grizzly.http;

import java.io.IOException;
import java.util.Date;
import java.util.concurrent.ExecutorService;

import javax.ws.rs.core.Application;

import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.grizzly.http.server.Response;
import org.glassfish.grizzly.http.util.HttpStatus;
import org.glassfish.grizzly.threadpool.GrizzlyExecutorService;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
import org.glassfish.jersey.server.ApplicationHandler;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spi.Container;

import com.grizzly.Utils;

/**
 * Jersey {@code Container} implementation based on Grizzly
 * {@link org.glassfish.grizzly.http.server.HttpHandler}.
 *
 * @author Jakub Podlesak (jakub.podlesak at oracle.com)
 * @author Libor Kramolis (libor.kramolis at oracle.com)
 * @author Marek Potociar (marek.potociar at oracle.com)
 */
public final class IHttpHandler extends HttpHandler implements Container {

    private static int reqNum = 0;

    final ExecutorService executorService = GrizzlyExecutorService
            .createInstance(ThreadPoolConfig.defaultConfig().copy()
                    .setCorePoolSize(4).setMaxPoolSize(4));

    private volatile ApplicationHandler appHandler;

    /**
     * Create a new Grizzly HTTP container.
     *
     * @param application
     *          JAX-RS / Jersey application to be deployed on Grizzly HTTP
     *          container.
     */
    public IHttpHandler(final Application application) {
    }

    @Override
    public void start() {
        super.start();
    }

    @Override
    public void service(final Request request, final Response response) {
        System.out.println("\nREQ_ID: " + reqNum++);
        System.out.println("THREAD_ID: " + Utils.getThreadName());

        response.suspend();
        // Instruct Grizzly to not flush response, once we exit service(...) method

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("Executor Service Current THREAD_ID: "
                            + Utils.getThreadName());
                    Thread.sleep(25 * 1000);
                } catch (Exception e) {
                    response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
                } finally {
                    String content = updateResponse(response);
                    System.out.println("Response resumed > " + content);
                    response.resume();
                }
            }
        });
    }

    @Override
    public ApplicationHandler getApplicationHandler() {
        return appHandler;
    }

    @Override
    public void destroy() {
        super.destroy();
        appHandler = null;
    }

    // Auto-generated stuff
    @Override
    public ResourceConfig getConfiguration() {
        return null;
    }

    @Override
    public void reload() {

    }

    @Override
    public void reload(ResourceConfig configuration) {
    }

    private String updateResponse(final Response response) {
        String data = null;
        try {
            data = new Date().toLocaleString();
            response.getWriter().write(data);
        } catch (IOException e) {
            data = "Unknown error from our server";
            response.setStatus(500, data);
        }

        return data;
    }

}

IHttpServerFactory.java

package com.grizzly.http;

import java.net.URI;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.http.server.ServerConfiguration;

/**
 * @author smc
 */
public class IHttpServerFactory {

    private static final int DEFAULT_HTTP_PORT = 80;

    public static HttpServer createHttpServer(URI uri, IHttpHandler handler) {

        final String host = uri.getHost() == null ? NetworkListener.DEFAULT_NETWORK_HOST
            : uri.getHost();
        final int port = uri.getPort() == -1 ? DEFAULT_HTTP_PORT : uri.getPort();

        final NetworkListener listener = new NetworkListener("IGrizzly", host, port);
        listener.setSecure(false);

        final HttpServer server = new HttpServer();
        server.addListener(listener);

        final ServerConfiguration config = server.getServerConfiguration();
        if (handler != null) {
            config.addHttpHandler(handler, uri.getPath());
        }

        config.setPassTraceRequest(true);
        return server;
    }
}

【问题讨论】:

  • 你能分辨出什么是阻塞的吗?如果在service 的开头放置断点,观察到的行为是什么?同样,如果你停在run 的第一行会发生什么?
  • @Raffaele 当我在服务开始时插入断点时,线程停在那里。如果我从不同的选项卡进行另一个 http 调用(实际上是任意数量的 http 调用),则执行不会发生任何事情。只有在我跳过并继续执行并最终完成此调用后,未完成的 http 调用之一才会进入服务方法。即使在我在 run() 的第一行中断后也有相同的行为。根本没有其他 http 调用并行运行(在其他一些工作线程中)
  • 所以配置错误似乎在 Grizzly 部分,好像它只使用一个线程来服务所有传入连接(作为双重检查,您应该始终在标准输出中看到相同的线程 ID)。尝试把GrizzlyMain开头的所有自定义配置去掉,看看出厂设置有没有不一样
  • @Raffaele 不走运!相同的行为;从网络侦听器(端口#7777)选择请求的 http 处理程序显然被阻止等待工作线程完成。
  • 你用的是什么灰熊版本?

标签: java grizzly


【解决方案1】:

问题似乎是浏览器等待第一个请求完成,因此更多的是客户端问题而不是服务器端问题。如果您使用两个不同的浏览器进程进行测试,或者即使您在同一个浏览器进程中打开两个不同的路径(比如 localhost:7777/foolocalhost:7777/bar),它也会消失(注意:查询字符串参与了 HTTP 中的路径构成请求行)。

我是怎么理解的

默认情况下,HTTP/1.1 中的连接是持久的,即浏览器一遍又一遍地回收相同的 TCP 连接以加快速度。但是,这并不意味着对同一域的所有请求都将被序列化:实际上,连接池是基于每个主机名分配的(source)。不幸的是,具有相同路径的请求被有效地排入队列(至少在 Firefox 和 Chrome 上)——我猜它是浏览器用来保护服务器资源(从而保护用户体验)的设备

真实应用程序不会因此受到影响,因为不同的资源被部署到不同的 URL。

免责声明:我根据我的观察和一些有根据的猜测写了这个答案。我认为事情实际上可能是这样的,但是应该使用像 Wireshark 这样的工具来跟踪 TCP 流并肯定会发生这种情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-29
    • 1970-01-01
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多