用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,一经查实,立即删除!

相关文章

.net core 编译时报错 Package Microsoft.Composition 1.0.27 is not compatible with netcoreapp1.1

查找错误项目 通过命令行cd到项目位置&#xff0c;执行dotnet restore命令查看具体报错项目。 解决方案 去项目中找到项目解决方案.csproj > 编辑 > PropertyGroup节点 > 添加如下代码 <PropertyGroup><TargetFramework>netcoreapp1.1</TargetFramewor…

支援一波 《面试数十人有感》

戳原文 无意间看到原博主的这篇文章&#xff0c;强调基础和原理&#xff0c;略有同感。 然而却发现评论区简直 ~ 辣眼睛 ~ 比如 活在梦里的面试官 我觉着吧&#xff0c;技术迭代的这么快&#xff0c;公司更需要的是有能力、有思想解决技术问题的人&#xff0c;并不代表说基础…

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…

Java Servlet的前100个问题

1&#xff09;是“ servlets”目录还是“ servlet”目录&#xff1f; 回答&#xff1a; 对于Java Web Server&#xff1a; 在文件系统上&#xff0c;它是“ servlet” c&#xff1a;\ JavaWebServer1.1 \ servlets \ DateServlet.class 在URL路径中&#xff0c;它是“ servl…

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

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

[UE4GamePlay架构(九)GameInstance(转)

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

php+原生代码+赋值,js重新赋值原生的方法

js的原生方法被覆盖掉以后&#xff0c;如果你还没让原生方法又从新指向一个新的变量名&#xff0c;那就gg了。所以&#xff0c;关键就是怎么再获取到原生的方法。实现的原理呢就是创建一个新的window对象&#xff0c;然后从新的window对象里面获取原生的方法&#xff0c;来重新…

python基础知识 - Day4

String相关的方法&#xff1a; 1. 字符串格式化 format "hello, %s. %s enough for you" values (cloris,hot) print (format % values) 2. 字符串方法 - find() 可以在一个较长的字符串中查找子字符串。返回子串所在位置的最左端索引。如果没有找到&#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…

【bzoj4008 hnoi2015】 亚瑟王

题目描述 小 K 不慎被 LL 邪教洗脑了&#xff0c;洗脑程度深到他甚至想要从亚瑟王邪教中脱坑。他决定&#xff0c;在脱坑之前&#xff0c;最后再来打一盘亚瑟王。既然是最后一战&#xff0c;就一定要打得漂亮。众所周知&#xff0c;亚瑟王是一个看脸的游戏&#xff0c;技能的发…

python 编码声明位置,python中的编码声明

python中的第一行&#xff0c;目的就是指出&#xff0c;你想要你的这个文件中的代码用什么可执行程序去运行它&#xff0c;就这么简单#!/usr/bin/python 相当于写死了 python 路径(是告诉OS执行这个py时&#xff0c;调用 /usr/bin 下的 python 解释器&#xff1b;)#!/usr/bin/e…

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

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