FastAPI部署演进:从Gunicorn+Uvicorn镜像到原生多进程的迁移指南

news2026/5/13 2:19:31
1. 项目背景与演进从“黄金搭档”到“历史遗产”如果你在过去几年里用 FastAPI 部署过 Web 服务大概率听说过或者用过tiangolo/uvicorn-gunicorn-fastapi-docker这个 Docker 镜像。它一度是 FastAPI 官方文档里推荐的部署方案之一由 FastAPI 框架的作者 Sebastián Ramírez网名 tiangolo亲自维护。这个镜像的核心思路很巧妙把 Uvicorn、Gunicorn 和 FastAPI 打包在一起形成一个“开箱即用”的 Web 服务器容器。Uvicorn 是一个基于 uvloop 和 httptools 构建的闪电般快速的 ASGI 服务器专门为异步 Python Web 框架而生Gunicorn 则是一个久经沙场的 WSGI HTTP 服务器以其稳定性和进程管理能力著称。这个镜像让 Gunicorn 作为“进程管理器”来启动和管理多个 Uvicorn 工作进程从而在单个容器内实现多进程并发旨在自动榨干服务器的 CPU 性能。我最初接触这个镜像是在 2020 年当时正在将一个内部数据分析 API 从 Flask 迁移到 FastAPI。面对突如其来的流量增长单进程的 Uvicorn 显然力不从心而手动去配置 Gunicorn Uvicorn 的组合又得处理一堆关于 worker 数量、worker 类型、超时设置等令人头疼的配置项。这个镜像的出现就像一份配好的“预制菜”你只需要关心自己的业务代码它帮你搞定服务器层面的性能调优。通过环境变量如WORKERS_PER_CORE它能自动根据容器可用的 CPU 核心数来设置 worker 数量这对于在云环境或 Kubernetes 中动态伸缩的容器来说非常友好。一时间它成了很多团队快速上线 FastAPI 服务的首选。然而技术栈的演进总是出人意料。就在不久前这个曾经风光无限的镜像项目被作者正式标记为“已弃用”。仓库的 README 开头赫然写着“此 Docker 镜像现已弃用。你不再需要它了直接使用 Uvicorn 并加上--workers参数就行。” 这句话对于许多依赖此镜像的项目来说不啻为一枚重磅炸弹。但仔细想想这背后反映的正是 Python 异步生态的成熟和部署模式的最佳实践演进。Uvicorn 自身不断发展如今已经原生支持了多 worker 进程的管理包括 worker 进程崩溃后的自动重启这使得 Gunicorn 作为中间层进程管理器的价值大大降低反而引入了不必要的复杂性。因此这个镜像完成了它的历史使命从“最佳实践”变成了“历史遗产”。理解它为什么被弃用以及如何平滑地迁移到新的部署模式对于任何正在或计划使用 FastAPI 的开发者来说都是一门必修课。2. 镜像核心机制与原理解析要理解为什么可以弃用首先得弄明白这个镜像当初是怎么工作的。它的核心是一个精心编写的启动脚本和一套默认配置其设计哲学是“约定大于配置”旨在为开发者提供一个高性能、生产就绪的基线。2.1 进程架构Gunicorn 作为主进程管理者在这个镜像的典型运行模式下当你启动容器时执行的默认命令是/start.sh。这个脚本会做几件关键事情环境变量解析读取我们前面提到的MODULE_NAME、VARIABLE_NAME、WORKERS_PER_CORE、MAX_WORKERS等一系列环境变量。CPU 核心探测与 Worker 数量计算脚本会检查容器内可见的 CPU 核心数在 Docker 和 Kubernetes 中这可以通过cpu.cfs_quota_us和cpu.cfs_period_us等控制组参数来计算或者直接读取/proc/cpuinfo。然后根据WORKERS_PER_CORE默认为1的比值计算出建议的 worker 数量。这里有一个经典的“小机器优化”如果计算出的 worker 数是 1即单核机器为了确保应用不会因为单个阻塞操作而完全失去响应它会自动将 worker 数设置为 2。最后这个数值会被MAX_WORKERS环境变量限制。生成 Gunicorn 配置基于计算出的 worker 数和其他环境变量如HOST、PORT、LOG_LEVEL等动态生成或选用一个 Gunicorn 配置文件。最关键的一步是它告诉 Gunicorn 使用uvicorn.workers.UvicornWorker这个 worker 类。这意味着Gunicorn 主进程会 fork 出多个子进程每个子进程都是一个独立的 Uvicorn 服务器实例运行着你的 FastAPI 应用。启动 Gunicorn最终脚本以生成或指定的配置启动 Gunicorn 主进程。这种架构的优势在于Gunicorn 是一个非常成熟的进程管理器。它负责进程生命周期管理启动、停止、重启 worker 进程。负载均衡通过一个主进程master监听端口将接收到的请求分发给不同的 worker 进程。优雅重启在代码更新时可以做到“零停机”部署先启动新 worker再关闭旧 worker。Worker 容错如果某个 worker 进程崩溃Gunicorn 主进程会立即重启一个新的 worker。2.2 自动性能调优的玄机与局限WORKERS_PER_CORE这个设计看起来很智能但它基于一个重要的假设你的 FastAPI 应用是 CPU 密集型的或者至少 worker 进程的数量与 CPU 核心数线性相关时性能最佳。对于纯 I/O 密集型、大量使用async/await的 FastAPI 应用单个 Uvicorn worker 就能处理成千上万的并发连接盲目增加 worker 数量反而可能因为进程间上下文切换和内存开销而降低性能。这个镜像的默认策略1 worker per core是一个比较通用的起点但对于特定应用往往需要根据实际负载类型CPU-bound vs I/O-bound进行更精细的调整。另一个关键配置是TIMEOUT默认为 120 秒。这个超时是 Gunicorn 用来判断 worker 是否“僵死”的。由于 Uvicorn 是异步服务器worker 在处理一个长耗时请求时例如等待一个外部 API 响应或进行大量计算并不会阻塞事件循环去处理其他请求。因此对于 ASGI 应用这个超时时间通常可以设置得更长或者需要根据你应用中最长的同步操作来谨慎设定。2.3 被弃用的根本原因Uvicorn 的进化这个镜像被弃用的直接技术原因是 Uvicorn 从某个版本开始引入了原生的多进程支持。通过uvicorn main:app --workers 4这样的命令Uvicorn 自己就能扮演 Gunicorn 的角色启动一个主进程然后 fork 出指定数量的 worker 子进程并管理它们的生命周期。这样一来原先的架构Gunicorn (Master) - Uvicorn Workers (Child)就简化为了Uvicorn (Master) - Uvicorn Workers (Child)。减少一层抽象带来了诸多好处依赖简化不再需要安装和配置 GunicornDocker 镜像可以更轻量。配置统一所有配置端口、主机、日志级别、worker 数等都可以通过 Uvicorn 的命令行参数或配置文件统一管理心智负担更小。潜在的性能与兼容性提升少一层转发可能减少极微小的开销。更重要的是避免了 Gunicorn 与 Uvicorn 在某些高级特性或版本配合上可能出现的边缘情况。更符合容器化部署趋势在 Kubernetes 等编排系统中更推荐通过水平伸缩 Pod容器的数量来实现扩展而不是在一个容器内做垂直扩展运行多个进程。每个 Pod 运行单个应用进程是更云原生、更易于监控和管理的模式。因此Uvicorn 单 worker 模式配合 Kubernetes 的 HPA水平 Pod 自动伸缩成为了更优雅的解决方案。所以作者的建议非常明确对于大多数现代部署场景尤其是使用容器编排系统的场景你应该直接构建一个运行单 Uvicorn 进程的镜像并在集群层面管理复制和伸缩。如果确实需要在单个容器内运行多进程直接使用uvicorn --workers即可。3. 从旧镜像迁移实战指南与 Dockerfile 重构既然官方镜像已弃用对于现有项目和新项目我们应该如何行动呢下面我将分几种常见场景给出具体的迁移方案和 Dockerfile 示例。3.1 场景一全新项目或彻底迁移对于新项目或者你决定对现有项目进行彻底改造最佳实践是遵循 FastAPI 官方文档推荐的模式。新的 Dockerfile 示例# 使用官方 Python 运行时作为父镜像 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 将当前目录内容复制到容器的 /app 下 COPY ./requirements.txt /app/requirements.txt # 安装任何需要的包 RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt # 复制应用代码 COPY ./app /app # 声明容器运行时暴露的端口 EXPOSE 80 # 运行应用 CMD [uvicorn, app.main:app, --host, 0.0.0.0, --port, 80]关键变化解析基础镜像不再使用tiangolo/uvicorn-gunicorn-fastapi:python3.11而是使用官方的python:3.11-slim。slim版本基于 Debian比 Alpine 更兼容 Python 生态镜像体积也足够小。启动命令CMD 指令直接调用uvicorn并指定应用入口app.main:app、主机和端口。这是最简洁的单进程模式。依赖管理你需要确保requirements.txt中包含fastapi和uvicorn通常还有httptools、uvloop、websockets等用于性能提升的可选依赖。如果需要在单容器内使用多 worker通常不推荐用于生产但可用于特定场景或过渡FROM python:3.11-slim WORKDIR /app COPY ./requirements.txt /app/requirements.txt RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt COPY ./app /app # 通过环境变量动态设置 worker 数量默认为 1 ARG UVI_WORKERS1 ENV UVI_WORKERS${UVI_WORKERS} CMD [sh, -c, uvicorn app.main:app --host 0.0.0.0 --port 80 --workers ${UVI_WORKERS}]这里通过构建参数ARG和环境变量ENV来传递 worker 数量保持了灵活性。在构建时可以通过--build-arg UVI_WORKERS4来指定。3.2 场景二现有项目渐进式迁移对于正在使用旧镜像的生产项目直接更换基础镜像风险较大。建议采用渐进式步骤测试阶段基于新的 Dockerfile 构建一个测试镜像。在测试环境中使用相同的环境变量如数据库连接字符串启动新镜像的容器。运行完整的集成测试和压力测试对比性能指标如 RPS、延迟、错误率和资源消耗CPU、内存。配置映射旧镜像的许多环境变量HOST,PORT,LOG_LEVEL可以直接对应到 Uvicorn 的命令行参数--host,--port,--log-level。WORKERS_PER_CORE和MAX_WORKERS的逻辑需要你在启动脚本中重新实现或者干脆简化为固定值或通过其他方式如 Kubernetes 的 Resource Limit来推断。一个简单的替代脚本start.sh可能如下#!/usr/bin/env bash set -e # 计算 worker 数 (简化版仅作示例) # 你可以在这里实现更复杂的逻辑比如读取容器 CPU limit if [[ -z ${UVI_WORKERS} ]]; then # 如果未设置尝试根据 CPU 核心数计算 CPU_CORES$(grep -c ^processor /proc/cpuinfo) UVI_WORKERS$((CPU_CORES * 1)) # 默认 1 worker per core # 确保至少有一个 worker if [[ $UVI_WORKERS -lt 1 ]]; then UVI_WORKERS1 fi fi exec uvicorn app.main:app --host 0.0.0.0 --port 80 --workers ${UVI_WORKERS} --log-level ${LOG_LEVEL:-info}将prestart.sh脚本的逻辑如数据库迁移保留并在新的 Dockerfile 中通过RUN chmod x /app/prestart.sh确保其可执行在 CMD 中先执行它。部署与回滚在准生产环境进行蓝绿部署或金丝雀发布用小部分流量验证新镜像的稳定性。务必准备好快速回滚到旧镜像的方案。3.3 依赖管理的进阶技巧使用 Poetry原镜像文档中提到了使用 Poetry 管理依赖这是一个非常好的实践可以确保依赖版本的精确锁定。在新的 Dockerfile 中我们依然可以沿用多阶段构建来优化# 第一阶段导出依赖 FROM python:3.11-slim as requirements-stage WORKDIR /tmp # 安装 poetry RUN pip install poetry # 复制依赖声明文件 COPY ./pyproject.toml ./poetry.lock* /tmp/ # 导出 requirements.txt排除哈希值为了更好的缓存 RUN poetry export -f requirements.txt --output requirements.txt --without-hashes # 第二阶段构建最终镜像 FROM python:3.11-slim WORKDIR /app # 从第一阶段复制生成的 requirements.txt COPY --fromrequirements-stage /tmp/requirements.txt /app/requirements.txt # 安装依赖 RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt # 复制应用代码 COPY ./app /app # 复制并赋予 prestart 脚本执行权限 COPY ./prestart.sh /app/ RUN chmod x /app/prestart.sh EXPOSE 80 # 启动命令先执行 prestart 脚本再启动应用 CMD [sh, -c, /app/prestart.sh uvicorn app.main:app --host 0.0.0.0 --port 80]这种多阶段构建能充分利用 Docker 的层缓存。当你只修改了应用代码/app目录下的文件而没有更改pyproject.toml或poetry.lock时构建过程会直接使用之前缓存的依赖安装层极大加快构建速度。4. 深入对比新旧方案下的配置与调优迁移不仅仅是换个启动命令围绕性能、日志、生命周期的配置思路也需要更新。下面我们用一个对比表格来梳理关键配置项的变化配置项旧方案 (Gunicorn Uvicorn Workers)新方案 (Uvicorn with--workers)说明与建议进程管理Gunicorn 主进程管理 Uvicorn worker 子进程Uvicorn 主进程管理 worker 子进程本质都是 prefork 模型管理者换了。Uvicorn 自带的进程管理已足够稳定。Worker 数量通过WORKERS_PER_CORE、MAX_WORKERS、WEB_CONCURRENCY等环境变量计算直接通过--workers命令行参数指定简化了。在生产中更推荐结合容器编排器如 K8s的 HPA根据 CPU/内存使用率动态调整 Pod 数量而非容器内 worker 数。Worker 类worker-class uvicorn.workers.UvicornWorker(在 Gunicorn 配置中)内置无需指定省去一个配置项。绑定地址通过HOST和PORT环境变量或BIND变量通过--host和--port参数几乎一致只是传递方式不同。日志配置通过LOG_LEVEL、ACCESS_LOG、ERROR_LOG环境变量控制 Gunicorn 日志通过--log-level参数控制 Uvicorn 日志级别。访问日志和错误日志默认输出到 stdout/stderr。Uvicorn 的日志配置稍简单。对于复杂的日志路由如按级别输出到不同文件可能需要自定义日志配置或在容器外使用日志收集器如 Fluentd。优雅关闭与超时graceful_timeout,timeout(Gunicorn 配置)--timeout-graceful-shutdown(Uvicorn 参数)Uvicorn 的--timeout-graceful-shutdown参数控制了在收到终止信号后等待正在处理的请求完成的最长时间。对于同步长任务需要合理设置。Keep-Alivekeepalive(Gunicorn 配置)由 ASGI 服务器内部管理在 HTTP/1.1 中Keep-Alive 是默认行为通常无需特别配置。自定义配置通过GUNICORN_CONF环境变量指定 Python 配置文件通过--config参数指定 Python 配置文件 (e.g.,uvicorn.config.Config)Uvicorn 也支持通过 Python 文件进行更复杂的配置例如设置 SSL、中间件、生命周期事件处理器等。开发热重载使用/start-reload.sh脚本运行单进程 Uvicorn 并开启--reload直接使用uvicorn ... --reload完全一致。在开发时通过挂载代码卷并添加--reload参数即可。调优经验分享Worker 数量不是越多越好这是从旧镜像迁移后最容易犯的错误。以前依赖WORKERS_PER_CORE的自动计算现在需要自己设定。对于 I/O 密集型 FastAPI 应用大多数场景从--workers 1开始测试往往是更好的选择。增加 worker 主要为了利用多核 CPU 处理 CPU 密集型任务或者规避 Python 的 GIL 对某些同步阻塞操作的影响。使用工具如locust或wrk进行压测观察不同 worker 数量下的 RPS 和延迟曲线找到性能拐点。关注内存开销每个 Uvicorn worker 进程都会加载一份完整的应用代码和依赖。如果你的应用内存占用很大例如加载了大型机器学习模型增加 worker 数会线性增加内存消耗。务必确保容器内存限制足够并监控内存使用情况。超时设置需谨慎Uvicorn 的--timeout-graceful-shutdown默认为 30 秒。如果你的应用有长时间运行的同步端点如下载大文件、复杂计算需要适当调大此值否则在滚动更新或缩容时这些请求会被强制中断。日志聚合至关重要无论是旧方案还是新方案在容器化部署中将每个容器的 stdout/stderr 日志集中收集、索引和查询使用 ELK Stack、Loki 等是运维的基石。不要依赖写入容器内部文件。5. 常见问题排查与实战避坑指南在实际迁移和运维过程中你肯定会遇到一些坑。下面是我和团队在多个项目中总结出来的常见问题及解决方案。5.1 启动失败ModuleNotFoundError 或 ImportError问题描述使用新镜像后启动容器报错ModuleNotFoundError: No module named app或ImportError: cannot import name app from app.main。根因分析工作目录不对旧镜像默认工作目录是/app并且期望应用模块在/app/app/main.py或/app/main.py。如果你的代码结构不同需要通过WORKDIR和 CMD 中的模块路径来调整。PYTHONPATH 问题Docker 容器内的 Python 解释器可能找不到你的模块。旧镜像可能设置了一些默认的 Python 路径而新镜像没有。解决方案检查你的 Dockerfile 中的WORKDIR指令是否与代码复制目标目录一致。在 CMD 中使用绝对路径导入模块或者确保当前工作目录正确。例如如果你的应用入口是/code/src/main.py中的app对象且WORKDIR是/code那么 CMD 应该是[uvicorn, src.main:app, ...]。一个更稳妥的方法是在 Dockerfile 中显式设置PYTHONPATHENV PYTHONPATH/app WORKDIR /app COPY . /app CMD [uvicorn, main:app, --host, 0.0.0.0]5.2 性能下降或内存异常增长问题描述迁移后在相同压力下应用的响应时间变长或者容器内存使用量不断上升直至被 OOM Kill。根因分析Worker 数量配置不当这是最常见的原因。新方案中--workers设得过高比如等于 CPU 核数对于 I/O 密集型应用反而导致过多的进程切换开销和内存占用。同步阻塞操作如果你的 FastAPI 路径操作函数中包含了未使用async def定义的同步函数并且内部有阻塞性 I/O 或计算它会阻塞整个事件循环。在单 worker 下这会导致其他请求排队在多 worker 下情况可能稍好但每个 worker 仍会被阻塞。旧镜像中 Gunicorn 的同步 worker 模型对这类代码容忍度稍高但性能也差而 Uvicorn 的异步特性会放大这类问题。内存泄漏可能与应用代码有关与服务器无关。但切换服务器后某些资源释放的时机可能不同。解决方案Worker 调优从--workers 1开始压测。使用docker stats或 Kubernetes 监控观察 CPU 和内存使用率。如果单个 worker 的 CPU 使用率持续高于 70%-80%且负载较高可以考虑逐步增加 worker 数并观察整体吞吐量是否提升。通常workers CPU核心数 * 1到CPU核心数 * 2是一个经验范围但务必以实际测试为准。代码异步化检查性能热点接口。将同步的def函数改为async def并在内部 I/O 操作中使用await调用异步客户端如httpx.AsyncClient,asyncpg,aiomysql。对于无法异步化的 CPU 密集型任务考虑使用fastapi.BackgroundTasks或将其丢到线程池中执行asyncio.to_thread避免阻塞事件循环。排查内存泄漏使用tracemalloc或objgraph等工具在开发环境进行内存分析。确保数据库连接、HTTP 会话等资源在使用后正确关闭或归还到连接池。5.3 健康检查与就绪探针失败问题描述在 Kubernetes 中Pod 的 Readiness/Liveness Probe 配置在迁移后开始失败导致服务不可用或不断重启。根因分析响应格式变化旧镜像可能内置了特定的健康检查端点或者 Gunicorn 对某些请求的处理方式与 Uvicorn 有细微差别。启动速度差异Uvicorn 的启动速度可能和 Gunicorn 不同导致 Kubernetes 在应用完全准备好之前就开始发送探针请求。探针配置未更新Kubernetes 的探针配置如initialDelaySeconds,periodSeconds,timeoutSeconds可能针对旧镜像的启动特性进行了优化不适用于新镜像。解决方案为 FastAPI 应用显式添加健康检查端点这是最佳实践。from fastapi import FastAPI from fastapi.responses import JSONResponse app FastAPI() app.get(/health) async def health_check(): # 这里可以添加更复杂的健康检查逻辑如数据库连接测试 return JSONResponse(content{status: healthy}, status_code200) app.get(/ready) async def readiness_check(): # 就绪检查可能依赖外部服务状态 # if not database.is_connected(): # raise HTTPException(status_code503, detailDatabase not ready) return JSONResponse(content{status: ready}, status_code200)更新 Kubernetes 部署配置apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: - name: my-api livenessProbe: httpGet: path: /health port: 80 initialDelaySeconds: 30 # 根据实际启动时间调整 periodSeconds: 10 timeoutSeconds: 5 # Uvicorn 默认响应很快可以设短一点 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 80 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 1关键是根据新镜像的启动日志合理调整initialDelaySeconds确保探针在应用真正准备好之后才开始执行。5.4 连接池与数据库连接耗尽问题描述迁移到多 worker 模式后数据库出现Too many connections错误。根因分析这是最经典的“踩坑”场景。旧镜像中你可能在应用启动时创建了一个全局的数据库连接池。当 worker 数量为 1 时只有一个连接池实例。当使用--workers 4后会启动 4 个独立的进程每个进程都会执行启动代码创建自己的连接池。如果每个池都设置了 10 个连接那么总共就会建立 40 个数据库连接很容易超过数据库的最大连接数限制。解决方案使用外部连接池服务对于像 PostgreSQL 这样的数据库可以考虑使用 PgBouncer 这样的外部连接池让多个应用 worker 共享一个物理连接池。降低每个 worker 的连接池大小在应用代码中根据 worker 数量动态计算每个 worker 应分配的最大连接数。例如max_connections_per_worker total_db_connections_allowed / number_of_workers。但这需要你确切知道部署时的 worker 数量不够灵活。采用更云原生的思路这是最推荐的做法。既然每个容器Pod运行多个 worker 会带来连接管理的复杂性不如回归本质每个容器只运行一个 Uvicorn worker 进程。然后通过 Kubernetes Horizontal Pod Autoscaler (HPA) 来水平扩展 Pod 的数量。这样每个 Pod 内的应用实例都拥有独立且固定的资源视图包括数据库连接池扩展和收缩都更清晰、更符合 Kubernetes 的设计哲学。数据库连接池的总数就是(连接池大小) * (Pod 数量)易于预估和管理。5.5 Alpine 镜像的兼容性问题警告旧镜像提供了 Alpine 标签但新方案强烈不建议使用 Alpine 作为 Python 应用的基础镜像。原因在原始文档中已经说得很清楚Alpine 使用 musl libc 而非 glibc且缺乏许多构建 Python 扩展包所需的底层库。这会导致pip install时大量依赖需要从源码编译不仅构建速度极慢、镜像体积未必更小还经常出现各种奇怪的兼容性错误。解决方案坚持使用python:3.11-slim或python:3.11作为基础镜像。slim版本基于 Debian删除了许多非必要文件是体积和兼容性之间的最佳平衡。如果你对镜像体积有极致要求可以尝试多阶段构建并在最终阶段使用scratch或alpine来运行编译好的二进制但这对于 Python Web 应用来说通常收益不大且复杂度陡增。迁移本身是一次架构简化的机会。tiangolo/uvicorn-gunicorn-fastapi-docker镜像的退役标志着一个时代的结束也预示着 Python ASGI 部署进入了更成熟、更简单的阶段。拥抱变化理解其背后的原理才能构建出更稳健、更易维护的系统。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…