Java tutorial
/* * (C) Copyright 2015-2016 the original author or authors. * * 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. * * Contributors: * ohun@live.cn () */ package com.mpush.netty.server; import com.mpush.api.service.BaseService; import com.mpush.api.service.Listener; import com.mpush.api.service.Server; import com.mpush.api.service.ServiceException; import com.mpush.netty.codec.PacketDecoder; import com.mpush.netty.codec.PacketEncoder; import com.mpush.tools.config.CC; import com.mpush.tools.thread.ThreadNames; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.*; import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.epoll.Native; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.channels.spi.SelectorProvider; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; /** * Created by ohun on 2015/12/22. * * @author ohun@live.cn */ public abstract class NettyTCPServer extends BaseService implements Server { private final Logger logger = LoggerFactory.getLogger(this.getClass()); public enum State { Created, Initialized, Starting, Started, Shutdown } protected final AtomicReference<State> serverState = new AtomicReference<>(State.Created); protected final int port; protected EventLoopGroup bossGroup; protected EventLoopGroup workerGroup; public NettyTCPServer(int port) { this.port = port; } public void init() { if (!serverState.compareAndSet(State.Created, State.Initialized)) { throw new ServiceException("Server already init"); } } @Override public boolean isRunning() { return serverState.get() == State.Started; } @Override public void stop(Listener listener) { if (!serverState.compareAndSet(State.Started, State.Shutdown)) { if (listener != null) listener.onFailure(new ServiceException("server was already shutdown.")); logger.error("{} was already shutdown.", this.getClass().getSimpleName()); return; } logger.info("try shutdown {}...", this.getClass().getSimpleName()); if (bossGroup != null) bossGroup.shutdownGracefully().syncUninterruptibly();//?main reactor if (workerGroup != null) workerGroup.shutdownGracefully().syncUninterruptibly();//??sub reactor logger.info("{} shutdown success.", this.getClass().getSimpleName()); if (listener != null) { listener.onSuccess(port); } } @Override public void start(final Listener listener) { if (!serverState.compareAndSet(State.Initialized, State.Starting)) { throw new ServiceException("Server already started or have not init"); } if (useNettyEpoll()) { createEpollServer(listener); } else { createNioServer(listener); } } private void createServer(Listener listener, EventLoopGroup boss, EventLoopGroup work, ChannelFactory<? extends ServerChannel> channelFactory) { /*** * NioEventLoopGroup ??I/O? * Netty????EventLoopGroup?????? * ?2NioEventLoopGroup * ???boss?? * ???worker??? * boss?worker * ???Channels??EventLoopGroup * ??? */ this.bossGroup = boss; this.workerGroup = work; try { /** * ServerBootstrap ?NIO?? * ??Channel */ ServerBootstrap b = new ServerBootstrap(); /** * groupjava.lang.IllegalStateException: group not set */ b.group(bossGroup, workerGroup); /*** * ServerSocketChannelNIOselector? * Channel?. */ b.channelFactory(channelFactory); /*** * ?????Channel * ChannelInitializer? * ?Channel * ?NettyServerHandler??Channel * ChannelPipeline?? * ??????pipeline * ?????? */ b.childHandler(new ChannelInitializer<Channel>() { // (4) @Override public void initChannel(Channel ch) throws Exception {//? initPipeline(ch.pipeline()); } }); initOptions(b); /*** * ??? */ b.bind(port).addListener(future -> { if (future.isSuccess()) { serverState.set(State.Started); logger.info("server start success on:{}", port); if (listener != null) listener.onSuccess(port); } else { logger.error("server start failure on:{}", port, future.cause()); if (listener != null) listener.onFailure(future.cause()); } }); } catch (Exception e) { logger.error("server start exception", e); if (listener != null) listener.onFailure(e); throw new ServiceException("server start exception, port=" + port, e); } } private void createNioServer(Listener listener) { EventLoopGroup bossGroup = getBossGroup(); EventLoopGroup workerGroup = getWorkerGroup(); if (bossGroup == null) { NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(getBossThreadNum(), getBossThreadFactory(), getSelectorProvider()); nioEventLoopGroup.setIoRatio(100); bossGroup = nioEventLoopGroup; } if (workerGroup == null) { NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(getWorkThreadNum(), getWorkThreadFactory(), getSelectorProvider()); nioEventLoopGroup.setIoRatio(getIoRate()); workerGroup = nioEventLoopGroup; } createServer(listener, bossGroup, workerGroup, getChannelFactory()); } private void createEpollServer(Listener listener) { EventLoopGroup bossGroup = getBossGroup(); EventLoopGroup workerGroup = getWorkerGroup(); if (bossGroup == null) { EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(getBossThreadNum(), getBossThreadFactory()); epollEventLoopGroup.setIoRatio(100); bossGroup = epollEventLoopGroup; } if (workerGroup == null) { EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(getWorkThreadNum(), getWorkThreadFactory()); epollEventLoopGroup.setIoRatio(getIoRate()); workerGroup = epollEventLoopGroup; } createServer(listener, bossGroup, workerGroup, EpollServerSocketChannel::new); } /*** * option()??NioServerSocketChannel?? * childOption()???ServerChannel * ?NioServerSocketChannel */ protected void initOptions(ServerBootstrap b) { //b.childOption(ChannelOption.SO_KEEPALIVE, false);// /** * Netty 4ByteBufJava jemalloc Facebook * Netty?????GC???? * ??? * Netty??? */ b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); } public abstract ChannelHandler getChannelHandler(); protected ChannelHandler getDecoder() { return new PacketDecoder(); } protected ChannelHandler getEncoder() { return PacketEncoder.INSTANCE;//?, ? } /** * ? * * @param pipeline */ protected void initPipeline(ChannelPipeline pipeline) { pipeline.addLast("decoder", getDecoder()); pipeline.addLast("encoder", getEncoder()); pipeline.addLast("handler", getChannelHandler()); } /** * netty ExecutorThreadPerTaskExecutor * SingleThreadEventExecutor#doStartThread * <p> * eventLoop.execute(runnable); * ??? * ?? * ?(startThread())??? * ??? * * @return */ protected ThreadFactory getBossThreadFactory() { return new DefaultThreadFactory(getBossThreadName()); } protected ThreadFactory getWorkThreadFactory() { return new DefaultThreadFactory(getWorkThreadName()); } protected int getBossThreadNum() { return 1; } protected int getWorkThreadNum() { return 0; } protected String getBossThreadName() { return ThreadNames.T_BOSS; } protected String getWorkThreadName() { return ThreadNames.T_WORKER; } protected int getIoRate() { return 70; } protected boolean useNettyEpoll() { if (CC.mp.core.useNettyEpoll()) { try { Native.offsetofEpollData(); return true; } catch (UnsatisfiedLinkError error) { logger.warn("can not load netty epoll, switch nio model."); } } return false; } public EventLoopGroup getBossGroup() { return bossGroup; } public EventLoopGroup getWorkerGroup() { return workerGroup; } public ChannelFactory<? extends ServerChannel> getChannelFactory() { return NioServerSocketChannel::new; } public SelectorProvider getSelectorProvider() { return SelectorProvider.provider(); } }