• Spring MVC 实现web Socket向前端实时推送数据


      最近项目中用到了webSocket服务,由后台实时向所有的前端推送消息,前端暂时是不可以发消息给后端的,数据的来源是由具体的设备数据收集器收集起来,然后通过socket推送给后端,后端收到数据后,再将这些数据推送给前端。

      听起来业务逻辑有点复杂。其实单独的实现socket或websocket都比较简单,但是二者之间的数据传输问题,困扰了我很久。也想过用redis做一个消息队列,将socket接收到的数据处理后丢进去,然后再用websocket从redis里取出数据,再推送给前端。

      但是。问题来了,这么配置的话,一个简单的功能,要另外再加一个服务出来,配置起来好麻烦的感觉。再说了,目前的业务逻辑是,数据不做任何处理就直接扔出去了,干嘛要一层一层的来设计这些东西呢?虽然它有很好的模式和很高的扩展性,可我就是懒的去写多余的代码来配置这些东西。so,本着能懒就懒的原则,我整出来一套自己适合的方案来做这个事情。

      思路:socket推送给后端的数据是实时的,有则推送,没有就一边呆着,等消息发过来。所以呢,我干嘛不弄个http接口来接收呢,本来数据就不多,老半天才会推一条出来,有时候一天都不会有几条数据,所以,搞一个socket还不如直接提供一个HTTP接口来接收数据来的划算,关键是代码写起来简单啊。所以就有了这个:

    @RequestMapping(value = "/socket", method = {RequestMethod.POST, RequestMethod.GET})
        public void webSocket(HttpServletRequest request) {
            Map map = request.getParameterMap();
            ...
        }

      这个东西没啥可说的,不用测试都知道没问题。

      好了,数据是接收到了,怎么发送给前端呢?我的想法是,把推送前端的代码直接写到上面的代码体里面,这样就能接到一个推送就直接广播给前端,接不到数据就不推送,多好啊。

      想象中的代码应该是这样的:

    @RequestMapping(value = "/socket", method = {RequestMethod.POST, RequestMethod.GET})
        public void webSocket(HttpServletRequest request) {
            Map map = request.getParameterMap();
            ...
            // sendMessageToFront(message);
        }

      如果想这样写,要么使用标签注解,要么自定义一个方法,继承websocket来实现功能。but how?

      <坑里的生活就不播了,直接写出坑后的成果吧>

      标签注解的方式或许可以实现,但是,这样以来,就有三个URI提供给前端了,一个用来握手,一个用来发送消息,一个用来接收消息。好吧,前端也以懒为天,能少写一个字母绝不多加半个符号。so,我的这种方案直接被否决了,所以得另寻出路。

      然后就是写个方法继承websocket来实现这个功能了。代码是这样的:

    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    
        @Resource
        private AliceWebSocketHandler webSocketHandler;
    
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(webSocketHandler, "/webSocket").setAllowedOrigins("*")
                    .addInterceptors(new AliceHandShakeInterceptor());
            registry.addHandler(webSocketHandler, "/webSockJs").setAllowedOrigins("*")
                    .addInterceptors(new AliceHandShakeInterceptor()).withSockJS();
        }
    }
    @Component
    public class AliceHandShakeInterceptor extends HttpSessionHandshakeInterceptor {
    
        @Override
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
            return super.beforeHandshake(request, response, wsHandler, attributes);
        }
    
        @Override
        public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
            super.afterHandshake(request, response, wsHandler, exception);
        }
    }
    @Component
    public class AliceWebSocketHandler extends TextWebSocketHandler {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(AliceWebSocketHandler.class);
    
        private static Map<String, WebSocketSession> SESSION_MAP = Maps.newConcurrentMap();
    
        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            LOGGER.debug("[{} : {}] has be connected...", session.getUri(), session.getId());
            SESSION_MAP.put(session.getId(), session);
        }
    
        @Override
        public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
    
        }
    
        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
            LOGGER.debug("[{} : {}]", session.getUri(), session.getId());
            SESSION_MAP.remove(session.getId());
        }
    
        @Override
        public boolean supportsPartialMessages() {
            return false;
        }
    
        /**
         * 群发消息
         */
        public void broadcast(final TextMessage message) throws IOException {
            for (Map.Entry<String, WebSocketSession> entry : SESSION_MAP.entrySet()) {
                if (entry.getValue().isOpen()) {
                    new Thread(() -> {
                        try {
                            if (entry.getValue().isOpen()) {
                                entry.getValue().sendMessage(message);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }).start();
                }
            }
        }
    }

      完事,方法体中,就直接调用broadcast方法就行了,推送消息服务完成。

  • 相关阅读:
    最大值及下标值
    查找整数
    打印沙漏
    抓老鼠啊~亏了还是赚了?
    币值转换
    秋季学期学习总结
    菜鸟学习Spring——SpringIoC容器基于三种配置的对比
    yanxin8文章归档
    2014——2015总结
    Java入门到精通——调错篇之Spring2.5利用aspect实现AOP时报错: error at ::0 can't find referenced pointcut XXX
  • 原文地址:https://www.cnblogs.com/SummerinShire/p/7262414.html
Copyright © 2020-2023  润新知