
io.datakernel.rpc.client.RpcClientConnection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datakernel-rpc Show documentation
Show all versions of datakernel-rpc Show documentation
High-performance and fault-tolerant remote procedure call module for building distributed applications.
Provides a high-performance asynchronous binary RPC streaming protocol.
The newest version!
/*
* Copyright (C) 2015-2018 SoftIndex LLC.
*
* 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.datakernel.rpc.client;
import io.datakernel.async.callback.Callback;
import io.datakernel.common.ApplicationSettings;
import io.datakernel.common.Stopwatch;
import io.datakernel.common.exception.AsyncTimeoutException;
import io.datakernel.datastream.StreamDataAcceptor;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.eventloop.jmx.EventStats;
import io.datakernel.eventloop.jmx.JmxRefreshable;
import io.datakernel.jmx.api.JmxAttribute;
import io.datakernel.jmx.api.JmxReducers.JmxReducerSum;
import io.datakernel.rpc.client.jmx.RpcRequestStats;
import io.datakernel.rpc.client.sender.RpcSender;
import io.datakernel.rpc.protocol.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static io.datakernel.rpc.client.IRpcClient.RPC_OVERLOAD_EXCEPTION;
import static io.datakernel.rpc.client.IRpcClient.RPC_TIMEOUT_EXCEPTION;
import static org.slf4j.LoggerFactory.getLogger;
public final class RpcClientConnection implements RpcStream.Listener, RpcSender, JmxRefreshable {
private static final Logger logger = getLogger(RpcClientConnection.class);
private static final int BUCKET_CAPACITY = ApplicationSettings.getInt(RpcClientConnection.class, "bucketCapacity", 16);
private StreamDataAcceptor downstreamDataAcceptor = this::addIntoInitialBuffer;
private boolean overloaded = false;
public static final RpcException CONNECTION_CLOSED = new RpcException(RpcClientConnection.class, "Connection closed.");
private final Eventloop eventloop;
private final RpcClient rpcClient;
private final RpcStream stream;
private final InetSocketAddress address;
private final Map> activeRequests = new HashMap<>();
private final Map expirationLists = new HashMap<>();
private ArrayList initialBuffer = new ArrayList<>();
private static final class ExpirationList {
private int size;
private int[] cookies;
ExpirationList(int[] cookies) {
this.cookies = cookies;
}
}
private int cookie = 0;
private boolean serverClosing;
// JMX
private boolean monitoring;
private final RpcRequestStats connectionStats;
private final EventStats totalRequests;
private final EventStats connectionRequests;
RpcClientConnection(Eventloop eventloop, RpcClient rpcClient,
InetSocketAddress address,
RpcStream stream) {
this.eventloop = eventloop;
this.rpcClient = rpcClient;
this.stream = stream;
this.address = address;
// JMX
this.monitoring = false;
this.connectionStats = RpcRequestStats.create(RpcClient.SMOOTHING_WINDOW);
this.connectionRequests = connectionStats.getTotalRequests();
this.totalRequests = rpcClient.getGeneralRequestsStats().getTotalRequests();
}
@Override
public void sendRequest(I request, int timeout, @NotNull Callback cb) {
assert eventloop.inEventloopThread();
// jmx
totalRequests.recordEvent();
connectionRequests.recordEvent();
if (!overloaded || request instanceof RpcMandatoryData) {
cookie++;
// jmx
if (monitoring) {
cb = doJmxMonitoring(request, timeout, cb);
}
if (timeout != Integer.MAX_VALUE) {
ExpirationList list = expirationLists.computeIfAbsent(eventloop.currentTimeMillis() + timeout, t -> {
ExpirationList l = new ExpirationList(new int[BUCKET_CAPACITY]);
eventloop.scheduleBackground(t, () -> {
expirationLists.remove(t);
for (int i = 0; i < l.size; i++) {
Callback> expiredCb = activeRequests.remove(l.cookies[i]);
if (expiredCb != null) {
// jmx
connectionStats.getExpiredRequests().recordEvent();
rpcClient.getGeneralRequestsStats().getExpiredRequests().recordEvent();
expiredCb.accept(null, RPC_TIMEOUT_EXCEPTION);
}
}
if (serverClosing && activeRequests.size() == 0) {
shutdown();
}
});
return l;
});
if (list.size >= list.cookies.length) {
list.cookies = Arrays.copyOf(list.cookies, list.cookies.length * 2);
}
list.cookies[list.size++] = cookie;
}
activeRequests.put(cookie, cb);
downstreamDataAcceptor.accept(RpcMessage.of(cookie, request));
} else {
doProcessOverloaded(cb);
}
}
@Override
public void sendRequest(I request, @NotNull Callback cb) {
assert eventloop.inEventloopThread();
// jmx
totalRequests.recordEvent();
connectionRequests.recordEvent();
if (!overloaded || request instanceof RpcMandatoryData) {
cookie++;
// jmx
if (monitoring) {
cb = doJmxMonitoring(request, Integer.MAX_VALUE, cb);
}
activeRequests.put(cookie, cb);
downstreamDataAcceptor.accept(RpcMessage.of(cookie, request));
} else {
doProcessOverloaded(cb);
}
}
private Callback doJmxMonitoring(I request, int timeout, @NotNull Callback cb) {
RpcRequestStats requestStatsPerClass = rpcClient.ensureRequestStatsPerClass(request.getClass());
requestStatsPerClass.getTotalRequests().recordEvent();
return new JmxConnectionMonitoringResultCallback<>(requestStatsPerClass, cb, timeout);
}
private void doProcessOverloaded(@NotNull Callback cb) {
// jmx
rpcClient.getGeneralRequestsStats().getRejectedRequests().recordEvent();
connectionStats.getRejectedRequests().recordEvent();
if (logger.isTraceEnabled()) logger.trace("RPC client uplink is overloaded");
cb.accept(null, RPC_OVERLOAD_EXCEPTION);
}
@Override
public void accept(RpcMessage message) {
if (message.getData().getClass() == RpcRemoteException.class) {
processErrorMessage(message);
} else if (message.getData().getClass() == RpcControlMessage.class) {
processControlMessage((RpcControlMessage) message.getData());
} else {
@SuppressWarnings("unchecked")
Callback
© 2015 - 2025 Weber Informatics LLC | Privacy Policy