首页 > 其他分享 >SpringMVC 异步(长轮询)实现消息定点推送

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

时间:2023-05-18 09:56:51浏览次数:32  
标签:异步 return String SpringMVC 轮询 userId DeferredResult msg public

$(function () {  
      
    getMsg();  
  
      
});  
  
function getMsg()  
{  
    $.ajax({  
        url:"/polling/msg",  
        type:"get",  
        data:{},  
        success:function(data)  
        {  
            if(data != null && data!="")  
                alertShow(data.msg);  
              
            getMsg();  
        }  
    });  
}   
/** 
 *  
 * @author {chensg}:2016年6月1日 
 * example 
 * 
 */  
@Controller  
@RequestMapping("/polling/")  
public class PollingController {  
      
    @Autowired  
    MessageContainer messageContainer;  //全局存放每一个user创建的DeferredResult实例,key:userId,value:DeferredResult  
    @Autowired  
    RabbitTemplate rabbitTemplate;   
    /** 
     * 长轮询 
     * @return 
     */  
    @RequestMapping(value="msg", method=RequestMethod.GET)  
    public @ResponseBody DeferredResult<UserMessage> getMessage() {  
        final String userId = (UserDetails) SecurityContextHolder.getContext()  
    .getAuthentication()  
    .getPrincipal().getUsername();  
        DeferredResult<UserMessage> result = new DeferredResult<UserMessage>(30000l,null);  //设置超时30s,超时返回null  
        final Map<String, DeferredResult> resultMap=messageContainer.getUserMessages();  
        resultMap.put(userId, result);    
        result.onCompletion(new Runnable()   
        {    
            @Override    
            public void run() {    
                resultMap.remove(userId);    
            }    
        });    
          
        return result;  
    }  
      
    /** 
     * test 新增需要推给某某用户的消息 
     * @return 
     */  
    @RequestMapping(value="msg", method=RequestMethod.POST)  
    public @ResponseBody RestResult addMessage(String msg,String userId) {  
          
        UserMessage userMsg = new UserMessage();  
        userMsg.setUserId(userId);  
        userMsg.setMsg(msg);  
        //系统或者其他用户需要推送的消息放入消息队列  
        rabbitTemplate.convertAndSend("test.exchange", "test.binding", userMsg);  
          
        return null;  
    }  
}  

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

消息实体类

public class UserMessage implements Serializable {  
    /** 
     *  
     */  
    private static final long serialVersionUID = 1L;   
      
    private String userId;  
      
    private String msg;  
  
    public String getUserId() {  
        return userId;  
    }  
  
    public void setUserId(String userId) {  
        this.userId = userId;  
    }  
  
    public String getMsg() {  
        return msg;  
    }  
  
    public void setMsg(String msg) {  
        this.msg = msg;  
    }  
  
      
}  

配置rabbitMQ

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:rabbit="http://www.springframework.org/schema/rabbit"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.5.xsd">  
  
      
    <!-- 创建一个connectionFactory -->  
    <rabbit:connection-factory id="rabbitConnectionFactory"   
        host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}"  
        virtual-host="/" />  
      
    <!-- 创建一个rabbitTemplate, 设置retryTemplate -->  
    <rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory"  
        retry-template="retryTemplate" />  
      
    <!-- 创建一个retryTemplate -->  
    <bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">  
        <property name="backOffPolicy">  
        <bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">  
            <property name="initialInterval" value="500" />  
            <property name="multiplier" value="10.0" />  
            <property name="maxInterval" value="10000" />  
        </bean>  
    </property>  
    </bean>  
      
    <rabbit:admin connection-factory="rabbitConnectionFactory" />  
      
    <!-- 创建一个用于消息推送的队列 -->  
    <rabbit:queue id="testQueue" name="test.polling" />  
      
    <rabbit:direct-exchange name="test.exchange">  
        <rabbit:bindings>  
            <rabbit:binding queue="test.polling" key="test.binding" />  
        </rabbit:bindings>  
    </rabbit:direct-exchange>  
      
    <!-- 创建一个消息处理器 -->  
    <bean id="servicePollingHandler"   
        class="com.xxx.controller.test.ServicePollingHandler" />  
  
    <!-- 绑定监听器和队列 -->  
    <rabbit:listener-container connection-factory="rabbitConnectionFactory">  
        <rabbit:listener ref="servicePollingHandler"  
            method="testPollingHandle"  
            queues="testQueue" />  
    </rabbit:listener-container>  
      
