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.log.Logs; 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.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Locale; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; /** * Created by ohun on 2015/12/22. * * @author ohun@live.cn */ public abstract class NettyServer 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 NettyServer(int port) { this.port = port; } public void init() { if (!serverState.compareAndSet(State.Created, State.Initialized)) { throw new IllegalStateException("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)) { IllegalStateException e = new IllegalStateException("server was already shutdown."); if (listener != null) listener.onFailure(e); Logs.Console.error("{} was already shutdown.", this.getClass().getSimpleName()); return; } Logs.Console.error("try shutdown {}...", this.getClass().getSimpleName()); if (workerGroup != null) workerGroup.shutdownGracefully().syncUninterruptibly(); if (bossGroup != null) bossGroup.shutdownGracefully().syncUninterruptibly(); Logs.Console.error("{} 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 IllegalStateException("Server already started or have not init"); } if (useNettyEpoll()) { createEpollServer(listener); } else { createNioServer(listener); } } private void createServer(final Listener listener, EventLoopGroup boss, EventLoopGroup work, Class<? extends ServerChannel> clazz) { /*** * 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.channel(clazz); /*** * ?????Channel * ChannelInitializer? * ?Channel * ?NettyServerHandler??Channel * ChannelPipeline?? * ??????pipeline * ?????? */ b.childHandler(new ChannelInitializer<SocketChannel>() { // (4) @Override public void initChannel(SocketChannel ch) throws Exception { initPipeline(ch.pipeline()); } }); initOptions(b); /*** * ??? */ ChannelFuture f = b.bind(port).sync().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { Logs.Console.error("server start success on:{}", port); if (listener != null) listener.onSuccess(port); } else { Logs.Console.error("server start failure on:{}", port, future.cause()); if (listener != null) listener.onFailure(future.cause()); } } }); if (f.isSuccess()) { serverState.set(State.Started); /** * socket */ f.channel().closeFuture().sync(); } } catch (Exception e) { logger.error("server start exception", e); if (listener != null) listener.onFailure(e); throw new ServiceException("server start exception, port=" + port, e); } finally { /*** * */ stop(null); } } private void createNioServer(Listener listener) { NioEventLoopGroup bossGroup = new NioEventLoopGroup(1, getBossExecutor()); NioEventLoopGroup workerGroup = new NioEventLoopGroup(0, getWorkExecutor()); createServer(listener, bossGroup, workerGroup, NioServerSocketChannel.class); } @SuppressWarnings("unused") private void createEpollServer(Listener listener) { EpollEventLoopGroup bossGroup = new EpollEventLoopGroup(1, getBossExecutor()); EpollEventLoopGroup workerGroup = new EpollEventLoopGroup(0, getWorkExecutor()); createServer(listener, bossGroup, workerGroup, EpollServerSocketChannel.class); } protected void initOptions(ServerBootstrap b) { /*** * option()??NioServerSocketChannel?? * childOption()???ServerChannel * ?NioServerSocketChannel */ b.childOption(ChannelOption.SO_KEEPALIVE, true); /** * 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; } protected void initPipeline(ChannelPipeline pipeline) { pipeline.addLast("decoder", getDecoder()); pipeline.addLast("encoder", getEncoder()); pipeline.addLast("handler", getChannelHandler()); } protected Executor getBossExecutor() { return null; } protected Executor getWorkExecutor() { return null; } private boolean useNettyEpoll() { if (!"netty".equals(CC.mp.core.epoll_provider)) return false; String name = CC.cfg.getString("os.name").toLowerCase(Locale.UK).trim(); return name.startsWith("linux"); } @Override protected void doStart(Listener listener) throws Throwable { } @Override protected void doStop(Listener listener) throws Throwable { } }