阅读目录

一、部署之殇

二、日志之殇

三、中间件之殇

四、配置文件之殇

五、其它

一、部署之殇

1 linux后台启动

nohup uvicorn main:app --host 0.0.0.0 --port 8080

2 Docker部署

FROM python:3.7

RUN pip install fastapi uvicorn

EXPOSE 80

COPY ./app /app

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]

2.1 Docker + gunicorn

gunicorn配置文件

#!usr/bin/env python

# encoding: utf-8

import multiprocessing

# 监听端口

bind = '0.0.0.0:8899'

# 工作模式

worker_class = 'uvicorn.workers.UvicornWorker'

# 并行工作进程数

workers = multiprocessing.cpu_count() * 2 + 1

# 设置守护进程

#daemon = True

# 配置文件方式配置日志

logconfig = "./logger.ini"

Dockerfile

FROM python:3.7

ENV TZ Asia/Shanghai

#将项目代码放入镜像

COPY . /app

WORKDIR /app

#安装第三方模块,更新数据库

RUN pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com \

&& rm -rf configure

ENTRYPOINT ["gunicorn", "-c", "gunicorn.conf.py", "main:app"]

3 k8s部署

3.1 service.yaml

apiVersion: v1

kind: Service

metadata:

name: project_name # 项目名称

spec:

ports:

- name: http

port: 80

targetPort: 8899

type: ClusterIP

3.2 deployment.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: project_name # 项目名称

spec:

template:

spec:

imagePullSecrets:

- name: registry-pull-secret

containers:

- name: project_name # 项目名称

image: registry-vpc.cn-shanghai.aliyuncs.com/xxx/project_name:lates # 镜像

imagePullPolicy: Always

volumeMounts:

- name: host-time

mountPath: /etc/localtime

volumes:

- hostPath:

path: /etc/localtime

name: host-time

二、日志之殇

1 日志配置

日志配置文件,本地环境、测试环境、生产环境可以配置不同的日志的打印

[loggers]

;这里面把uvicorn创建的logger配置都覆盖了,注意最后一个`,`不能缺少、防止日志多次打印

keys=root, gunicorn.error, gunicorn.access,uvicorn.error,uvicorn.access,

[handlers]

keys=error_file, access_file

[formatters]

keys=generic, access

[logger_root]

level=DEBUG

handlers=access_file

[logger_]

level=INFO

handlers=access_file

qualname=

propagate=0

[logger_uvicorn.error]

level=INFO

handlers=error_file

qualname=uvicorn.error

propagate=0

[logger_uvicorn.access]

level=INFO

handlers=access_file

qualname=uvicorn.access

propagate=0

[logger_gunicorn.error]

level=INFO

handlers=error_file

propagate=1

qualname=gunicorn.error

[logger_gunicorn.access]

level=INFO

handlers=access_file

propagate=0

qualname=gunicorn.access

;注意日志配置的地址

[handler_error_file]

class=logging.FileHandler

formatter=generic

args=('/app/log/error.log',)

[handler_access_file]

class=logging.FileHandler

formatter=access

args=('/app/log/access.log',)

[formatter_generic]

format=[%(asctime)s] %(levelname)s in %(module)s: %(message)s

datefmt=%Y-%m-%d %H:%M:%S

class=logging.Formatter

;配置日志打印的信息

[formatter_access]

format=[%(asctime)s] %(levelname)s in %(module)s: %(message)s

class=logging.Formatter

2 读取配置

方案:读取文件 or 启动时设配置

# 环境变量

fast_api_env = os.environ.get('FAST_API_ENV')

# 获取logger对象

def get_logger(filename="logger.ini", logger_name='root'):

logging.config.fileConfig(fname=filename, disable_existing_loggers=False)

return logging.getLogger(logger_name)

def init_log():

"""初始化日志"""

print("加载log文件...")

try:

global common_config

if fast_api_env == 'local':

# 本地环境

LOG_CONFIG_PATH = os.path.join(BASE_DIR, 'conf', 'logger-local.ini')

# logger = get_logger(os.path.join(BASE_DIR, 'conf', 'logger.ini'))

else:

common_config.LOG_CONFIG_PATH = os.path.join(BASE_DIR, 'conf', 'logger-prod.ini')

# logger = get_logger(common_config.LOG_CONFIG_PATH, logger_name='file')

except Exception as e:

raise LogConfigError(e)

3 启动配置logger.ini

