在我们的最后一集中 , Agile Cowboys Inc.的首席执行官刚刚雇用了Java / Spring顾问,方法是为他提供最初为女友购买的保时捷。 这位首席执行官的女友因失去保时捷而感到不安,已将其婚外情告诉了他的妻子。 他的妻子在分拆了CEO的套房后已申请离婚。 同时,首席执行官在办公室实施了新的“休闲”着装要求,而Java / Spring顾问刚从他的新保时捷换乘回来,正坐在办公桌旁准备修理电视公司的软件……如果不这样做的话对您没有任何意义,然后看一下使用Spring的Long Polling Tomcat 。
Java / Spring顾问必须在下一个大型游戏之前解决电视公司的服务器资源问题,他知道他可以通过使用在Tomcat 7 1上实现的Servlet 3规范实现Spring的Deferred Result技术来解决此问题。
Java / Spring顾问要做的第一件事是检查 项目的pom.xml
文件。 对于异步Servlet 3项目,必须包括以下依赖项:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
接下来,您必须告诉Tomcat Spring DispatcherServlet
支持Servlet 3异步通信。 这是通过将以下行添加到web.xml
:
<async-supported>true</async-supported>
完整的DispatcherServlet
配置为:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
整理完项目配置后,Java / Spring ConsultantSwift进入了控制器代码。 他用新的DeferredMatchUpdateController
代替了研究生培训生的SimpleMatchUpdateController
:
@Controller()
public class DeferredMatchUpdateController {
@Autowired
private DeferredResultService updateService;
@RequestMapping(value = "/matchupdate/begin" + "", method = RequestMethod.GET)
@ResponseBody
public String start() {
updateService.subscribe();
return "OK";
}
@RequestMapping("/matchupdate/deferred")
@ResponseBody
public DeferredResult<Message> getUpdate() {
final DeferredResult<Message> result = new DeferredResult<Message>();
updateService.getUpdate(result);
return result;
}
}
新的DeferredMatchUpdateController
非常简单。 与SimpleMatchUpdateController
一样,它包含两个方法: start()
和getUpdate()
,它们的功能与简单方法完全相同。 这使该控制器成为SimpleMatchUpdateController
的插件替代品。 最大的不同是, getUpdate()
方法创建Spring的DeferredResult
的实例,该实例在将其返回给Spring之前将其传递给新的DeferredResultService
。 然后Spring停放HTTP请求,使其挂起,直到DeferredResult
对象具有一些要返回到浏览器的数据为止。
@Service("DeferredService")
public class DeferredResultService implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(DeferredResultService.class);
private final BlockingQueue<DeferredResult<Message>> resultQueue = new LinkedBlockingQueue<>();
private Thread thread;
private volatile boolean start = true;
@Autowired
@Qualifier("theQueue")
private LinkedBlockingQueue<Message> queue;
@Autowired
@Qualifier("BillSkyes")
private MatchReporter matchReporter;
public void subscribe() {
logger.info("Starting server");
matchReporter.start();
startThread();
}
private void startThread() {
if (start) {
synchronized (this) {
if (start) {
start = false;
thread = new Thread(this, "Studio Teletype");
thread.start();
}
}
}
}
@Override
public void run() {
while (true) {
try {
DeferredResult<Message> result = resultQueue.take();
Message message = queue.take();
result.setResult(message);
} catch (InterruptedException e) {
throw new UpdateException("Cannot get latest update. " + e.getMessage(), e);
}
}
}
public void getUpdate(DeferredResult<Message> result) {
resultQueue.add(result);
}
}
同样,像其对应的SimpleMatchUpdateService
, DeferredResultService
包含两个方法: subscribe()
和getUpdate()
与处理getUpdate(...)
它的作用是为新创建的添加DeferredResult
对象到LinkedBlockingQueue
叫resultQueue
,以便它可以在以后当比赛更新可用来处理。
实际工作是通过subscribe()
方法完成的。 首先,此方法启动matchReporter
,它在适当的时候将匹配更新输入到自动装配的queue
实例中。 然后,它调用私有的startThread()
方法来启动工作线程。 该操作仅启动一次,并使用双重检查锁定来确保高效且没有问题。
线程的run()
方法首先无限循环,从resultQueue
(如果有)中获取DeferredResult
对象,然后是Message
对象,该对象代表来自update queue
的匹配更新(如果有)。 然后,使用message
对象作为参数调用DeferredResult
的setResult(...)
。 Spring现在将接管工作,原始的长轮询请求将完成,并且数据将延迟地返回给浏览器。
请注意,在此示例代码中,
run()
方法包含while(true)
循环。 尽管此技术简化了示例代码,但在生产代码方面并不是一个好主意。 使用任性的不受控制的线程的问题之一是它们会阻止Tomcat正确关闭,并且您通常必须使用良好的Unixkill
命令来停止服务器。 在生产代码中,最好包含代码以正常关闭此类工作线程。
经过数小时的辛苦工作,Java / Spring顾问将其代码推广到生活中,拿起保时捷的钥匙,然后旋转一下。 下个星期六,使用Spring的DeferredResult
,服务器可以很好地应对:用户很高兴,电视公司的总裁很高兴, Agile Cowboys Inc的首席执行官很高兴,尽管他一直怀疑自己付给顾问的钱太多了,嘿,这只是钱。
1在撰写此博客时,我使用的是Tomcat 7.0.42版
该博客随附的代码可在Github上找到: https : //github.com/roghughe/captaindebug/tree/master/long-poll
翻译自: https://www.javacodegeeks.com/2013/09/long-polling-with-spring-3-2s-deferredresult.html