SpringMVC 异步(长轮询)实现消息定点推送

Javascript代码
 
  1. $(function () {  
  2.       
  3.     getMsg();  
  4.   
  5.       
  6. });  
  7.   
  8. function getMsg()  
  9. {  
  10.     $.ajax({  
  11.         url:"/polling/msg",  
  12.         type:"get",  
  13.         data:{},  
  14.         success:function(data)  
  15.         {  
  16.             if(data != null && data!="")  
  17.                 alertShow(data.msg);  
  18.               
  19.             getMsg();  
  20.         }  
  21.     });  
  22. }   



Java代码
 
  1. /** 
  2.  *  
  3.  * @author {chensg}:2016年6月1日 
  4.  * example 
  5.  * 
  6.  */  
  7. @Controller  
  8. @RequestMapping("/polling/")  
  9. public class PollingController {  
  10.       
  11.     @Autowired  
  12.     MessageContainer messageContainer;  //全局存放每一个user创建的DeferredResult实例,key:userId,value:DeferredResult  
  13.     @Autowired  
  14.     RabbitTemplate rabbitTemplate;   
  15.     /** 
  16.      * 长轮询 
  17.      * @return 
  18.      */  
  19.     @RequestMapping(value="msg", method=RequestMethod.GET)  
  20.     public @ResponseBody DeferredResult<UserMessage> getMessage() {  
  21.         final String userId = (UserDetails) SecurityContextHolder.getContext()  
  22.     .getAuthentication()  
  23.     .getPrincipal().getUsername();  
  24.         DeferredResult<UserMessage> result = new DeferredResult<UserMessage>(30000l,null);  //设置超时30s,超时返回null  
  25.         final Map<String, DeferredResult> resultMap=messageContainer.getUserMessages();  
  26.         resultMap.put(userId, result);    
  27.         result.onCompletion(new Runnable()   
  28.         {    
  29.             @Override    
  30.             public void run() {    
  31.                 resultMap.remove(userId);    
  32.             }    
  33.         });    
  34.           
  35.         return result;  
  36.     }  
  37.       
  38.     /** 
  39.      * test 新增需要推给某某用户的消息 
  40.      * @return 
  41.      */  
  42.     @RequestMapping(value="msg", method=RequestMethod.POST)  
  43.     public @ResponseBody RestResult addMessage(String msg,String userId) {  
  44.           
  45.         UserMessage userMsg = new UserMessage();  
  46.         userMsg.setUserId(userId);  
  47.         userMsg.setMsg(msg);  
  48.         //系统或者其他用户需要推送的消息放入消息队列  
  49.         rabbitTemplate.convertAndSend("test.exchange", "test.binding", userMsg);  
  50.           
  51.         return null;  
  52.     }  
  53. }  



页面加载完成时,该用户请求/polling/msg控制器接口,接口里会创建一个DeferredResult实例,设置超时30S,超时返回null。DeferredResult<?> 允许应用程序从一个线程中返回,而何时返回则由线程决定。 
 
消息实体类 

Java代码  收藏代码
  1. public class UserMessage implements Serializable {  
  2.     /** 
  3.      *  
  4.      */  
  5.     private static final long serialVersionUID = 1L;   
  6.       
  7.     private String userId;  
  8.       
  9.     private String msg;  
  10.   
  11.     public String getUserId() {  
  12.         return userId;  
  13.     }  
  14.   
  15.     public void setUserId(String userId) {  
  16.         this.userId = userId;  
  17.     }  
  18.   
  19.     public String getMsg() {  
  20.         return msg;  
  21.     }  
  22.   
  23.     public void setMsg(String msg) {  
  24.         this.msg = msg;  
  25.     }  
  26.   
  27.       
  28. }  



