《Python Web部署应知应会》No2:如何基于FastAPI 和 OLLAMA 架构实现高并发 AI 推理服务

news2025/5/20 10:30:21

《Python Web部署应知应会》No2:如何基于FastAPI 和 OLLAMA 架构实现高并发 AI 推理服务(上)

摘要:

在 FastAPI 和 OLLAMA 架构中实现高并发 AI 推理服务,并优化性能指标采集和缓存策略,可以充分利用 asyncio 的异步 I/O 操作来提升吞吐量和响应速度。以下是一个详细的解决方案,分为基础实现架构概述、实现步骤、性能指标采集、结合 FastAPI 和 OLLAMA、优化方案详细实现(批量推理、负载均衡、动态缓存失效、监控与告警)几个关键部分进行说明,本文章末提供了一个小型博客网站实现高并发推理的案例设计思路,本文章分为上中下三个系列组成,文章系列(中)将介绍具体网站实践,文章系列(下)将介绍高并发网站性能测试和监控的实现。


一、基础方案和优化方法:

1. 架构概述

  • FastAPI: 提供高性能的异步 Web 框架,支持异步请求处理。
  • OLLAMA: 假设它是一个 AI 推理引擎(例如基于 LLM 的推理服务),可以通过 API 或库调用进行交互。
  • Asyncio: 用于管理异步任务,确保 I/O 密集型操作(如网络请求、数据库访问等)不会阻塞主线程。
  • 缓存策略: 使用内存缓存(如 Redis 或 functools.lru_cache)存储频繁使用的推理结果,减少重复计算。
  • 性能指标采集: 利用异步任务记录性能数据(如请求耗时、错误率等),并将其汇总到监控系统。

在这里插入图片描述

2. 实现步骤

(1) 异步推理调用

使用 asyncio 实现对 OLLAMA 推理服务的异步调用。假设 OLLAMA 提供了一个 HTTP API,可以使用 httpx 库(支持异步请求)与之交互。

import httpx
from fastapi import FastAPI, Depends

app = FastAPI()

# 假设 OLLAMA 的推理服务地址
OLLAMA_API_URL = "http://ollama-service:8000/inference"

async def call_ollama(prompt: str):
    async with httpx.AsyncClient() as client:
        response = await client.post(
            OLLAMA_API_URL,
            json={"prompt": prompt},
            timeout=30.0
        )
        response.raise_for_status()  # 确保请求成功
        return response.json()

(2) 缓存策略

为了减少重复推理的计算开销,可以引入缓存机制。以下是两种常见的缓存方式:

  • 内存缓存(适合小规模应用):
    使用 functools.lru_cacheaiocache 库实现简单的内存缓存。
from functools import lru_cache

@lru_cache(maxsize=128)
def cached_inference(prompt: str):
    # 这里假设推理是同步的,如果是异步的,需要调整逻辑
    return call_ollama_sync(prompt)

async def call_ollama_with_cache(prompt: str):
    # 异步包装同步缓存调用
    return await asyncio.to_thread(cached_inference, prompt)
  • 分布式缓存(适合大规模应用):
    使用 Redis 作为分布式缓存,利用 aioredis 库实现异步操作。
import aioredis

redis = aioredis.from_url("redis://localhost")

async def call_ollama_with_redis_cache(prompt: str):
    cache_key = f"inference:{prompt}"
    result = await redis.get(cache_key)
    if result:
        return result.decode("utf-8")  # 缓存命中
    
    # 缓存未命中,调用推理服务
    result = await call_ollama(prompt)
    await redis.setex(cache_key, 3600, result)  # 缓存1小时
    return result

(3) 性能指标采集

通过中间件或后台任务记录性能指标,例如请求耗时、成功率等。

  • 中间件记录请求耗时
    在 FastAPI 中添加一个中间件,记录每个请求的处理时间。
import time
from fastapi import Request, Response

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response: Response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response
  • 后台任务采集指标
    使用 asyncio.create_task 定期将性能数据发送到监控系统(如 Prometheus 或 Datadog)。
import asyncio

metrics_queue = asyncio.Queue()

