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

io.journalkeeper.core.client.RemoteClientRpc Maven / Gradle / Ivy

There is a newer version: 0.1.11
Show newest version
/**
 * 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 io.journalkeeper.core.client;

import io.journalkeeper.core.api.ClusterConfiguration;
import io.journalkeeper.core.exception.NoLeaderException;
import io.journalkeeper.exceptions.NotLeaderException;
import io.journalkeeper.exceptions.RequestTimeoutException;
import io.journalkeeper.exceptions.ServerBusyException;
import io.journalkeeper.exceptions.ServerNotFoundException;
import io.journalkeeper.exceptions.TransportException;
import io.journalkeeper.rpc.BaseResponse;
import io.journalkeeper.rpc.LeaderResponse;
import io.journalkeeper.rpc.client.ClientServerRpc;
import io.journalkeeper.rpc.client.ClientServerRpcAccessPoint;
import io.journalkeeper.rpc.client.GetServersResponse;
import io.journalkeeper.utils.event.EventWatcher;
import io.journalkeeper.utils.retry.CheckRetry;
import io.journalkeeper.utils.retry.CompletableRetry;
import io.journalkeeper.utils.retry.RandomDestinationSelector;
import io.journalkeeper.utils.retry.RetryPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

/**
 * @author LiYue
 * Date: 2019-09-09
 */
public class RemoteClientRpc implements ClientRpc {
    private static final Logger logger = LoggerFactory.getLogger(RemoteClientRpc.class);
    private final ClientServerRpcAccessPoint clientServerRpcAccessPoint;
    private URI leaderUri = null;

    private final CompletableRetry completableRetry;
    private final RandomDestinationSelector uriSelector;
    private final ClientCheckRetry clientCheckRetry = new ClientCheckRetry();
    private final Executor executor;
    private URI preferredServer = null;
    public RemoteClientRpc(List servers, ClientServerRpcAccessPoint clientServerRpcAccessPoint, RetryPolicy retryPolicy, Executor executor) {
        this.executor = executor;
        if(servers == null || servers.isEmpty()) {
            throw new IllegalArgumentException("Argument servers can not be empty!");
        }
        this.clientServerRpcAccessPoint = clientServerRpcAccessPoint;
        uriSelector = new RandomUriSelector(servers);
        completableRetry = new CompletableRetry<>(retryPolicy,
                uriSelector);
//        completableRetry = new CompletableRetry<>(new IncreasingRetryPolicy(new long [] {50, 100, 500, 1000, 3000, 10000, 30000}, 50),
//                uriSelector);
    }


    @Override
    public final   CompletableFuture invokeClientServerRpc(CompletableRetry.RpcInvoke invoke) {
        return completableRetry.retry((uri) -> invoke.invoke(clientServerRpcAccessPoint.getClintServerRpc(uri)), clientCheckRetry, executor);
    }

    @Override
    public  CompletableFuture invokeClientServerRpc(URI uri, CompletableRetry.RpcInvoke invoke) {
        return invoke.invoke(clientServerRpcAccessPoint.getClintServerRpc(uri));
    }

    @Override
    public final   CompletableFuture invokeClientLeaderRpc(CompletableRetry.RpcInvoke invoke) {
        return invokeClientServerRpc((rpc) -> unSetLeaderUriWhenLeaderRpcFailed(
                getCachedLeaderRpc(rpc)
                .thenCompose(invoke::invoke)
                )
        );
    }


    private  CompletableFuture unSetLeaderUriWhenLeaderRpcFailed(CompletableFuture future) {
        return future
                .exceptionally(e -> {
                    this.leaderUri = null;
                    throw new CompletionException(e);
                }).thenApply(response -> {
                    if(!response.success()) {
                        this.leaderUri = null;
                    }
                    return response;
                });
    }

    private CompletableFuture getCachedLeaderRpc(ClientServerRpc clientServerRpc) {
        CompletableFuture leaderUriFuture = new CompletableFuture<>();
        if(this.leaderUri == null) {
            GetServersResponse getServersResponse = null;
            try {
                getServersResponse = clientServerRpc.getServers().get();
            } catch (Throwable e) {
                Throwable ex = e instanceof ExecutionException ? e.getCause(): e;
                leaderUriFuture = new CompletableFuture<>();
                leaderUriFuture.completeExceptionally(ex);
            }
            if(null != getServersResponse && getServersResponse.success()) {
                ClusterConfiguration clusterConfiguration = getServersResponse.getClusterConfiguration();
                if(null != clusterConfiguration) {
                    this.leaderUri = clusterConfiguration.getLeader();
                }
            }

        }

        if(this.leaderUri != null) {
            leaderUriFuture.complete(leaderUri);
        } else if(!leaderUriFuture.isDone()){
            leaderUriFuture.completeExceptionally(new NoLeaderException());
        }
//        logger.info("Current leader in client: {}", leaderUri);
        return leaderUriFuture.thenApply(clientServerRpcAccessPoint::getClintServerRpc);
    }

    @Override
    public void stop () {
        this.clientServerRpcAccessPoint.stop();
    }


    private  class ClientCheckRetry implements CheckRetry {

        @Override
        public boolean checkException(Throwable exception) {
            try {

                logger.debug("Rpc exception: {}-{}", exception.getClass().getCanonicalName(), exception.getMessage());
                throw exception;
            } catch (RequestTimeoutException | ServerBusyException | TransportException | ServerNotFoundException ne) {
                return true;
            } catch (NoLeaderException ne) {
                leaderUri = null;
                return true;
            } catch (NotLeaderException ne) {
                leaderUri = ne.getLeader();
                return true;
            }
            catch (Throwable ignored) {}
            return false;
        }

        @Override
        public boolean checkResult(BaseResponse response) {
            switch (response.getStatusCode()) {
                case NOT_LEADER:
                    leaderUri = ((LeaderResponse) response).getLeader();
                    logger.warn(response.errorString());
                    return true;
                case TIMEOUT:
                case SERVER_BUSY:
                case RETRY_LATER:
                case TRANSPORT_FAILED:
                case EXCEPTION:
                    logger.warn(response.errorString());
                    return true;
                case SUCCESS:
                    return false;
                default:
                    logger.warn(response.errorString());
                    return false;
            }
        }
    }

    @Override
    public URI getPreferredServer() {
        return preferredServer;
    }

    @Override
    public void setPreferredServer(URI preferredServer) {
        this.preferredServer = preferredServer;
    }

    @Override
    public void updateServers(List servers) {
        uriSelector.setAllDestinations(servers);
    }

    @Override
    public void watch(EventWatcher eventWatcher) {
        completableRetry.retry(uri -> {
            clientServerRpcAccessPoint.getClintServerRpc(uri)
                    .watch(event -> executor.execute(() -> eventWatcher.onEvent(event)));
            return CompletableFuture.completedFuture(null);
        }, clientCheckRetry, executor);
    }

    @Override
    public void unWatch(EventWatcher eventWatcher) {
        completableRetry.retry(uri -> {
            clientServerRpcAccessPoint.getClintServerRpc(uri).unWatch(eventWatcher);
            return CompletableFuture.completedFuture(null);
        }, clientCheckRetry, executor);
    }



    private class RandomUriSelector extends RandomDestinationSelector {

        RandomUriSelector(Collection allDestinations) {
            super(allDestinations);
        }

        @Override
        public URI select(Set usedDestinations) {
            if((null == usedDestinations || usedDestinations.isEmpty()) && null != preferredServer) {
                return preferredServer;
            } else {
                return super.select(usedDestinations);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy