2021SC@SDUSC
前言
之前三章我们具体分析了一些关于RpcClient以及RpcServer的方法以及具体使用方式,还有中间的类RpcSteam和RpcMessage。在完成了对client客户端以及server服务器的创建之后,明白了中间的大概传输后,下一步就是将客户端与服务器通过中间类进行实际连接,让我们一起来看一下RpcClientConnection以及RpcServerConnection是如何通过steam和message完成通信的。
RpcClientConnection
我找出了在RpcClientConnection和RpcServerConnection两个连接类中均存在的变量,下面先把他们列出来,这两个也是之后两段通信的关键:
private StreamDataAcceptor<RpcMessage> downstreamDataAcceptor = null;
private final RpcStream stream;
RpcClientConnection接入了以下三个接口,能帮助我们更好地理清本类的具体作用,:
public final class RpcClientConnection implements RpcStream.Listener, RpcSender, JmxRefreshable
首先我们来看看RpcClientConnection的构造方法:
RpcClientConnection(Eventloop eventloop, RpcClient rpcClient, InetSocketAddress address, RpcStream stream,
long keepAliveMillis) {
this.eventloop = eventloop;
this.rpcClient = rpcClient;
this.stream = stream;
this.address = address;
this.keepAliveMillis = keepAliveMillis;
// JMX
this.monitoring = false;
this.connectionStats = RpcRequestStats.create(RpcClient.SMOOTHING_WINDOW);
this.connectionRequests = connectionStats.getTotalRequests();
this.totalRequests = rpcClient.getGeneralRequestsStats().getTotalRequests();
}
需要eventloop、rpcClient以及InteSockeAddress(与server端的InteAddress不同,这里为IP 地址+port端口号)还有一个rpcSteam。
public <I, O> void sendRequest(I request, int timeout, @NotNull Callback<O> cb) {
if (CHECK) checkState(eventloop.inEventloopThread(), "Not in eventloop thread");
// jmx
totalRequests.recordEvent();
connectionRequests.recordEvent();
if (!overloaded || request instanceof RpcMandatoryData) {
cookie++;
// jmx
if (monitoring) {
cb = doJmxMonitoring(request, timeout, cb);
}
if (timeout == Integer.MAX_VALUE) {
activeRequests.put(cookie, cb);
} else {
ScheduledCallback<O> scheduledCallback = new ScheduledCallback<>(cookie, cb);
scheduledCallback.scheduledRunnable = eventloop.delayBackground(timeout, scheduledCallback);
activeRequests.put(cookie, scheduledCallback);
}
downstreamDataAcceptor.accept(RpcMessage.of(cookie, request));
} else {
doProcessOverloaded(cb);
}
}
在上面我们提到RpcClientConnection继承了RpcSender类,那么之前在Client中看见的
requestSender.sendRequest(request, timeout, cb);
就可以解释了,这里的requestSend为RpcSender类,所以实际上是调用了RpcClientConnection的sendRequest()方法
下面我们来看一看RpcClientConnection中的sendRequest方法:
public <I, O> void sendRequest(I request, @NotNull Callback<O> cb) {
if (CHECK) checkState(eventloop.inEventloopThread(), "Not in eventloop thread");
// 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);
}
}
可以看到在connection中的sendRequest方法的关键在与设置activeRequests以及downstreamDataAcceptor两个变量,且使cookie++保证不会出现相同索引的情况。在本地的map activeRequests记录下本次的操作,并在downstreamDataAcceptor发送具体的请求,通过StreamDataAcceptor进行接收发送。
doProcessOverloaded()方法对应进程过载的情况。
private <O> void doProcessOverloaded(@NotNull Callback<O> 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);
}
调用accept()方法可以使当前连接得到RpcMessage中的消息,在正常情况下,会跳转至processControlMessage()方法进行具体的操作
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<Object> cb = (Callback<Object>) activeRequests.remove(message.getCookie());
if (cb == null) return;
cb.accept(message.getData(), null);
if (serverClosing && activeRequests.size() == 0) {
shutdown();
}
}
}
processErrorMessage()处理进程在执行过程中的错误。
private void processErrorMessage(RpcMessage message) {
RpcRemoteException remoteException = (RpcRemoteException) message.getData();
// jmx
connectionStats.getFailedRequests().recordEvent();
rpcClient.getGeneralRequestsStats().getFailedRequests().recordEvent();
connectionStats.getServerExceptions().recordException(remoteException, null);
rpcClient.getGeneralRequestsStats().getServerExceptions().recordException(remoteException, null);
Callback<?> cb = activeRequests.remove(message.getCookie());
if (cb != null) {
cb.accept(null, remoteException);
}
}
判断RpcControlMessage 的状态,执行对应的操作。
private void processControlMessage(RpcControlMessage controlMessage) {
if (controlMessage == RpcControlMessage.CLOSE) {
rpcClient.removeConnection(address);
serverClosing = true;
if (activeRequests.size() == 0) {
shutdown();
}
} else if (controlMessage == RpcControlMessage.PONG) {
pongReceived = true;
} else {
throw new RuntimeException("Received unknown RpcControlMessage");
}
}
在onSenderReady()方法中调用了ping()方法,使downstreamDataAcceptor接受对应的ping message。
private void ping() {
if (isClosed()) return;
if (keepAliveMillis == 0) return;
pongReceived = false;
downstreamDataAcceptor.accept(RpcMessage.of(-1, RpcControlMessage.PING));
eventloop.delayBackground(keepAliveMillis, () -> {
if (isClosed()) return;
if (!pongReceived) {
onReceiverError(CONNECTION_UNRESPONSIVE);
} else {
ping();
}
});
}
以下六个方法是对于RpcSteam中的接口Listener的实现(client需要接收来自server的信息,也需要实现listener接口)。
@Override
public void onReceiverEndOfStream() {
if (isClosed()) return;
logger.info("Receiver EOS: {}", address);
stream.close();
doClose();
}
onReceiverEndOfStream()方法用于告知已到末尾执行关闭。
下面三种方法都是应对对应的error,共分为三种:接收器错误,发送器错误,系列化器错误。
@Override
public void onReceiverError(@NotNull Exception e) {
if (isClosed()) return;
logger.error("Receiver error: {}", address, e);
rpcClient.getLastProtocolError().recordException(e, address);
stream.close();
doClose();
}
@Override
public void onSenderError(@NotNull Exception e) {
if (isClosed()) return;
logger.error("Sender error: {}", address, e);
rpcClient.getLastProtocolError().recordException(e, address);
stream.close();
doClose();
}
@Override
public void onSerializationError(RpcMessage message, @NotNull Exception e) {
if (isClosed()) return;
logger.error("Serialization error: {} for data {}", address, message.getData(), e);
rpcClient.getLastProtocolError().recordException(e, address);
activeRequests.remove(message.getCookie()).accept(null, e);
}
onSenderReady()方法将sender置为已准备好的状态,为downstreamDataAcceptor 进行赋值,并且发送已经在初始化buffer中的信息。
@Override
public void onSenderReady(@NotNull StreamDataAcceptor<RpcMessage> acceptor) {
if (isClosed()) return;
downstreamDataAcceptor = acceptor;
overloaded = false;
if (initialBuffer != null) {
for (RpcMessage message : initialBuffer) {
acceptor.accept(message);
}
initialBuffer = null;
ping();
}
}
onSenderSuspended()用于中止发送,置过载位为true。
@Override
public void onSenderSuspended() {
overloaded = true;
}
doClose()方法:设置本connection的acceptor为null,并且将其从RpcClient的连接池中除去。并让回馈CallBack得到对应的活动,并接收“connection closed” 的通知。
private void doClose() {
if (isClosed()) return;
downstreamDataAcceptor = null;
closed = true;
rpcClient.removeConnection(address);
while (!activeRequests.isEmpty()) {
for (Integer cookie : new HashSet<>(activeRequests.keySet())) {
Callback<?> cb = activeRequests.remove(cookie);
if (cb != null) {
cb.accept(null, new AsyncCloseException("Connection closed"));
}
}
}
}
isClosed()方法:判断是否已经完毕连接。
public boolean isClosed() {
return closed;
}
shutdown()方法:调用RpcSteam中的sendEndOfStream()方法,告知服务器进行关闭。
public void shutdown() {
if (isClosed()) return;
stream.sendEndOfStream();
}
小结
本来本章是想要同时分析RpcClientConnection以及RpcServerConnection类的,但却发现RpcClientConnection类中的部分和涉及的拓展比我想得要多,所以暂时只分析了这个类,也大体知道了是如何向server发送消息并接收消息的。这方面的解释和具体的过程也放在下一篇博客分析完RpcServerConnection后一起完成。