Python 內(nèi)存數(shù)據(jù)庫 CyberDB 使用實(shí)例
CyberDB,一個(gè)基于 Python 字典和列表的內(nèi)存數(shù)據(jù)庫。
概括
CyberDB 是一個(gè)輕量級的 Python 內(nèi)存數(shù)據(jù)庫。它旨在利用 Python 內(nèi)置數(shù)據(jù)結(jié)構(gòu)字典、列表作數(shù)據(jù)存儲(chǔ),通過 TCP 套接字高效通信,并提供了數(shù)據(jù)持久化。該數(shù)據(jù)庫的亮點(diǎn)在于它使用了 Pythonic 的方式編程,你可以像使用字典和列表一樣使用 CyberDB。
現(xiàn)在我們把 CyberDB 帶到能發(fā)揮其作用的地方,在生產(chǎn)環(huán)境中將 CyberDB 作為 Flask 的內(nèi)存數(shù)據(jù)庫,使用 Gunicorn 運(yùn)行,并實(shí)現(xiàn)多進(jìn)程間的通信。
這篇文章通過一個(gè)盡可能精簡的 Flask 實(shí)例講解,不會(huì)涉及復(fù)雜的 Web 知識。核心思路為 CyberDB + Gunicorn + Gevent + Flask(多進(jìn)程 + 協(xié)程),啟動(dòng)一個(gè) CyberDB 服務(wù)器,使用 Gunicorn 多進(jìn)程運(yùn)行 Flask 實(shí)例,每個(gè)進(jìn)程的實(shí)例通過 Gevent 運(yùn)行,進(jìn)程中使用 CyberDB 客戶端連接至內(nèi)存數(shù)據(jù)庫,由此實(shí)現(xiàn)對 CyberDB 數(shù)據(jù)庫的高并發(fā)訪問。
源碼解析
文章使用 PyPy 運(yùn)行,同樣適用 CPython。
運(yùn)行環(huán)境: Debian 10, Python 3.8.12, PyPy 7.3.7
此項(xiàng)目的目錄結(jié)構(gòu)
.
├── app.py
├── cyberdb_init.py
├── cyberdb_serve.py
├── requirements.txt
└── venv
我們通過列舉每個(gè)文件的內(nèi)容順序講解 CyberDB 的核心操作。
文件 requirements.txt
CyberDB>=0.7.1
Flask==2.1.1
gevent==21.12.0
gunicorn==20.1.0
這是此項(xiàng)目的依賴。這篇文章不是 Python 基礎(chǔ)教程,如果你不清楚,請查詢相關(guān)文檔創(chuàng)建虛擬環(huán)境 venv 目錄并安裝 requirements.txt 中的依賴。
生成 venv 目錄并安裝好依賴后,下面所有操作都在激活的虛擬環(huán)境中運(yùn)行。
文件 cyberdb_init.py
功能:初始化 CyberDB 的表結(jié)構(gòu),只在第一次運(yùn)行時(shí)使用,后續(xù)不再使用。
import time
import cyberdb
db = cyberdb.Server()
# 配置 CyberDB 服務(wù)端的 地址、端口、密碼。
db.start(host='127.0.0.1', port=9980, password='123456')
# 待服務(wù)端啟動(dòng)后,連接 CyberDB 服務(wù)端。
time.sleep(3)
client = cyberdb.connect(host='127.0.0.1', port=9980, password='123456')
# 生成 proxy 對象。
with client.get_proxy() as proxy:
# 創(chuàng)建類型為 CyberDict 的表 centre,并初始化內(nèi)容。
proxy.create_cyberdict('centre')
centre = proxy.get_cyberdict('centre')
centre['content'] = 'Hello CyberDB!'
# 將 CyberDB 保存至 data.cdb 。
db.save_db('data.cdb')
在項(xiàng)目根目錄執(zhí)行
python cyberdb_init.py
以完成 CyberDB 數(shù)據(jù)庫表的初始化。
它會(huì)在 CyberDB 中創(chuàng)建了一個(gè)名為 centre、類型為 CyberDict 的表;初始化 content 鍵的值為 Hello CyberDB!;最后將 CyberDB 數(shù)據(jù)庫保存至硬盤(在項(xiàng)目根目錄生成了名為 data.cdb 的文件)。
文件 cyberdb_serve.py
功能:運(yùn)行 CyberDB 服務(wù)端。
import cyberdb
def main():
# 后臺運(yùn)行 CyberDB 服務(wù)端,設(shè)置相關(guān)信息。
db = cyberdb.Server()
# 從硬盤讀取 data.cdb 至 CyberDB。
db.load_db('data.cdb')
# 每 300 秒備份一次數(shù)據(jù)庫。
db.set_backup('data.cdb', cycle=300)
db.run(
host='127.0.0.1', # TCP 運(yùn)行地址
port=9980, # TCP 監(jiān)聽端口
password='hWjYvVdqRC', # 數(shù)據(jù)庫連接密碼
max_con=10000, # 最大并發(fā)數(shù)
encrypt=True, # 加密通信
print_log=False # 不打印日志
)
if __name__ == '__main__':
main()
在項(xiàng)目根目錄執(zhí)行
python cyberdb_serve.py
以運(yùn)行 CyberDB 服務(wù)端。
此處設(shè)置了 encrypt=True ,CyberDB 會(huì)將 TCP 通信內(nèi)容使用 AES-256 算法加密。開啟 encrypt=True 后,CyberDB 僅允許白名單中的 IP 通信,默認(rèn)白名單為 ['127.0.0.1'](查看白名單 設(shè)置方法)。一般,若只需在本地進(jìn)程間通信,無需開啟 encrypt=True 和設(shè)置白名單,只有遠(yuǎn)程通信時(shí)需要此操作。
文件 app.py
功能:運(yùn)行 Flask 實(shí)例和 CyberDB 客戶端。
import cyberdb
from flask import Flask, g
# 連接 CyberDB 并生成客戶端實(shí)例。
client = cyberdb.connect(
host='127.0.0.1',
port=9980,
password='hWjYvVdqRC',
# 服務(wù)端若加密,客戶端必須加密,反之亦然。
encrypt=True,
# 每個(gè)連接若超過900秒無操作,將舍棄該連接。
# 連接由連接池智能管理,無需關(guān)注細(xì)節(jié)。
time_out=900
)
# 創(chuàng)建 Flask 實(shí)例,此部分請參考
# Flask 文檔 https://flask.palletsprojects.com/
app = Flask(__name__)
@app.before_request
def before_request():
# 每次請求執(zhí)行前生成 proxy 對象。
g.proxy = client.get_proxy()
# 從連接池獲取連接。
g.proxy.connect()
@app.get("/")
def hello_world():
# 從數(shù)據(jù)庫獲取 centre 表。
centre = g.proxy.get_cyberdict('centre')
return {
'code': 1,
'content': centre['content']
}
@app.teardown_request
def teardown_request(error):
# 每次請求執(zhí)行后歸還連接至連接池。
g.proxy.close()
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8000)
該模塊會(huì)在每次請求執(zhí)行前(before_request())使用 client.get_proxy() 獲取 proxy 對象,每個(gè)獲取的 proxy 對象可以綁定一個(gè) TCP 連接,此處使用 proxy.connect() 從連接池獲取連接。視圖函數(shù) hello_world() 中,由 proxy 獲取的對象 centre,與 proxy 共用同一個(gè)連接,proxy 的連接釋放后,centre 也會(huì)失去連接。在每次請求后(teardown_request())使用 proxy.close() 方法釋放 proxy 綁定的連接,歸還至連接池。
cyberdb.connect 的 time_out 參數(shù)表示連接池中每個(gè)連接的超時(shí)時(shí)間,此處每個(gè)連接超過 900 秒無操作將被舍棄。若不設(shè)置該參數(shù),連接池的每個(gè)連接會(huì)維持到失效為止。
使用 Gunicorn 運(yùn)行 Flask 實(shí)例
Gunicorn 是一個(gè)用于 UNIX 的 Python WSGI HTTP 服務(wù)器,通常在生產(chǎn)環(huán)境使用,可以利用多核 CPU 。
Gevent 是一個(gè)基于協(xié)程的 Python 網(wǎng)絡(luò)庫。Gevent 會(huì)更改 CyberDB 客戶端的底層套接字通信,使之支持協(xié)程。
在項(xiàng)目根目錄運(yùn)行
gunicorn -w 4 -b 127.0.0.1:8000 -k gevent app:app
使用 4 進(jìn)程、Gevent 啟動(dòng) Flask 實(shí)例。
瀏覽器訪問 127.0.0.1:8000 ,得到如下響應(yīng):
{"code":1,"content":"Hello CyberDB!"}
參考信息
CyberDB 源碼: https://github.com/Cyberbolt/CyberDB
總結(jié)
通過此例,你可以把 CyberDB 部署到更復(fù)雜的 Web 環(huán)境中,充分享受內(nèi)存的低延遲特性。CyberDB 的核心是以 Pythonic 的方式編程,你可以在任何 Python 代碼中將 CyberDB 作為內(nèi)存數(shù)據(jù)庫。
作者簡介:
Cyberbolt:一個(gè)自由的 Python 開發(fā)者。