一、短轮询方式,客户端定时向服务器请求,服务器收到请求后立即返回
@Controller
public class ShowTimeController {
private static Logger logger = LoggerFactory.getLogger(ShowTimeController.class);
@RequestMapping("/time")
public String normal(){
return "showtime";
}
@RequestMapping(value="/showTime",produces = "text/html;charset=UTF-8")
@ResponseBody
public String getTime(){
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return formatter.format(new Date());
}
}
二、长轮询,客户端向服务器请求,服务器先阻塞 有数据后再返回给客户端,使用了DeferredResult类,进行阻塞
@Controller
@RequestMapping(produces="text/html;charset=UTF-8")
/*记得要在WebInitializer中增加servlet.setAsyncSupported(true);*/
public class PushNewsController {
private ExecutorService executorService
= Executors.newFixedThreadPool(1);
@RequestMapping("/pushnews")
public String news(){
return "pushNews";
}
@RequestMapping(value="/realTimeNews")
@ResponseBody
/*在WebInitializer中要加上servlet.setAsyncSupported(true);*/
public DeferredResult<String> realtimeNews(HttpServletRequest request){
final DeferredResult<String> dr = new DeferredResult<String>();
executorService.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int index = new Random().nextInt(Const.NEWS.length);
dr.setResult(Const.NEWS[index]);
}
});
return dr;
}
}
三、sse技术,长连接,非轮询,原生实现sse,response.setContentType("text/event-stream")进行了声明,需要浏览器支持
@Controller
public class NobleMetalController {
private static Logger logger = LoggerFactory.getLogger(NobleMetalController.class);
@RequestMapping("/nobleMetal")
public String stock(){
return "nobleMetal";
}
@RequestMapping(value="needPrice")
@ResponseBody
public void push(HttpServletResponse response){
response.setContentType("text/event-stream");
response.setCharacterEncoding("utf-8");
Random r = new Random();
int sendCount = 0;/*服务器数据发送完*/
try {
PrintWriter pw = response.getWriter();
while(true){
if(pw.checkError()){
System.out.println("客户端断开连接");
return;
}
Thread.sleep(1000);
//字符串拼接
StringBuilder sb = new StringBuilder("");
sb//.append("retry:2000\n")
.append("data:")
.append((r.nextInt(1000)+50)+",")
.append((r.nextInt(800)+100)+",")
.append((r.nextInt(2000)+150)+",")
.append((r.nextInt(1500)+100)+",")
.append("\n\n");
pw.write(sb.toString());
pw.flush();
sendCount++;
if(sendCount>=100){
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、spring整合的sse实现
@Controller
public class SseController {
private static Logger logger = LoggerFactory.getLogger(SseController.class);
private static Map<String,SseEmitter> sseEmitters
= new ConcurrentHashMap<>();
private ExecutorService executorService
= Executors.newFixedThreadPool(2);
@RequestMapping("/weChatPay")
public String stock(){
return "weChatPay";
}
@RequestMapping(value="/payMoney")
@ResponseBody
public SseEmitter pay(String weCharId){
SseEmitter emitter = new SseEmitter();
sseEmitters.put(weCharId,emitter);
executorService.submit(new Pay(weCharId) );
return emitter;
}
private static class Pay implements Runnable{
private String weCharId;
public Pay(String weCharId) {
this.weCharId = weCharId;
}
@Override
public void run() {
SseEmitter sseEmitter = sseEmitters.get(weCharId);
try {
logger.info("联系支付服务,准备扣款");
Thread.sleep(500);
sseEmitter.send("支付完成");
logger.info("准备通知自动售货机");
Thread.sleep(1500);//售货机的动作
sseEmitter.send("已通知自动售货机C9出货,请勿走开!");
sseEmitter.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
注意:二三四需要加配置类
//WebApplicationInitializer 相当于web.xml文件
public class WebInit implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext)
throws ServletException {
AnnotationConfigWebApplicationContext ctx
= new AnnotationConfigWebApplicationContext();
ctx.register(CometMvcConfig.class);
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher",
new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);//启用异步
}
}
下面这个配置是处理前端模板和静态资源
@Configuration
@EnableWebMvc
@EnableScheduling
@ComponentScan("cn.enjoyedu")
//WebMvcConfigurerAdapter spring的视图处理配置类,解决跨域,配置静态资源等
public class CometMvcConfig extends WebMvcConfigurerAdapter{
//InternalResourceViewResolver 视图解析器
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
//配置静态资源
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**")
.addResourceLocations("/assets/");
}
}