com.dinstone.jrpc.transport.netty5.NettyAcceptance.java Source code

Java tutorial

Introduction

Here is the source code for com.dinstone.jrpc.transport.netty5.NettyAcceptance.java

Source

/*
 * Copyright (C) 2014~2017 dinstone<dinstone@163.com>
 *
 * 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 com.dinstone.jrpc.transport.netty5;

import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dinstone.jrpc.NamedThreadFactory;
import com.dinstone.jrpc.binding.ImplementBinding;
import com.dinstone.jrpc.protocol.Heartbeat;
import com.dinstone.jrpc.protocol.Request;
import com.dinstone.jrpc.protocol.Response;
import com.dinstone.jrpc.transport.AbstractAcceptance;
import com.dinstone.jrpc.transport.Acceptance;
import com.dinstone.jrpc.transport.NetworkAddressUtil;
import com.dinstone.jrpc.transport.TransportConfig;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
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.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.DefaultExecutorServiceFactory;

public class NettyAcceptance extends AbstractAcceptance {

    private static final Logger LOG = LoggerFactory.getLogger(NettyAcceptance.class);

    private static final AttributeKey<String> LOCAL_REMOTE_ADDRESS_KEY = AttributeKey
            .valueOf("local-remote-address-key");

    private final ConcurrentMap<String, Channel> connectionMap = new ConcurrentHashMap<>();

    private EventLoopGroup bossGroup;

    private EventLoopGroup workGroup;

    private ExecutorService executorService;

    public NettyAcceptance(TransportConfig transportConfig, ImplementBinding implementBinding,
            InetSocketAddress serviceAddress) {
        super(transportConfig, implementBinding, serviceAddress);
    }

    @Override
    public Acceptance bind() {
        bossGroup = new NioEventLoopGroup(1, new DefaultExecutorServiceFactory("N5A-Boss"));
        workGroup = new NioEventLoopGroup(transportConfig.getNioProcessorCount(),
                new DefaultExecutorServiceFactory("N5A-Work"));

        ServerBootstrap boot = new ServerBootstrap();
        boot.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        TransportProtocolDecoder decoder = new TransportProtocolDecoder();
                        decoder.setMaxObjectSize(transportConfig.getMaxSize());
                        TransportProtocolEncoder encoder = new TransportProtocolEncoder();
                        encoder.setMaxObjectSize(transportConfig.getMaxSize());
                        ch.pipeline().addLast("TransportProtocolDecoder", decoder);
                        ch.pipeline().addLast("TransportProtocolEncoder", encoder);

                        int intervalSeconds = transportConfig.getHeartbeatIntervalSeconds();
                        ch.pipeline().addLast("IdleStateHandler", new IdleStateHandler(intervalSeconds * 2, 0, 0));
                        ch.pipeline().addLast("NettyServerHandler", new NettyServerHandler());
                    }
                });
        boot.option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_BACKLOG, 128);
        boot.childOption(ChannelOption.SO_RCVBUF, 16 * 1024).childOption(ChannelOption.SO_SNDBUF, 16 * 1024)
                .childOption(ChannelOption.TCP_NODELAY, true);

        try {
            boot.bind(serviceAddress).sync();

            int processorCount = transportConfig.getBusinessProcessorCount();
            if (processorCount > 0) {
                NamedThreadFactory threadFactory = new NamedThreadFactory("N5A-BusinssProcessor");
                executorService = Executors.newFixedThreadPool(processorCount, threadFactory);
            }
        } catch (Exception e) {
            throw new RuntimeException("can't bind service on " + serviceAddress, e);
        }
        LOG.info("netty5 acceptance bind on {}", serviceAddress);

        return this;
    }

    @Override
    public void destroy() {
        if (workGroup != null) {
            workGroup.shutdownGracefully();
        }
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }

        if (executorService != null) {
            executorService.shutdownNow();
            try {
                executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
            }
        }
    }

    public class NettyServerHandler extends ChannelHandlerAdapter {

        private final int maxConnectionCount = transportConfig.getMaxConnectionCount();

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof IdleStateEvent) {
                IdleStateEvent event = (IdleStateEvent) evt;
                if (event.state() == IdleState.READER_IDLE) {
                    ctx.close();
                }
            } else {
                super.userEventTriggered(ctx, evt);
            }
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            int currentConnectioncount = connectionMap.size();
            if (currentConnectioncount >= maxConnectionCount) {
                ctx.close();
                LOG.warn("connection count is too big: limit={},current={}", maxConnectionCount,
                        currentConnectioncount);
            } else {
                Channel channel = ctx.channel();
                String addressLabel = NetworkAddressUtil.addressLabel(channel.remoteAddress(),
                        channel.localAddress());
                channel.attr(LOCAL_REMOTE_ADDRESS_KEY).set(addressLabel);
                connectionMap.put(addressLabel, channel);

                super.channelActive(ctx);
            }
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            String connectionKey = ctx.channel().attr(LOCAL_REMOTE_ADDRESS_KEY).get();
            if (connectionKey != null) {
                connectionMap.remove(connectionKey);
            }

            super.channelInactive(ctx);
        }

        @Override
        public void channelRead(final ChannelHandlerContext ctx, final Object message) {
            if (message instanceof Request) {
                if (executorService != null) {
                    executorService.execute(new Runnable() {

                        @Override
                        public void run() {
                            process(ctx, message);
                        }

                    });
                } else {
                    process(ctx, message);
                }
            } else if (message instanceof Heartbeat) {
                ctx.writeAndFlush(message);
            }
        }

        protected void process(final ChannelHandlerContext ctx, final Object message) {
            Response response = handle((Request) message);
            ctx.writeAndFlush(response);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            LOG.error("untreated exception", cause);
            ctx.close();
        }

    }

}