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方法就行了,推送消息服务完成。

posted @ 2017-07-31 11:56  王云十三  阅读(21491)  评论(2编辑  收藏  举报