package io.springside.engine.thrift.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.util.concurrent.DefaultThreadFactory; import io.springside.engine.thrift.utils.Exceptions; import io.springside.engine.thrift.utils.Threads; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.thrift.server.TServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Thrift NIO Server base on Netty. * * @author calvin */ public class TNettyServer extends TServer { private static Logger logger = LoggerFactory.getLogger(TNettyServer.class); private ServerArgs args; private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; private ExecutorService userThreadPool; private ChannelFuture f; public TNettyServer(ServerArgs args) { super(args); this.args = args; } @Override public void serve() { logger.info("Netty Server is starting"); args.validate(); ServerBootstrap b = configServer(); try { // start server f = b.bind(args.port).sync(); logger.info("Netty Server started and listening on " + args.port); setServing(true); // register shutown hook Runtime.getRuntime().addShutdownHook(new ShutdownThread()); } catch (Exception e) { logger.error("Exception happen when start server", e); throw Exceptions.unchecked(e); } } /** * blocking to wait for close. */ public void waitForClose() throws InterruptedException { f.channel().closeFuture().sync(); } @Override public void stop() { logger.info("Netty server is stopping"); bossGroup.shutdownGracefully(); Threads.gracefulShutdown(userThreadPool, args.shutdownTimeoutMills, args.shutdownTimeoutMills, TimeUnit.SECONDS); workerGroup.shutdownGracefully(); logger.info("Netty server stoped"); } private ServerBootstrap configServer() { bossGroup = new NioEventLoopGroup(args.bossThreads, new DefaultThreadFactory("NettyBossGroup", true)); workerGroup = new NioEventLoopGroup(args.workerThreads, new DefaultThreadFactory("NettyWorkerGroup", true)); userThreadPool = Executors.newFixedThreadPool(args.userThreads, new DefaultThreadFactory("UserThreads", true)); final ThriftHandler thriftHandler = new ThriftHandler(this.processorFactory_, this.inputProtocolFactory_, this.outputProtocolFactory_, userThreadPool); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .childOption(ChannelOption.SO_REUSEADDR, true).childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); if (args.socketTimeoutMills > 0) { b.childOption(ChannelOption.SO_TIMEOUT, args.socketTimeoutMills); } if (args.recvBuff > 0) { b.childOption(ChannelOption.SO_RCVBUF, args.recvBuff); } if (args.sendBuff > 0) { b.childOption(ChannelOption.SO_SNDBUF, args.sendBuff); } b.childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(createThriftFramedDecoder(), createThriftFramedEncoder(), thriftHandler); } }); return b; } private ChannelHandler createThriftFramedDecoder() { return new ThriftFrameedDecoder(); } private ChannelHandler createThriftFramedEncoder() { return new ThriftFrameedEncoder(); } class ShutdownThread extends Thread { @Override public void run() { TNettyServer.this.stop(); } } }