上一节介绍了Inbox收件箱,这一节来进一步阅读Outbox发件箱的源码。
Outbox源码
//sealed限制继承层级
//消息发件箱
private[netty] sealed trait OutboxMessage {
def sendWith(client: TransportClient): Unit
def onFailure(e: Throwable): Unit
}
//单向消息发件箱
private[netty] case class OneWayOutboxMessage(content: ByteBuffer) extends OutboxMessage
with Logging {
override def sendWith(client: TransportClient): Unit = {
//向服务器端的RpcHandler发送不透明消息。该消息不需要任何回复,也不提供任何交付保证。
client.send(content)
}
//发送失败打印异常信息
override def onFailure(e: Throwable): Unit = {
e match {
case e1: RpcEnvStoppedException => logDebug(e1.getMessage)
case e1: Throwable => logWarning(s"Failed to send one-way RPC.", e1)
}
}
}
//消息发件箱 有回复的
private[netty] case class RpcOutboxMessage(
content: ByteBuffer,
_onFailure: (Throwable) => Unit,
_onSuccess: (TransportClient, ByteBuffer) => Unit)
extends OutboxMessage with RpcResponseCallback with Logging {
//_表示默认值
//传输客户端
private var client: TransportClient = _
//请求ID
private var requestId: Long = _
override def sendWith(client: TransportClient): Unit = {
this.client = client
//向服务器端的RpcHandler发送不透明消息。回调将与服务器的响应一起调用,或者在出现任何故障时调用。
this.requestId = client.sendRpc(content, this)
}
private[netty] def removeRpcRequest(): Unit = {
if (client != null) {
//删除与给定RPC关联的任何状态。
client.removeRpcRequest(requestId)
} else {
logError("Ask terminated before connecting successfully")
}
}
//超时
def onTimeout(): Unit = {
removeRpcRequest()
}
//终止
def onAbort(): Unit = {
removeRpcRequest()
}
//失败
override def onFailure(e: Throwable): Unit = {
_onFailure(e)
}
//成功
override def onSuccess(response: ByteBuffer): Unit = {
_onSuccess(client, response)
}
}
//发件箱
private[netty] class Outbox(nettyEnv: NettyRpcEnv, val address: RpcAddress) {
outbox => //给它一个别名,这样我们就可以在闭包中更清楚地使用它。
//修饰变量 使用之前需要先获取锁
//消息列表 每个结点都是消息发件箱
@GuardedBy("this")
private val messages = new java.util.LinkedList[OutboxMessage]
//发送客户端
@GuardedBy("this")
private var client: TransportClient = null
//connectFuture指向连接任务。如果没有连接任务,那么connectFuture将为null。
@GuardedBy("this")
private var connectFuture: java.util.concurrent.Future[Unit] = null
//状态 默认没停止
@GuardedBy("this")
private var stopped = false
//如果有任何线程正在耗尽消息队列
@GuardedBy("this")
private var draining = false
//发送消息。如果没有活动连接,请缓存它并启动新连接。如果[[Outbox]]已停止,则会以[[SparkException]]通知发件人。
def send(message: OutboxMessage): Unit = {
val dropped = synchronized {
//如果发件箱停止
if (stopped) {
true
} else {
//将消息放入链表
messages.add(message)
false
}
}
if (dropped) {
//消息发送失败 因为发件箱停止运行了
message.onFailure(new SparkException("Message is dropped because Outbox is stopped"))
} else {
//清空消息队列
drainOutbox()
}
}
//清空消息队列。如果还有其他排水线程,请退出。
//如果尚未建立连接,请在“nettyEnv.clientConnectionExecutor”中启动任务以设置连接。
private def drainOutbox(): Unit = {
var message: OutboxMessage = null
synchronized {
//如果发件箱停止了 直接返回
if (stopped) {
return
}
if (connectFuture != null) {
//我们正在连接到远程地址,所以退出即可
return
}
if (client == null) {
//没有连接任务,但客户端为null,因此我们需要启动连接任务。
//启动连接任务
launchConnectTask()
return
}
//如果发件箱消息正在被发送 则退出
if (draining) {
return
}
//取第一个结点的值
message = messages.poll()
//如果消息为空则返回
if (message == null) {
return
}
//表示消息正在被处理
draining = true
}
while (true) {
try {
//发送客户端
val _client = synchronized { client }
if (_client != null) {
//如果客户端不为空则通过该客户端发送消息
message.sendWith(_client)
} else {
//如果发件箱运行中则抛出异常
assert(stopped)
}
} catch {//抛出异常
case NonFatal(e) =>
handleNetworkFailure(e)
return
}
synchronized {
//如果发件箱停止则返回
if (stopped) {
return
}
//取出消息
message = messages.poll()
//如果消息为空则返回
if (message == null) {
draining = false
return
}
}
}
}
//启动连接任务
private def launchConnectTask(): Unit = {
connectFuture = nettyEnv.clientConnectionExecutor.submit(new Callable[Unit] {
override def call(): Unit = {
try {
//生成客户端
val _client = nettyEnv.createClient(address)
outbox.synchronized {
client = _client
//如果发件箱停止则关闭客户端
if (stopped) {
closeClient()
}
}
} catch {//抛出异常
case ie: InterruptedException =>
return
case NonFatal(e) =>
outbox.synchronized { connectFuture = null }
handleNetworkFailure(e)
return
}
outbox.synchronized { connectFuture = null }
//现在可能没有线程在处理消息队列。如果我们不在这里耗尽,我们就无法发送消息,直到下一条消息到达。
drainOutbox()
}
})
}
//停止[[Inbox]]并将原因通知等待的邮件。
private def handleNetworkFailure(e: Throwable): Unit = {
synchronized {
//断言 如果连接不为空则抛出异常
assert(connectFuture == null)
//如果发件箱停止则返回
if (stopped) {
return
}
//将发件箱状态设置成停止
stopped = true
//关闭客户端
closeClient()
}
//从nettyEnv中删除此发件箱,以便其他邮件将创建一个新的发件箱和一个新连接 一个地址对应一个发件箱
nettyEnv.removeOutbox(address)
//通知剩余消息的连接失败
//我们总是在更新消息之前检查“stopped”,所以在这里我们可以确保没有线程会更新消息,并且只排干队列是安全的。
var message = messages.poll()
while (message != null) {
//取出每一个消息 并通知消息发送方消息发送失败
message.onFailure(e)
message = messages.poll()
}
//如果消息不为空则抛出异常
assert(messages.isEmpty)
}
//关闭客户端
private def closeClient(): Unit = synchronized {
// Just set client to null. Don't close it in order to reuse the connection.
//只需将客户端设置为null。不要为了重新使用连接而关闭它。
client = null
}
//停止[[发件箱]]。[[Outbox]]中的其余消息将通过[[SparkException]]进行通知。
def stop(): Unit = {
synchronized {
//如果发件箱停止则返回
if (stopped) {
return
}
//将发件箱状态设置成停止
stopped = true
//如果连接不为空则抛出异常
if (connectFuture != null) {
//停止连接
connectFuture.cancel(true)
}
//关闭客户端
closeClient()
}
//我们总是在更新消息之前检查“stopped”,所以在这里我们可以确保没有线程会更新消息,并且只排干队列是安全的。
var message = messages.poll()
while (message != null) {
//取出每一个消息 并通知消息发送方消息发送失败
message.onFailure(new SparkException("Message is dropped because Outbox is stopped"))
message = messages.poll()
}
}
}
Outbox中首先定义了消息特质OutboxMessage,自带发送消息方法sendWith和发送失败抛出异常onFailure方法,然后定义单向发件箱消息OneWayOutboxMessage继承OutboxMessage,定义有回复的rpc发件箱消息RpcOutboxMessage,该消息中还指定具体发送消息的客户端client,类型是传输客户端TransportClient,还包括消息发送状态超时、终止、成功等状态方法。
//超时
def onTimeout(): Unit = {
removeRpcRequest()
}
//终止
def onAbort(): Unit = {
removeRpcRequest()
}
//失败
override def onFailure(e: Throwable): Unit = {
_onFailure(e)
}
//成功
override def onSuccess(response: ByteBuffer): Unit = {
_onSuccess(client, response)
}
Outbox中通过链表messages来存储消息,每个节点都是一个OutboxMessage,messages通过@GuardedBy("this")来修饰,每次使用之前都需要先获取messages的锁,确保线程安全:
//修饰变量 使用之前需要先获取锁
//消息列表 每个结点都是消息发件箱
@GuardedBy("this")
private val messages = new java.util.LinkedList[OutboxMessage]
同时指定消息发送客户端:
//发送客户端
@GuardedBy("this")
private var client: TransportClient = null
send负责发送消息,先检查发件箱是否停止,没有就将消息放进messages链表中,然后开始执行drainOutbox消费messages中的消息:
//发送消息。如果没有活动连接,请缓存它并启动新连接。如果[[Outbox]]已停止,则会以[[SparkException]]通知发件人。
def send(message: OutboxMessage): Unit = {
val dropped = synchronized {
//如果发件箱停止
if (stopped) {
true
} else {
//将消息放入链表
messages.add(message)
false
}
}
if (dropped) {
//消息发送失败 因为发件箱停止运行了
message.onFailure(new SparkException("Message is dropped because Outbox is stopped"))
} else {
//清空消息队列
drainOutbox()
}
}
drainOutbox先判断发件箱状态和客户端是否为空,如果发件箱停止运行则直接返回,如果客户端为空则启动连接任务,然后正式开始处理消息,先获取客户端,然后messages链表的第一个节点的值,然后调用具体消息的sendWith方法通过该客户端发送该消息,发送过程如遇异常则抛出,然后接着获取下一个消息,如果获取的消息是空则停止,否则循环处理链表中的消息,指导处理完毕。
//清空消息队列。如果还有其他排水线程,请退出。
//如果尚未建立连接,请在“nettyEnv.clientConnectionExecutor”中启动任务以设置连接。
private def drainOutbox(): Unit = {
var message: OutboxMessage = null
synchronized {
//如果发件箱停止了 直接返回
if (stopped) {
return
}
if (connectFuture != null) {
// We are connecting to the remote address, so just exit
//我们正在连接到远程地址,所以退出即可
return
}
if (client == null) {
// There is no connect task but client is null, so we need to launch the connect task.
//没有连接任务,但客户端为null,因此我们需要启动连接任务。
//启动连接任务
launchConnectTask()
return
}
//如果发件箱消息正在被发送 则退出
if (draining) {
// There is some thread draining, so just exit
return
}
//取第一个结点的值
message = messages.poll()
//如果消息为空则返回
if (message == null) {
return
}
//表示消息正在被处理
draining = true
}
while (true) {
try {
//发送客户端
val _client = synchronized { client }
if (_client != null) {
//如果客户端不为空则通过该客户端发送消息
message.sendWith(_client)
} else {
//如果发件箱运行中则抛出异常
assert(stopped)
}
} catch {//抛出异常
case NonFatal(e) =>
handleNetworkFailure(e)
return
}
synchronized {
//如果发件箱停止则返回
if (stopped) {
return
}
//取出消息
message = messages.poll()
//如果消息为空则返回
if (message == null) {
draining = false
return
}
}
}
}
总结:Outbox中定义了不同类型的消息,消息中自带发送方法,只需要传入客户端即可,然后定义了发件箱,发件箱中通过链表来管理需要发送的消息,循环扫描处理需要发送的消息。