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),你也应该考虑定制这些属性:
@Configuration
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {
@Override
public 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
@EnableWebSocket
public class MyWebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler")
.withSockJS(); // 改成这个
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
/*
spring 为了支持每种容器自己的 websocket 升级策略,抽象了 RequestUpgradeStrategy,
<p>对 tomcat 提供了 TomcatRequestUpgradeStrategy 策略</p>
如果不申明这个,就会在启动的时候抛出异常:No suitable default RequestUpgradeStrategy found
*/
@Bean
public 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);
});
}
}
启动这个测试后,就可以往服务端发送消息了。