用Spring长轮询Tomcat

就像喜剧演员弗兰基· 豪威尔 ( Frankie Howerd)所说的“哦,小姐小姐” ,但足够多的英国影射和双重诱惑,因为长轮询雄猫对隔壁的闷气不是某种性偏见,这是一种技术(或更像是一种骇客)由于乔布斯(Steve Jobs)拒绝在iPhone和iPad上支持Adobe Flash Player而开发的 。 事实是,将Flash Player用作Web应用程序的一部分是一种很好的支持“ 发布和订阅”范例的方式,因为它能够满足那些需要实时更新的情况,例如实时股票价格,新闻更新和更改。博彩赔率,而直接HTTP及其请求/回复范例是支持静态页面的一种好方法。 许多公司在开发使用Flash的应用程序方面花费了大量精力,以便为用户提供实时数据。 当苹果公司宣布iOS将不支持Adobe Flash时,如果没有iPhone解决方案,它们就会变得干and无力,并且要重新进入移动市场,我想其中有很多经过长时间的投票。

那么,什么是长期 投票 ? 嗯,这不是来自华沙的高个子,其想法是模仿“发布和订阅”模式。 场景如下:

  1. 浏览器从服务器请求一些数据。
  2. 服务器没有可用的数据,并允许请求挂起。
  3. 稍后,响应数据可用,服务器完成请求。
  4. 浏览器一接收到数据,便立即显示该数据,然后立即请求更新。
  5. 现在,流程循环回到点2。

我怀疑Spring的Guy不太热衷于“长期投票”一词,因为他们更正式地将此技术称为异步请求处理

通过查看上面的“长轮询”或“ 异步请求处理”流程,您可能会猜到服务器将发生什么。 每次您强迫服务器等待数据可用时,都会占用其一些宝贵资源。 如果您的网站很受欢迎并且负担很重,那么等待更新消耗的资源数量会激增,因此服务器可能会用尽并崩溃。

在2月和3月,我写了一系列有关Producer Consumer模式的博客,这似乎是进行长期投票的理想情况。 如果您还没有阅读我的Producer Consumer模式博客,请在这里查看

在那种原始情况下,我说过“电视公司会向每个游戏发送记者,以将实时更新反馈到系统中,然后将其发送回演播室。 到达工作室后,更新将被放入队列中,然后由电传打字机显示在屏幕上。”

随着时代的变迁,电视公司希望用Web应用程序替换旧的Teletype,该应用程序可以实时显示比赛更新。

屏幕截图2013年8月19日在18.04.01

在这种新情况下,电视公司总裁聘请了Agile Cowboys inc的朋友来整理更新。 为了使事情变得更容易,他为他们提供了MessageMatchMatchReporter类的源代码,这些类在新项目中可以重复使用。 Agile Cowboys的首席执行官聘请了一些新开发人员来完成这项工作:JavaScript,JQuery,CSS和HTML的专家来做前端,Spring MVC Webapp的人来做Java。

前端专家提供了以下JavaScript轮询代码:

