com.tongbanjie.tarzan.rpc.netty.NettyRpcServer.java Source code

Java tutorial

Introduction

Here is the source code for com.tongbanjie.tarzan.rpc.netty.NettyRpcServer.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 com.tongbanjie.tarzan.rpc.netty;

import com.tongbanjie.tarzan.rpc.InvokeCallback;
import com.tongbanjie.tarzan.rpc.RpcHook;
import com.tongbanjie.tarzan.rpc.RpcServer;
import com.tongbanjie.tarzan.rpc.protocol.NettyDecoder;
import com.tongbanjie.tarzan.rpc.protocol.NettyEncoder;
import com.tongbanjie.tarzan.rpc.util.RpcHelper;
import com.tongbanjie.tarzan.rpc.exception.RpcSendRequestException;
import com.tongbanjie.tarzan.rpc.exception.RpcTimeoutException;
import com.tongbanjie.tarzan.rpc.exception.RpcTooMuchRequestException;
import com.tongbanjie.tarzan.rpc.protocol.RpcCommand;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import org.javatuples.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class NettyRpcServer extends NettyRpcAbstract implements RpcServer {

    private static final Logger LOGGER = LoggerFactory.getLogger(NettyRpcServer.class);

    /******************************** Netty *******************************/
    private final NettyServerConfig nettyServerConfig;
    private final ServerBootstrap serverBootstrap;
    private final EventLoopGroup eventLoopGroupSelector;
    private final EventLoopGroup eventLoopGroupBoss;
    private DefaultEventExecutorGroup defaultEventExecutorGroup;

    /********************************  *******************************/
    private final ExecutorService publicExecutor;
    private final Timer timer = new Timer("ServerHouseKeepingService", true);

    /******************************** ? *******************************/
    private final ChannelEventListener channelEventListener;

    /********************************  *******************************/
    private RpcHook rpcHook;

    private int port;

    public NettyRpcServer(final NettyServerConfig nettyServerConfig) {
        this(nettyServerConfig, null);
    }

    public NettyRpcServer(final NettyServerConfig nettyServerConfig,
            final ChannelEventListener channelEventListener) {
        super(nettyServerConfig.getServerOneWaySemaphoreValue(), nettyServerConfig.getServerAsyncSemaphoreValue());
        this.serverBootstrap = new ServerBootstrap();
        this.nettyServerConfig = nettyServerConfig;
        this.channelEventListener = channelEventListener;

        int publicThreadNum = nettyServerConfig.getServerCallbackExecutorThreads();
        if (publicThreadNum <= 0) {
            publicThreadNum = 4;
        }

        this.publicExecutor = Executors.newFixedThreadPool(publicThreadNum, new ThreadFactory() {
            private AtomicInteger threadIndex = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "NettyServerPublicExecutor_" + this.threadIndex.incrementAndGet());
            }
        });

        this.eventLoopGroupBoss = new NioEventLoopGroup(1, new ThreadFactory() {
            private AtomicInteger threadIndex = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, String.format("NettyBoss_%d", this.threadIndex.incrementAndGet()));
            }
        });

        if (RpcHelper.isLinuxPlatform() //
                && nettyServerConfig.isUseEpollNativeSelector()) {
            this.eventLoopGroupSelector = new EpollEventLoopGroup(nettyServerConfig.getServerSelectorThreads(),
                    new ThreadFactory() {
                        private AtomicInteger threadIndex = new AtomicInteger(0);
                        private int threadTotal = nettyServerConfig.getServerSelectorThreads();

                        @Override
                        public Thread newThread(Runnable r) {
                            return new Thread(r, String.format("NettyServerEPOLLSelector_%d_%d", threadTotal,
                                    this.threadIndex.incrementAndGet()));
                        }
                    });
        } else {
            this.eventLoopGroupSelector = new NioEventLoopGroup(nettyServerConfig.getServerSelectorThreads(),
                    new ThreadFactory() {
                        private AtomicInteger threadIndex = new AtomicInteger(0);
                        private int threadTotal = nettyServerConfig.getServerSelectorThreads();

                        @Override
                        public Thread newThread(Runnable r) {
                            return new Thread(r, String.format("NettyServerNIOSelector_%d_%d", threadTotal,
                                    this.threadIndex.incrementAndGet()));
                        }
                    });
        }
    }

    @Override
    public void start() {
        this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(//
                nettyServerConfig.getServerWorkerThreads(), //
                new ThreadFactory() {

                    private AtomicInteger threadIndex = new AtomicInteger(0);

                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "NettyServerCodecThread_" + this.threadIndex.incrementAndGet());
                    }
                });

        ServerBootstrap childHandler = //
                this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector)
                        .channel(NioServerSocketChannel.class)
                        //
                        .option(ChannelOption.SO_BACKLOG, 1024)
                        //
                        .option(ChannelOption.SO_REUSEADDR, true)
                        //
                        .option(ChannelOption.SO_KEEPALIVE, false)
                        //
                        .childOption(ChannelOption.TCP_NODELAY, true)
                        //
                        .option(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSndBufSize())
                        //
                        .option(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketRcvBufSize())
                        //
                        .localAddress(new InetSocketAddress(this.nettyServerConfig.getListenPort()))
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(
                                        //
                                        defaultEventExecutorGroup, //
                                        new NettyEncoder(), //
                                        new NettyDecoder(), //
                                        new IdleStateHandler(0, 0,
                                                nettyServerConfig.getServerChannelMaxIdleTimeSeconds()), //
                                        new NettyConnectManageHandler(), //
                                        new NettyServerHandler());
                            }
                        });

        if (nettyServerConfig.isServerPooledByteBufAllocatorEnable()) {
            childHandler.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        }

        try {
            ChannelFuture sync = this.serverBootstrap.bind().sync();
            InetSocketAddress addr = (InetSocketAddress) sync.channel().localAddress();
            this.port = addr.getPort();
        } catch (InterruptedException e1) {
            throw new RuntimeException("this.serverBootstrap.bind().sync() InterruptedException", e1);
        }

        if (this.channelEventListener != null) {
            this.nettyEventExecutor.start();
        }

        this.timer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                try {
                    NettyRpcServer.this.scanResponseTable();
                } catch (Exception e) {
                    LOGGER.error("scanResponseTable exception", e);
                }
            }
        }, 1000 * 3, 1000);

        LOGGER.info("Rpc server [port:{}] start success", port);
    }

    @Override
    public void registerProcessor(int requestCode, NettyRequestProcessor processor, ExecutorService executor) {
        ExecutorService executorThis = executor;
        if (null == executor) {
            executorThis = this.publicExecutor;
        }

        Pair<NettyRequestProcessor, ExecutorService> pair = new Pair<NettyRequestProcessor, ExecutorService>(
                processor, executorThis);
        this.processorTable.put(requestCode, pair);
    }

    @Override
    public void registerDefaultProcessor(NettyRequestProcessor processor, ExecutorService executor) {
        this.defaultRequestProcessor = new Pair<NettyRequestProcessor, ExecutorService>(processor, executor);
    }

    @Override
    public RpcCommand invokeSync(final Channel channel, final RpcCommand request, final long timeoutMillis)
            throws InterruptedException, RpcTooMuchRequestException, RpcSendRequestException, RpcTimeoutException {
        return this.invokeSyncImpl(channel, request, timeoutMillis);
    }

    @Override
    public void invokeAsync(Channel channel, RpcCommand request, long timeoutMillis, InvokeCallback invokeCallback)
            throws InterruptedException, RpcTooMuchRequestException, RpcTimeoutException, RpcSendRequestException {
        this.invokeAsyncImpl(channel, request, timeoutMillis, invokeCallback);
    }

    @Override
    public void invokeOneWay(Channel channel, RpcCommand request, long timeoutMillis)
            throws InterruptedException, RpcTooMuchRequestException, RpcTimeoutException, RpcSendRequestException {
        this.invokeOneWayImpl(channel, request, timeoutMillis);
    }

    @Override
    public void shutdown() {
        try {
            if (this.timer != null) {
                this.timer.cancel();
            }

            this.eventLoopGroupBoss.shutdownGracefully();

            this.eventLoopGroupSelector.shutdownGracefully();

            if (this.nettyEventExecutor != null) {
                this.nettyEventExecutor.shutdown();
            }

            if (this.defaultEventExecutorGroup != null) {
                this.defaultEventExecutorGroup.shutdownGracefully();
            }
        } catch (Exception e) {
            LOGGER.error("NettyRpcClientServer shutdown exception, ", e);
        }

        if (this.publicExecutor != null) {
            try {
                this.publicExecutor.shutdown();
            } catch (Exception e) {
                LOGGER.error("NettyRpcClientServer shutdown exception, ", e);
            }
        }
    }

    @Override
    public ChannelEventListener getChannelEventListener() {
        return channelEventListener;
    }

    @Override
    public ExecutorService getCallbackExecutor() {
        return this.publicExecutor;
    }

    class NettyServerHandler extends SimpleChannelInboundHandler<RpcCommand> {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, RpcCommand msg) throws Exception {
            processMessageReceived(ctx, msg);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            final String remoteAddress = RpcHelper.parseChannelRemoteAddr(ctx.channel());
            LOGGER.warn("NettyServerHandler read exception " + remoteAddress, cause);
        }
    }

    class NettyConnectManageHandler extends ChannelDuplexHandler {
        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            final String remoteAddress = RpcHelper.parseChannelRemoteAddr(ctx.channel());
            LOGGER.info("NETTY SERVER PIPELINE: channelRegistered {}", remoteAddress);
            super.channelRegistered(ctx);
        }

        @Override
        public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
            final String remoteAddress = RpcHelper.parseChannelRemoteAddr(ctx.channel());
            LOGGER.info("NETTY SERVER PIPELINE: channelUnregistered, the channel[{}]", remoteAddress);
            super.channelUnregistered(ctx);
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            final String remoteAddress = RpcHelper.parseChannelRemoteAddr(ctx.channel());
            LOGGER.info("NETTY SERVER PIPELINE: channelActive, the channel[{}]", remoteAddress);
            super.channelActive(ctx);

            if (NettyRpcServer.this.channelEventListener != null) {
                NettyRpcServer.this.putNettyEvent(
                        new NettyEvent(NettyEventType.CONNECT, remoteAddress.toString(), ctx.channel()));
            }
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            final String remoteAddress = RpcHelper.parseChannelRemoteAddr(ctx.channel());
            LOGGER.info("NETTY SERVER PIPELINE: channelInactive, the channel[{}]", remoteAddress);
            super.channelInactive(ctx);

            if (NettyRpcServer.this.channelEventListener != null) {
                NettyRpcServer.this.putNettyEvent(
                        new NettyEvent(NettyEventType.CLOSE, remoteAddress.toString(), ctx.channel()));
            }
        }

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof IdleStateEvent) {
                IdleStateEvent event = (IdleStateEvent) evt;
                if (event.state().equals(IdleState.ALL_IDLE)) {
                    final String remoteAddress = RpcHelper.parseChannelRemoteAddr(ctx.channel());
                    LOGGER.warn("NETTY SERVER PIPELINE: IDLE exception [{}]", remoteAddress);
                    RpcHelper.closeChannel(ctx.channel());
                    if (NettyRpcServer.this.channelEventListener != null) {
                        NettyRpcServer.this.putNettyEvent(
                                new NettyEvent(NettyEventType.IDLE, remoteAddress.toString(), ctx.channel()));
                    }
                }
            }

            ctx.fireUserEventTriggered(evt);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            final String remoteAddress = RpcHelper.parseChannelRemoteAddr(ctx.channel());
            LOGGER.warn("NETTY SERVER PIPELINE: exceptionCaught {}", remoteAddress);
            LOGGER.warn("NETTY SERVER PIPELINE: exceptionCaught exception.", cause);

            if (NettyRpcServer.this.channelEventListener != null) {
                NettyRpcServer.this.putNettyEvent(
                        new NettyEvent(NettyEventType.EXCEPTION, remoteAddress.toString(), ctx.channel()));
            }

            RpcHelper.closeChannel(ctx.channel());
        }
    }

    @Override
    public void registerRpcHook(RpcHook rpcHook) {
        this.rpcHook = rpcHook;
    }

    @Override
    public RpcHook getRpcHook() {
        return this.rpcHook;
    }

    @Override
    public int localListenPort() {
        return this.port;
    }

    @Override
    public Pair<NettyRequestProcessor, ExecutorService> getProcessorPair(int requestCode) {
        return processorTable.get(requestCode);
    }

}