import asyncio
from contextlib import asynccontextmanager
import json
import logging
import numpy as np
import aioamqp
import aioredis
import oss2
from typing import AsyncGenerator, List, Optional
from builtins import set
from minio import Minio
from motor.motor_asyncio import AsyncIOMotorClient
from aioamqp.channel import Channel
from aioamqp.envelope import Envelope
from aioamqp.properties import Properties
from aioredlock import Aioredlock, LockAcquiringError, LockError, Sentinel
from fastapi import FastAPI, Request, Response
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from fastapi_utils.api_settings import APISettings
from sqlalchemy.exc import DatabaseError, IntegrityError
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine, AsyncSession
from starlette.exceptions import HTTPException
from starlette.middleware.exceptions import ExceptionMiddleware
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from starlette.exceptions import HTTPException as StarletteHTTPException
from pydantic.main import BaseModel
from insightface.app import FaceAnalysis
from pymilvus import MilvusClient, AsyncMilvusClient, DataType, RRFRanker, AnnSearchRequest
from typing import Dict, Any, Union, Type, TypeVar, Callable, Tuple
from pydantic.networks import RedisDsn
from pydantic.env_settings import BaseSettings
from pydantic.fields import Field
import os
import torch
import filelock
URL = "mysql+aiomysql://root:123.com@10.8.0.2/prison"
class AppSettings(BaseModel):
"""App Settings"""
API_DEBUG: Optional[bool] = False
APP_INFO: Optional[Dict[str, Any]]
HTTP_TIMEOUT: Optional[float] = 4.0
DATACENTER_ID: Optional[int] = 1
RBAC_MODEL_PATH: Optional[str] = 'rbac_model.conf'
RBAC_POLICY_PATH: Optional[str] = 'rbac_policy.csv'
PUBKEY: Optional[str]
SECRET_KEY: Optional[str]
ALGORITHM: Optional[str] = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: Optional[int] = 30
class MilvusSettings(BaseModel):
"""数据库配置"""
MILVUS_DRIVER: str = "http"
MILVUS_HOST: str = "192.168.1.159"
MILVUS_PORT: int = "19530"
MILVUS_USER: str = "root"
MILVUS_PASSWORD: str = "Milvus"
def get_milvus_url(self) -> Union[str]:
return {
"uri": f"{self.MILVUS_DRIVER}://{self.MILVUS_HOST}:{self.MILVUS_PORT}",
# "token": f"{self.MILVUS_USER}:{self.MILVUS_PASSWORD}"
}
class DatabaseSettings(BaseModel):
"""数据库配置"""
DATABASE_DRIVER: str = "mysql+aiomysql"
DATABASE_HOST: str
DATABASE_PORT: int
DATABASE_USER: str
DATABASE_PASSWORD: str
DATABASE_NAME: str
DATABASE_URL: Optional[str] = None
def get_dburl(self) -> str:
return URL
class RedisSettings(BaseModel):
"""Redis 配置"""
REDIS_HOST: str
REDIS_PORT: int
REDIS_PASSWORD: str
REDIS_DB: int
REDIS_URL: Optional[RedisDsn] = None
REDIS_MINSIZE: int = 1
REDIS_MAXSIZE: int = 10
REDIS_ENCODING: str = "utf-8"
def get_redis_address(self) -> Tuple[str, Dict[str, Any]]:
opts = dict(
db=self.REDIS_DB,
minsize=self.REDIS_MINSIZE,
maxsize=self.REDIS_MAXSIZE,
encoding=self.REDIS_ENCODING,
)
if self.REDIS_PASSWORD:
opts.update({"password": self.REDIS_PASSWORD})
if self.REDIS_URL:
return self.REDIS_URL, opts
else:
return f"redis://10.8.0.2:6379/0", opts
class MongoDBSettings(BaseModel):
MONGO_HOST: Optional[str]
MONGO_PORT: Union[Optional[int], str]
MONGO_USER: Optional[str]
MONGO_PASSWORD: Optional[str]
MONGO_URL: Optional[str] = None
MONGO_DB: Optional[str]
def connect_opts(self) -> Dict[str, Any]:
if self.MONGO_URL:
return {"host": self.MONGO_URL}
return {
"host": self.MONGO_HOST,
"port": self.MONGO_PORT,
"username": self.MONGO_USER,
"password": self.MONGO_PASSWORD,
}
class AMQPSettings(BaseModel):
AMQP_HOST: Optional[str]
AMQP_PORT: Union[Optional[int], str]
AMQP_USER: Optional[str]
AMQP_PASSWORD: Optional[str]
AMQP_URL: Optional[str] = None
AMQP_QUEUE: Optional[str]
class OSSSettings(BaseModel):
OSS_ENDPOINT: str
OSS_ACCESS_KEY_ID: Optional[str]
OSS_ACCESS_KEY_SECRET: Optional[str]
OSS_BUCKET_NAME: str
class MINIIOSettings(BaseModel):
MINIIO_HOST: Optional[str]
MINIIO_PORT: Optional[str] = "9000"
MINIIO_ACCESS_KEY: Optional[str]
MINIIO_SECRET_KEY: Optional[str]
MINIIO_BUCKET_NAME: Optional[str]
MINIIO_DOINGS_PATH: Optional[str]
MINIIO_SHOPPING_PATH: Optional[str]
class SettingsMixin(
BaseSettings,
MilvusSettings,
DatabaseSettings,
RedisSettings,
MongoDBSettings,
AMQPSettings,
OSSSettings,
MINIIOSettings,
AppSettings,
):
"""Settings Mixin"""
T = TypeVar("T")
def singleton(cls: Type[T]) -> Callable[[Any, Any], T]:
"""单例模式装饰器"""
instances: Dict[Type[T], T] = {}
def get_instance(*args: Any, **kwargs: Any) -> T:
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Settings(SettingsMixin):
"""基础配置"""
env: str = Field(default="dev", env="ENV")
class Config:
env_prefix = ""
env_file: str = os.getenv("CONFIG_PATH", "conf/online.conf")
env_file_encoding: str = "utf-8"
@property
def debug(self) -> bool:
if self.env == "prod":
return False
return True
@property
def milvus(self) -> MilvusSettings:
return MilvusSettings(**self.dict())
@property
def db(self) -> DatabaseSettings:
return DatabaseSettings(**self.dict())
@property
def redis(self) -> RedisSettings:
return RedisSettings(**self.dict())
@property
def api(self) -> APISettings:
return APISettings(_env_file=self.Config.env_file)
@property
def app(self) -> AppSettings:
return AppSettings(**self.dict())
background_tasks = set()
# 定义异步打印函数
async def async_print(msg):
print(msg) # 实际打印操作仍是同步的
await asyncio.sleep(0) # 主动让出控制权,实现异步效果
async def register_async_cron() -> None:
task = asyncio.create_task(async_cron())
# 将 task 添加到集合中,以保持强引用,避免垃圾回收
background_tasks.add(task)
# 等待所有任务完成
# await asyncio.gather(*[task])
# 为了防止 task 被永远保持强引用,而无法被垃圾回收
# 让每个 task 在结束后将自己从集合中移除:
# task.add_done_callback(background_tasks.discard)
await asyncio.sleep(0) # 主动让出控制权,实现异步效果
async def register_redis() -> None:
address = "redis://10.8.0.2:6379/0"
redis: aioredis.Redis = await aioredis.create_redis_pool(address)
return redis
async def async_cron():
async def get_database(app: FastAPI) -> AsyncGenerator[AsyncSession, None]:
db = AsyncSession(expire_on_commit=False)
try:
yield db
except Exception as e:
await db.rollback()
raise e
finally:
await db.close()
seconds = 5
db = AsyncSession(expire_on_commit=False)
redis_client: aioredis.Redis = await register_redis()
logger.info("开始执行定时任务")
# 配置Redis实例连接
redis_instances = [
f'redis://:123.com@10.8.0.2:6379/0'
]
# 初始化锁管理器
lock_manager = Aioredlock(redis_instances)
while True:
await asyncio.sleep(seconds)
try:
await lock_manager.lock("resource_name", lock_timeout=seconds)
logger.info(f"成功获取锁,正在执行受保护的任务...")
data: Equipment = Equipment()
list_content: Optional[List[Equipment]] = await data.list(db, 0, 200, preload=False)
for item in list_content:
static = "offline"
equ = Equipment(**item.to_dict())
tester = ICMPSocketPing(equ.ip_address, timeout=1, retry=1)
redis_icmp_ping = await redis_client.get(f"ipadder_{equ.ip_address}")
# if not redis_icmp_ping: redis_icmp_ping = "offline"
if tester.ping(): # "online"
static = "online"
if redis_icmp_ping != static:
await equ.update_part(session=db, detail=False, **{'id': equ.id, 'status': EquipmentStatusEnum.ONLINE})
else: # "offline"
static = "offline"
if redis_icmp_ping != static:
await equ.update_part(session=db, detail=False, **{'id': equ.id, 'status': EquipmentStatusEnum.OFFLINE})
pass
logger.info(f'摄像头 {equ.ip_address} 状态 {static}')
await redis_client.setex(f"ipadder_{equ.ip_address}", seconds, static)
except LockError as e:
# Lock not acquired
continue
await asyncio.sleep(0) # 主动让出控制权,实现异步效果
await lock_manager.destroy()
async def main():
# 创建异步任务并发执行
task1 = asyncio.create_task(register_async_cron())
# 等待所有任务完成
await task1
if __name__ == "__main__":
asyncio.run(main()) # 运行异步主函数
print("Hello World!")解析全段代码
最新发布