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