服务器端的异步和同步 HTTP 请求,性能比较

我试图找出异步和同步HTTP请求处理的优缺点。我正在使用Dropwizard和Jersey作为我的框架。测试是比较异步和同步HTTP请求处理,这是我的代码

@Path("/")
public class RootResource {

    ExecutorService executor;

    public RootResource(int threadPoolSize){
        executor = Executors.newFixedThreadPool(threadPoolSize);
    }

    @GET
    @Path("/sync")
    public String sayHello() throws InterruptedException {
        TimeUnit.SECONDS.sleep(1L);
        return "ok";
    }

    @GET
    @Path("/async")
    public void sayHelloAsync(@Suspended final AsyncResponse asyncResponse) throws Exception {
        executor.submit(() -> {
            try {
                doSomeBusiness();
                asyncResponse.resume("ok");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }


    private void doSomeBusiness() throws InterruptedException {
        TimeUnit.SECONDS.sleep(1L);
    }

}

同步 API 将在 Jetty 维护的工作线程中运行,异步 API 将主要在自定义线程池中运行。这是我的Jmeter的结果

  • 测试 1、500 个 Jetty 工作线程、/sync 终结点

    enter image description here

  • 测试 2、500 个自定义线程、/异步终结点

    enter image description here结果显示,这两种方法之间没有太大区别。

我的问题是:这两种方法之间有什么区别,我应该在哪种情况下使用哪种模式?

相关主题:同步 HTTP 处理程序和异步 HTTP 处理程序之间的性能差异

更新


我按照建议运行测试,延迟了 10 次

  • 同步 500 服务器线程

sync-500-server-thread

  • 异步 500 工作线程

async-500-workerthread


答案 1

以下是我的想法。

无论是同步请求还是异步请求,它与HTTP的性能无关,但与应用程序的性能有关

同步请求将阻塞应用程序,直到它收到响应,而在异步请求中,基本上,您将在单独的工作线程中分配此工作,该线程将负责其余工作。因此,在异步设计中,主线程仍然可以继续自己的工作。

假设由于某些限制(不是服务器的资源限制),您的服务器可以处理有限数量的连接(基本上每个连接都将在我们使用的服务器之间的单独线程中处理)。如果您的服务器可以处理的线程数多于连接数,并且如果您不希望由于您创建的异步工作而返回任何数据,则可以设计异步逻辑。因为您将创建一个新线程来处理请求的任务。

但是,如果您希望在响应中返回操作结果,则不会有任何不同。


答案 2

您正在使用与异步相结合的@Suspended,该异步仍在等待响应

@Suspended将暂停/暂停当前线程,直到它得到响应

如果要在异步中获得更好的性能,请使用 和 编写具有即时响应的其他异步方法ExecutorServiceFuture

private ExecutorService executor;
private Future<String> futureResult;
@PostConstruct
public void onCreate() {
    this.executor = Executors.newSingleThreadExecutor();
}
@POST
public Response startTask() {
    futureResult = executor.submit(new ExpensiveTask());
    return Response.status(Status.ACCEPTED).build();
}
@GET
public Response getResult() throws ExecutionException, InterruptedException {
    if (futureResult != null && futureResult.isDone()) {
        return Response.status(Status.OK).entity(futureResult.get()).build();
    } else {
        return Response.status(Status.FORBIDDEN).entity("Try later").build();
    }
}

推荐