优化 SocketServer 结构

This commit is contained in:
tursom 2019-10-18 21:26:04 +08:00
parent 2dd2e8c5fb
commit 3b78a9c998
15 changed files with 690 additions and 754 deletions

View File

@ -21,22 +21,22 @@ data class Demo(
)
fun main() {
// 获取数据库访问协助对象
val helper = SQLiteHelper("demo.db")
// 获取数据库访问协助对象
val helper = SQLiteHelper("demo.db")
// 插入数据
helper.insert(Demo(name = "tursom"))
// 插入数据
helper.insert(Demo(name = "tursom"))
// 更新数据
helper.update(Demo(name = "tursom", money = 100.0), where = ClauseMaker.make {
// 更新数据
helper.update(Demo(name = "tursom", money = 100.0), where = ClauseMaker.make {
!Demo::name equal "tursom"
})
})
// 获取数据
val data = helper.select(Demo::class.java, where = ClauseMaker {
// 获取数据
val data = helper.select(Demo::class.java, where = ClauseMaker {
(!Demo::id greaterThan !0) and (!Demo::id lessThan !10)
})
})
// 删除数据
helper.delete(Demo::class.java.tableName, where = ClauseMaker.make { !Demo::name equal "tursom" })
// 删除数据
helper.delete(Demo::class.java.tableName, where = ClauseMaker.make { !Demo::name equal "tursom" })
}

View File

