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

com.github.netty.protocol.dubbo.DubboClient Maven / Gradle / Ivy

The newest version!
package com.github.netty.protocol.dubbo;

import com.github.netty.core.AbstractNettyClient;
import com.github.netty.core.util.LoggerFactoryX;
import com.github.netty.core.util.LoggerX;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelUtils;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.internal.PlatformDependent;

import java.net.InetSocketAddress;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;

public class DubboClient extends AbstractNettyClient {
    protected final LoggerX heartLogger = LoggerFactoryX.getLogger(DubboClient.class.getName() + ".heart");
    private final String serviceName;
    private final AtomicBoolean scheduleReconnectTaskIngFlag = new AtomicBoolean(false);
    private final ChannelHandler handler;
    private long connectTimeout = 1000;
    private int reconnectScheduledIntervalMs = -1;
    /**
     * Connection status
     */
    private volatile State state = State.DOWN;
    /**
     * reconnectScheduleFuture
     */
    private ScheduledFuture reconnectScheduleFuture;
    private long reconnectCount = 0;
    /**
     * reconnectTaskSuccessConsumer Callback method after successful reconnect
     */
    private BiConsumer reconnectTaskSuccessConsumer;
    private BiConsumer stateConsumer;
    /**
     * Connecting timeout timestamp
     */
    private volatile long connectTimeoutTimestamp;

    public DubboClient(String serviceName, ChannelHandler channelInitializer) {
        super(serviceName + "-", null);
        this.serviceName = serviceName;
        this.handler = channelInitializer;
    }

    public String getServiceName() {
        return serviceName;
    }

    @Override
    protected ChannelHandler newBossChannelHandler() {
        return handler;
    }

    public State getState() {
        return state;
    }

    @Override
    public SocketChannel getChannel() {
        SocketChannel socketChannel = super.getChannel();
        if (socketChannel == null || !socketChannel.isActive()) {
            long timestamp = System.currentTimeMillis();
            socketChannel = waitGetConnect(connect(), connectTimeout);
            if (socketChannel == null) {
                throw new DubboConnectException("The [" + remoteAddress + "] channel no connect. maxConnectTimeout=[" + connectTimeout + "], connectTimeout=[" + (System.currentTimeMillis() - timestamp) + "]");
            } else if (!socketChannel.isActive()) {
                if (reconnectScheduledIntervalMs > 0) {
                    scheduleReconnectTask(reconnectScheduledIntervalMs, TimeUnit.MILLISECONDS);
                }
                if (this.state != State.DOWN) {
                    this.state = State.DOWN;
                    BiConsumer stateConsumer = this.stateConsumer;
                    if (stateConsumer != null) {
                        stateConsumer.accept(state, this);
                    }
                }
                throw new DubboConnectException("The [" + socketChannel + "] channel no connect. maxConnectTimeout=[" + connectTimeout + "], connectTimeout=[" + (System.currentTimeMillis() - timestamp) + "]");
            }
        }

        int yieldCount = 0;
        if (!socketChannel.isWritable()) {
            socketChannel.flush();
        }
        while (!socketChannel.isWritable()) {
            ChannelUtils.forceFlush(socketChannel);
            if (!socketChannel.eventLoop().inEventLoop()) {
                Thread.yield();
                yieldCount++;
            }
        }
        if (yieldCount != 0 && heartLogger.isDebugEnabled()) {
            heartLogger.debug("RpcClient waitWritable... yieldCount={}", yieldCount);
        }
        return socketChannel;
    }

    @Override
    public void setChannel(SocketChannel newChannel) {
        super.setChannel(newChannel);
        state = State.UP;
        BiConsumer stateConsumer = this.stateConsumer;
        if (stateConsumer != null) {
            stateConsumer.accept(state, this);
        }
    }

    public boolean scheduleReconnectTask(long reconnectIntervalMillSeconds, TimeUnit timeUnit) {
        if (this.scheduleReconnectTaskIngFlag.compareAndSet(false, true)) {
            this.reconnectScheduleFuture = getWorker().scheduleWithFixedDelay(() -> {
                if (state == State.UP) {
                    cancelScheduleReconnectTask();
                } else {
                    reconnectCount++;
                    connect();
                }
            }, reconnectIntervalMillSeconds, reconnectIntervalMillSeconds, timeUnit);
            return true;
        }
        return false;
    }

