2021SC@SDUSC
前言
上一次的博客我们根据activej官方提供的案例RpcExample,初步了解了rpc的大概运行机制,在有了对于整个模型的大致框架之后,下面正式开始对于源代码的解析。
未来的关于源代码的分析,将主要根据以下思路为重点进行:
1.RPCclient
2.RPCserver
3.RPCclient与server沟通相关类:如connection、sender、requeshandler等类
下面的包结构中红框部分为之后的重点研究所在。
RpcClient
接下来我们正式开始本次的源代码分析,本次以RPCclient类作为起始。
首先,RpcClient在整一个rpc框架中起到的作用是什么?
Sends requests to the specified servers according to defined {@code RpcStrategy} strategy. Strategies, represented in {@link RpcStrategies} satisfy most cases.
通过类RpcStrategy定义的策略,向指定的服务器RpcServer发送请求。
其与RpcServer一般的连接过程为:
1.为客户端创建请求-响应类
2.为服务端创建请求处理类
3.创建一个客户端并根据策略等进行调整
明确clinent要做的事之后,我们来看一下client的具体代码。跳过定义变量环节,直接查看具体的方法。
创建一个使用提供的socket(套接字)的client
public RpcClient withSocketSettings(SocketSettings socketSettings) {
this.socketSettings = socketSettings;
return this;
}
创建具有指定消息类型处理能力的client
public RpcClient withMessageTypes(Class<?>... messageTypes) {
return withMessageTypes(Arrays.asList(messageTypes));
}
public RpcClient withMessageTypes(List<Class<?>> messageTypes) {
Checks.checkArgument(new HashSet<>(messageTypes).size() == messageTypes.size(), "Message types must be unique");
this.messageTypes = messageTypes;
return this;
}
使用序列化器(Serializer)构建器创建客户端。序列化生成器用于在运行时创建快速序列化器。
public RpcClient withSerializerBuilder(SerializerBuilder serializerBuilder) {
this.serializerBuilder = serializerBuilder;
return this;
}
用一些策略创建一个客户端。考虑一些来自RpcStrategies类的现成策略。
public RpcClient withStrategy(RpcStrategy requestSendingStrategy) {
this.strategy = requestSendingStrategy;
return this;
}
设置client在连接前等待指定的时间。指定最大连接时间,返回一个具有最大连接时间的client。
public RpcClient withConnectTimeout(Duration connectTimeout) {
this.connectTimeoutMillis = connectTimeout.toMillis();
return this;
}
public RpcClient withReconnectInterval(Duration reconnectInterval) {
this.reconnectIntervalMillis = reconnectInterval.toMillis();
return this;
}
在无连接的情况下启动客户端,返回一个无论连接是否可用的client
public RpcClient withForcedStart() {
this.forcedStart = true;
return this;
}
以上是客户端的前期准备工作的方法
下面是客户端在具体实现时调用的方法
首先是start和stop方法,他实现了RpcClient实现的接口EventloopService,其主要功能就是为了实现异步处理。
public @NotNull Promise<Void> start() {
if (CHECK) Checks.checkState(eventloop.inEventloopThread(), "Not in eventloop thread");
Checks.checkNotNull(messageTypes, "Message types must be specified");
Checks.checkState(stopPromise == null);
serializer = serializerBuilder.withSubclasses(RpcMessage.MESSAGE_TYPES, messageTypes).build(RpcMessage.class);
return Promise.<Map<Object, InetSocketAddress>>ofCallback(cb -> strategy.getDiscoveryService().discover(null, cb))
.map(result -> {
this.previouslyDiscovered = result;
Collection<InetSocketAddress> addresses = result.values();
this.addresses = new ArrayList<>(addresses);
for (InetSocketAddress address : addresses) {
if (!connectsStatsPerAddress.containsKey(address)) {
connectsStatsPerAddress.put(address, new RpcConnectStats(eventloop));
}
}
return addresses;
})
.then(addresses -> Promises.all(
addresses.stream()
.map(address -> {
logger.info("Connecting: {}", address);
return connect(address)
.map(($, e) -> null);
}))
.then(() -> !forcedStart && requestSender instanceof NoSenderAvailable ?
Promise.ofException(START_EXCEPTION) :
Promise.complete()))
.whenResult(this::rediscover);
}
public @NotNull Promise<Void> stop() {
if (CHECK) Checks.checkState(eventloop.inEventloopThread(), "Not in eventloop thread");
if (stopPromise != null) return stopPromise;
stopPromise = new SettablePromise<>();
if (connections.size() == 0) {
stopPromise.set(null);
return stopPromise;
}
for (RpcClientConnection connection : connections.values()) {
connection.shutdown();
}
return stopPromise;
}
下面的三个方法分别为client连接,移除连接和关闭连接。
private Promise<Void> connect(InetSocketAddress address) {
return AsyncTcpSocketNio.connect(address, connectTimeoutMillis, socketSettings)
.whenResult(asyncTcpSocketImpl -> {
if (stopPromise != null || !addresses.contains(address)) {
asyncTcpSocketImpl.close();
return;
}
statsSocket.onConnect(asyncTcpSocketImpl);
asyncTcpSocketImpl.setInspector(statsSocket);
AsyncTcpSocket socket = sslContext == null ?
asyncTcpSocketImpl :
wrapClientSocket(asyncTcpSocketImpl, sslContext, sslExecutor);
RpcStream stream = new RpcStream(socket, serializer, defaultPacketSize,
autoFlushInterval, frameFormat, false); // , statsSerializer, statsDeserializer, statsCompressor, statsDecompressor);
RpcClientConnection connection = new RpcClientConnection(eventloop, this, address, stream, keepAliveInterval.toMillis());
stream.setListener(connection);
// jmx
if (isMonitoring()) {
connection.startMonitoring();
}
connections.put(address, connection);
requestSender = nonNullElseGet(strategy.createSender(pool), NoSenderAvailable::new);
// jmx
connectsStatsPerAddress.get(address).recordSuccessfulConnect();
logger.info("Connection to {} established", address);
})
.whenException(e -> {
logger.warn("Connection {} failed: {}", address, e);
if (stopPromise == null) {
processClosedConnection(address);
}
})
.toVoid();
}
void removeConnection(InetSocketAddress address) {
if (connections.remove(address) == null) return;
requestSender = nonNullElseGet(strategy.createSender(pool), NoSenderAvailable::new);
logger.info("Connection closed: {}", address);
processClosedConnection(address);
}
private void processClosedConnection(InetSocketAddress address) {
if (stopPromise == null) {
if (!addresses.contains(address)) return;
//jmx
connectsStatsPerAddress.get(address).recordFailedConnect();
eventloop.delayBackground(reconnectIntervalMillis, () -> {
if (stopPromise == null) {
logger.info("Reconnecting: {}", address);
connect(address);
}
});
} else {
if (connections.size() == 0) {
stopPromise.set(null);
}
}
}
完成连接之后,下一步就是发送请求到服务器,等待结果超时并处理结果与回调。此处是实现了接口IRpcCilent中的方法sendRequest()。
变量说明:
I:请求类
O:响应类
request:向server发送的请求
timeout:超时时间,以毫秒为单位。
cb:在收到响应或遇到错误后完成的回调
public <I, O> void sendRequest(I request, int timeout, Callback<O> cb) {
if (CHECK) Checks.checkState(eventloop.inEventloopThread(), "Not in eventloop thread");
if (timeout > 0) {
requestSender.sendRequest(request, timeout, cb);
} else {
cb.accept(null, new AsyncTimeoutException("RPC request has timed out"));
}
}
public <I, O> void sendRequest(I request, Callback<O> cb) {
if (CHECK) Checks.checkState(eventloop.inEventloopThread(), "Not in eventloop thread");
requestSender.sendRequest(request, cb);
}
其中注意,如果超时后服务器没有响应,请求将以AsyncTimeoutException异常结束。且设置超时时间会将任务调度到Eventloop事件循环中,所以很多有较大timeouts的请求可能会降低性能。
更新socket地址
private void updateAddresses(Map<Object, InetSocketAddress> newAddresses) {
this.previouslyDiscovered = newAddresses;
List<InetSocketAddress> previousAddresses = this.addresses;
this.addresses = new ArrayList<>(newAddresses.values());
boolean changed = false;
for (InetSocketAddress address : previousAddresses) {
if (!this.addresses.contains(address)) {
connections.remove(address).shutdown();
connectsStatsPerAddress.remove(address);
changed = true;
}
}
if (changed) {
requestSender = nonNullElseGet(strategy.createSender(pool), NoSenderAvailable::new);
}
for (InetSocketAddress address : this.addresses) {
if (!previousAddresses.contains(address)) {
connectsStatsPerAddress.put(address, new RpcConnectStats(eventloop));
connect(address);
}
}
}
再下面的是一些关于ActiveJ的jmx管理生命周期的部分,在这里不再过多详解。
总结
本次的RpcClient的具体内容部分就到这里,从每一个方法中我们其实都能看到,ActiveJ在做的是使用自己的方式,去重写java中的一些特定的接口或功能,以此来达到加快运行速度等期望的效果,例如其中的promise就可以和java的future作类比去进行理解,其与future相同,表示的是一个可能还没有完成的异步结果。这一种类比能让我们更快地理解好ActiveJ的具体实现。而在下一次的代码分析中,我们将解析RpcServer部分以及相关类。