@ -20,13 +20,10 @@ import kotlin.coroutines.suspendCoroutine
class AsyncNioSocket(override val key: SelectionKey, override val nioThread: INioThread) : IAsyncNioSocket {
override val channel: SocketChannel = key.channel() as SocketChannel
override suspend fun read(buffer: ByteBuffer): Int {
if (buffer.remaining() == 0) return -1
private suspend inline fun <T> operate(crossinline action: (Continuation<T>) -> Unit): T {
return try {
suspendCoroutine {
key.attach(SingleContext(buffer, it))
readMode()
nioThread.wakeup()
action(it)
}
} catch (e: Exception) {
waitMode()
@ -34,154 +31,112 @@ class AsyncNioSocket(override val key: SelectionKey, override val nioThread: INi
}
}
override suspend fun read(buffer: ByteBuffer): Int {
if (buffer.remaining() == 0) return emptyBufferCode
return operate {
key.attach(SingleContext(buffer, it))
readMode()
nioThread.wakeup()
}
}
override suspend fun read(buffer: Array<out ByteBuffer>): Long {
if (buffer.size == 0) return -1
return try {
suspendCoroutine {
if (buffer.isEmpty()) return emptyBufferLongCode
return operate {
key.attach(MultiContext(buffer, it))
readMode()
nioThread.wakeup()
}
} catch (e: Exception) {
waitMode()
throw RuntimeException(e)
}
}
override suspend fun write(buffer: ByteBuffer): Int {
if (buffer.remaining() == 0) return -1
return try {
suspendCoroutine {
if (buffer.remaining() == 0) return emptyBufferCode
return operate {
key.attach(SingleContext(buffer, it))
writeMode()
nioThread.wakeup()
}
} catch (e: Exception) {
waitMode()
throw Exception(e)
}
}
override suspend fun write(buffer: Array<out ByteBuffer>): Long {
if (buffer.isEmpty()) return -1
return try {
suspendCoroutine {
if (buffer.isEmpty()) return emptyBufferLongCode
return operate {
key.attach(MultiContext(buffer, it))
writeMode()
nioThread.wakeup()
}
} catch (e: Exception) {
waitMode()
throw Exception(e)
}
}
override suspend fun read(buffer: ByteBuffer, timeout: Long): Int {
if (timeout <= 0) return read(buffer)
if (buffer.remaining() == 0) return -1
return try {
val result: Int = suspendCoroutine {
if (buffer.remaining() == 0) return emptyBufferCode
return operate {
key.attach(
SingleContext(
buffer,
it,
timer.exec(timeout) {
key.attach(null)
try {
it.resumeWithException(TimeoutException())
} catch (e: Exception) {
}
})
)
readMode()
nioThread.wakeup()
}
result
} catch (e: Exception) {
waitMode()
throw RuntimeException(e)
}
}
override suspend fun read(buffer: Array<out ByteBuffer>, timeout: Long): Long {
if (timeout <= 0) return read(buffer)
if (buffer.isEmpty()) return -1
return try {
val result: Long = suspendCoroutine {
if (buffer.isEmpty()) return emptyBufferLongCode
return operate {
key.attach(
MultiContext(
buffer,
it,
timer.exec(timeout) {
key.attach(null)
try {
it.resumeWithException(TimeoutException())
} catch (e: Exception) {
}
})
)
readMode()
nioThread.wakeup()
}
result
} catch (e: Exception) {
waitMode()
throw Exception(e)
}
}
override suspend fun write(buffer: ByteBuffer, timeout: Long): Int {
if (timeout <= 0) return write(buffer)
if (buffer.remaining() == 0) return -1
return try {
val result: Int = suspendCoroutine {
if (buffer.remaining() == 0) return emptyBufferCode
return operate {
key.attach(
SingleContext(
buffer,
it,
timer.exec(timeout) {
key.attach(null)
try {
it.resumeWithException(TimeoutException())
} catch (e: Exception) {
}
})
)
writeMode()
nioThread.wakeup()
}
result
} catch (e: Exception) {
waitMode()
throw Exception(e)
}
}
override suspend fun write(buffer: Array<out ByteBuffer>, timeout: Long): Long {
if (timeout <= 0) return write(buffer)
if (buffer.isEmpty()) return -1
return try {
val result: Long = suspendCoroutine {
if (buffer.isEmpty()) return emptyBufferLongCode
return operate {
key.attach(
MultiContext(
buffer,
it,
timer.exec(timeout) {
key.attach(null)
try {
it.resumeWithException(TimeoutException())
} catch (e: Exception) {
}
})
)
writeMode()
nioThread.wakeup()
}
result
} catch (e: Exception) {
waitMode()
throw Exception(e)
}
}
override fun close() {
@ -259,5 +214,8 @@ class AsyncNioSocket(override val key: SelectionKey, override val nioThread: INi
//val timer = StaticWheelTimer.timer
val timer = WheelTimer.timer
const val emptyBufferCode = 0
const val emptyBufferLongCode = 0L
}
}

View File

@ -48,7 +48,7 @@ interface IAsyncNioSocket : AsyncSocket {
* 如果通道已断开则会抛出异常
*/
suspend fun recv(buffer: ByteBuffer): Int {
if (buffer.remaining() == 0) return 0
if (buffer.remaining() == 0) return emptyBufferCode
val readSize = read(buffer)
if (readSize < 0) {
throw SocketException("channel closed")
@ -57,7 +57,7 @@ interface IAsyncNioSocket : AsyncSocket {
}
suspend fun recv(buffer: ByteBuffer, timeout: Long): Int {
if (buffer.remaining() == 0) return 0
if (buffer.remaining() == 0) return emptyBufferCode
val readSize = read(buffer, timeout)
if (readSize < 0) {
throw SocketException("channel closed")
@ -66,7 +66,7 @@ interface IAsyncNioSocket : AsyncSocket {
}
suspend fun recv(buffers: Array<out ByteBuffer>, timeout: Long): Long {
if (buffers.isEmpty()) return 0
if (buffers.isEmpty()) return emptyBufferLongCode
val readSize = read(buffers, timeout)
if (readSize < 0) {
throw SocketException("channel closed")
@ -87,4 +87,9 @@ interface IAsyncNioSocket : AsyncSocket {
value
}
}
companion object {
const val emptyBufferCode = 0
const val emptyBufferLongCode = 0L
}
}

View File

@ -9,11 +9,11 @@ import java.nio.channels.SelectionKey
/**
* 有多个工作线程的协程套接字服务器
* 不过因为结构复杂所以性能实际上比多线程的 ProtocolAsyncNioServer
* 不过因为结构复杂所以性能一般比单个工作线程的 AsyncNioServer
*/
@Suppress("MemberVisibilityCanBePrivate")
class AsyncGroupNioServer(
val port: Int,
override val port: Int,
val threads: Int = Runtime.getRuntime().availableProcessors(),
backlog: Int = 50,
val handler: suspend AsyncNioSocket.() -> Unit

View File

@ -9,11 +9,11 @@ import java.nio.channels.SelectionKey
/**
* 只有一个工作线程的协程套接字服务器
* 不过因为结构更加简单所以性能实际上比多线程的 ProtocolGroupAsyncNioServer
* 不过因为结构更加简单所以性能一般比多个工作线程的 ProtocolGroupAsyncNioServer
* 而且协程是天生多线程并不需要太多的接受线程来处理所以一般只需要用本服务器即可
*/
class AsyncNioServer(
val port: Int,
override val port: Int,
backlog: Int = 50,
val handler: suspend AsyncNioSocket.() -> Unit
) : ISocketServer by NioServer(port, object : INioProtocol by AsyncNioSocket.nioSocketProtocol {
@ -40,9 +40,7 @@ class AsyncNioServer(
port: Int,
backlog: Int = 50,
handler: Handler
) : this(port, backlog, {
handler.handle(this)
})
) : this(port, backlog, { handler.handle(this) })
interface Handler {
fun handle(socket: AsyncNioSocket)

View File

@ -0,0 +1,51 @@
package cn.tursom.socket.server
import cn.tursom.socket.AsyncAioSocket
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.Closeable
import java.net.InetSocketAddress
import java.nio.channels.AsynchronousCloseException
import java.nio.channels.AsynchronousServerSocketChannel
import java.nio.channels.AsynchronousSocketChannel
import java.nio.channels.CompletionHandler
class AsyncSocketServer(
override val port: Int,
host: String = "0.0.0.0",
private val handler: suspend AsyncAioSocket.() -> Unit
) : ISocketServer {
private val server = AsynchronousServerSocketChannel
.open()
.bind(InetSocketAddress(host, port))
override fun run() {
server.accept(0, object : CompletionHandler<AsynchronousSocketChannel, Int> {
override fun completed(result: AsynchronousSocketChannel?, attachment: Int) {
try {
server.accept(attachment + 1, this)
} catch (e: Throwable) {
e.printStackTrace()
}
result ?: return
GlobalScope.launch {
AsyncAioSocket(result).handler()
}
}
override fun failed(exc: Throwable?, attachment: Int?) {
when (exc) {
is AsynchronousCloseException -> {
}
else -> exc?.printStackTrace()
}
}
})
}
override fun close() {
server.close()
}
}

View File

@ -1,7 +0,0 @@
package cn.tursom.socket.server.async
import java.io.Closeable
interface AsyncServer : Runnable, Closeable {
val port: Int
}

View File

@ -1,51 +0,0 @@
package cn.tursom.socket.server.async
import cn.tursom.socket.AsyncAioSocket
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.Closeable
import java.net.InetSocketAddress
import java.nio.channels.AsynchronousCloseException
import java.nio.channels.AsynchronousServerSocketChannel
import java.nio.channels.AsynchronousSocketChannel
import java.nio.channels.CompletionHandler
class AsyncSocketServer(
port: Int,
host: String = "0.0.0.0",
private val handler: suspend AsyncAioSocket.() -> Unit
) : Runnable, Closeable {
private val server = AsynchronousServerSocketChannel
.open()
.bind(InetSocketAddress(host, port))
override fun run() {
server.accept(0, object : CompletionHandler<AsynchronousSocketChannel, Int> {
override fun completed(result: AsynchronousSocketChannel?, attachment: Int) {
try {
server.accept(attachment + 1, this)
} catch (e: Throwable) {
e.printStackTrace()
}
result ?: return
GlobalScope.launch {
AsyncAioSocket(result).handler()
}
}
override fun failed(exc: Throwable?, attachment: Int?) {
when (exc) {
is AsynchronousCloseException -> {
}
else -> exc?.printStackTrace()
}
}
})
}
override fun close() {
server.close()
}
}

View File

@ -16,7 +16,7 @@ import java.util.concurrent.LinkedBlockingDeque
*/
@Suppress("MemberVisibilityCanBePrivate")
class GroupNioServer(
val port: Int,
override val port: Int,
val threads: Int = Runtime.getRuntime().availableProcessors(),
private val protocol: INioProtocol,
backlog: Int = 50,

View File

@ -2,4 +2,6 @@ package cn.tursom.socket.server
import java.io.Closeable
interface ISocketServer : Runnable, Closeable
interface ISocketServer : Runnable, Closeable {
val port: Int
}

View File

@ -1,5 +1,6 @@
package cn.tursom.socket.server
import cn.tursom.core.cpuNumber
import cn.tursom.socket.BaseSocket
import java.net.ServerSocket
@ -9,8 +10,9 @@ class MultithreadingSocketServer(
val exception: Exception.() -> Unit = {
printStackTrace()
},
handler: BaseSocket.() -> Unit
) : SocketServer(handler) {
override val handler: BaseSocket.() -> Unit
) : SocketServer {
override val port = serverSocket.localPort
constructor(
port: Int,

View File

@ -12,7 +12,7 @@ import java.util.concurrent.ConcurrentLinkedDeque
* 工作在单线程上的 Nio 服务器
*/
class NioServer(
val port: Int,
override val port: Int,
private val protocol: INioProtocol,
backLog: Int = 50,
val nioThreadGenerator: (threadName: String, workLoop: (thread: INioThread) -> Unit) -> INioThread

View File

@ -7,8 +7,9 @@ import java.net.SocketException
class SingleThreadSocketServer(
private val serverSocket: ServerSocket,
val exception: Exception.() -> Unit = { printStackTrace() },
handler: BaseSocket.() -> Unit
) : SocketServer(handler) {
override val handler: BaseSocket.() -> Unit
) : SocketServer {
override val port = serverSocket.localPort
constructor(
port: Int,

View File

@ -2,7 +2,9 @@ package cn.tursom.socket.server
import cn.tursom.socket.BaseSocket
abstract class SocketServer(val handler: BaseSocket.() -> Unit) : ISocketServer {
interface SocketServer : ISocketServer {
val handler: BaseSocket.() -> Unit
companion object {
val cpuNumber = Runtime.getRuntime().availableProcessors() //CPU处理器的个数
}

View File

@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit
* }
*
*/
open class ThreadPoolSocketServer
class ThreadPoolSocketServer
/**
* 使用代码而不是配置文件的构造函数
*
@ -39,15 +39,15 @@ open class ThreadPoolSocketServer
* @param queueSize 线程池任务队列大小
* @param keepAliveTime 线程最长存活时间
* @param timeUnit timeout的单位默认毫秒
* @param startImmediately 是否立即启动
* @param handler 对套接字处理的业务逻辑
*/(
port: Int,
override val port: Int,
threads: Int = 1,
queueSize: Int = 1,
keepAliveTime: Long = 60_000L,
timeUnit: TimeUnit = TimeUnit.MILLISECONDS,
handler: BaseSocket.() -> Unit
) : SocketServer(handler) {
override val handler: BaseSocket.() -> Unit
) : SocketServer {
constructor(
port: Int,
@ -59,13 +59,6 @@ open class ThreadPoolSocketServer
ThreadPoolExecutor(threads, threads, keepAliveTime, timeUnit, LinkedBlockingQueue(queueSize))
private var serverSocket: ServerSocket = ServerSocket(port)
/**
* 为了在构造函数中自动启动服务我们需要封闭start()防止用户重载start()
*/
private fun start() {
Thread(this).start()
}
/**
* 主要作用
* 循环接受连接请求
@ -73,7 +66,7 @@ open class ThreadPoolSocketServer
* 连接初期异常处理
* 自动关闭套接字服务器与线程池
*/
final override fun run() {
override fun run() {
while (!serverSocket.isClosed) {
try {
socket = serverSocket.accept()
@ -99,7 +92,6 @@ open class ThreadPoolSocketServer
break
}
}
whenClose()
close()
System.err.println("server closed")
}
@ -136,28 +128,11 @@ open class ThreadPoolSocketServer
closeServer()
}
/**
* 关闭服务器时执行
*/
open fun whenClose() {
}
companion object {
val TAG = getTAG(this::class.java)
/**
* 线程池满时返回给客户端的信息
*/
open val poolIsFull
get() = Companion.poolIsFull
private data class ServerConfigData(
val port: Int = 0,
val threads: Int = 1,
val queueSize: Int = 1,
val timeout: Long = 0L,
val startImmediately: Boolean = false
)
companion object {
val TAG = getTAG(this::class.java)
val poolIsFull = "server pool is full".toByteArray()
}
}