uvicorn.run(app, host='0.0.0.0', port=8899, log_config=common_config.LOG_CONFIG_PATH)

*配置完成后,logging.debug()等使用即可

三、中间件之殇(自定义中间件)

1 @app.middleware("http")

@app.middleware("http")

async def add_process_time_header(request: Request, call_next):

start_time = time.time()

response = await call_next(request)

process_time = time.time() - start_time

# 添加响应头

response.headers["X-Process-Time"] = str(process_time)

return response

2 app.add_middleware

from starlette.datastructures import Headers

from starlette.responses import PlainTextResponse

from starlette.types import ASGIApp, Receive, Scope, Send

class AuthMiddleware:

def __init__(self, app: ASGIApp) -> None:

self.app = app

async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:

logging.info(scope.get('path'))

if scope.get('path'):

url = URL(scope=scope)

if url.path not in common_config.WHITE_LIST: # 设置白名单

headers = Headers(scope=scope)

token = headers.get("Token")

# 自定义访问拦截

if not token or not headers.get("username") or not check_devops_auth(token): # 自定义验证token,和其他请求信息作为认证拦截

response = PlainTextResponse("未登陆用户", status_code=401)

await response(scope, receive, send)

return

await self.app(scope, receive, send)

app.add_middleware(AuthMiddleware)

四、配置文件之殇

方案:采用ini配置文件,读取后写入全局变量

1 配置文件

[common]

PROJECT_NAME = kk-jira-monitor

BACKEND_CORS_ORIGINS = http://127.0.0.1:8080,

API_V1_STR = /api/v1.0

[mysql]

USERNAME = admin

PASSWORD = 123456

HOST =

PORT = 3306

DATABASE =

SQLALCHEMY_DATABASE_URI = mysql+pymysql://%(USERNAME)s:%(PASSWORD)s@%(HOST)s:%(PORT)s/%(DATABASE)s

2 初始化

common_config = None

mysql_config = None

def init_config():

"""初始化配置文件"""

global mysql_config, common_config

print("加载配置文件...")

config = ReConfigParser()

try:

if fast_api_env == 'local':

config.read(os.path.join(BASE_DIR, 'conf', 'conf-local.ini'), encoding='utf-8')

elif fast_api_env == 'dev':

config.read(os.path.join(BASE_DIR, 'conf', 'conf-dev.ini'), encoding='utf-8')

else:

config.read(os.path.join(BASE_DIR, 'conf', 'conf-prod.ini'), encoding='utf-8')

mysql_config = MySQLConfig(**dict(config.items('mysql')))

common_config = CommonConfig(**dict(config.items('common')))

except Exception as e:

# logger.exception(f"配置文件初始化失败,{e.__str__()}")

raise ConfigError(e)

3 configparse

from configparser import ConfigParser

class ReConfigParser(ConfigParser):

def __init__(self, defaults=None):

ConfigParser.__init__(self, defaults=defaults)

"""复写方法实现key值区分大小写"""

def optionxform(self, optionstr):

return optionstr

4 配置变量验证

import os

from typing import Optional

from pydantic import BaseModel

class CommonConfig(BaseModel):

SECRET_KEY: str = os.urandom(32)

PROJECT_NAME: str

API_V1_STR: str

# 允许访问的origins

BACKEND_CORS_ORIGINS: str

class MySQLConfig(BaseModel):

USERNAME: str = None

PASSWORD: str = None

HOST: str = None

PORT: int = None

DATABASE: str = None

SQLALCHEMY_DATABASE_URI: str = (

f"mysql://{USERNAME}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}"

)

五、其它

1 问题一(中间件执行报错)

ASGI 'lifespan' protocol appears unsupported.

@app.on_event('startup') 将不会执行

2 问题二(定时任务报错)

借助的apshechduler注册的定时任务如果执行报错,捕获不到异常信息

解决办法可见 分离定时任务

