可调用/可运行控制器方法:重点是什么?

时间:2017-02-06 20:40:38

标签: java spring multithreading asynchronous

在Spring中,您可以让控制器返回Callable而不是T,它将立即释放请求处理线程并在WebAsyncManager管理的MvcAsync线程中计算结果。您只需要将控制器方法内容包装在 public static System.Drawing.Image PDFToImage(MemoryStream inputMS) { GhostscriptRasterizer rasterizer = null; GhostscriptVersionInfo version = null; if (Environment.Is64BitProcess) version = new GhostscriptVersionInfo( new Version(0, 0, 0), @"C:\Program Files\gs\gs9.20\bin\gswin64.exe", string.Empty, GhostscriptLicense.GPL); else version = new GhostscriptVersionInfo( new Version(0, 0, 0), @"C:\Program Files (x86)\gs\gs9.20\bin\gswin32.exe", string.Empty, GhostscriptLicense.GPL); int dpi = 96; System.Drawing.Image img = null; using (rasterizer = new GhostscriptRasterizer()) { rasterizer.Open(inputMS, version, true); for (int i = 1; i <= rasterizer.PageCount; i++) { using (MemoryStream ms = new MemoryStream()) { img = rasterizer.GetPage(dpi, dpi, i); img.Save(ms, ImageFormat.Jpeg); ms.Close(); } } rasterizer.Close(); } return img; } 中。很简单!

但重点是什么?有什么区别 a)拥有500个请求处理线程并让他们完成所有工作和 b)只有几个请求处理线程并使用concurrencyLimit 500 在Callables中执行所有请求?

第二个选项b)实际上对我来说看起来更糟糕,因为管理整个MvcAsync魔术需要花费很多。

我知道如何收集@Async方法一次执行两个方法并在完成后返回结果,但我显然不了解Callable控制器方法。

1 个答案:

答案 0 :(得分:4)

假设您有一个拥有10个线程的Tomcat服务器侦听客户端请求。如果您的客户端调用需要5秒钟响应的端点,则该客户端将持续该线程持续5秒钟。添加一些并发客户端,在这5秒内你很快就会用完线程。

情况更糟,因为在这5秒的大部分时间里,你的请求主要是I / O,这意味着你只是阻止你的线程做什么,只能等待。

因此,Spring使用Callable,CompletableFuture或ListenableFuture作为控制器返回类型的能力正是为了让程序员在一定程度上克服这类问题。

从根本上说,只返回其中一种类型只会释放Web服务器线程,使其可供其他客户端使用。因此,您可以在相同的时间内参加更多的客户,但这本身可能不足以实现非阻塞IO(也称为NIO)API。

这些功能大多来自Servlet API和Servlet Async IO提供的核心功能,Spring应该可以在其中使用。您可能想看看以下有趣的视频,这些视频帮助我从头开始理解这一点:

这些视频解释了Servlet异步I / O背后的想法以及实现NIO Web应用程序的最终目标。

这里的圣杯是要达到一个点,在这个点上线程池中的线程永远不会被阻塞,等待一些I / O发生。他们要么做一些CPU绑定工作,要么他们回到线程池中,其他客户端可以使用它们。当您执行I / O时,您不会引入等待,您会注册某种形式的回调,用于告诉您何时结果准备就绪,同时您可以使用您宝贵的CPU内核来处理其他事情。如果您考虑一下,Callable,CompletableFuture或ListenableFuture就是那种Spring基础设施正在使用的回调对象,用于调用其功能以在单独的线程中参与请求。

这会增加您的吞吐量,因为您可以同时参与更多客户端,只需优化使用宝贵的CPU资源,特别是如果您以NIO方式执行,因为您可以想象,只需将请求移动到另一个线程虽然有益(因为你释放了一个有价值的Tomcat线程),但仍然会阻塞,因此,你只是将你的问题转移到另一个线程池。

我相信这个基本原则也是Spring团队目前在Project Reactor中所做的大部分工作的背后,因为为了利用这类功能的强大功能,你需要在你的系统中引入异步编程。 API很难做到。

这也是NettyRxJavaReactive Streams InitiativeProject Reactor等框架激增的原因。他们都在寻求推广这种优化和编程模型。

还有一个有趣的新框架运动,它利用这些强大的功能,并试图在该领域与Spring竞争甚至补充Spring功能。我正在谈论有趣的项目,例如Vert.xRatpack,现在我们就此了,这个功能也是Node.js的主要卖点之一。