async def metrics_collector():
    while True:
        metric = await metrics_queue.get()
        # 将 metric 发送到监控系统
        print(f"Collected Metric: {metric}")
        metrics_queue.task_done()

# 启动后台任务
@app.on_event("startup")
async def startup_event():
    asyncio.create_task(metrics_collector())

(4) 结合 FastAPI 和 OLLAMA

将上述组件整合到 FastAPI 中,提供一个完整的高并发推理接口。

@app.post("/infer")
async def infer(prompt: str):
    start_time = time.time()
    
    # 调用推理服务(带缓存)
    try:
        result = await call_ollama_with_redis_cache(prompt)
        status = "success"
    except Exception as e:
        result = {"error": str(e)}
        status = "failure"
    
    process_time = time.time() - start_time
    
    # 记录性能指标
    await metrics_queue.put({
        "prompt": prompt,
        "status": status,
        "process_time": process_time
    })
    
    return result

3. 优化建议

(1) 批量推理

如果多个请求可以合并为一个批量推理请求(Batch Inference),可以显著提高吞吐量。例如,累积一定数量的请求后一次性发送给 OLLAMA。

(2) 负载均衡

在高并发场景下,部署多个实例并通过负载均衡器(如 Nginx 或 Kubernetes Service)分发流量。

(3) 动态缓存失效

对于时效性要求较高的数据,可以设置动态缓存失效策略。例如,根据数据更新频率自动刷新缓存。

(4) 监控与告警

结合 Prometheus 和 Grafana,实时监控服务性能,并设置告警规则(如请求失败率超过阈值)。


4. 总结

通过上述方法,我们可以实现一个高效的 FastAPI + OLLAMA 高并发推理服务:

  • 利用 asyncio 和异步库(如 httpxaioredis)提升 I/O 性能。
  • 通过缓存策略减少重复计算,降低延迟。
  • 使用中间件和后台任务采集性能指标,持续优化服务。

这种架构不仅能够满足高并发需求,还能通过缓存和性能监控进一步提升用户体验和系统稳定性。

二、完整实例代码:个人博客

以下是一个完整的个人博客 Flask 网站设计方案,结合了 FastAPI 和 OLLAMA 架构中的高并发 AI 推理服务技术(包括批量推理、负载均衡、动态缓存失效、监控与告警)。这个方案将分为以下几个部分:


1. 项目结构

项目的文件框架如下:

personal_blog/
├── app/                     # Flask 应用代码
│   ├── __init__.py          # 初始化 Flask 应用
│   ├── routes.py            # 博客路由
│   ├── models.py            # 数据库模型
│   ├── ai_service.py        # AI 推理服务集成
│   ├── cache.py             # 缓存逻辑
│   └── metrics.py           # 性能指标采集
├── static/                  # 静态资源
│   ├── css/                 # 样式表
│   ├── js/                  # JavaScript 文件
│   └── images/              # 图片资源
├── templates/               # 模板文件
│   ├── base.html            # 基础模板
│   ├── index.html           # 首页
│   ├── post.html            # 博客文章页面
│   └── ai_response.html     # AI 推理结果页面
├── migrations/              # 数据库迁移文件
├── requirements.txt         # Python 依赖
├── run.py                   # 启动脚本
└── README.md                # 项目说明文档

2. 技术栈

  • 前端: HTML + CSS + JavaScript(Bootstrap 或 TailwindCSS 可选)
  • 后端: Flask(主框架) + FastAPI(AI 推理服务)
  • 数据库: SQLite(小型项目适用)或 PostgreSQL(生产环境推荐)
  • 缓存: Redis(动态缓存失效)
  • 监控: Prometheus + Grafana
  • 负载均衡: Nginx 或 Kubernetes Service(可选)

3. 详细实现

(1) Flask 应用初始化

app/__init__.py 中初始化 Flask 应用,并集成数据库和缓存。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import aioredis

# 初始化 Flask 应用
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# 初始化数据库
db = SQLAlchemy(app)

# 初始化 Redis 缓存
redis = aioredis.from_url("redis://localhost")

# 导入路由
from app.routes import *

# 创建数据库表
with app.app_context():
    db.create_all()

(2) 数据库模型

app/models.py 中定义博客文章的数据库模型。

