avatar

NeoBlog

Neo的个人博客

  • 首页
  • 三千问道
  • 万法归宗
  • 诗酒田园
  • 关于NeoBlog
主页 本地大模型分离部署逻辑推理服务器实战(下)
文章

本地大模型分离部署逻辑推理服务器实战(下)

发表于 最近 更新于 最近
作者 Neo
14~19 分钟 阅读

3、异步升级思路:

Python环境加入Redis:

pip install redis

使用AioRedis消除线程阻塞,配合FastAPI 实现高并发,Nginx禁用缓冲及超长等待,Python加入心跳保活,前端防止SSE响应并加入重连

2、代码升级:

异步服务层:

发送请求:

import redis.asyncio as redis
import json
import logging

logger = logging.getLogger(__name__)

class AsyncRedisStreamService:
    def __init__(self, host='localhost', port=6379, db=0):
        self.redis_client = redis.Redis(host=host, port=port, db=db, decode_responses=True)

    async def publish_chunk(self, task_id: str, chunk: str, status: str = "processing"):
        """异步发布推理片段"""
        channel = f"stream:{task_id}"
        message = json.dumps({"task_id": task_id, "chunk": chunk, "status": status})
        await self.redis_client.publish(channel, message)

    async def get_pubsub(self):
        """获取异步 PubSub 对象"""
        return self.redis_client.pubsub()

    async def close(self):
        await self.redis_client.aclose()

利用 aio-pika 和 aioredis 实现全异步链路:

import aio_pika
import json
import asyncio
from langchain_community.llms import Ollama
from app.services.async_redis_service import AsyncRedisStreamService

llm = Ollama(model="llama3")
redis_service = AsyncRedisStreamService()


async def process_task(message: aio_pika.IncomingMessage):
    async with message.process():
        body = json.loads(message.body.decode())
        task_id = body['task_id']

        try:
            # LangChain 异步流式推理
            async for chunk in llm.astream(f"请总结:{body['cleaned_content']}"):
                await redis_service.publish_chunk(task_id, chunk)

            await redis_service.publish_chunk(task_id, "", status="completed")
        except Exception as e:
            await redis_service.publish_chunk(task_id, str(e), status="failed")


async def main():
    connection = await aio_pika.connect("amqp://guest:guest@localhost/")
    channel = await connection.channel()
    queue = await channel.declare_queue("llm_inference_queue", durable=True)
    await queue.consume(process_task)
    print("Async Consumer Started...")
    await asyncio.Future()


if __name__ == "__main__":
    asyncio.run(main())

异步接口层:

import json
from fastapi import APIRouter, BackgroundTasks, HTTPException
from app.schemas.models import CleanRequest, InferenceResult
from app.services.producer import process_and_publish_task
from app.services.state_store import task_results
from fastapi.responses import StreamingResponse
from app.services.async_redis_service import AsyncRedisStreamService

import asyncio
router = APIRouter()

@router.post("/clean-and-submit")
async def submit_task(request: CleanRequest, background_tasks: BackgroundTasks):
    try:
        task_id = await process_and_publish_task(request)
        return {"task_id": task_id, "status": "submitted"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/result/{task_id}", response_model=InferenceResult)
def get_result(task_id: str):
    if task_id not in task_results:
        raise HTTPException(status_code=404, detail="Task not found")
    data = task_results[task_id]
    return InferenceResult(task_id=task_id, status=data["status"], result=data.get("result"))


@router.get("/stream/{task_id}")
async def stream_result(task_id: str):
    async def event_generator():
        service = AsyncRedisStreamService()
        pubsub = await service.get_pubsub()
        await pubsub.subscribe(f"stream:{task_id}")
        last_heartbeat = asyncio.get_event_loop().time()

        try:
            async for message in pubsub.listen():
                if message['type'] == 'message':
                    data = json.loads(message['data'])
                    yield f"data: {json.dumps(data)}\n\n"
                    last_heartbeat = asyncio.get_event_loop().time()

                    if data.get('status') in ['completed', 'failed']:
                        break

                # 心跳保活:若 15 秒无数据,发送注释行防止连接断开
                elif asyncio.get_event_loop().time() - last_heartbeat > 15:
                    yield ": heartbeat\n\n"
                    last_heartbeat = asyncio.get_event_loop().time()

        except asyncio.CancelledError:
            pass
        finally:
            await pubsub.unsubscribe(f"stream:{task_id}")
            await service.close()

    return StreamingResponse(
        event_generator(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no"  # 关键:禁用 Nginx 缓冲
        }
    )

Nginx配置文件优化:

location /api/v1/stream/ {

    proxy_pass http://127.0.0.1:8000;

    
    # 关键配置:禁用代理缓冲

    proxy_buffering off;

    
    # 确保 HTTP 版本支持长连接

    proxy_http_version 1.1;

    proxy_set_header Connection "";
  

    # 增加超时时间,适应长时推理任务

    proxy_read_timeout 3600s;

    proxy_send_timeout 3600s;

    
    # 禁用 gzip 压缩,避免分块传输编码冲突

    gzip off;

}

前端优化:

在HTTP Header加入

Cache-Control: no-cache

前端处理SSE时,加入自动重连:


const eventSource = new EventSource('/api/v1/stream/123');

// 设置重连时间为 3 秒

eventSource.onopen = () => {

    console.log('Connection opened');

};

eventSource.onmessage = (event) => {

    const data = JSON.parse(event.data);

    updateUI(data);

};

eventSource.onerror = (err) => {

    console.error('Connection lost, reconnecting...', err);

};

后端指定重连时间:

yield "retry: 3000\n\n"

万法归宗
AI
许可协议:  CC BY 4.0
分享

相关文章

7月 25, 2026

本地大模型分离部署逻辑推理服务器实战(下)

3、异步升级思路: Python环境加入Redis: pip install redis 使用AioRedis消除线程阻塞,配合FastAPI 实现高并发,Nginx禁用缓冲及超长等待,Python加入心跳保活,前端防止SSE响应并加入重连 2、代码升级:</

7月 25, 2026

本地大模型分离部署逻辑推理服务器实战(上)

1、前置准备: Python环境: Conda python3.10.20 Python包基础依赖: fastapi==0.109.0 uvicorn==0.27.0 pika==1.3.2 pydantic==2.5.3 pydantic-settings==2.1.0 RabbitMQ 环境:

7月 20, 2026

window10安装最新的Erlang 和RabbitMq

1、版本兼容: 打开https://www.rabbitmq.com/docs/which-erlang看需要安装的RabbitMq和Erlang版本兼容。 2、下载Erlang

下一篇

本地大模型分离部署逻辑推理服务器实战(上)

上一篇

最近更新

  • 本地大模型分离部署逻辑推理服务器实战(下)
  • 本地大模型分离部署逻辑推理服务器实战(上)
  • window10安装最新的Erlang 和RabbitMq
  • 安装RabbitMQ服务名无效的解决方案
  • Python的快速学习总结

热门标签

散文 AI

©2026 All Rights Reserved Neo 鲁ICP备2026037083号