这篇文章将描述一种性能优化技术,适用于与现代Web应用程序相关的常见问题。 如今的应用程序不再只是被动地等待浏览器发起请求,而是希望自己开始通信。 一个典型的示例可能涉及聊天应用程序,拍卖行等–共同点是以下事实:大多数时间与浏览器的连接处于空闲状态并等待某个事件被触发。

这类应用程序已经开发出自己的问题类别,尤其是在面对重负载时。 症状包括线程不足,用户交互受苦,陈旧性问题等。

根据最近在加载此类应用程序方面的经验,我认为现在是演示简单解决方案的好时机。 在Servlet API 3.0实现成为主流之后,该解决方案就变得真正简单,标准化和优雅。

但是在进入演示解决方案之前,我们应该更详细地了解问题。 对于我们的读者–在某些源代码的帮助下,比解释问题更容易的是:

@WebServlet(urlPatterns = "/BlockingServlet")
public class BlockingServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {try {long start = System.currentTimeMillis();Thread.sleep(2000);String name = Thread.currentThread().getName();long duration = System.currentTimeMillis() - start;response.getWriter().printf("Thread %s completed the task in %d ms.", name, duration);} catch (Exception e) {throw new RuntimeException(e.getMessage(), e);}}

上面的servlet是上面描述的应用程序看起来像的示例:

  • 请求到达,宣布有兴趣监视某些事件
  • 线程被阻塞,直到事件到达
  • 收到事件后,响应将被编译并发送回客户端

为了简单起见,我们将等待部分替换为对Thread.sleep()的调用。

现在,您可能会认为这是一个完全正常的servlet。 在许多情况下,您是完全正确的–在应用程序面临大量负载之前,代码没有错。

为了模拟此负载,我在JMeter的帮助下创建了一个相当简单的测试,在该测试中,我启动了2,000个线程,每个线程运行10次迭代,以对/ BlockedServlet的请求轰炸应用程序。 在现成的Tomcat 7.0.42上使用已部署的servlet运行测试,结果如下:

  • 平均响应时间:19,324毫秒
  • 最小响应时间:2,000毫秒
  • 最大响应时间:21,869 ms
  • 吞吐量:97个请求/秒

Tomcat的默认配置有200个工作线程,再加上将模拟工作替换为2,000ms睡眠周期这一事实很好地说明了最小和最大响应时间– 200秒中的每个线程应该能够完成100个睡眠周期,每个2秒。 最重要的是,加上上下文切换成本,达到的97个请求/秒的吞吐量非常接近我们的预期。

对于99.9%的应用程序而言,吞吐量本身看起来不会太差。 从最大响应时间(尤其是平均响应时间)来看,问题似乎开始变得更加严重。 在20秒(而不是预期的2秒)内获得响应是确定惹恼用户的肯定方法。

现在让我们看一下利用Servlet API 3.0异步支持的替代实现:

@WebServlet(asyncSupported = true, value = "/AsyncServlet")
public class AsyncServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Work.add(request.startAsync());}
}
public class Work implements ServletContextListener {private static final BlockingQueue queue = new LinkedBlockingQueue();private volatile Thread thread;public static void add(AsyncContext c) {queue.add(c);}@Overridepublic void contextInitialized(ServletContextEvent servletContextEvent) {thread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {Thread.sleep(2000);AsyncContext context;while ((context = queue.poll()) != null) {try {ServletResponse response = context.getResponse();response.setContentType("text/plain");PrintWriter out = response.getWriter();out.printf("Thread %s completed the task", Thread.currentThread().getName());out.flush();} catch (Exception e) {throw new RuntimeException(e.getMessage(), e);} finally {context.complete();}}} catch (InterruptedException e) {return;}}}});thread.start();}@Overridepublic void contextDestroyed(ServletContextEvent servletContextEvent) {thread.interrupt();}
}

这部分代码稍微复杂一点,所以也许在我们开始深入研究解决方案细节之前,我可以概述一下,该解决方案在延迟方面的性能提高约75倍,在吞吐量方面的性能提高了约20倍 。 掌握了此类结果的知识后,您应该更加有动力去理解第二个示例中的实际情况。

Servlet本身看起来确实很简单。 但是,有两个事实值得概述,第一个事实声明了该Servlet支持异步方法调用:

@WebServlet(asyncSupported = true, value = "/AsyncServlet")

第二个重要方面隐藏在以下行中

Work.add(request.startAsync());

其中整个请求处理都委派给Work类。 使用AsyncContext实例存储请求的上下文,该实例保存由容器提供的请求和响应。

现在,第二个更复杂的类–以ServletContextListener实现的Work开始看起来更简单。 传入的请求只是在实现中排队等待通知-这可能是受监控拍卖的更新出价,也可能是群聊中所有请求都在等待的下一条消息。

通知到达时-再次简化为在Thread.sleep()中等待2,000ms,队列中所有被阻止的任务都由一个负责编译和发送响应的工作线程处理。 我们没有阻塞数百个线程来等待外部通知,而是以更简单,更简洁的方式实现了这一点–将兴趣组批处理在一起,并在单个线程中处理请求。

结果不言而喻–在具有默认配置的相同Tomcat 7.0.24上进行的相同测试导致以下结果:

  • 平均响应时间:265毫秒
  • 最小响应时间:6毫秒
  • 最长响应时间:2,058毫秒
  • 吞吐量:1,965请求/秒

此处的具体情况很小且综合,但在实际应用中可以实现类似的改进。

