源码分析:
private static class Listener implements ApplicationListener<RSocketServerInitializedEvent> {
private static final String PROPERTY_NAME = "local.rsocket.server.port";
private final ConfigurableApplicationContext applicationContext;
Listener(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(RSocketServerInitializedEvent event) {
if (event.getServer().address() != null) {
setPortProperty(this.applicationContext, event.getServer().address().getPort());
}
}
private void setPortProperty(ApplicationContext context, int port) {
if (context instanceof ConfigurableApplicationContext configurableContext) {
setPortProperty(configurableContext.getEnvironment(), port);
}
if (context.getParent() != null) {
setPortProperty(context.getParent(), port);
}
}
private void setPortProperty(ConfigurableEnvironment environment, int port) {
MutablePropertySources sources = environment.getPropertySources();
PropertySource<?> source = sources.get("server.ports");
if (source == null) {
source = new MapPropertySource("server.ports", new HashMap<>());
sources.addFirst(source);
}
setPortProperty(port, source);
}
@SuppressWarnings("unchecked")
private void setPortProperty(int port, PropertySource<?> source) {
((Map<String, Object>) source.getSource()).put(PROPERTY_NAME, port);
}
}
RSocketServerBootstrap.java
public class RSocketServerBootstrap implements ApplicationEventPublisherAware, SmartLifecycle {
private final RSocketServer server;
private ApplicationEventPublisher eventPublisher;
public RSocketServerBootstrap(RSocketServerFactory serverFactory, SocketAcceptor socketAcceptor) {
Assert.notNull(serverFactory, "ServerFactory must not be null");
this.server = serverFactory.create(socketAcceptor);
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.eventPublisher = applicationEventPublisher;
}
@Override
public void start() {
this.server.start();
this.eventPublisher.publishEvent(new RSocketServerInitializedEvent(this.server));
}
@Override
public void stop() {
this.server.stop();
}
@Override
public boolean isRunning() {
RSocketServer server = this.server;
if (server != null) {
return server.address() != null;
}
return false;
}
}
源码分析:
public class RSocketServerInitializedEvent extends ApplicationEvent {
public RSocketServerInitializedEvent(RSocketServer server) {
super(server);
}
/**
* Access the {@link RSocketServer}.
* @return the embedded RSocket server
*/
public RSocketServer getServer() {
return getSource();
}
/**
* Access the source of the event (an {@link RSocketServer}).
* @return the embedded web server
*/
@Override
public RSocketServer getSource() {
return (RSocketServer) super.getSource();
}
}
message 中考虑:
@FunctionalInterface
public interface RSocketStrategiesCustomizer {
/**
* Callback to customize a {@link RSocketStrategies#builder()} instance.
* @param strategies rSocket codec strategies to customize
*/
void customize(RSocketStrategies.Builder strategies);
}
netty server:
public class NettyRSocketServer implements RSocketServer {
private static final Log logger = LogFactory.getLog(NettyRSocketServer.class);
private final Mono<CloseableChannel> starter;
private final Duration lifecycleTimeout;
private CloseableChannel channel;
public NettyRSocketServer(Mono<CloseableChannel> starter, Duration lifecycleTimeout) {
Assert.notNull(starter, "starter must not be null");
this.starter = starter;
this.lifecycleTimeout = lifecycleTimeout;
}
@Override
public InetSocketAddress address() {
if (this.channel != null) {
return this.channel.address();
}
return null;
}
@Override
public void start() throws RSocketServerException {
this.channel = block(this.starter, this.lifecycleTimeout);
logger.info("Netty RSocket started on port(s): " + address().getPort());
startDaemonAwaitThread(this.channel);
}
private void startDaemonAwaitThread(CloseableChannel channel) {
Thread awaitThread = new Thread(() -> channel.onClose().block(), "rsocket");
awaitThread.setContextClassLoader(getClass().getClassLoader());
awaitThread.setDaemon(false);
awaitThread.start();
}
@Override
public void stop() throws RSocketServerException {
if (this.channel != null) {
this.channel.dispose();
this.channel = null;
}
}
private <T> T block(Mono<T> mono, Duration timeout) {
return (timeout != null) ? mono.block(timeout) : mono.block();
}
}
NettyRSocketServerFactory方法:
public class NettyRSocketServerFactory implements RSocketServerFactory, ConfigurableRSocketServerFactory {
private int port = 9898;
private DataSize fragmentSize;
private InetAddress address;
private RSocketServer.Transport transport = RSocketServer.Transport.TCP;
private ReactorResourceFactory resourceFactory;
private Duration lifecycleTimeout;
private List<RSocketServerCustomizer> rSocketServerCustomizers = new ArrayList<>();
private Ssl ssl;
private SslStoreProvider sslStoreProvider;
@Override
public void setPort(int port) {
this.port = port;
}
@Override
public void setFragmentSize(DataSize fragmentSize) {
this.fragmentSize = fragmentSize;
}
@Override
public void setAddress(InetAddress address) {
this.address = address;
}
@Override
public void setTransport(RSocketServer.Transport transport) {
this.transport = transport;
}
@Override
public void setSsl(Ssl ssl) {
this.ssl = ssl;
}
@Override
public void setSslStoreProvider(SslStoreProvider sslStoreProvider) {
this.sslStoreProvider = sslStoreProvider;
}
/**
* Set the {@link ReactorResourceFactory} to get the shared resources from.
* @param resourceFactory the server resources
*/
public void setResourceFactory(ReactorResourceFactory resourceFactory) {
this.resourceFactory = resourceFactory;
}
/**
* Set {@link RSocketServerCustomizer}s that should be called to configure the
* {@link io.rsocket.core.RSocketServer} while building the server. Calling this
* method will replace any existing customizers.
* @param rSocketServerCustomizers customizers to apply before the server starts
* @since 2.2.7
*/
public void setRSocketServerCustomizers(Collection<? extends RSocketServerCustomizer> rSocketServerCustomizers) {
Assert.notNull(rSocketServerCustomizers, "RSocketServerCustomizers must not be null");
this.rSocketServerCustomizers = new ArrayList<>(rSocketServerCustomizers);
}
/**
* Add {@link RSocketServerCustomizer}s that should be called to configure the
* {@link io.rsocket.core.RSocketServer}.
* @param rSocketServerCustomizers customizers to apply before the server starts
* @since 2.2.7
*/
public void addRSocketServerCustomizers(RSocketServerCustomizer... rSocketServerCustomizers) {
Assert.notNull(rSocketServerCustomizers, "RSocketServerCustomizers must not be null");
this.rSocketServerCustomizers.addAll(Arrays.asList(rSocketServerCustomizers));
}
/**
* Set the maximum amount of time that should be waited when starting or stopping the
* server.
* @param lifecycleTimeout the lifecycle timeout
*/
public void setLifecycleTimeout(Duration lifecycleTimeout) {
this.lifecycleTimeout = lifecycleTimeout;
}
@Override
public NettyRSocketServer create(SocketAcceptor socketAcceptor) {
ServerTransport<CloseableChannel> transport = createTransport();
io.rsocket.core.RSocketServer server = io.rsocket.core.RSocketServer.create(socketAcceptor);
configureServer(server);
Mono<CloseableChannel> starter = server.bind(transport);
return new NettyRSocketServer(starter, this.lifecycleTimeout);
}
private void configureServer(io.rsocket.core.RSocketServer server) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.fragmentSize).asInt(DataSize::toBytes).to(server::fragment);
this.rSocketServerCustomizers.forEach((customizer) -> customizer.customize(server));
}
private ServerTransport<CloseableChannel> createTransport() {
if (this.transport == RSocketServer.Transport.WEBSOCKET) {
return createWebSocketTransport();
}
return createTcpTransport();
}
private ServerTransport<CloseableChannel> createWebSocketTransport() {
HttpServer httpServer = HttpServer.create();
if (this.resourceFactory != null) {
httpServer = httpServer.runOn(this.resourceFactory.getLoopResources());
}
if (this.ssl != null && this.ssl.isEnabled()) {
httpServer = customizeSslConfiguration(httpServer);
}
return WebsocketServerTransport.create(httpServer.bindAddress(this::getListenAddress));
}
@SuppressWarnings("deprecation")
private HttpServer customizeSslConfiguration(HttpServer httpServer) {
org.springframework.boot.web.embedded.netty.SslServerCustomizer sslServerCustomizer = new org.springframework.boot.web.embedded.netty.SslServerCustomizer(
this.ssl, null, getOrCreateSslStoreProvider());
return sslServerCustomizer.apply(httpServer);
}
private ServerTransport<CloseableChannel> createTcpTransport() {
TcpServer tcpServer = TcpServer.create();
if (this.resourceFactory != null) {
tcpServer = tcpServer.runOn(this.resourceFactory.getLoopResources());
}
if (this.ssl != null && this.ssl.isEnabled()) {
TcpSslServerCustomizer sslServerCustomizer = new TcpSslServerCustomizer(this.ssl,
getOrCreateSslStoreProvider());
tcpServer = sslServerCustomizer.apply(tcpServer);
}
return TcpServerTransport.create(tcpServer.bindAddress(this::getListenAddress));
}
private SslStoreProvider getOrCreateSslStoreProvider() {
if (this.sslStoreProvider != null) {
return this.sslStoreProvider;
}
return CertificateFileSslStoreProvider.from(this.ssl);
}
private InetSocketAddress getListenAddress() {
if (this.address != null) {
return new InetSocketAddress(this.address.getHostAddress(), this.port);
}
return new InetSocketAddress(this.port);
}
@SuppressWarnings("deprecation")
private static final class TcpSslServerCustomizer
extends org.springframework.boot.web.embedded.netty.SslServerCustomizer {
private TcpSslServerCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) {
super(ssl, null, sslStoreProvider);
}
private TcpServer apply(TcpServer server) {
AbstractProtocolSslContextSpec<?> sslContextSpec = createSslContextSpec();
return server.secure((spec) -> spec.sslContext(sslContextSpec));
}
}
}
Rsocket 方法:
@FunctionalInterface
public interface RSocketServerCustomizer {
/**
* Callback to customize a {@link RSocketServer} instance.
* @param rSocketServer the RSocket server to customize
*/
void customize(RSocketServer rSocketServer);
}
Rscoket:
@FunctionalInterface
public interface RSocketServerFactory {
/**
* Gets a new fully configured but paused {@link RSocketServer} instance. Clients
* should not be able to connect to the returned server until
* {@link RSocketServer#start()} is called (which happens when the
* {@code ApplicationContext} has been fully refreshed).
* @param socketAcceptor the socket acceptor
* @return a fully configured and started {@link RSocketServer}
* @see RSocketServer#stop()
*/
RSocketServer create(SocketAcceptor socketAcceptor);
}
RSocket应用层协议支持 Reactive Streams语义, 例如:用RSocket作为HTTP的一种替代方案。
让我们从添加spring-boot-starter-rsocket依赖开始:
首先,我们设置由springboot应用程序引导的RSocket server服务器。 因为有spring-boot-starter-rsocket dependency依赖,所以springboot会自动配置RSocket server。 跟平常一样, 可以用属性驱动的方式修改RSocket server默认配置值。例如:通过增加如下配置在application.properties中,来修改RSocket端口:
这儿我们正在创建RSocket客户端并且配置TCP端口为:7000。注意: 该服务端口我们在前面已经配置过。 接下来我们定义了一个RSocket的装饰器对象RSocketRequester。 这个对象在我们跟RSocket server交互时会为我们提供帮助。 定义这些对象配置后,我们还只是有了一个骨架。在接下来,我们将暴露不同的交互模式, 并看看springboot在这个地方提供帮助的。
在服务器这边,我们首先应该创建一个controller 来持有我们的处理器方法。 我们会使用 @MessageMapping注解来代替像SpringMVC中的@RequestMapping或者@GetMapping注解
教你如何在Spring Boot中使用RSocket
来研究下我们的控制器。 我们将使用@Controller注解来定义一个控制器来处理进入RSocket的请求。 另外,注解@MessageMapping让我们定义我们感兴趣的路由和如何响应一个请求。 在这个示例中, 服务器监听路由currentMarketData, 并响应一个单一的结果Mono给客户端。
6.Spring Boot RSocket中的Request Stream
请求流是一种更复杂的交互模式, 这个模式中客户端发送一个请求,但是在一段时间内从服务器端获取到多个响应。 为了模拟这种交互模式, 客户端会询问给定股票的所有市场数据。
这里我们给异常处理方法标记注解为@MessageExceptionHandler。作为结果, 这个方法将处理所有类型的异常, 因为Exception是所有其他类型的异常的超类。 我们也可以明确地创建更多的不同类型的,不同的异常处理方法。 这当然是请求/响应模式,并且我们返回的是Mono。我们期望这里的响应类型跟我们的交互模式的返回类型相匹配。