泽西服务器端异步API未按预期工作

Cui*_*崔鹏飞 2 java rest asynchronous jersey

我只是阅读了jersey文档并试图通过使用异步服务器端api来比较我可以节省多少时间,但我得到的结果令人困惑,请帮助发现我的代码中是否有问题.

这是资源类:

@Path("/async-sync")
public class CompareAsyncAndSyncResource {
    @GET
    @Path("sync-call")
    public String syncCall() throws InterruptedException {
        expensiveComputation();
        return "sync call finished";
    }

    @GET
    @Path("async-call")
    public void asyncCall(@Suspended final AsyncResponse asyncResponse) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                expensiveComputation();
                asyncResponse.resume("async call finished");
            }
        }).start();
    }

    private void expensiveComputation() {
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

一个同步get调用和一个async get调用.

然后我做了这些测试:

public class CompareAsyncAndSyncResourceTest extends JerseyTest {
    @Override
    protected Application configure() {
        return new ResourceConfig().register(CompareAsyncAndSyncResource.class);
    }

    @Test
    public void sync_call_should_take_longer() throws Exception {
        final Stopwatch stopwatch = new Stopwatch();
        stopwatch.start();

        callFiveTimes(new Runnable() {
            @Override
            public void run() {
                String resp = target("async-sync/sync-call").request().get().readEntity(String.class);
                System.out.println("sync returned " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
                assertThat(resp, is("sync call finished"));
            }
        });

        stopwatch.stop();

        System.out.println("five clients calling sync get at same time " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
    }

    @Test
    public void async_call_should_take_shorter() throws Exception {
        final Stopwatch stopwatch = new Stopwatch();
        stopwatch.start();

        callFiveTimes(new Runnable() {
            @Override
            public void run() {
                String resp = target("async-sync/async-call").request().get().readEntity(String.class);
                System.out.println("async returned " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
                assertThat(resp, is("async call finished"));
            }
        });

        stopwatch.stop();

        System.out.println("five clients calling async get at same time " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
    }

    private void callFiveTimes(final Runnable runnable) throws InterruptedException {

        Iterable<Integer> rangeOfFive = newArrayList(1, 2, 3, 4, 5);
        FluentIterable<Thread> threads = from(rangeOfFive).transform(new Function<Integer, Thread>() {
            @Override
            public Thread apply(Integer number) {
                Thread thread = new Thread(runnable);
                thread.start();
                return thread;
            }
        });

        for (Thread thread : threads) {
            thread.join();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在测试中,我模拟了五个同时调用两个get调用的客户端,我想象五个异步调用get会更快完成,但这是我得到的结果:

sync returned 1052
sync returned 2063
sync returned 3073
sync returned 4082
sync returned 5095
five clients calling sync get at same time 5096
Run Code Online (Sandbox Code Playgroud)

同步部分是预期的,它需要5秒钟才能完成,包括五个调用和一点开销.

但异步结果让我失望:

async returned 1847
async returned 2893
async returned 3904
async returned 4913
async returned 5923
five clients calling async get at same time 5923
Run Code Online (Sandbox Code Playgroud)

看起来这五个异步调用并不是真正异步处理的.

我究竟做错了什么?

PS:代码在这里:https://github.com/cuipengfei/jerseywhatnot/blob/master/src/main/java/com/jersey/whatnot/async/CompareAsyncAndSyncResource.java

编辑:我知道我现在做错了,它是番石榴,我本应该调用toList.懒惰的评价再次让我感到高兴.但是看起来奇怪的是同步调用也变得更快,为什么呢?根据泽西的文档,同步获取调用应该阻止IO线程并减慢所有减少,但似乎它没有发生.

Wil*_*ung 5

这不是异步所做的.

从根本上说,Java Servlet规范中的所有异步工具(Jersey利用)都允许您将传入请求与初始线程分开.

通常,请求和处理线程绑定在一起.

促进这种分离本身并不会使任何事情变得更快.这就是你用它做的事情,可以让它更快.

您迷你基准测试连续5次调用服务器.同步版本放置原始请求线程上的处理,阻塞一秒钟,然后恢复.但是如果你考虑到这一点,你的服务器在任何时候都不会消耗多个活动的处理线程,因为所有的调用都被序列化到服务器(即1,然后是2,然后是3 ......).

你的异步调用接受请求,然后,它立即创建一个新线程,然后该线程完成处理,而不是直接服务它.在这种情况下,每个请求都会占用初始线程的一部分,然后继续使用第二个线程.但是,它必须分配和分配给新线程,而原始同步代码则不会.该调度需要时间,您的同步线程没有采取的时间,因此"慢".它的速度较慢,因为它只是做了更多的工作.(服务器还在异步上下文中包含您的请求以及需要的内部簿记).

异步为您提供的值不在卸载的服务器上.它在加载的服务器上.

线程处理过程本身需要花费一些时间在服务器上,特别是当你有100或1000的请求时.在该级别,线程开销本身是有影响的,尤其是在加载时.Async可以让你更好地将请求的工作分配给其他设施,以解决繁重的问题.请求线程然后简单地成为接待台,"我怎么可以直接呼叫"运营商.一旦完成工作路由,他们就可以继续接受其他请求.

理想情况下,您可能会注意到许多公司没有很多"我可以如何指导您的电话"操作员,因为这项工作并不是特别耗时.与实际执行请求的人相比,单个运营商可以指导大量呼叫.因此,与进行实际处理相比,仅需要更少的线程来接受请求和路由它们.

通过异步,您可以将应用程序内部转移到其中一个客户支持位置,这些位置可以让您的人与人之间保持一致.这实际上可以让您的系统在负载下表现得更有效率,因为人们并非都在走廊里堵塞,而是静静地坐在个人等候室(队列)中等待轮到他们进行实际工作.

这是Java Servlets中Async工具的价值.通过一个简单的微基准测试来做你正在做的事情,他们只是无缘无故地做更多的工作.