var allow = true; 
var startUrl; 
var pollUrl; function Poll() { this.start = function start(start, poll) { startUrl = start; pollUrl = poll; if (request) { request.abort(); // abort any pending request } // fire off the request to MatchUpdateController var request = $.ajax({ url : startUrl, type : "get", }); // This is jQuery 1.8+ // callback handler that will be called on success request.done(function(reply) { console.log("Game on..." + reply); setInterval(function() { if (allow === true) { allow = false; getUpdate(); } }, 500); }); // callback handler that will be called on failure request.fail(function(jqXHR, textStatus, errorThrown) { // log the error to the console console.log("Start - the following error occured: " + textStatus, errorThrown); }); }; function getUpdate() { console.log("Okay let's go..."); if (request) { request.abort();  // abort any pending request } // fire off the request to MatchUpdateController var request = $.ajax({ url : pollUrl, type : "get", }); // This is jQuery 1.8+ // callback handler that will be called on success request.done(function(message) { console.log("Received a message"); var update = getUpdate(message); $(update).insertAfter('#first_row'); }); function getUpdate(message) { var update = "<div class='span-4  prepend-2'>" + "<p class='update'>Time:</p>" + "</div>" + "<div class='span-3 border'>" + "<p id='time' class='update'>" + message.matchTime + "</p>" + "</div>" + "<div class='span-13 append-2 last' id='update-div'>" + "<p id='message' class='update'>" + message.messageText + "</p>" + "</div>"; return update; }; // callback handler that will be called on failure request.fail(function(jqXHR, textStatus, errorThrown) { // log the error to the console console.log("Polling - the following error occured: " + textStatus, errorThrown); }); // callback handler that will be called regardless if the request failed or succeeded request.always(function() { allow = true; }); };  
};

名为Poll的类具有一个方法start() ,该方法带有两个参数。 浏览器使用第一个订阅匹配更新数据供稿,而第二个是用于轮询服务器以获取更新的URL。 从JQuery ready(…)函数调用此代码。

$(document).ready(function() {var startUrl = "matchupdate/subscribe";var pollUrl = "matchupdate/simple";var poll = new Poll();poll.start(startUrl,pollUrl);});

调用start()方法时,它将向服务器发出Ajax请求以订阅匹配更新。 当服务器以简单的“ OK”答复时, request.done(…)处理程序通过使用带有匿名函数作为参数的setInterval(…)启动1/2秒计时器。 此函数使用一个简单的标志“ allow ”,如果为true,则允许调用getUpdate()方法。 然后将allow标志设置为false,以确保不存在重入问题。

getUpdate(…)函数使用上述第二个URL参数对服务器进行另一个Ajax调用。 这次request.done(…)处理程序获取匹配更新并将其转换为HTML,并将其插入到“ first_row ” div之后以在屏幕上显示。

回到场景, 敏捷牛仔公司的首席执行官想打动他的新女友,于是他给她买了一辆保时捷911 。 现在他无法用自己的现金来支付,因为他的妻子会发现发生了什么事,所以他用电视公司交易中的一部分现金来支付。 这意味着他只能负担一名应届毕业生来编写服务器端代码。 这位毕业生可能没有经验,但是他确实重用了MessageMatchMatchReporter类以提供匹配更新。 请记住,将QueueMatch注入到MatchReporter 。 调用MatchReporter.start()方法时,它将加载匹配项并读取更新消息,并在其中检查其时间戳,并在适当的时候将其添加到队列中。 如果您想查看MatchReporterMatch等的代码,请查看原始博客 。

然后,该研究生将创建一个简单的Spring比赛更新控制器

@Controller() 
public class SimpleMatchUpdateController { private static final Logger logger = LoggerFactory.getLogger(SimpleMatchUpdateController.class); @Autowired private SimpleMatchUpdateService updateService; @RequestMapping(value = "/matchupdate/subscribe" + "", method = RequestMethod.GET) @ResponseBody public String start() { updateService.subscribe(); return "OK"; } /** * Get hold of the latest match report - when it arrives But in the process * hold on to server resources */ @RequestMapping(value = "/matchupdate/simple", method = RequestMethod.GET) @ResponseBody public Message getUpdate() { Message message = updateService.getUpdate(); logger.info("Got the next update in a really bad way: {}", message.getMessageText()); return message; } 
}

SimpleMatchUpdateController包含两个非常简单的方法。 第一个start()仅调用SimpleMatchUpdateService来订阅比赛更新,而第二个getUpdate()SimpleMatchUpdateService请求下一个比赛更新。 看到这一点,您可能会猜测所有工作都是由SimpleMatchUpdateService完成的。

@Service("SimpleService") 
public class SimpleMatchUpdateService { @Autowired @Qualifier("theQueue") private LinkedBlockingQueue<Message> queue; @Autowired @Qualifier("BillSkyes") private MatchReporter matchReporter; /** * Subscribe to a match */ public void subscribe() { matchReporter.start(); } /** * * Get hold of the latest match update */ public Message getUpdate() { try { Message message = queue.take(); return message; } catch (InterruptedException e) { throw new UpdateException("Cannot get latest update. " + e.getMessage(), e); } } }

SimpleMatchUpdateService也包含两个方法。 第一个, MatchReporter subscribe() ,告诉MatchReporter开始将更新放入队列中。 第二个getUpdate()Queue删除下一个更新,并将其作为JSON返回给浏览器以显示。

到目前为止,一切都很好; 但是,在这种情况下,队列是由LinkedBlockingQueue的实例实现的。 这意味着,如果浏览器发出请求时没有可用的更新,则请求线程将阻塞queue.take()方法,从而占用宝贵的服务器资源。 可用更新时, queue.take()返回并将Message发送到浏览器。 对于没有经验的研究生培训生来说,一切似乎都很好,并且该代码可以生效。 在接下来的周六,这是英超联赛的开始(如果您在美国,则是足球比赛),这是体育日历最繁忙的周末之一,并且大量用户希望获得有关大型比赛的最新信息。 当然,服务器资源不足,无法处理负载并不断崩溃。 电视公司的总裁对此不太满意,因此召集了敏捷牛仔公司的首席执行官到他的办公室。 他明确表示,如果不解决此问题,血液将流动。 Agile Cowboys的首席执行官意识到了自己的错误,并在与女友吵架后收回了保时捷。 然后,他通过电子邮件向Java / Spring顾问发送电子邮件,如果他要来修复代码,则向他提供保时捷。 Spring顾问不能拒绝这样的提议并接受。 这主要是因为他知道Servlet 3规范通过允许将ServletRequest置于异步模式来解决了这个问题。 这样可以释放服务器资源,但可以保持ServletResponse打开,从而允许其他一些第三方线程来完成处理。 他还知道Spring的家伙们在Spring 3.2中提出了一种新技术,称为“延迟结果”,该技术专为这些情况而设计。 同时,Agile Cowboys CEO的前女友仍对失去保时捷感到不安,并通过电子邮件将其妻子的全部情况告诉了她,告知她丈夫的外遇……

当这个博客变成达拉斯的一集时,我认为它的时代到此结束。 那么,代码会及时修复吗? Java / Spring顾问会花太多时间驾驶他的新保时捷吗? 首席执行官会原谅他的女朋友吗? 他的妻子会与他离婚吗? 有关这些问题的答案,以及下次有关Spring的DeferredResult技术调整的更多信息, …

您可能已经注意到示例代码中还有另一个巨大的漏洞,即只有一个订阅者的事实。 由于这仅是示例代码,而我在谈论的是长时间轮询,而不是实现发布和订阅,因此问题出在“主题之外”。 我稍后可能会(也可能不会)对其进行修复。

该博客随附的代码可在Github上找到:https://github.com/roghughe/captaindebug/tree/master/long-poll

参考:来自Captain Debug's Blog博客的JCG合作伙伴 Roger Hughes 提供的Spring Long Polling Tomcat 。

翻译自: https://www.javacodegeeks.com/2013/08/long-polling-tomcat-with-spring.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367494.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

exchange 删除邮件

一 批量删除特定主题的邮件1.1 批量删除所有数据库中特定主题的邮件1) 群发了几封主题为“backup”的邮件&#xff1b; 2) 当前操作账号需要满足如下需求&#xff1a; a)该账号需属于Exchange Server 管理员角色以及源服务器和目标服务器的本地 Administrator组&#xff1b; b)…

js点击取消按钮关闭当前弹框_UI设计中“取消按钮”的分析详解

按钮&#xff0c;无论是在 Web 还是 App 上都被广泛地使用&#xff0c;而很少有设计师会注意到按钮当中的细节&#xff0c;导致在设计过程中出现一些低级的错误&#xff0c;使得用户在完成任务的过程中产生阻碍&#xff0c;无法顺利达成目的。在许多优秀的产品中&#xff0c;关…

MATLAB飞机大战第二版,windows程序设计——飞机大战札记(单文档文件登陆界面)...

windows程序设计——飞机大战笔记(单文档文件登陆界面)//2015/07/21/by xbw////环境 VS 2013飞机大战做的差不多了&#xff0c;闲来无事加点高大上的东西&#xff0c;关于单文档的登陆界面&#xff1b;&#xff1b;&#xff1b;界面有点丑&#xff0c;但是足够账号登陆了&#…

吸收Mockito的流利度

我最近发现自己编写了一些代码来集成两个不同的平台。 这些系统之一是基于Java的系统&#xff0c;而另一个虽然不是用Java编写的&#xff0c;却提供了Java API。 我将这些系统分别称为Foo和Bar。 在编写一行代码之前就很明显了&#xff0c;但是&#xff0c;测试最终的适配器将…

软件工程第三次作业

题目要求 最大连续子数组和&#xff08;最大子段和&#xff09; 问题&#xff1a; 给定n个整数&#xff08;可能为负数&#xff09;组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]a[i1]…a[j]的子段和的最大值。 当所给的整数均为负数时定义子段和为0&#xff0c;依此定义&a…

使用HTML5 IndexDB存储图像和文件

使用IndexedDB存储图像和文件 有一天&#xff0c;我们写了关于如何在localStorage中保存图像和文件的文章&#xff0c;它是关于我们今天可用的实用主义。 然而&#xff0c;localStorage有一些性能影响 - 我们将在稍后的博客中讨论这个问题 - 并且未来期望的方法是使用IndexedD…

Gitlab 项目上传

一&#xff0c;登陆gitab&#xff0c;新建reject Repository name: 仓库名称 Description(可选): 仓库描述介绍 Public, Private : 仓库权限&#xff08;公开共享&#xff0c;私有或指定合作者&#xff09; Initialize this repository with a README: 添加一个README.md gitig…

stm32f103r6最小系统原理图_超强PCB布线设计经验谈附原理图

在当今激烈竞争的电池供电市场中&#xff0c;由于成本指标限制&#xff0c;设计人员常常使用双面板。尽管多层板(4层、6层及8层)方案在尺寸、噪声和性能方面具有明显 优势&#xff0c;成本压力却促使工程师们重新考虑其布线策略&#xff0c;采用双面板。在本文中&#xff0c;我…

[UE4GamePlay架构(九)GameInstance(转)

GameInstance这个类可以跨关卡存在&#xff0c;它不会因为切换关卡或者切换游戏模式而被销毁。然而&#xff0c;GameMode和PlayController就会再切换关卡或者游戏模式时被引擎销毁重置&#xff0c;这样他们里面的状态就不能被保存。比如&#xff0c;你想再下一个关卡中知道上一…

灵格斯怎么屏幕取词_电脑包尺寸怎么选?手提的好还是双肩的好?比较推荐哪款电脑包?...

在携带笔记本电脑的时候&#xff0c;一般都会用到电脑包&#xff0c;那么电脑包的尺寸该怎么选呢&#xff1f;为了能更好的装下电脑&#xff0c;电脑包的尺寸必须要比笔记本电脑的尺寸大一些&#xff0c;而不能刚刚好。笔记本的尺寸参数一般是指屏幕尺寸&#xff0c;而整个机身…

JSBridge实战

前言 H5 VS Native 一直是前端技术界争执不下的话题。react、vue等技术栈引领着纯H5开发&#xff0c;rn、week则倡导原生体验。但在项目实战中&#xff0c;经常会选择一个中立的方案&#xff1a;混合开发。大众称呼&#xff1a;Hybrid。 本人目前从事新闻类产品研发&#xff…

单元测试Java Hadoop作业

在我以前的文章中&#xff0c;我展示了如何设置一个完整的基于Maven的项目&#xff0c;以用Java创建Hadoop作业。 当然并没有完成&#xff0c;因为它缺少单元测试部分。 在本文中&#xff0c;我将展示如何将MapReduce单元测试添加到我之前开始的项目中。 对于单元测试&#xff…

vue2.0中的:is和is的区别

此文首发于 https://lijing0906.github.io/ 最近&#xff0c;工作之余在翻阅vue.js的官方文档&#xff0c;在查看到动态组件和解析 DOM 模板时的注意事项的时候&#xff0c;讲到一个特殊的is特性&#xff0c;觉得很有意思&#xff0c;就来写一篇自己理解的总结。 现场 写栗子…

mysql复制模式第二部分-----双主模式

双主配置 我在配置主从服务器时&#xff0c;使用了两台服务器&#xff1a;10.19.34.126和10.19.34.91。 1、首先需要在这两台上搭建单独的mysql服务masterA和masterB。 2、配置数据库masterA&#xff0c;要对每一个数据库服务配置唯一标示&#xff0c;参数名为server-id&#x…

pde中微元分析法的主要思想_初中数学常用的思想方法丨所有题型的考试技巧最全整理,高分必备...

【导语】初中数学虽然是基础数学&#xff0c;但是这并不意味着就没有难度&#xff0c;特别是在素质教育下&#xff0c;从培养学生综合素质能力的角度出发&#xff0c;初中数学越来越重视数学思维的培养&#xff0c;因此在很多数学问题的设置上&#xff0c;都进行了相当难度的调…

解决IntelliJ IDEA控制台乱码问题[包含程序运行时的log4j日志以及tomcat日志乱码]...

一、控制台打印的程序运行时的log4j日志中包含中文乱码 在IDEA安装目录的bin目录下找到名为"idea.exe.vmoptions"的文件&#xff1a; 使用文本编译软件(Notepad等)打开此文件&#xff0c;在文件内容从末尾追加一行设置&#xff08;-Dfile.encodingUTF-8&#xff09;&…

php识别地址,实现地址自动识别实例(PHP)

具体问题具体分析&#xff01;代码实现基于laravel完成。一个laravel完整的功能得具备这些&#xff1a;路由route&#xff0c;Model, View, Controller, 我这里用的有依赖注入服务容器等功能&#xff0c;当然&#xff0c;用到地址&#xff0c;你首先要有地址库。。。下面来看看…

kubernetes cpu限制参数说明

docker CPU限制参数 Option Description --cpus<value> Specify how much of the available CPU resources a container can use. For instance, if the host machine has two CPUs and you set --cpus"1.5", the container is guaranteed at most one and …

Java 8备忘单中的可选

Java 8 java.util.Optional<T>是scala.Option[T]和Data.Maybe在Haskell中的较差表亲。 但这并不意味着它没有用。 如果您不熟悉此概念&#xff0c;请将Optional想象为可能包含或不包含某些值的容器。 就像Java中的所有引用都可以指向某个对象或为null &#xff0c; Optio…

让 Chrome 崩溃的一行 CSS 代码

一般的 CSS 代码只会出现 UI 版式或者兼容性方面的小问题。但这里我们要分享一行有趣的 CSS&#xff0c;它可以直接让你的 Chrome 页面挂掉 :) 复现 在 Chrome 里打开一个稍复杂的页面&#xff0c;比如知乎或者掘金打开开发者工具&#xff0c;为页面 <body> 增加样式 s…