/**
* Copyright 2024 Beijing Baolande Software Corporation
*
* 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.
*
* Runtime Library Exception to the Apache 2.0 License:
*
* As an exception, if you use this Software to compile your source code and
* portions of this Software are embedded into the binary product as a result,
* you may redistribute such product without providing attribution as would
* otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
*/
package hyperion.transport
/**
* 读消息事件处理器
*
* 支持在当前线程编解码和处理消息,或者将编解码和处理消息的任务交给业务线程池去执行
*
* @author yangfuping
*/
public abstract class ReadEventLoopHandler <: Runnable {
protected static let logger = LoggerFactory.getLogger("transport")
private let threadPoolVal: ThreadPool
protected let sessionVal: IoSession
private let allocator: ByteBufferAllocator
private var execOnReadThread = true
/**
* 能处理的最大消息长度,默认64M
*/
private var maxMessageSizeInBytes: Int64 = 64 * 1024 * 1024
public init(threadPool: ThreadPool, allocator: ByteBufferAllocator, session: IoSession) {
this.threadPoolVal = threadPool
this.allocator = allocator
this.sessionVal = session
}
public func setExecOnReadThread(execOnReadThread: Bool) {
this.execOnReadThread = execOnReadThread
}
public func setMaxMessageSizeInBytes(maxMessageSizeInBytes: Int64) {
if (maxMessageSizeInBytes > 0) {
this.maxMessageSizeInBytes = maxMessageSizeInBytes
}
}
public prop threadPool: ThreadPool {
get() {
return threadPoolVal
}
}
public prop session: IoSession {
get() {
return sessionVal
}
}
public func run() {
try {
doEventLoopProcess(session.connection)
} catch (ex: Exception) {
logger.log(LogLevel.ERROR, "Failure to process message on connection ${session.connection}", ex)
}
}
/**
* 读取消息
*/
private func doEventLoopProcess(connection: Connection) {
let status = MessageCompletedStatus()
var readCount = 0
var soTimeoutTimes = 0
var readCompletion: ?ReadCompletion = None
var readBuffer = allocator.allocate()
while (readCount != -1) {
try {
if (readBuffer.position() > 0) {
session.messageCompleted(readBuffer, status)
if (status.completed) {
var messageBuffer = readBuffer
if (let Some(messageSize) <- status.messageSize) {
messageBuffer = sliceMessageBuffer(messageSize, readBuffer)
// 重置MessageCompletedStatus
status.completed = false
status.messageSize = None
}
if (execOnReadThread) {
this.codecAndProcessMessage(session, messageBuffer)
} else {
// 交给业务线程池去执行编解码和消息的处理
let processTask = CodecAndProcessMessageHandler(session, messageBuffer,
this.codecAndProcessMessage)
threadPool.addTask(processTask)
}
continue
}
}
if (!readBuffer.hasRemaining()) {
// 扩展容量
readBuffer = expandBuffer(readBuffer)
}
// 尝试读取一些数据
readCount = connection.read(readBuffer)
soTimeoutTimes = 0
// 取消超时时间
connection.cancelExpireTime()
} catch (ex: SocketTimeoutException) {
if (logger.isDebugEnabled()) {
logger.log(LogLevel.DEBUG, ex.message, ex)
}
if (!(readBuffer.position() > 0)) {
if (connection.isExpired()) {
// 连接空闲超时
logger.log(LogLevel.WARN, "Close idle timeout Connection ${connection}")
connection.close(markInvalid: true)
return
}
connection.setExpireTimeIfAbsent()
// 继续尝试读取
continue
} else {
soTimeoutTimes++
if (soTimeoutTimes > 3) {
logger.log(LogLevel.WARN, "Cloud not read data from ${connection}, close it")
connection.close(markInvalid: true)
return
}
// 读取到部分报文,再次尝试读取剩余报文
continue
}
} catch (ex: Exception) {
if (!connection.isClosed()) {
logger.log(LogLevel.ERROR, "Connection closed: ${connection}", ex)
connection.close(markInvalid: true)
} else {
if (logger.isDebugEnabled()) {
logger.log(LogLevel.DEBUG, "Connection already closed: ${connection}", ex)
}
}
onConnectionReadException(ex)
return
}
}
}
/**
* 分割出读取到的完整的消息报文,并将剩余部分设置到attachment中
*/
private func sliceMessageBuffer(messageSize: Int64, byteBuffer: ByteBuffer): ByteBuffer {
// 将已经读取的报文分割出来
let slice = byteBuffer.slice(index: 0, length: messageSize)
// 跳过已经读取到的报文
byteBuffer.skip(messageSize)
return slice
}
/**
* 扩展ByteBuffer
*
* @throws TransportException
*/
private func expandBuffer(byteBuffer: ByteBuffer): ByteBuffer {
if (byteBuffer.limit() > maxMessageSizeInBytes) {
throw TransportException(
"Too large message size: ${byteBuffer.limit()}, exceeding maxMessageSizeInBytes: ${maxMessageSizeInBytes}"
)
}
if (byteBuffer.expandSupport) {
var expandSize = byteBuffer.limit() << 1
if (expandSize > maxMessageSizeInBytes) {
expandSize = maxMessageSizeInBytes
}
byteBuffer.expandAndLimit(expandSize)
return byteBuffer
} else {
var expandSize = byteBuffer.limit() << 1
if (expandSize > maxMessageSizeInBytes) {
expandSize = maxMessageSizeInBytes
}
let expandBuffer = allocator.allocate(expandSize)
expandBuffer.put(byteBuffer.flip().toArray())
if (logger.isTraceEnabled()) {
logger.trace("Expand ${byteBuffer} to ${expandBuffer}")
}
// 释放扩容之前的ByteBuffer
byteBuffer.release()
return expandBuffer
}
}
public func onConnectionReadException(ex: Exception): Unit
/**
* 编解码和处理编解码后的消息
*/
private func codecAndProcessMessage(session: IoSession, messageBuffer: ByteBuffer): Unit {
/*
* 读完报文之后的回调,用于执行需要和读报文的顺序保持一致的操作。
* 例如: Redis的响应报文和发送的Redis命令的顺序需要保持一致。
*/
var readCompletion: ?ReadCompletion = None
let connection = session.connection
if (let Some(completionHandler) <- connection.getReadCompletionHandler()) {
readCompletion = completionHandler.readCompletion()
}
try {
if (let Some(readCompletion) <- readCompletion) {
let results = session.onMessageRead(messageBuffer, readCompletion)
if (!results.isEmpty()) {
processMessage(results)
}
} else {
let results = session.onMessageRead(messageBuffer)
if (!results.isEmpty()) {
processMessage(results)
}
}
} catch (ex: Exception) {
logger.log(LogLevel.ERROR, "Failure to process inbound message on ${connection}", ex)
} finally {
messageBuffer.release()
}
}
public func processMessage(messages: LinkedList<Any>): Unit
}
/**
* 只处理入栈消息
*/
public class StickyReadInboudHandler <: ReadEventLoopHandler {
public init(threadPool: ThreadPool, allocator: ByteBufferAllocator, session: IoSession) {
super(threadPool, allocator, session)
}
public func onConnectionReadException(ex: Exception): Unit {
session.onConnectionReadException(ex)
}
public func processMessage(messages: LinkedList<Any>): Unit {
// do nothing
}
}
/**
* 处理入栈消息,并将处理结果出栈
*/
public open class StickyReadInboudOutboundHandler <: ReadEventLoopHandler {
public init(threadPool: ThreadPool, allocator: ByteBufferAllocator, session: IoSession) {
super(threadPool, allocator, session)
}
public func onConnectionReadException(ex: Exception): Unit {
// do nothing
}
public func processMessage(messages: LinkedList<Any>): Unit {
for (message in messages) {
session.writeMessage(message)
}
session.flush()
}
}
/**
* 编解码和处理编解码后的消息
*/
public class CodecAndProcessMessageHandler <: Runnable {
protected static let logger = LoggerFactory.getLogger("transport")
private let session: IoSession
private let messageBuffer: ByteBuffer
private let handler: (session: IoSession, messageBuffer: ByteBuffer) -> Unit
public init(session: IoSession, messageBuffer: ByteBuffer,
handler: (session: IoSession, messageBuffer: ByteBuffer) -> Unit) {
this.session = session
this.messageBuffer = messageBuffer
this.handler = handler
}
public func run() {
handler(session, messageBuffer)
}
}