All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.netty.core.AbstractNettyClient Maven / Gradle / Ivy

The newest version!
package com.github.netty.core;

import com.github.netty.core.util.LoggerFactoryX;
import com.github.netty.core.util.LoggerX;
import com.github.netty.core.util.NamespaceUtil;
import com.github.netty.core.util.ThreadFactoryX;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ChannelFactory;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.Closeable;
import java.net.InetSocketAddress;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * An abstract netty client
 *
 * @author wangzihao
 * 2018/8/18/018
 */
public abstract class AbstractNettyClient implements Closeable {
    protected final AtomicBoolean connectIngFlag = new AtomicBoolean(false);
    protected final LoggerX logger = LoggerFactoryX.getLogger(getClass());
    private final String name;
    private final String namePre;
    protected InetSocketAddress remoteAddress;
    private Bootstrap bootstrap;
    private EventLoopGroup worker;
    private final boolean enableEpoll;
    private volatile SocketChannel channel;
    private int ioThreadCount = 0;
    private int ioRatio = 100;
    private final AtomicBoolean initFlag = new AtomicBoolean(false);
    private volatile ChannelFuture connectFuture;

    public AbstractNettyClient() {
        this("", null);
    }

    public AbstractNettyClient(String remoteHost, int remotePort) {
        this(new InetSocketAddress(remoteHost, remotePort));
    }

    public AbstractNettyClient(InetSocketAddress remoteAddress) {
        this("", remoteAddress);
    }

    /**
     * @param namePre       名称前缀
     * @param remoteAddress 远程地址
     */
    public AbstractNettyClient(String namePre, InetSocketAddress remoteAddress) {
        this.enableEpoll = Epoll.isAvailable();
        this.remoteAddress = remoteAddress;
        this.namePre = namePre;
        this.name = NamespaceUtil.newIdName(namePre, getClass());
        if (enableEpoll) {
            logger.info("enable epoll client = {}", this);
        }
    }

    protected abstract ChannelHandler newBossChannelHandler();

    protected Bootstrap newClientBootstrap() {
        return new Bootstrap();
    }

    protected EventLoopGroup newWorkerEventLoopGroup() {
        EventLoopGroup worker;
        if (enableEpoll) {
            EpollEventLoopGroup epollWorker = new EpollEventLoopGroup(ioThreadCount, new ThreadFactoryX("Epoll", namePre + "Client-Worker", true));
//            epollWorker.setIoRatio(ioRatio);
            worker = epollWorker;
        } else {
            NioEventLoopGroup nioWorker = new NioEventLoopGroup(ioThreadCount, new ThreadFactoryX("NIO", namePre + "Client-Worker", true));
            nioWorker.setIoRatio(ioRatio);
            worker = nioWorker;
        }
        return worker;
    }

    protected ChannelFactory newClientChannelFactory() {
        ChannelFactory channelFactory;
        if (enableEpoll) {
            channelFactory = EpollSocketChannel::new;
        } else {
            channelFactory = NioSocketChannel::new;
        }
        return channelFactory;
    }

    protected AbstractNettyClient init() {
        this.bootstrap = newClientBootstrap();
        this.worker = newWorkerEventLoopGroup();
        ChannelFactory channelFactory = newClientChannelFactory();
        ChannelHandler bossChannelHandler = newBossChannelHandler();

        this.bootstrap
                .group(worker)
                .channelFactory(channelFactory)
                .handler(bossChannelHandler)
                .remoteAddress(remoteAddress)
                //用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
//                    .option(ChannelOption.SO_BACKLOG, 1024) // determining the number of connections queued
                //netty boos的默认内存分配器
//                    .option(ChannelOption.ALLOCATOR, ByteBufAllocatorX.INSTANCE)
                //禁用Nagle算法,即数据包立即发送出去 (在TCP_NODELAY模式下,假设有3个小包要发送,第一个小包发出后,接下来的小包需要等待之前的小包被ack,在这期间小包会合并,直到接收到之前包的ack后才会发生)
                .option(ChannelOption.TCP_NODELAY, true)
                //开启TCP/IP协议实现的心跳机制
                .option(ChannelOption.SO_KEEPALIVE, true);
        return this;
    }

    public AbstractNettyClient config(Bootstrap bootstrap) {
        return this;
    }

    public boolean isConnect() {
        return getActiveSocketChannelCount() > 0;
    }

    public Optional connect() {
        return connect(remoteAddress);
    }

    public Optional connect(InetSocketAddress remoteAddress) {
        if (connectIngFlag.compareAndSet(false, true)) {
            if (initFlag.compareAndSet(false, true)) {
                init();
            }
            this.remoteAddress = remoteAddress == null ? (InetSocketAddress) bootstrap.config().remoteAddress() : remoteAddress;
            ChannelFuture connectFuture = bootstrap.connect(this.remoteAddress)
                    .addListener((ChannelFutureListener) future -> {
                        try {
                            if (future.isSuccess()) {
                                setChannel((SocketChannel) future.channel());
                            } else {
                                Channel channel1 = future.channel();
                                if (channel1.isRegistered()) {
                                    channel1.close();
                                }
                            }
                        } finally {
                            connectIngFlag.set(false);
                        }
                        connectAfter(future);
                    });
            this.connectFuture = connectFuture;
        }
        return Optional.ofNullable(this.connectFuture);
    }

    public SocketChannel getChannel() {
        return channel;
    }

    public void setChannel(SocketChannel channel) {
        this.channel = channel;
    }

    public InetSocketAddress getRemoteAddress() {
        return remoteAddress;
    }

    public EventLoopGroup getWorker() {
        return worker;
    }

    public int getIoRatio() {
        return ioRatio;
    }

    public void setIoRatio(int ioRatio) {
        if (worker instanceof NioEventLoopGroup) {
            ((NioEventLoopGroup) worker).setIoRatio(ioRatio);
        } else if (worker instanceof EpollEventLoopGroup) {
//            ((EpollEventLoopGroup) worker).setIoRatio(ioRatio);
        }
        this.ioRatio = ioRatio;
    }

    public int getIoThreadCount() {
        return ioThreadCount;
    }

    public void setIoThreadCount(int ioThreadCount) {
        this.ioThreadCount = ioThreadCount;
    }

    public boolean isConnectIng() {
        return connectIngFlag.get();
    }

    public boolean isEnableEpoll() {
        return enableEpoll;
    }

    public Bootstrap getBootstrap() {
        return bootstrap;
    }

    public ChannelFuture stop() {
        if (channel == null) {
            throw new IllegalStateException("channel is null");
        }
        return channel.close().addListener((ChannelFutureListener) future -> {
            AbstractNettyClient.this.bootstrap = null;
            AbstractNettyClient.this.worker.shutdownGracefully();
            AbstractNettyClient.this.worker = null;
            AbstractNettyClient.this.initFlag.set(false);
            AbstractNettyClient.this.channel = null;
            stopAfter(future);
        });
    }

    @Override
    public void close() {
        if (channel != null) {
            stop();
        }
    }

    protected void stopAfter(ChannelFuture future) {
        //有异常抛出
        if (future.cause() != null) {
            logger.warn("stopAfter. error={}", future.cause(), future.cause());
        }
        logger.info("{} stop [remoteAddress = {}]...", getName(), getRemoteAddress());
    }

    public String getName() {
        return name;
    }

    public int getPort() {
        return remoteAddress.getPort();
    }

    protected void connectAfter(ChannelFuture future) {
        logger.info("{} connect [activeSocketConnectCount = {}, remoteAddress = {}]...",
                getName(), getActiveSocketChannelCount(), getRemoteAddress());
    }

    public int getActiveSocketChannelCount() {
        return channel != null && channel.isActive() ? 1 : 0;
    }

    @Override
    public String toString() {
        return name + "{" +
                "channel=" + channel +
                ", remoteAddress=" + remoteAddress.getHostName() + ":" + remoteAddress.getPort() + "}";
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy