简体   繁体   中英

Spring SseEmitter over WebSocket connection

My goal is to send one request from frontend to backend and receive multiple responses. I'm using WebSocket because responses are very frequent and WebSocket seems to be best protocol to do it and SseEmitter send multiple responses from backend.

Here is my request controller:

@MessageMapping("/emitter")
@SendTo("/topic/response")
public SseEmitter output(RunData runData) throws Exception {
    SseEmitter emitter = new SseEmitter();
    new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                RemoteHostController rhc = new RemoteHostController(runData);
                rhc.execute();
                while (rhc.getActiveCount() > 0) {
                    emitter.send(rhc.getAllOutput());
                    Thread.sleep(2000);
                }

                 emitter.complete();
            } catch (Exception ee) {
                ee.printStackTrace();
                emitter.completeWithError(ee);
            }
        }
    }).start();

    return emitter;
}

RemoteHostController is managing connections and getAllOutput returns output from the hosts.

Frontend application is running quite simple index.html that is connecting to websocket using Stomp and SockJS, sends data to server and generate

tag with data from response:

function connect() {
        var socket = new SockJS('http://localhost:8080/emitter');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            setConnected(true);
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/response', function(greeting){
                showOutput(greeting.body);
            });
        });
}

function sendData() {
        var hostname = document.getElementById('hostname').value;
        var username = document.getElementById('username').value;
        var password = document.getElementById('password').value;
        var command = document.getElementById('command').value;
        stompClient.send("/app/emitter", {}, JSON.stringify({ 'hostname': hostname,
                                                    'username': username,
                                                    'password': password,
                                                    'command': command}));
}

function showOutput(message) {
        var response = document.getElementById('response');
        var p = document.createElement('p');
        p.style.wordWrap = 'break-word';
        p.appendChild(document.createTextNode(message));
        response.appendChild(p);
}

When I send data to backend only response I get is:

{"timeout":null}

It's SseEmitter timeout field, when I change timeout it will return {"timeout":<timeout_value>} .

I can see in logs that RemoteHostController is connecting to hosts and executing commands properly.

Am I doing something wrong? Or WebSocket only supports one request one response communication?

Here is an example of both WebSocket and SSE. As noted above SSE is not supported by IE browsers. Adding as much as I can for completeness. Make sure you are not using a RestController when you use SeeEmitter because that will return the object and that is my guess from the description above.

pom.xml

<dependencies>
    <!-- Spring boot framework -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
</dependencies>

Web socket configuration:

@Configuration
@EnableWebSocketMessageBroker
public class ApplicationWebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        super.configureMessageBroker(registry);
        registry.enableSimpleBroker("/topic");
    }

    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/socketrequest").withSockJS();
    }
}

Request Data:

public class RequestData {
    private String string1;
    private String string2;
    // excluding getters and setters
}

Web socket controller:

@Controller
public class WebSocketController {
   @Autowired
    SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/processrequest")
    void runWebSocket( RequestData requestData ) {
        new Thread(new RunProcess(requestData)).start();
    }

    private class RunProcess implements Runnable {
        private RequestData requestData;

        RunProcess(RequestData requestData) {
            this.requestData = requestData;
        }

        public void run() {
            simpMessagingTemplate.convertAndSend("/topic/response", requestData.getString1());
            simpMessagingTemplate.convertAndSend("/topic/response", requestData.getString2());
            simpMessagingTemplate.convertAndSend("/topic/response", "A third response via websocket");
        }
    }
}

Sse Controller:

@Controller
public class SseController {

    @RequestMapping("/emitter")
    public SseEmitter runEmitter(@RequestParam(value = "string1") String string1,
                                 @RequestParam(value = "string2") String string2)
    {
        SseEmitter sseEmitter = new SseEmitter();
        RequestData requestData = new RequestData();
        requestData.setString1(string1);
        requestData.setString2(string2);
        new Thread(new RunProcess(requestData,sseEmitter)).start();
        return sseEmitter;
    }

    private class RunProcess implements Runnable {
        private RequestData requestData;
        private SseEmitter sseEmitter;

        RunProcess(RequestData requestData, SseEmitter sseEmitter) {
            this.requestData = requestData;
            this.sseEmitter = sseEmitter;
        }

        public void run() {
            try {
                sseEmitter.send(requestData.getString1());
                sseEmitter.send(requestData.getString2());
                sseEmitter.send("A third response from SseEmitter");
                sseEmitter.complete();
            } catch (IOException e) {
                e.printStackTrace();
                sseEmitter.completeWithError(e);
            }
        }
    }

}

Html code:

    <script src="/javascript/sockjs-0.3.4.js"></script>
    <script src="/javascript/stomp.js"></script>

    <script type="text/javascript">
    var stompClient = null;

    function connect() {
        var socket = new SockJS('http://localhost:8085/socketrequest');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/response', function(message){
                showOutput(message.body);
            });
        });
    }

    function doWebsocket() {
        stompClient.send("/processrequest", {}, JSON.stringify({ 'string1': 'The first string', 'string2' : 'The second string' }));
    }


    function doSse() {
        console.log("doSse");
        var rtUrl= '/emitter?string1=first string sse&string2=second string sse';
        var source = new EventSource(rtUrl);
        source.onmessage=function(event){
            showOutput(event.data)
        };
    }

    function showOutput(message) {
        var response = document.getElementById('response');
        var p = document.createElement('p');
        p.style.wordWrap = 'break-word';
        p.appendChild(document.createTextNode(message));
        response.appendChild(p);
    }

    connect();

    </script>
    </head>

<div>
Starting page
</div>
<div>
    <button id="websocket" onclick="doWebsocket();">WebSocket</button>
    <button id="sse" onclick="doSse();">Server Side Events</button>
</div>
<div >
    Response:
    <p id="response"></p>
</div>

</html>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM