python基于FastAPI實(shí)現(xiàn)一個(gè)簡(jiǎn)易的在線用戶統(tǒng)計(jì)功能
概述
這是一個(gè)基于Python的FastAPI框架實(shí)現(xiàn)的服務(wù),用于統(tǒng)計(jì)客戶端的心跳信息,并據(jù)此維護(hù)在線用戶列表以及記錄活躍用戶數(shù)。
功能特性
- 心跳接收:接受來(lái)自客戶端的心跳包,以更新客戶端的狀態(tài)。
- 在線用戶統(tǒng)計(jì):提供API接口來(lái)獲取當(dāng)前在線用戶的數(shù)量。
- 活躍用戶統(tǒng)計(jì):提供API接口來(lái)獲取最近指定天數(shù)內(nèi)活躍的用戶數(shù)量。
- 請(qǐng)求頻率限制:對(duì)每個(gè)IP地址實(shí)施每秒一次的請(qǐng)求頻率限制。
安裝與運(yùn)行
請(qǐng)確保已經(jīng)安裝了Python 3.10+。
克隆或下載項(xiàng)目源代碼到本地。
在項(xiàng)目根目錄下安裝所需的依賴庫(kù):
pip install fastapi uvicorn
運(yùn)行服務(wù):
python main.py
或者使用uvicorn命令直接運(yùn)行(假設(shè)文件名為main.py):
uvicorn main:app --reload --host 0.0.0.0 --port 8001
API 文檔
http://127.0.0.1:8001/docs