from . import db

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=db.func.current_timestamp())

(3) 路由和视图

app/routes.py 中定义博客的路由和视图函数。

from flask import render_template, request, redirect, url_for
from .models import Post
from .ai_service import call_ollama_with_cache
from .cache import redis

@app.route('/')
def index():
    posts = Post.query.order_by(Post.created_at.desc()).all()
    return render_template('index.html', posts=posts)

@app.route('/post/<int:post_id>')
def view_post(post_id):
    post = Post.query.get_or_404(post_id)
    return render_template('post.html', post=post)

@app.route('/create', methods=['GET', 'POST'])
def create_post():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        new_post = Post(title=title, content=content)
        db.session.add(new_post)
        db.session.commit()
        return redirect(url_for('index'))
    return render_template('create_post.html')

@app.route('/ai-inference', methods=['POST'])
async def ai_inference():
    prompt = request.form['prompt']
    result = await call_ollama_with_cache(prompt)
    return render_template('ai_response.html', result=result)

(4) AI 推理服务

app/ai_service.py 中实现 FastAPI 的高并发 AI 推理服务集成。

批量推理
import asyncio
from collections import defaultdict
import httpx

BATCH_SIZE = 10
BATCH_TIMEOUT = 2
OLLAMA_API_URL = "http://ollama-service:8000/inference"
batch_queue = asyncio.Queue()
batch_results = defaultdict(asyncio.Future)

async def batch_inference_worker():
    while True:
        batch_prompts = []
        try:
            while len(batch_prompts) < BATCH_SIZE:
                prompt, future = await asyncio.wait_for(batch_queue.get(), timeout=BATCH_TIMEOUT)
                batch_prompts.append(prompt)
                batch_results[prompt] = future
        except asyncio.TimeoutError:
            pass

        if batch_prompts:
            results = await call_ollama_batch(batch_prompts)
            for prompt, result in zip(batch_prompts, results):
                batch_results[prompt].set_result(result)

async def call_ollama_batch(prompts: list):
    async with httpx.AsyncClient() as client:
        response = await client.post(
            OLLAMA_API_URL,
            json={"prompts": prompts},
            timeout=30.0
        )
        response.raise_for_status()
        return response.json()["results"]
Redis 动态缓存失效
async def call_ollama_with_cache(prompt: str):
    cache_key = f"inference:{prompt}"
    result = await redis.get(cache_key)
    if result:
        return result.decode("utf-8")
    
    result = await call_ollama(prompt)
    ttl = calculate_ttl(prompt)
    await redis.setex(cache_key, ttl, result)
    return result

def calculate_ttl(prompt: str) -> int:
    if "urgent" in prompt.lower():
        return 60
    return 3600

(5) 监控与告警

app/metrics.py 中集成 Prometheus。

from prometheus_client import Counter, Histogram, start_http_server

REQUEST_COUNT = Counter("request_count", "Total number of requests", ["status"])
REQUEST_LATENCY = Histogram("request_latency_seconds", "Request latency in seconds")

@app.before_request
def before_request():
    request.start_time = time.time()

@app.after_request
def after_request(response):
    process_time = time.time() - request.start_time
    status = "success" if response.status_code < 400 else "failure"
    REQUEST_COUNT.labels(status=status).inc()
    REQUEST_LATENCY.observe(process_time)
    return response

start_http_server(8001)

(6) 静态资源与模板

templates/static/ 中提供前端页面和静态资源。

示例模板 (base.html)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Personal Blog</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
    <header>
        <h1>My Personal Blog</h1>
    </header>
    <main>
        {% block content %}{% endblock %}
    </main>
</body>
</html>

4. 启动与部署

(1) 启动脚本

run.py 中启动 Flask 应用。

from app import app

if __name__ == '__main__':
    app.run(debug=True)
(2) 部署建议
  • 使用 Gunicorn 启动 Flask 应用:gunicorn -w 4 app:app
  • 配置 Nginx 作为反向代理和负载均衡器。
  • 部署 Prometheus 和 Grafana 进行性能监控。

5. 总结

通过上述设计,我们实现了一个完整的个人博客网站,集成了高并发 AI 推理服务(FastAPI + OLLAMA),并实现了批量推理、动态缓存失效、监控与告警等优化方案。这种架构不仅功能强大,还能满足高并发需求,适合中小型博客应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2325060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NUUO摄像头debugging_center_utils命令执行漏洞

免责声明&#xff1a;本号提供的网络安全信息仅供参考&#xff0c;不构成专业建议。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权&#xff0c;请及时与我联系&#xff0c;我将尽快处理并删除相关内容。 漏洞描述 NUUO NVR是中国台湾省NUUO公司旗…

2.pycharm部署Ai - 编程好助手

一、pycharm安装continue插件 1.提前安装好pycharm&#xff0c;并双击打开 2.File – Setting 3.Plugins – 搜索Continue &#xff0c; 点击Install安装 4.点ok 二、获取硅基流动API 1.登入网站&#xff1a;https://siliconflow.cn/zh-cn/#/&#xff0c;并注册登入 2.获取AP…

uniapp + Axios + 小程序封装网络请求

前言 小程序自带的网络请求使用起来比较麻烦&#xff0c;不便于管理&#xff0c;就需要封装网络请求&#xff0c;减少繁琐步骤&#xff0c;封装最终效果&#xff0c;根据类别将网络请求封装在文件中&#xff0c;使用得时候调用文件名名称加文件中得自定义名称&#xff0c;就可…

初始数据库--MySQL

一&#xff0c;数据库&#xff08;DataBase&#xff09;概述 思考程序开发中数据存储问题&#xff1f; 变量 int c 10; 对象{名字&#xff0c;价格&#xff0c;颜色} 集合类-->对象 以上方式存储数据都是在内存中存储 程序运行结束后数据都释放了 …

ENSP学习day12

链路聚合–LACP&#xff08;Link Aggregation Control Protocol&#xff09; 链路聚合是指将多条物理链路通过一种技术进行绑定&#xff0c;从而形成一个逻辑上更高带宽、更可靠的链路。这种技术能够提高网络传输速率和可靠性&#xff0c;同时实现负载均衡。链路聚合可以在不同…

C++中ShellExecute函数使用方法说明,如果一开始参数为隐藏,后面还能再显示出来吗

文章目录 一、ShellExecute基础用法函数原型关键参数 nShowCmd示例代码&#xff1a;启动程序并隐藏窗口 二、隐藏后能否重新显示窗口直接答案 三、实现隐藏后显示窗口的步骤1. 获取目标窗口句柄2. 显示窗口 四、完整流程示例五、注意事项六、总结 在C中使用ShellExecute函数时&…

MySQL数据库精研之旅第五期:CRUD的趣味探索(上)

专栏&#xff1a;MySQL数据库成长记 个人主页&#xff1a;手握风云 目录 一、CRUD简介 二、Create新增 2.1. 语法 2.2. 示例 三、Retrieve检索 3.1. 语法 3.2. 示例 一、CRUD简介 CURD是对数据库中的记录进行基本的增删改查操作&#xff1a;Create(创建)、Retrieve(检索…

【文件上传】✈️大文件的上传服务器的简单实现

&#x1f4a5;&#x1f4a5;✈️✈️欢迎阅读本文章❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;本篇文章阅读大约耗时五分钟。 ⛳️motto&#xff1a;不积跬步、无以千里 &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff1a;&#x1f381;&#x1f381;&a…

Windows DOS窗口12个命令

DOS 命令是指在 Windows 命令提示符&#xff08;CMD&#xff09;中使用的命令行工具&#xff0c;源于早期的 Disk Operating System。虽然现代 Windows 系统更多使用图形界面&#xff0c;但命令提示符仍然是测试人员的重要工具。测试人员通常需要执行文件操作、测试网络连接、监…

AI加Python的文本数据情感分析流程效果展示与代码实现

本文所使用数据来自于梯田景区评价数据。 一、数据预处理 数据清洗 去除重复值、空值及无关字符(如表情符号、特殊符号等)。 提取中文文本,过滤非中文字符。 统一文本格式(如全角转半角、繁体转简体)。 中文分词与去停用词 使用 jieba 分词工具进行分词。 加载自定义词…

docker启动nacos+redis+seata

docker启动nacos 最新版本的nacos需要再启动的时候设置mysql的一些属性&#xff0c;【也可以先启动nacos&#xff0c;再到配置文件中找到application.yml设置mysql的一些属性】。 1.如果直接启动nacos设置的mysql我们需要确定两个容器的ip都是一样的。 查看mysql容器中的ip命令…

从 select 到 epoll:拆解 I/O 多路复用的演进与实战

目录 一、引言&#xff1a;为什么需要 I/O 多路复用&#xff1f; 二、select 1.函数介绍 2.原理 3.样例代码 4.优缺点总结 三、poll 1.函数介绍 2.样例代码 3.优缺点总结 四、epoll 1.函数介绍 2.原理 3.LT和ET两种工作模式 4.优缺点总结 五、核心机制对比&…

【力扣hot100题】(017)矩阵置零

还是挺简单的&#xff0c;使用哈希表记录需要置换的行列即可&#xff0c;这样就可以避免重复节省时间。 class Solution { public:void setZeroes(vector<vector<int>>& matrix) {unordered_set<int> row;unordered_set<int> line;for(int i0;i&l…

One Commander 3,文件管理新体验

One Commander 3 是一款集多功能于一体 Windows 10/11的文件管理工具&#xff0c;其设计目的在于为用户带来多元化的操作体验。这款工具通过支持多栏界面布局&#xff0c;让用户能够迅速且高效地组织和管理文件。此外&#xff0c;它还提供了多主题选项和多种图标集&#xff0c;…

非手性分子发光有妙招:借液晶之力,实现高不对称圆偏振发光

*本文只做阅读笔记分享* 一、圆偏振发光研究背景与挑战 圆偏振发光&#xff08;CPL&#xff09;材料在3D显示、光电器件等领域大有用处&#xff0c;衡量它的一个重要指标是不对称发光因子&#xff08;glum&#xff09;。早期CPL材料的glum值低&#xff0c;限制了实际应用。为…

YOLOv8+ Deepsort+Pyqt5车速检测系统

该系统通过YOLOv8进行高效的目标检测与分割&#xff0c;结合DeepSORT算法完成目标的实时跟踪&#xff0c;并利用GPU加速技术提升处理速度。系统支持模块化设计&#xff0c;可导入其他权重文件以适应不同场景需求&#xff0c;同时提供自定义配置选项&#xff0c;如显示标签和保存…

【干货】前端实现文件保存总结

⚠️⚠️文前推荐一下&#x1f449; 前端必备工具推荐网站(图床、API和ChatAI、智能AI简历、AI思维导图神器等实用工具): 站点入口&#xff1a;http://luckycola.com.cn/ 前端实现文件保存实现总结 在Web开发中&#xff0c;文件下载是常见的交互需求。本文将系统总结前端实现文…

并发编程之FutureTask.get()阻塞陷阱:深度解析线程池CPU飚高问题排查与解决方案

FutureTask.get方法阻塞陷阱&#xff1a;深度解析线程池CPU飚高问题排查与解决方法 FutureTask.get()方法阻塞陷阱&#xff1a;深度解析线程池CPU飚高问题排查与解决方法1、情景复现1.1 线程池工作原理1.2 业务场景模拟1.3 运行结果1.4 发现问题&#xff1a;线程池没有被关闭1.…

在Ubuntu中固定USB设备的串口号

获取设备信息 lsusb # 记录设备的Vendor ID和Product ID&#xff08;例如&#xff1a;ID 0403:6001&#xff09;# 获取详细属性&#xff08;替换X和Y为实际设备号&#xff09; udevadm info -a /dev/ttyUSBX 结果一般如下 创建udev规则文件 sudo gedit /etc/udev/rules.d/us…

javaSE————文件IO(2)、

文件内容的读写——数据流 我们对于文件操作使用流对象Stream来操作&#xff0c;什么是流对象呢&#xff0c;水流是什么样的&#xff0c;想象一下&#xff0c;水流的流量是多种的&#xff0c;可以流100ml&#xff0c;也可以流1ml&#xff0c;流对象就和水流很像&#xff0c;我…