io.journalkeeper.core.client.RemoteClientRpc Maven / Gradle / Ivy
Show all versions of journalkeeper-core Show documentation
/**
* 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.exceptions.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;
import java.util.concurrent.ScheduledExecutorService;
/**
* @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 final CompletableRetry completableRetry;
private final RandomDestinationSelector uriSelector;
private final ClientCheckRetry clientCheckRetry = new ClientCheckRetry();
private final Executor executor;
private final ScheduledExecutorService scheduledExecutor;
private URI leaderUri = null;
private URI preferredServer = null;
public RemoteClientRpc(List servers, ClientServerRpcAccessPoint clientServerRpcAccessPoint, RetryPolicy retryPolicy, Executor executor, ScheduledExecutorService scheduledExecutor) {
this.executor = executor;
this.scheduledExecutor = scheduledExecutor;
if (servers == null || servers.isEmpty()) {
throw new IllegalArgumentException("Argument servers can not be empty!");
}
this.clientServerRpcAccessPoint = clientServerRpcAccessPoint;
uriSelector = new PreferredServerRandomUriSelector(servers);
completableRetry = new CompletableRetry<>(retryPolicy,
uriSelector);
}
@Override
public final CompletableFuture invokeClientServerRpc(CompletableRetry.RpcInvoke invoke) {
return completableRetry.retry(uri -> invoke.invoke(clientServerRpcAccessPoint.getClintServerRpc(uri)), clientCheckRetry, executor, scheduledExecutor);
}
@Override
public CompletableFuture invokeClientServerRpc(URI uri, CompletableRetry.RpcInvoke invoke) {
return completableRetry.retry(uri1 -> invoke.invoke(clientServerRpcAccessPoint.getClintServerRpc(uri1)), clientCheckRetry, uri, executor, scheduledExecutor);
}
@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();
}
@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(eventWatcher);
return CompletableFuture.completedFuture(null);
}, clientCheckRetry, executor, scheduledExecutor);
}
@Override
public void unWatch(EventWatcher eventWatcher) {
completableRetry.retry(uri -> {
clientServerRpcAccessPoint.getClintServerRpc(uri).unWatch(eventWatcher);
return CompletableFuture.completedFuture(null);
}, clientCheckRetry, executor, scheduledExecutor);
}
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.info("{} failed, cause: {}, Retry...", response.getClass().getName(), response.errorString());
return true;
case TIMEOUT:
case SERVER_BUSY:
case RETRY_LATER:
case TRANSPORT_FAILED:
case EXCEPTION:
logger.info("{} failed, cause: {}, Retry...", response.getClass().getName(), response.errorString());
return true;
case SUCCESS:
return false;
default:
logger.warn("{} failed, cause: {}!", response.getClass().getName(), response.errorString());
return false;
}
}
}
private class PreferredServerRandomUriSelector extends RandomDestinationSelector {
PreferredServerRandomUriSelector(Collection allDestinations) {
super(allDestinations);
}
@Override
public URI select(Set usedDestinations) {
if ((null == usedDestinations || usedDestinations.isEmpty()) && null != preferredServer) {
return preferredServer;
} else {
return super.select(usedDestinations);
}
}
}
}