    public int getReconnectScheduledIntervalMs() {
        return reconnectScheduledIntervalMs;
    }

    public void setReconnectScheduledIntervalMs(int reconnectScheduledIntervalMs) {
        this.reconnectScheduledIntervalMs = reconnectScheduledIntervalMs;
    }

    public long getConnectTimeout() {
        return connectTimeout;
    }

    public void setConnectTimeout(long connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public void cancelScheduleReconnectTask() {
        ScheduledFuture scheduledFuture = this.reconnectScheduleFuture;
        if (scheduledFuture != null) {
            scheduledFuture.cancel(false);
        }
        BiConsumer reconnectSuccessHandler = this.reconnectTaskSuccessConsumer;
        if (reconnectSuccessHandler != null) {
            reconnectSuccessHandler.accept(reconnectCount, this);
        }
        this.reconnectScheduleFuture = null;
        this.reconnectCount = 0;
        this.scheduleReconnectTaskIngFlag.set(false);
    }

    public void setReconnectTaskSuccessConsumer(BiConsumer reconnectTaskSuccessConsumer) {
        this.reconnectTaskSuccessConsumer = reconnectTaskSuccessConsumer;
    }

    public void setStateConsumer(BiConsumer stateConsumer) {
        this.stateConsumer = stateConsumer;
    }

    @Override
    public Optional connect(InetSocketAddress remoteAddress) {
        connectTimeoutTimestamp = System.currentTimeMillis();
        return super.connect(remoteAddress);
    }

    protected SocketChannel waitGetConnect(Optional optional, long connectTimeout) {
        if (optional.isPresent()) {
            ChannelFuture future = optional.get();
            try {
                future.await(connectTimeout, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                PlatformDependent.throwException(e);
            }
            return (SocketChannel) future.channel();
        } else {
            int yieldCount = 0;
            long timeoutTimestamp = connectTimeoutTimestamp;
            long waitTime;
            while (timeoutTimestamp != 0 && (waitTime = timeoutTimestamp - System.currentTimeMillis()) > 0) {
                if (waitTime > 200) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        PlatformDependent.throwException(e);
                    }
                } else {
                    yieldCount++;
                    Thread.yield();
                }
            }
            SocketChannel channel = super.getChannel();
            if (channel != null) {
                while (state != State.UP) {
                    yieldCount++;
                    Thread.yield();
                }
                if (heartLogger.isDebugEnabled()) {
                    heartLogger.debug("RpcClient waitGetConnect... yieldCount={}", yieldCount);
                }
            }
            return super.getChannel();
        }
    }

    @Override
    protected void connectAfter(ChannelFuture future) {
        if (future.isSuccess()) {
            if (heartLogger.isDebugEnabled()) {
                heartLogger.debug("DubboClient connect success... {}", future.channel());
            }
        } else {
            if (heartLogger.isDebugEnabled()) {
                heartLogger.debug("DubboClient connect fail... {}", future.channel(), Objects.toString(future.cause()));
            }
        }
        connectTimeoutTimestamp = 0;
    }

    @Override
    protected void stopAfter(ChannelFuture future) {
        super.stopAfter(future);
        if (this.state != State.DOWN) {
            this.state = State.DOWN;
            BiConsumer stateConsumer = this.stateConsumer;
            if (stateConsumer != null) {
                stateConsumer.accept(state, this);
            }
        }
    }

    @Override
    public String toString() {
        return serviceName + remoteAddress + "(" + state + ")";
    }

    /**
     * Client connection status
     */
    public enum State {
        DOWN, UP
    }

    /**
     * DubboConnectException
     *
     * @author wangzihao
     * 2024/4/29
     */
    public static class DubboConnectException extends RuntimeException {

        public DubboConnectException(String message) {
            super(message, null, false, false);
        }

        public DubboConnectException(String message, Throwable cause) {
            super(message, cause, false, false);
            if (cause != null) {
                setStackTrace(cause.getStackTrace());
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy