The push server message SpringBoot

After reading with the group Friends "Weather son", a human suddenly realized God is in communication with some of them selected as a connection carrier, then unidirectional communication (human upwardly days). This time on the thought JAVAof communication between the server and the client.

WebSocket

I believe this is not some small partner familiar with it, are the most contact with the server-side push message is selected websocket. After all, our business scenario most of them communicate with each other.

The main code is as follows:

package com.montos.endpoint;

import com.montos.config.SessionMap;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;

/**
 * @Author: Montos
 * @Date:2019/11/26 3:04 下午
 * @Version 1.0
 * 服务节点
 */
@Component
@ServerEndpoint(value = "/websocket/{userId}")
public class ServiceWebSocketServer {

    // 这是要返回到界面上的会话
    private Session session;
    // 连接的用户id
    private String userId;

    /**
     * 会话开启
     *
     * @param userId
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("userId") String userId, Session session) {
        this.session = session;
        this.userId = userId;
        SessionMap.putSession(userId, session);
        try {
            this.send(session, String.format("欢迎你的加入%s", userId));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 会话关闭
     */
    @OnClose
    public void onClose() {
        SessionMap.removeSession(this.userId);
    }

    /**
     * 会话出错
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }


    /**
     * 发送消息
     *
     * @param message
     * @throws IOException
     */
    @OnMessage
    public void sendMessage(String message) throws IOException {
        Map<String, Session> allSessionMap = SessionMap.getAllSessionMap();
        for (Map.Entry<String, Session> map : allSessionMap.entrySet()) {
            if (map.getKey().equalsIgnoreCase(userId)) {
                continue;
            } else {
                this.send(map.getValue(), message);
            }
        }
    }

    /**
     * 发送消息
     *
     * @param message
     * @throws IOException
     */
    public void send(Session session, String message) throws IOException {
        if (session.isOpen()) {
            session.getAsyncRemote().sendText(message);
        } else {// 有可能关闭了 此时等待5s     再次进行 发送,前端会有对应的轮询任务
            try {
                Thread.sleep(5000);
                session.getBasicRemote().sendText(message);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
复制代码

In SpringBootthe Note that, if you are jarpackage runs, you need to manually injected in a container ServerEndpointExporterthat Bean (not loaded, the connection is not on the session).

Posted just above the main connection session, the session close operation method and the like, there is also a collection class, stored in the corresponding user session ( websocketin Sessioncan not be serialized, meaning it can not be stored in the sequence of the other ) on the device.

SseEmitter

SseEmitterIs a push message server to the client in the art, this may also need to be connected before the operation, but can not communicate directly with the server. And above there are some differences.

The main code is as follows:

package com.montos.sseemitter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: Montos
 * @Date:2019/11/26 9:12 上午
 * @Version 1.0
 * * 1.请求http://localhost:8888/sse/start?userId=111接口,浏览器会阻塞,等待服务器返回结果;
 * * 2.请求http://localhost:8888/sse/send?userId=111接口,可以请求多次,并观察第1步的浏览器返回结果;
 * * 3.请求http://localhost:8888/sse/end?userId=111接口结束某个请求,第1步的浏览器将结束阻塞;
 */


@RestController
@RequestMapping("/sse")
public class SseEmitterController {
    private static final Logger logger = LoggerFactory.getLogger(SseEmitterController.class);

    // 用于保存每个请求对应的 SseEmitter
    private Map<String, Result> sseEmitterMap = new ConcurrentHashMap<>();

    /**
     * 返回SseEmitter对象
     *
     * @param userId
     * @return
     */
    @RequestMapping("/start")
    public SseEmitter testSseEmitter(String userId) {
        // 默认30秒超时,设置为0L则永不超时
        SseEmitter sseEmitter = new SseEmitter(0L);
        sseEmitterMap.put(userId, new Result(userId, System.currentTimeMillis(), sseEmitter));
        logger.info(String.format("testSseEmitter is start"));
        return sseEmitter;
    }

    /**
     * 向SseEmitter对象发送数据
     *
     * @param userId
     * @return
     */
    @RequestMapping("/send")
    public String setSseEmitter(String userId) {
        try {
            Result result = sseEmitterMap.get(userId);
            if (result != null && result.sseEmitter != null) {
                String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
                result.sseEmitter.send(format);
            }
        } catch (IOException e) {
            logger.error("IOException!", e);
            return "error";
        }
        return "Succeed!";
    }

    /**
     * 将SseEmitter对象设置成完成
     *
     * @param userId
     * @return
     */
    @RequestMapping("/end")
    public String completeSseEmitter(String userId) {
        Result result = sseEmitterMap.get(userId);
        if (result != null) {
            sseEmitterMap.remove(userId);
            result.sseEmitter.complete();
        }
        return "Succeed!";
    }

    private class Result {
        public String userId;
        public long timestamp;
        public SseEmitter sseEmitter;

        public Result(String userId, long timestamp, SseEmitter sseEmitter) {
            this.userId = userId;
            this.timestamp = timestamp;
            this.sseEmitter = sseEmitter;
        }
    }
}
复制代码

Start related items, and then follow the steps above comments, we will be able to view the information we push the browser start page of a connection. If the server does not push, clients are in a state of waiting.

Difference between the two

The achievement of the above two methods can push message server to the client, both cases there are still some differences.

  1. WebSocket channel is full duplex, bidirectional communication can be more powerful; the SSE is a unidirectional channel, the server can only be sent to the browser.
  2. WebSocket is a new protocol, you need server-side support; SSE is deployed on top of the HTTP protocol, the existing server software support.
  3. SSE is a lightweight protocol is relatively simple; a WebSocket protocol is a heavy, relatively complex.
  4. SSE supported by default reconnection, WebSocket will require additional deployment.
  5. SSE supports custom data types transmitted.
  6. SSE does not support CORS url parameter is the server URL, and the URL of the current page must be in the same domain (domain), and the protocols and ports must be the same.

Where WebSocketthe HAProxysituation may arise in combination, you need to look at this article pit of WebSocket

Section above information reproduced from [ www.cnblogs.com/like2372/p/... ]

Guess you like

Origin juejin.im/post/5ddb88b5f265da7dd07947d1