现在,在您将所有servlet重写为异步servlet之前-稍等片刻。 该解决方案可以完美地用于部分用例,例如群聊通知和拍卖行价格警报。 对于请求在唯一数据库查询完成后等待的情况,您很可能不会从中受益。 因此,与往常一样,我必须重申我最喜欢的与性能相关的建议–衡量所有事情。 什么都不要猜。

但是,当问题确实适合解决方案形式时,我只能称赞它。 除了对吞吐量和延迟的明显改进之外,我们还优雅地避免了在高负载下可能出现的线程不足问题。

另一个重要方面–异步请求处理方法最终实现了标准化。 独立于您最喜欢的Servlet API 3.0 –兼容应用程序服务器(例如Tomcat 7 , JBoss 6或Jetty 8),您可以确定该方法有效。 不再为不同的Comet实现或与平台相关的解决方案(例如Weblogic FutureResponseServlet)而费力

参考: Plumbr Blog博客上的JCG合作伙伴 Nikita Salnikov Tarnovski 如何使用异步Servlet来提高性能 。

翻译自: https://www.javacodegeeks.com/2013/10/how-to-use-asynchronous-servlets-to-improve-performance.html

如何使用异步Servlet来提高性能相关推荐

  1. servlet异步_如何使用异步Servlet来提高性能

    servlet异步 这篇文章将描述一种性能优化技术,该技术适用于与现代Web应用程序相关的常见问题. 如今的应用程序不再只是被动地等待浏览器发起请求,而是希望自己开始通信. 一个典型的示例可能涉及聊天 ...

  2. 使用异步Servlet改进应用性能

    http://www.infoq.com/cn/news/2013/11/use-asynchronous-servlet-improve?utm_source=infoq&utm_mediu ...

  3. java异步处理_SpringBoot异步开发之异步请求,在高并发的情况下,提高性能

    何为异步请求 在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理.如果一个请求需要进行IO操作,比如 ...

  4. servlet异步_关于Servlet和异步Servlet

    servlet异步 Servlet API是Java EE标准的一部分,自1998年正式发布2.1规范以来,一直是基于Java的企业体系结构的重要组成部分. 它是一种自以为是的API,用于服务围绕一些 ...

  5. 为什么非阻塞io性能更好_提高性能:流的非阻塞处理

    为什么非阻塞io性能更好 1.简介 想象一下,我们有一个需要访问外部Web服务的应用程序,以便收集有关客户端的信息,然后对其进行处理. 更具体地说,我们无法在一次调用中获得所有这些信息. 如果我们要查 ...

  6. 关于Servlet和异步Servlet

    Servlet API是Java EE标准的一部分,自1998年正式发布2.1规范以来,一直是基于Java的企业体系结构的重要组成部分. 它是一种自以为是的API,用于服务围绕一些基本概念构建的请求/ ...

  7. 提高性能:流的非阻塞处理

    1.简介 想象一下,我们有一个需要访问外部Web服务的应用程序,以便收集有关客户端的信息,然后对其进行处理. 更具体地说,我们无法在一次调用中获得所有这些信息. 如果我们要查找不同的客户端,则需要多次 ...

  8. .NET Framework 如何:提高性能

    以下编程做法可以节省内存和改善设备应用程序的性能. 使用 Windows 窗体和图形节省内存 对提供 BeginUpdate 和 EndUpdate 方法的控件使用这两种方法,提供这两种方法的控件包括 ...

  9. JavaScript 总结几个提高性能知识点(转)

    具体看连接吧: JavaScript 总结几个提高性能知识点 http://www.cnblogs.com/ys-ys/p/5132013.html 前段时间花时间看了大半的<High Perf ...

最新文章

  1. python常用魔法函数
  2. 用Js的eval解析JSON中的注意点
  3. 雅虎开源发布/订阅消息平台Pulsar
  4. 安卓在代码中设置TextView的drawableLeft、drawableRight、drawableTop、drawableBottom
  5. 实战解读增长黑客在 B 端业务中的应用
  6. 清华大学人工智能研究院成立自然语言处理与社会人文计算研究中心
  7. MySQL(八)子查询和分组查询
  8. fprintf 和 perror 的理解1
  9. BZOJ-1066 蜥蜴 最大流+拆点+超级源超级汇
  10. 华为云FusionInsight助力宇宙行打造金融数据湖新标杆
  11. 计算机系统-电路设计04-全加器的内部电路实现
  12. 这有一个机器人,粉刷本领强
  13. 安全监控、告警及自动化!
  14. FragmentTabHost+FrameLayout实现底部菜单栏
  15. abb机器人指令手册_ABB机器人常见故障
  16. 小程序获取附近IBeacon设备
  17. Kubernetes等待部署完成 kubectl wait rollout
  18. css引入自定义字体/特殊字体/ttf格式语言包
  19. QuantLib 金融计算——基本组件之天数计算规则详解
  20. 计算机桌面用什么实木板好,这才叫实木桌面,教你怎么做出来,拿去不谢

热门文章

  1. cursor 过滤 android,Android cursor query方法详解
  2. jakarta ee_Jakarta EE贡献–入门
  3. spark应用程序_Sparklens:Spark应用程序优化工具
  4. java 和javafx_Java,JavaFX的流利设计风格文本字段和密码字段
  5. 应用程序缓存_应用程序模块和实体缓存
  6. oracle中悲观锁定_如何使用悲观锁定修复乐观锁定竞争条件
  7. jboss默认进程名称_快速指南:剖析JBoss BPM跨进程通信
  8. u3d ab包 循环依赖_为什么要保持软件包依赖项自由循环的五个原因
  9. 限定通配符和非限定通配符_为什么我不信任通配符以及为什么我们仍然需要通配符...
  10. 将Websocket与Spring Framework和Vuejs结合使用