配置rabbitMQ 

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xmlns:rabbit="http://www.springframework.org/schema/rabbit"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  6.         http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.5.xsd">  
  7.   
  8.       
  9.     <!-- 创建一个connectionFactory -->  
  10.     <rabbit:connection-factory id="rabbitConnectionFactory"   
  11.         host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}"  
  12.         virtual-host="/" />  
  13.       
  14.     <!-- 创建一个rabbitTemplate, 设置retryTemplate -->  
  15.     <rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory"  
  16.         retry-template="retryTemplate" />  
  17.       
  18.     <!-- 创建一个retryTemplate -->  
  19.     <bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">  
  20.         <property name="backOffPolicy">  
  21.         <bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">  
  22.             <property name="initialInterval" value="500" />  
  23.             <property name="multiplier" value="10.0" />  
  24.             <property name="maxInterval" value="10000" />  
  25.         </bean>  
  26.     </property>  
  27.     </bean>  
  28.       
  29.     <rabbit:admin connection-factory="rabbitConnectionFactory" />  
  30.       
  31.     <!-- 创建一个用于消息推送的队列 -->  
  32.     <rabbit:queue id="testQueue" name="test.polling" />  
  33.       
  34.     <rabbit:direct-exchange name="test.exchange">  
  35.         <rabbit:bindings>  
  36.             <rabbit:binding queue="test.polling" key="test.binding" />  
  37.         </rabbit:bindings>  
  38.     </rabbit:direct-exchange>  
  39.       
  40.     <!-- 创建一个消息处理器 -->  
  41.     <bean id="servicePollingHandler"   
  42.         class="com.xxx.controller.test.ServicePollingHandler" />  
  43.   
  44.     <!-- 绑定监听器和队列 -->  
  45.     <rabbit:listener-container connection-factory="rabbitConnectionFactory">  
  46.         <rabbit:listener ref="servicePollingHandler"  
  47.             method="testPollingHandle"  
  48.             queues="testQueue" />  
  49.     </rabbit:listener-container>  
  50.       
  51. </beans>  



Java代码  收藏代码
  1. public class ServicePollingHandler {  
  2.       
  3.     @Autowired  
  4.     MessageContainer messageContainer;  
  5.       
  6.     public void testPollingHandle(UserMessage userMessage)  
  7.     {  
  8.         Map<String, DeferredResult> msgContainer = messageContainer.getUserMessages();  
  9.         DeferredResult<UserMessage> deferredResult = msgContainer.get(userMessage.getUserId());    
  10.   
  11.                 if (deferredResult!=null){    
  12.             deferredResult.setResult(userMessage);  //调用setResult(),线程返回信息。  
  13.         }    
  14.     }  
  15. }  

 

Java代码  收藏代码
  1. @PropertySource(value="classpath:application.properties")  
  2. @ImportResource({"classpath:amqp.xml"})  
  3. public class RootConfig {  
  4.   
  5.     @Bean  
  6.     public MessageContainer messageContainer() {  
  7.         return new MessageContainer();  
  8.     }  
  9.       
  10. }  




Java代码  收藏代码
  1. public class MessageContainer {  
  2.   
  3.     private ConcurrentHashMap<String, DeferredResult> userMessages = new ConcurrentHashMap<String, DeferredResult>();   //线程安全  
  4.   
  5.     public ConcurrentHashMap<String, DeferredResult> getUserMessages() {  
  6.         return userMessages;  
  7.     }  
  8.       
  9. }  



该例子的用途,当一个用户登录页面时,异步请求后台/polling/msg,后台创建一个线程,维持改长连接30s,当超时或者返回信息,页面则再次请求后台,维持一个30s的长连接(长轮询)。 
系统或者其他用户调用/polling/msg  method:post,传入msg与userId,控制器把消息放入消息队列,消息队列把消息推送到ServicePollingHandler类testPollingHandle()方法,该方法根据userId获得该用户登陆之后的页面长轮询创建的deferredResult实例,调用setResult,页面接受到线程返回消息。 

可以基于以上代码,实现web聊天 

 

转自:http://chenshangge.iteye.com/blog/2302710

posted @ 2016-10-26 09:16  N神3  阅读(8458)  评论(0编辑  收藏  举报