心跳接收
- URL:
/heartbeat - 方法:
POST - 描述: 接收客戶端發(fā)送的心跳信號(hào),并更新客戶端為在線狀態(tài)。
- 響應(yīng):
200 OK: 返回JSON格式的信息確認(rèn)收到心跳。429 Too Many Requests: 如果客戶端在1秒內(nèi)發(fā)送了多個(gè)請(qǐng)求。
獲取在線用戶數(shù)量
- URL:
/online_clients - 方法:
POST - 描述: 返回當(dāng)前在線的客戶端數(shù)量。
- 響應(yīng):
200 OK: 返回包含在線用戶數(shù)量的JSON對(duì)象。429 Too Many Requests: 請(qǐng)求過(guò)于頻繁。
獲取活躍用戶數(shù)量
- URL:
/total_users - 方法:
GET 或 POST - 參數(shù):
days(可選, 默認(rèn)值為7): 指定要查詢的天數(shù)。
- 描述: 返回最近幾天內(nèi)有活動(dòng)記錄的用戶數(shù)量。
- 響應(yīng):
200 OK: 返回包含活躍用戶數(shù)量和查詢天數(shù)的JSON對(duì)象。429 Too Many Requests: 請(qǐng)求過(guò)于頻繁。
數(shù)據(jù)存儲(chǔ)
所有客戶端的心跳時(shí)間戳將被持久化到一個(gè)JSON文件中,該文件位于服務(wù)啟動(dòng)時(shí)所在的目錄下的users_data.json。每次接收到新的心跳信號(hào)時(shí),都會(huì)更新此文件。
注意事項(xiàng)
- 本服務(wù)僅用于演示目的,實(shí)際生產(chǎn)環(huán)境中可能需要考慮更健壯的數(shù)據(jù)存儲(chǔ)解決方案、安全性增強(qiáng)措施等。
- 為了保護(hù)服務(wù)器免受濫用,已實(shí)施了基本的請(qǐng)求頻率限制。根據(jù)實(shí)際需求,可以調(diào)整這個(gè)限制。
- 服務(wù)默認(rèn)監(jiān)聽(tīng)在8001端口上,可以通過(guò)修改
uvicorn.run函數(shù)中的port參數(shù)來(lái)更改。
希望這份文檔能對(duì)你有所幫助!如果有任何問(wèn)題或需要進(jìn)一步的幫助,請(qǐng)隨時(shí)告訴我。
源碼 main.py
from fastapi import FastAPI, Request, Depends, HTTPException
from collections import defaultdict
from datetime import datetime, timedelta
import asyncio
import json
import os
app = FastAPI()
# 存儲(chǔ)客戶端的心跳數(shù)據(jù)
clients_last_heartbeat = defaultdict(datetime)
# 每個(gè)IP請(qǐng)求時(shí)間間隔限制為1秒
last_request_time = defaultdict(datetime)
# 在線客戶端統(tǒng)計(jì)
online_clients = set()
# 心跳超時(shí)時(shí)間設(shè)置為1分鐘
HEARTBEAT_TIMEOUT = timedelta(minutes=10)
# 用戶數(shù)據(jù)文件路徑
USER_DATA_FILE = "users_data.json"
# 加載用戶數(shù)據(jù)
def load_user_data():
if os.path.exists(USER_DATA_FILE):
with open(USER_DATA_FILE, "r") as f:
return json.load(f)
return {}
# 保存用戶數(shù)據(jù)
def save_user_data(data):
with open(USER_DATA_FILE, "w") as f:
json.dump(data, f)
# 初始化用戶數(shù)據(jù)
all_users = load_user_data()
@app.on_event("startup")
async def startup_event():
# 啟動(dòng)后臺(tái)任務(wù),每1分鐘檢查一次在線設(shè)備
asyncio.create_task(remove_offline_clients())
async def remove_offline_clients():
"""
定時(shí)任務(wù):移除超過(guò)心跳超時(shí)時(shí)間未發(fā)送心跳的客戶端
"""
while True:
await asyncio.sleep(HEARTBEAT_TIMEOUT.total_seconds())
now = datetime.utcnow()
# 找出超過(guò)超時(shí)時(shí)間未發(fā)送心跳的設(shè)備,并將其從在線列表中移除
offline_clients = {ip for ip, last_heartbeat in clients_last_heartbeat.items()
if now - last_heartbeat > HEARTBEAT_TIMEOUT}
# 從在線設(shè)備中移除離線的客戶端
for client in offline_clients:
online_clients.discard(client)
del clients_last_heartbeat[client] # 刪除心跳記錄
print(f"清除離線客戶端, 當(dāng)前在線客戶端數(shù)量: {len(online_clients)}")
# 請(qǐng)求頻率限制,1秒內(nèi)只能請(qǐng)求一次
def request_limit(request: Request):
client_ip = request.client.host
now = datetime.utcnow()
if client_ip in last_request_time and (now - last_request_time[client_ip]).total_seconds() < 1:
raise HTTPException(status_code=429, detail="Too Many Requests")
last_request_time[client_ip] = now
@app.post("/heartbeat")
async def receive_heartbeat(request: Request, limit: None = Depends(request_limit)):
"""
接受客戶端的心跳包
"""
client_ip = request.client.host
now = datetime.utcnow()
# 更新心跳時(shí)間并將客戶端標(biāo)記為在線
clients_last_heartbeat[client_ip] = now
online_clients.add(client_ip)
# 更新所有用戶數(shù)據(jù)并保存到文件
all_users[client_ip] = now.isoformat()
save_user_data(all_users)
return {"message": "Heartbeat received", "ip": client_ip}
@app.get("/online_clients")
async def get_online_clients(request: Request, limit: None = Depends(request_limit)):
"""
獲取當(dāng)前在線客戶端數(shù)量
"""
return {"online_clients_count": len(online_clients)}
@app.post("/online_clients")
async def get_online_clients2(request: Request, limit: None = Depends(request_limit)):
"""
獲取當(dāng)前在線客戶端數(shù)量
"""
return {"online_clients_count": len(online_clients)}
@app.get("/total_users")
async def get_total_users(days: int = 7, request: Request = None, limit: None = Depends(request_limit)):
"""
獲取最近n天活躍的用戶數(shù)
"""
now = datetime.utcnow()
cutoff_date = now - timedelta(days=days)
# 篩選最近n天活躍的用戶
recent_users_count = sum(
1 for last_seen in all_users.values()
if datetime.fromisoformat(last_seen) >= cutoff_date
)
return {"recent_users_count": recent_users_count, "days": days}
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8001)
到此這篇關(guān)于python基于FastAPI實(shí)現(xiàn)一個(gè)簡(jiǎn)易的在線用戶統(tǒng)計(jì)功能的文章就介紹到這了,更多相關(guān)python統(tǒng)計(jì)在線用戶內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Python+PyQt5設(shè)計(jì)實(shí)現(xiàn)一個(gè)C++源代碼行數(shù)統(tǒng)計(jì)工具
- 從入門(mén)到實(shí)戰(zhàn)詳解Python數(shù)據(jù)統(tǒng)計(jì)的完全指南
- 基于Python制作一個(gè)詞頻統(tǒng)計(jì)工具的完整指南
- Python+Qt實(shí)現(xiàn)智能應(yīng)用時(shí)長(zhǎng)統(tǒng)計(jì)工具
- 基于Python開(kāi)發(fā)圖片文件信息統(tǒng)計(jì)工具
- 使用Python統(tǒng)計(jì)網(wǎng)站訪問(wèn)流量
- Python字符串查找和統(tǒng)計(jì)方法詳解
相關(guān)文章
Python實(shí)現(xiàn)ping指定IP的示例
今天小編就為大家分享一篇Python實(shí)現(xiàn)ping指定IP的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
在Python的Django框架的視圖中使用Session的方法
這篇文章主要介紹了在Python的Django框架的視圖中使用Session的方法,包括相關(guān)的設(shè)置測(cè)試Cookies的方法,需要的朋友可以參考下2015-07-07
Pytest+Yaml+Excel?接口自動(dòng)化測(cè)試框架的實(shí)現(xiàn)示例
本文主要介紹了Pytest+Yaml+Excel?接口自動(dòng)化測(cè)試框架,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
Python自動(dòng)化測(cè)試之登錄腳本的實(shí)現(xiàn)
本文主要介紹了Python自動(dòng)化測(cè)試之登錄腳本的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
Python實(shí)現(xiàn)字典轉(zhuǎn)字符串的五種方法
本文介紹了在Python中如何將字典數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為字符串格式的多種方法,首先可以通過(guò)內(nèi)置的str()函數(shù)進(jìn)行簡(jiǎn)單轉(zhuǎn)換;其次利用ison.dumps()函數(shù)能夠更靈活地控制輸出格式;最后還提供了使用格式化字符串手動(dòng)轉(zhuǎn)換的示例,需要的朋友可以參考下2025-09-09
使用Python手工計(jì)算x的算數(shù)平方根,來(lái)自中國(guó)古人的數(shù)學(xué)智慧
本篇采用的計(jì)算方法既非二分法也非牛頓迭代法,而是把中國(guó)古代的手工計(jì)算平方根的方法轉(zhuǎn)成代碼來(lái)完成。代碼有點(diǎn)煩雜,算是拋磚引玉吧,期待高手們寫(xiě)出更好的代碼來(lái)2021-09-09
詳解Python和Rust中內(nèi)存管理機(jī)制的實(shí)現(xiàn)與對(duì)比
Python和Rust都采用了垃圾收集(Garbage?Collection)機(jī)制來(lái)管理內(nèi)存,但它們各自的實(shí)現(xiàn)方式有很大的不同,下面就跟隨小編一起來(lái)深入了解下二者的區(qū)別吧2024-03-03
Python實(shí)現(xiàn)快速大文件比較代碼解析
這篇文章主要介紹了Python實(shí)現(xiàn)快速大文件比較代碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09

