Java tutorial
/* * Copyright (c) 2011-2014 Pivotal Software, Inc. * * 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.io.net.netty.tcp; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannelConfig; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.Environment; import reactor.bus.EventBus; import reactor.core.support.NamedDaemonThreadFactory; import reactor.fn.Consumer; import reactor.io.buffer.Buffer; import reactor.io.codec.Codec; import reactor.io.net.AbstractNetChannel; import reactor.io.net.NetChannel; import reactor.io.net.config.ServerSocketOptions; import reactor.io.net.config.SslOptions; import reactor.io.net.netty.*; import reactor.io.net.tcp.TcpServer; import reactor.io.net.tcp.ssl.SSLEngineSupplier; import reactor.rx.Promise; import reactor.rx.Promises; 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.atomic.AtomicInteger; /** * A Netty-based {@code TcpServer} implementation * * @param <IN> * The type that will be received by this server * @param <OUT> * The type that will be sent by this server * * @author Jon Brisbin * @author Stephane Maldini */ public class NettyTcpServer<IN, OUT> extends TcpServer<IN, OUT> { private final Logger log = LoggerFactory.getLogger(getClass()); private final NettyServerSocketOptions nettyOptions; private final ServerBootstrap bootstrap; private final EventLoopGroup selectorGroup; private final EventLoopGroup ioGroup; protected NettyTcpServer(@Nonnull Environment env, @Nonnull EventBus reactor, @Nullable InetSocketAddress listenAddress, final ServerSocketOptions options, final SslOptions sslOptions, @Nullable Codec<Buffer, IN, OUT> codec, @Nonnull Collection<Consumer<NetChannel<IN, OUT>>> consumers) { super(env, reactor, listenAddress, options, sslOptions, codec, consumers); if (options instanceof NettyServerSocketOptions) { this.nettyOptions = (NettyServerSocketOptions) options; } else { this.nettyOptions = null; } int selectThreadCount = env.getProperty("reactor.tcp.selectThreadCount", Integer.class, Environment.PROCESSORS / 2); int ioThreadCount = env.getProperty("reactor.tcp.ioThreadCount", Integer.class, Environment.PROCESSORS); this.selectorGroup = new NioEventLoopGroup(selectThreadCount, new NamedDaemonThreadFactory("reactor-tcp-select")); if (null != nettyOptions && null != nettyOptions.eventLoopGroup()) { this.ioGroup = nettyOptions.eventLoopGroup(); } else { this.ioGroup = new NioEventLoopGroup(ioThreadCount, new NamedDaemonThreadFactory("reactor-tcp-io")); } this.bootstrap = new ServerBootstrap().group(selectorGroup, ioGroup).channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, options.backlog()) .option(ChannelOption.SO_RCVBUF, options.rcvbuf()).option(ChannelOption.SO_SNDBUF, options.sndbuf()) .option(ChannelOption.SO_REUSEADDR, options.reuseAddr()) .localAddress((null == listenAddress ? new InetSocketAddress(3000) : listenAddress)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(final SocketChannel ch) throws Exception { SocketChannelConfig config = ch.config(); config.setReceiveBufferSize(options.rcvbuf()); config.setSendBufferSize(options.sndbuf()); config.setKeepAlive(options.keepAlive()); config.setReuseAddress(options.reuseAddr()); config.setSoLinger(options.linger()); config.setTcpNoDelay(options.tcpNoDelay()); if (log.isDebugEnabled()) { log.debug("CONNECT {}", ch); } if (null != sslOptions) { SSLEngine ssl = new SSLEngineSupplier(sslOptions, false).get(); if (log.isDebugEnabled()) { log.debug("SSL enabled using keystore {}", (null != sslOptions.keystoreFile() ? sslOptions.keystoreFile() : "<DEFAULT>")); } ch.pipeline().addLast(new SslHandler(ssl)); } if (null != nettyOptions && null != nettyOptions.pipelineConfigurer()) { nettyOptions.pipelineConfigurer().accept(ch.pipeline()); } ch.pipeline().addLast(createChannelHandlers(ch)); ch.closeFuture().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (log.isDebugEnabled()) { log.debug("CLOSE {}", ch); } close(ch); } }); } }); } @Override public TcpServer<IN, OUT> start(@Nullable final Runnable started) { ChannelFuture bindFuture = bootstrap.bind(); if (null != started) { bindFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { log.info("BIND {}", future.channel().localAddress()); notifyStart(started); } }); } return this; } @Override public Promise<Boolean> shutdown() { final Promise<Boolean> d = Promises.ready(getEnvironment(), getReactor().getDispatcher()); getReactor().schedule(new Consumer<Void>() { @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void accept(Void v) { final AtomicInteger groupsToShutdown = new AtomicInteger(2); GenericFutureListener listener = new GenericFutureListener() { @Override public void operationComplete(Future future) throws Exception { if (groupsToShutdown.decrementAndGet() == 0) { notifyShutdown(); d.onNext(true); } } }; selectorGroup.shutdownGracefully().addListener(listener); if (null == nettyOptions || null == nettyOptions.eventLoopGroup()) { ioGroup.shutdownGracefully().addListener(listener); } } }, null); return d; } @Override protected <C> NetChannel<IN, OUT> createChannel(C ioChannel) { return new NettyNetChannel<IN, OUT>(getEnvironment(), getCodec(), new NettyEventLoopDispatcher(((Channel) ioChannel).eventLoop(), 256), getReactor(), (Channel) ioChannel); } protected ChannelHandler[] createChannelHandlers(SocketChannel ch) { AbstractNetChannel<IN, OUT> netChannel = (AbstractNetChannel<IN, OUT>) select(ch); NettyNetChannelInboundHandler readHandler = new NettyNetChannelInboundHandler().setNetChannel(netChannel); NettyNetChannelOutboundHandler writeHandler = new NettyNetChannelOutboundHandler(); return new ChannelHandler[] { readHandler, writeHandler }; } }