</beans>  
public class ServicePollingHandler {  
      
    @Autowired  
    MessageContainer messageContainer;  
      
    public void testPollingHandle(UserMessage userMessage)  
    {  
        Map<String, DeferredResult> msgContainer = messageContainer.getUserMessages();  
        DeferredResult<UserMessage> deferredResult = msgContainer.get(userMessage.getUserId());    
  
                if (deferredResult!=null){    
            deferredResult.setResult(userMessage);  //调用setResult(),线程返回信息。  
        }    
    }  
}  
@PropertySource(value="classpath:application.properties")  
@ImportResource({"classpath:amqp.xml"})  
public class RootConfig {  
  
    @Bean  
    public MessageContainer messageContainer() {  
        return new MessageContainer();  
    }  
      
}  
public class MessageContainer {  
  
    private ConcurrentHashMap<String, DeferredResult> userMessages = new ConcurrentHashMap<String, DeferredResult>();   //线程安全  
  
    public ConcurrentHashMap<String, DeferredResult> getUserMessages() {  
        return userMessages;  
    }  
      
}  

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

可以基于以上代码,实现web聊天
转自http://chenshangge.iteye.com/blog/2302710

标签:异步,return,String,SpringMVC,轮询,userId,DeferredResult,msg,public
From: https://www.cnblogs.com/codeLearn/p/17410997.html

相关文章

  • WPF 异步加载数据,窗体事件
    加载WPF界面时,需要获取数据,而数据返回的时间比较长,这个时候可以异步加载数据到界面。 1、首先在XAML中触发窗口载入事件 2、在后台代码中处理窗口载入事件(1)找到主窗口类 (2)在MainWindow类中添加XAML中加入的窗口载入事件 这个事件中可以放置各种界面预处理代码 n......
  • 基于转子磁链定向的异步电机矢量控制系统仿真模型 转速在2
    基于转子磁链定向的异步电机矢量控制系统仿真模型转速在2秒由500变为1400,3秒由空载变为额定负载的图如下。转速可调。ID:88160676868051065......
  • 异步编排多任务异步执行
    参考: https://blog.csdn.net/teachy/article/details/104971814创建线程池:/***自定义线程池*intcorePoolSize,核心线程数=CPU核数*intmaximumPoolSize,最大线程数=CPU核数*2*longkeepAliveTime,等待时间,线程池线程>核心线程数,没......
  • vue elementui validate异步校验改成同步校验返回结果
     异步的校验 <script>import{defineComponent,ref}from'vue'exportdefaultdefineComponent({methods:{getFormDataStatus(){letresult=ref(false)this.ruleForm.validate((valid)=>{if(valid){......
  • Spring+SpringMVC19_Spring练习-业务实现2
    六、用户表和角色表的分析  七、角色列表展示分析  八、角色列表展示-controller层实现  九、角色列表展示-service和dao层实现  十、角色列表展示-配置实现  十一、角色列表展示-页面展示  十二、角色的添加操作  十三、用户列表展示1 ......
  • 初识Springmvc
    1.什么是springmvc1).轻量级的框架2).基于spring的IOC和AOP进行封装3).基于Servlet进行封装4).实现了mvc设计思想的框架2.springmvc的执行流程图1).浏览器发送http请求2).前端控制器DispatcherServlet负责接收所有的请求3).前端控制器调度处理器映射器HandlerMapping处理请......
  • 全注解springMVC实例20230517
     1、pom<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.3.13.RELEASE</version></dependency><dependency&g......
  • 使复杂异步方法变为同步。用promise阻塞
    使复杂异步方法变为同步假如有一个functionasyncFather(){awaitA();awaitb()},需要等方法A执行完,再继续往下执行B的代码,而A()中有很多东西,可能会导致异步。则可在A()内写成promise(resolve()),在A()内所有方法执行完毕后,再执行promise的resolve(),使得Father()在等到A()的resole才......
  • 又踩坑了!SpringMVC+ant+tomcat实现自动化打包,原来这么多坑
    背景是项目使用的是tomcat进行打包,没有使用Maven,需要构建一个ant执行的文件,用于后期可以在实际中使用,运行的平台是Windows。在使用中遇到了不少的坑点,一一记录下来,方便后期或者和我遇到相同问题的朋友们查看和借鉴。错误:打包报错[2023-05-16 10:35:23,164] Artifact meta......
  • 异步消息的传递-回调机制
    1什么是回调软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消......