ActiveJ学习RPC(5)——RpcClientConnection

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后一起完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值