reactor.net.netty.tcp.NettyTcpClient Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2011-2013 GoPivotal, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.net.netty.tcp;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Environment;
import reactor.core.Reactor;
import reactor.core.composable.Deferred;
import reactor.core.composable.Promise;
import reactor.core.composable.Stream;
import reactor.core.composable.spec.Promises;
import reactor.core.composable.spec.Streams;
import reactor.function.Consumer;
import reactor.function.Supplier;
import reactor.io.Buffer;
import reactor.io.encoding.Codec;
import reactor.net.NetChannel;
import reactor.net.Reconnect;
import reactor.net.config.ClientSocketOptions;
import reactor.net.config.SslOptions;
import reactor.net.netty.*;
import reactor.net.tcp.TcpClient;
import reactor.net.tcp.ssl.SSLEngineSupplier;
import reactor.support.NamedDaemonThreadFactory;
import reactor.tuple.Tuple2;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.net.ssl.SSLEngine;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A Netty-based {@code TcpClient}.
*
* @param
* The type that will be received by this client
* @param
* The type that will be sent by this client
*
* @author Jon Brisbin
* @author Stephane Maldini
*/
public class NettyTcpClient extends TcpClient {
private final Logger log = LoggerFactory.getLogger(NettyTcpClient.class);
private final NettyClientSocketOptions nettyOptions;
private final Bootstrap bootstrap;
private final EventLoopGroup ioGroup;
private final Supplier connectionSupplier;
private volatile InetSocketAddress connectAddress;
private volatile boolean closing;
/**
* Creates a new NettyTcpClient that will use the given {@code env} for configuration and the given {@code reactor} to
* send events. The number of IO threads used by the client is configured by the environment's {@code
* reactor.tcp.ioThreadCount} property. In its absence the number of IO threads will be equal to the {@link
* Environment#PROCESSORS number of available processors}. The client will connect to the given {@code
* connectAddress}, configuring its socket using the given {@code opts}. The given {@code codec} will be used for
* encoding and decoding of data.
*
* @param env
* The configuration environment
* @param reactor
* The reactor used to send events
* @param connectAddress
* The address the client will connect to
* @param options
* The configuration options for the client's socket
* @param sslOptions
* The SSL configuration options for the client's socket
* @param codec
* The codec used to encode and decode data
* @param consumers
* The consumers that will interact with the connection
*/
public NettyTcpClient(@Nonnull Environment env,
@Nonnull Reactor reactor,
@Nonnull InetSocketAddress connectAddress,
@Nonnull final ClientSocketOptions options,
@Nullable final SslOptions sslOptions,
@Nullable Codec codec,
@Nonnull Collection>> consumers) {
super(env, reactor, connectAddress, options, sslOptions, codec, consumers);
this.connectAddress = connectAddress;
if (options instanceof NettyClientSocketOptions) {
this.nettyOptions = (NettyClientSocketOptions) options;
} else {
this.nettyOptions = null;
}
if (null != nettyOptions && null != nettyOptions.eventLoopGroup()) {
this.ioGroup = nettyOptions.eventLoopGroup();
} else {
int ioThreadCount = env.getProperty("reactor.tcp.ioThreadCount", Integer.class, Environment.PROCESSORS);
this.ioGroup = new NioEventLoopGroup(ioThreadCount, new NamedDaemonThreadFactory("reactor-tcp-io"));
}
this.bootstrap = new Bootstrap()
.group(ioGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_RCVBUF, options.rcvbuf())
.option(ChannelOption.SO_SNDBUF, options.sndbuf())
.option(ChannelOption.SO_KEEPALIVE, options.keepAlive())
.option(ChannelOption.SO_LINGER, options.linger())
.option(ChannelOption.TCP_NODELAY, options.tcpNoDelay())
.remoteAddress(this.connectAddress)
.handler(new ChannelInitializer() {
@Override
public void initChannel(final SocketChannel ch) throws Exception {
ch.config().setConnectTimeoutMillis(options.timeout());
if (null != sslOptions) {
SSLEngine ssl = new SSLEngineSupplier(sslOptions, true).get();
if (log.isDebugEnabled()) {
log.debug("SSL enabled using keystore {}",
(null != sslOptions.keystoreFile() ? sslOptions.keystoreFile() : ""));
}
ch.pipeline().addLast(new SslHandler(ssl));
}
if (null != nettyOptions && null != nettyOptions.pipelineConfigurer()) {
nettyOptions.pipelineConfigurer().accept(ch.pipeline());
}
ch.pipeline().addLast(createChannelHandlers(ch));
}
});
this.connectionSupplier = new Supplier() {
@Override
public ChannelFuture get() {
if (!closing) {
return bootstrap.connect(getConnectAddress());
} else {
return null;
}
}
};
}
@Override
public Promise> open() {
final Deferred, Promise>> connection
= Promises.defer(getEnvironment(), getReactor().getDispatcher());
openChannel(new ConnectingChannelListener(connection));
return connection.compose();
}
@Override
public Stream> open(final Reconnect reconnect) {
final Deferred, Stream>> connections
= Streams.defer(getEnvironment(), getReactor().getDispatcher());
openChannel(new ReconnectingChannelListener(connectAddress, reconnect, connections));
return connections.compose();
}
@Override
public void close(@Nullable final Consumer onClose) {
if (null != nettyOptions && null != nettyOptions.eventLoopGroup()) {
ioGroup.submit(new Runnable() {
@Override
public void run() {
if (null != onClose) {
onClose.accept(true);
}
}
});
} else {
ioGroup.shutdownGracefully().addListener(new FutureListener
© 2015 - 2024 Weber Informatics LLC | Privacy Policy