fastapi日志重复打印_【FastAPI】踩坑总结相关推荐

  1. java数字转大写 其他报异常_【踩坑系列】使用long类型处理金额,科学计数法导致金额转大写异常...

    python科学计算数据应用(第2版) 156.4元 (需用券) 去购买 > 1. 踩坑经历 上周,一个用户反馈他创建的某个销售单无法打开,但其余销售单都可以正常打开,当时查看了生产环境的ERR ...

  2. WebSocket服务器端的日志重复打印问题

    日志中backend-ms与webshop的每次通信,require,response都是只有一次记录, (截图来自log_latestBackend_with_oldFrontend) 但是back ...

  3. mysql追溯历史性能问题_【踩坑記錄】記一次MySQL主從復制延遲的坑

    最近開發中遇到的一個MySQL主從延遲的坑,記錄並總結,避免再次犯同樣的錯誤. 情景 一個活動信息需要審批,審批之后才能生效.因為之后活動要編輯,編輯后也可能觸發審批,審批中展示的是編輯前的活動內容, ...

  4. python array赋值_从踩坑学Python内部原理(5):执行时机的差异

    (给Python开发者加星标,提升Python技能) 英文:Satwik Kansal,翻译:暮晨 Python开发者整理自 GitHub [导读]:Python 是一个设计优美的解释型高级语言,它提 ...

  5. python 扒数据_不踩坑的Python爬虫:如何在一个月内学会爬取大规模数据

    Python爬虫为什么受欢迎 如果你仔细观察,就不难发现,懂爬虫.学习爬虫的人越来越多,一方面,互联网可以获取的数据越来越多,另一方面,像 Python这样的编程语言提供越来越多的优秀工具,让爬虫变得 ...

  6. mysql通用日志不打印_解决logback不打印mybatis的SQL日志的问题

    工作这么多年,今天还是因为Logback的这个问题稍微卡了一下,惭愧. 问题描述: logback配置了如下信息: ...此处省略File Appender内容 ${logbase}sql.%d{yy ...

  7. postgresql批量插入数据脚本_每日踩坑 2020-11-04 PostgreSQL 批量插入数据

    一看居然两三个月没写博文了.凑.最近去考了个试,老天保佑吧. 今天有个同事跑来问我,我之前写的数据同步工具支不支持 PostgreSQL. 然后跟我讲了他的需求.感觉我随手写的小东西开始变成整个部门的 ...

  8. mysql scrapy 重复数据_小心避坑:MySQL分页时使用 limit+order by 会出现数据重复问题...

    作者:猿码道http://www.jianshu.com/p/544c319fd838 0 问题描述 在MySQL中我们通常会采用limit来进行翻页查询,比如limit(0,10)表示列出第一页的1 ...

  9. mysql 结束符报错_【踩坑记录】MySQL 实现自定义递归函数

    因项目需要,需根据某个商品类别path,查询该类别下的所有子类别 表  goods-categories(path,parent_id,id) 该处使用的表为临时创建的表 t1(id,parent_i ...

最新文章

  1. AI将成科学家“高级定制”工具
  2. 极速理解设计模式系列【目录索引】
  3. const volatile同时限定一个类型int a = 10
  4. 数据结构--二叉树(1)
  5. swing开发图形界面工具配置(可自由拖控件上去)
  6. springboot 插入返回id_实战SpringBoot缓存开发
  7. 地图定义一个中间不动标注_高精度地图制作(三)
  8. Android 第二课 Intent
  9. makefile 文件 (​ http://blog.csdn.net/ruglcc/article/details/7814546/ )
  10. [Pro]斐波那契数列阿【斐波那契数列】
  11. 五、鼎捷T100生产管理之报工
  12. 理论+故事,开启你的量化阅读之旅
  13. IoT中的高音质音频设计
  14. EasyGUI-5:文本显示
  15. 数据集成之数据整合(Data Consolidation)
  16. 多说高级应用-添加 UA 浏览器标识、旋转头像等
  17. 创建自己的RSS服务——debian docker TinyTinyRSS搭建
  18. ibili的使用教程
  19. 手动安装couchdb并且指定版本 详细教程
  20. 在计算机中字节的英文名称是bit么,计算机中的字节是常用单位,它的英文名字是( )。A.bitB.byteC.boutD.baud - 试题答案网问答...

热门文章

  1. JS----JavaScript数组去重(12种方法,史上最全)
  2. JS----JavaScript数组方法及总结
  3. supervisor 守护多个进程_supervisor管理守护进程
  4. Android 应用内启动第三方应用
  5. android 手势事件 重写,Android实现通过手势控制图片大小缩放的方法
  6. java页面可配置化_web.xml页面配置
  7. vmware配置centos7网络
  8. Maven 中央仓库地址
  9. FCN Caffe:可视化featureMaps和Weights(C++)、获取FCN结果
  10. OpenCV:使用OpenCV3随机森林进行统计特征多类分析