我正在重新实现一个web应用程序,部分是为了学习nim以及多线程编程。作为学习练习的一部分,我想实现连接池,因为在nim中没有我知道的库或包为我实现它,同时允许我使用我选择的ORM。我连接到的数据库是sqlite。
因此,我编写了我认为是正确的代码:我创建了一个ConnectionPool类型的全局对象D1,它仅仅是与sqlite数据库的一系列连接,并且还具有一个锁。每个连接都有一个明确定义的生命周期,在此之后,它将被销毁,如果需要连接,则创建新的连接,但没有可用的连接。您可以使用borrowConnection获得连接,并通过recycleConnection返回连接。这两种方法都锁定POOL对象以检索连接或将所述连接放回。
import ../applicationSettings
import constructor/defaults
import std/[times, locks, db_sqlite]
proc createRawDatabaseConnection(): DbConn =
return open(applicationSettings.database, "", "", "")
type PoolConnection* {.defaults.} = object
connection*: DbConn = createRawDatabaseConnection()
deathTime: DateTime = now() + initTimeInterval(days = 1)
implDefaults(PoolConnection)
type ConnectionPool* = object
connections: seq[PoolConnection]
lock: Lock
var POOL {.global.}: ConnectionPool
proc isEmptyPool(): bool = POOL.connections.len() == 0
proc initConnectionPool*(initialPoolSize: static int) =
POOL.connections = @[]
initLock(POOL.lock)
withLock POOL.lock:
for i in 1..initialPoolSize:
POOL.connections.add(initPoolConnection())
proc borrowConnection*(): PoolConnection {.gcsafe.} =
{.cast(gcsafe).}:
withLock POOL.lock:
if isEmptyPool():
return initPoolConnection()
result = POOL.connections.pop()
proc recycleConnection*(connection: sink PoolConnection) {.gcsafe.} =
if connection.deathTime < now():
return
{.cast(gcsafe).}:
withLock POOL.lock:
POOL.connections.add(connection)
proc destroyConnectionPool*() =
deinitLock(POOL.lock)上面的代码有什么明显的问题吗?我认为这不是疯狂复制记忆,但我可能错了,所以请告诉我,如果我是
发布于 2022-01-21 22:04:16
Leorize从nim不和谐服务器上提出了一个有效的建议。上述问题有几个:
您可以采用的一个设计是给池一个固定的连接限制,它可以包含这些连接。然后也给它一个"burstMode“,它允许它超越这个限制,但前提是它的"burstModeTimer”允许它。这个burstModeTimer被修正为30分钟(设置为任何你想要的),并且每次你借用一个连接的时候,当你借用一个连接时,你的池没有溢出。这是这样的,因为“突发模式”广告了整个连接,这意味着,只有当池在正常负载下溢出时,它们才是不必要的。
当池处于突发模式时,它将接受从循环proc获得的任何连接。一旦突发模式结束,在它满的时候返回的任何连接都将被关闭和垃圾收集。
import ../applicationSettings
import std/[times, monotimes, locks, db_sqlite]
proc createRawDatabaseConnection(): DbConn =
return open(applicationSettings.database, "", "", "")
type ConnectionPool = object
connections: seq[DbConn]
lock: Lock
defaultPoolSize: int
burstEndTime: MonoTime
isInBurstMode: bool
var POOL {.global.}: ConnectionPool
proc isPoolEmpty(): bool = POOL.connections.len() == 0
proc isPoolFull(): bool = POOL.connections.len() >= CONNECTION_POOL_SIZE
proc refillPoolConnections() =
withLock POOL.lock:
for i in 1..POOL.defaultPoolSize:
POOL.connections.add(createRawDatabaseConnection())
proc initConnectionPool*() =
POOL.connections = @[]
POOL.isInBurstMode = false
POOL.burstEndTime = getMonoTime()
POOL.defaultPoolSize = CONNECTION_POOL_SIZE
initLock(POOL.lock)
refillPoolConnections()
proc activateBurstMode() =
POOL.isInBurstMode = true
POOL.burstEndTime = getMonoTime() + initDuration(minutes = 30)
refillPoolConnections()
proc updatePoolBurstModeState() =
if not POOL.isInBurstMode:
return
if getMonoTime() > POOL.burstEndTime:
POOL.isInBurstMode = false
proc extendBurstModeLifetime() =
if POOL.isInBurstMode == false:
raise newException(DbError, "Tried to extend pool lifetime while Pool wasn't in burst mode, there's a logic issue")
let hasMaxLifetimeDuration: bool = POOL.burstEndTime - getMonoTime() > initDuration(minutes = 30)
if hasMaxLifetimeDuration:
return
POOL.burstEndTime = POOL.burstEndTime + initDuration(seconds = 5)
proc borrowConnection(): DbConn {.gcsafe.} =
{.cast(gcsafe).}:
withLock POOL.lock:
if isPoolEmpty():
activateBurstMode()
elif not isPoolFull() and POOL.isInBurstMode:
extendBurstModeLifetime()
result = POOL.connections.pop()
echo "After Borrow: POOL size: " & $POOL.connections.len()
proc recycleConnection(connection: DbConn) {.gcsafe.} =
{.cast(gcsafe).}:
withLock POOL.lock:
updatePoolBurstModeState()
if isPoolFull() and not POOL.isInBurstMode:
connection.close()
else:
POOL.connections.add(connection)
echo "After Recycle: POOL size: " & $POOL.connections.len()
proc destroyConnectionPool*() =
deinitLock(POOL.lock)
template withDbConn*(connection: untyped, body: untyped) =
#Borrows a database connection, executes the body and then recycles the connection
block: #ensures connection exists only within the scope of this block
let connection: DbConn = borrowConnection()
try:
body
finally:
recycleConnection(connection)小用法示例:
withDbConn(connection): #connection is of type DbConn, which is a connection to an sqlite3 db
connection.select(entries, "campaign_id.name = ?", campaignName)模板非常方便,理想情况下是模块中唯一的公共部分,因为它会自动为您获取和回收您的连接。
https://codereview.stackexchange.com/questions/273234
复制相似问题