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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM