Spring 提供了一个 SockJS Java 客户端,无需使用浏览器即可连接到远程 SockJS 端点。当需要在公共网络上的两个服务器之间进行双向通信时,这可能特别有用(也就是说,在网络代理可能排除使用 WebSocket 协议的地方)。SockJS Java 客户端对于测试目的也非常有用(例如,模拟大量的并发用户)。
SockJS Java 客户端支持 websocket、xhr-streaming 和 xhr-polling 传输。其余的只有在浏览器中使用才有意义。
你可以用 WebSocketTransport 来配置:
- JSR-356 运行时中的 StandardWebSocketClient。
- JettyWebSocketClient,通过使用 Jetty 9+ 本地 WebSocket API。
- Spring 的 WebSocketClient 的任何实现。
根据定义,XhrTransport 同时支持 xhr-streaming 和 xhr-polling,因为从客户的角度来看,除了用于连接到服务器的 URL 外,没有任何区别。目前,有两种实现方式:
- RestTemplateXhrTransport 使用 Spring 的 RestTemplate 进行 HTTP 请求。
- JettyXhrTransport 使用 Jetty 的 HttpClient 进行 HTTP 请求。
下面的例子展示了如何创建一个 SockJS 客户端并连接到一个 SockJS 端点:
List<Transport> transports = new ArrayList<>(2);transports.add(new WebSocketTransport(new StandardWebSocketClient()));transports.add(new RestTemplateXhrTransport());SockJsClient sockJsClient = new SockJsClient(transports);sockJsClient.doHandshake(new MyWebSocketHandler(), "ws://example.com:8080/sockjs");
:::info SockJS 使用 JSON 格式的数组来传递信息。默认情况下,Jackson 2 被使用,并且需要在 classpath 中出现。另外,你可以配置一个SockJsMessageCodec 的自定义实现,并在 SockJsClient 上配置它。 :::
为了使用 SockJsClient 来模拟大量的并发用户,你需要配置底层的 HTTP 客户端(用于 XHR 传输)以允许足够数量的连接和线程。下面的例子展示了如何用 Jetty 做到这一点:
HttpClient jettyHttpClient = new HttpClient();jettyHttpClient.setMaxConnectionsPerDestination(1000);jettyHttpClient.setExecutor(new QueuedThreadPool(1000));
下面的例子显示了服务器端 SockJS 相关的属性(详见 javadoc),你也应该考虑定制这些属性:
@Configurationpublic class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/sockjs").withSockJS()// 将 streamBytesLimit 属性设为 512KB(默认为 128KB-128*1024)。.setStreamBytesLimit(512 * 1024)// 设置 httpMessageCacheSize 属性为 1,000(默认为 100)。.setHttpMessageCacheSize(1000)// 将 disconnectDelay 属性设置为 30 属性秒(默认为 5 秒 - 5*1000)。.setDisconnectDelay(30 * 1000);}// ...}
一个例子
在 前面的 demo 例子 上修改部分代码
package cn.mrcode.study.springdocsread.websocket;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.config.annotation.EnableWebSocket;import org.springframework.web.socket.config.annotation.WebSocketConfigurer;import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;/*** @author mrcode*/@Configuration@EnableWebSocketpublic class MyWebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(myHandler(), "/myHandler").withSockJS(); // 改成这个}@Beanpublic WebSocketHandler myHandler() {return new MyHandler();}/*spring 为了支持每种容器自己的 websocket 升级策略,抽象了 RequestUpgradeStrategy,<p>对 tomcat 提供了 TomcatRequestUpgradeStrategy 策略</p>如果不申明这个,就会在启动的时候抛出异常:No suitable default RequestUpgradeStrategy found*/@Beanpublic TomcatRequestUpgradeStrategy tomcatRequestUpgradeStrategy() {return new TomcatRequestUpgradeStrategy();}}
然后启动项目就可以了。浏览器里面我就不测试了。自己看 Github 的官方文档如何使用 SockJS 链接后端,这里用本章说到的 Spring Java 客户端来对 SockJS 发起测试
package cn.mrcode.study.springdocsread.test;import org.springframework.util.concurrent.ListenableFuture;import org.springframework.web.socket.TextMessage;import org.springframework.web.socket.WebSocketSession;import org.springframework.web.socket.client.standard.StandardWebSocketClient;import org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport;import org.springframework.web.socket.sockjs.client.SockJsClient;import org.springframework.web.socket.sockjs.client.Transport;import org.springframework.web.socket.sockjs.client.WebSocketTransport;import java.io.IOException;import java.util.ArrayList;import java.util.List;import cn.mrcode.study.springdocsread.websocket.MyHandler;/*** @author mrcode*/public class SockjsTest {public static void main(String[] args) {List<Transport> transports = new ArrayList<>(2);transports.add(new WebSocketTransport(new StandardWebSocketClient()));transports.add(new RestTemplateXhrTransport());SockJsClient sockJsClient = new SockJsClient(transports);final ListenableFuture<WebSocketSession> future = sockJsClient.doHandshake(new MyHandler(), "ws://localhost:8080/myHandler");sockJsClient.start(); // 开始// 添加一个回调,链接成功后,就可以通过 websocketSession 往服务端发起信息了future.addCallback(t -> {try {t.sendMessage(new TextMessage("可以发送信息了:你好"));} catch (IOException e) {throw new RuntimeException(e);}}, fall ->{System.out.println(fall);});}}
启动这个测试后,就可以往服务端发送消息了。
