/* * Copyright 2014 The Skfiy Open Association. * * 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 org.skfiy.typhon.net; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.timeout.IdleStateHandler; import javax.management.MBeanServer; import org.skfiy.typhon.AbstractMBeanLifecycle; import org.skfiy.typhon.Connector; import org.skfiy.typhon.Container; import org.skfiy.typhon.Globals; import static org.skfiy.typhon.Lifecycle.START_EVENT; import org.skfiy.typhon.LifecycleException; import org.skfiy.typhon.LifecycleState; import org.skfiy.typhon.Service; import org.skfiy.typhon.TyphonException; import org.skfiy.typhon.Typhons; import org.skfiy.typhon.util.MBeanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Kevin */ public class Netty4Connector extends AbstractMBeanLifecycle implements Connector { private static final Logger CLOG = LoggerFactory.getLogger(Globals.CONSOLE_LOG_NAME); private Service service; private String host; private int port; private boolean logEnabled; private long connectionTimeout; private Channel channel; private ServerBootstrap serverBootstrap; @Override public Service getService() { return service; } @Override public void setService(Service service) { this.service = service; } @Override public String getHost() { return host; } @Override public void setHost(String host) { this.host = host; } @Override public int getPort() { return port; } @Override public void setPort(int port) { this.port = port; } @Override public boolean isLogEnabled() { return logEnabled; } @Override public void setLogEnabled(boolean enabled) { this.logEnabled = enabled; } @Override public long getConnectionTimeout() { return connectionTimeout; } @Override public void setConnectionTimeout(long connectionTimeout) { this.connectionTimeout = connectionTimeout; } @Override protected void startInternal() throws LifecycleException { setState(LifecycleState.STARTING); fireLifecycleListener(START_EVENT); System.setProperty("io.netty.noJdkZlibDecoder", "false"); final byte[] delimiters = new byte[]{'\n'}; final String compressionMode = Typhons.getProperty("typhon.spi.net.compressionMode"); final Netty4EndpointHandler handler = new Netty4EndpointHandler(); final Netty4ConnectionLimitHandler limitHandler = new Netty4ConnectionLimitHandler(); // 协议解析处理器 final LengthFieldPrepender lengthFieldPrepender; final DelimiterBasedFrameEncoder delimiterBasedFrameEncoder; if ("zlib".equals(compressionMode)) { lengthFieldPrepender = new LengthFieldPrepender(4); delimiterBasedFrameEncoder = null; } else { lengthFieldPrepender = null; delimiterBasedFrameEncoder = new DelimiterBasedFrameEncoder(delimiters); } // 日志记录器 final LoggingHandler loggingHandler; if (isLogEnabled()) { loggingHandler = new LoggingHandler(LogLevel.DEBUG); } else { loggingHandler = null; } serverBootstrap = new ServerBootstrap(); serverBootstrap.group(new NioEventLoopGroup(1), new NioEventLoopGroup()) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .childHandler(new ChannelInitializer() { @Override protected void initChannel(Channel c) throws Exception { ChannelPipeline pipeline = c.pipeline(); if ("zlib".equals(compressionMode)) { pipeline.addLast("lengthFieldPrepender", lengthFieldPrepender); pipeline.addLast("lengthFieldBasedFrameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); pipeline.addLast("deflater", ZlibCodecFactory.newZlibEncoder(ZlibWrapper.ZLIB)); } else { pipeline.addLast("delimiterBasedFrameDecoder", new DelimiterBasedFrameDecoder(65535, new ByteBuf[]{ Unpooled.wrappedBuffer(delimiters) })); pipeline.addLast("delimiterBasedFrameEncoder", delimiterBasedFrameEncoder); } if (isLogEnabled()) { pipeline.addLast(loggingHandler); } pipeline.addLast(new IdleStateHandler(60 * 10, 60 * 10, 0)); pipeline.addLast(limitHandler, handler); } }); channel = serverBootstrap.bind(host, port).channel(); CLOG.debug("Netty4Connector started on port {}", port); MBeanServer mbs = MBeanUtils.REGISTRY.getMBeanServer(); Object obj = null; try { obj = mbs.invoke(Container.OBJECT_NAME, "getInstance", new Object[]{ProtocolHandler.class}, new String[]{Class.class.getName()}); } catch (Exception ex) { CLOG.error("ProtocolHandler", ex); throw new TyphonException(ex); } handler.setProtocolHandler((ProtocolHandler) obj); } @Override protected void stopInternal() throws LifecycleException { setState(LifecycleState.STOPPING); ChannelFuture channelFuture = channel.closeFuture(); serverBootstrap.group().shutdownGracefully(); serverBootstrap.childGroup().shutdownGracefully(); channelFuture.getNow(); fireLifecycleListener(STOP_EVENT); } @Override protected String getMBeanDomain() { return "Connector"; } @Override protected String getObjectNameKeyProperties() { return "name=Netty4Connector"; } }