Spring SseEmitter over WebSocket连接

时间:2016-03-11 07:52:50

标签: java spring websocket spring-boot stomp

我的目标是从前端发送一个请求到后端并收到多个响应。我使用WebSocket是因为响应非常频繁,而WebSocket似乎是最好的协议,SseEmitter会从后端发送多个响应。

这是我的请求控制器:

self.table_name = 'something_b'

RemoteHostController管理连接,getAllOutput返回主机的输出。

前端应用程序正在运行非常简单的index.html,它使用Stomp和SockJS连接到websocket,将数据发送到服务器并使用来自响应的数据生成

标记:

@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;
}

当我向后端发送数据时,我得到的响应是:

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);
}

它的SseEmitter超时字段,当我更改超时时,它将返回{"timeout":null}

我可以在日志中看到RemoteHostController正在连接到主机并正确执行命令。

我做错了吗?或者WebSocket只支持一个请求一个响应通信?

1 个答案:

答案 0 :(得分:4)

以下是WebSocket和SSE的示例。如上所述,IE浏览器不支持SSE。添加尽可能多的完整性。使用SeeEmitter时确保没有使用RestController,因为这将返回对象,这是我从上面的描述中猜到的。

的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套接字配置:

@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();
    }
}

请求数据:

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

Web套接字控制器:

@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代码:

        

    <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>