Flask+Django双框架实战:从零构建游戏攻略社区论坛系统
发布时间:2026/9/12 17:56:44 锦皓数字建站

这个游戏攻略社区论坛交流系统是我在完整自学 Python Web 开发阶段从零做出来的一个线上项目。很多人看到标题里同时出现 Flask 和 Django 时都会问一句这两个框架不是互相竞争的吗为什么放在一起我最初也有这个疑惑直到真正动手设计时才发现把项目拆成“内容主站”和“辅助采集服务”两块之后两个框架各管一摊反而比硬塞进单一方框舒服得多。Django 负责论坛核心业务——用户体系、版块、帖子、评论、点赞、通知Flask 扛下游戏资讯采集、定时生成热门榜单、给主站提供内部管理接口。这篇文章我会把整套系统的需求拆解、技术选型、数据库设计、核心功能实现和上线部署链路全部写出来帖子发布、多级评论、收藏点赞、热门排序、消息通知这些功能都会给出具体实现思路和关键代码。如果你正在做社区交流类项目或者毕设题目正好是“XXX论坛系统”这篇内容可以帮你省掉很多试错时间。不用怀疑这个项目没有用到任何花哨的前沿框架就是老老实实的 Python Web 工程。但正因为基础它把社区类系统从 0 到 1 会遇到的典型问题都覆盖了一遍。下面我按设计和实现顺序展开。1. 项目需求拆解先弄明白社区论坛的主链路是什么做系统设计的第一个大忌是一上来就画架构图、建表。我在这个项目里最开始的版本就是先建了十几张表结果写到一半发现业务流程根本跑不通。后来推倒重来才总结出一条经验先定义清楚用户在这个系统里“走完一个完整动作”的路径再谈功能再谈表和代码。1.1 用户角色与权限边界一个综合游戏攻略社区表面上用户都是“浏览帖子的人”实际上至少要区分出三类角色游客只能浏览公开内容和帖子列表不能发帖、不能评论。游戏攻略这种内容对外可见才能带来自然流量。注册用户可以发帖、编辑自己的帖子、评论、回复、点赞、收藏、关注板块。发帖和评论是社区内容生产的核心来源所以必须登录后才能操作。版主与管理员管理员负责用户管理、板块新建/删除、帖子置顶和删除版主只负责自己分管板块内的内容审核、违规帖处理。权限边界在设计时可以简化为一张矩阵资源归属者为“本人”时拥有编辑/删除权限角色为“版主”时对分管板块内容有管理权限管理员是最终兜底。这个矩阵不需要一开始就做很复杂只要在视图层加一层校验工具就能覆盖大部分场景。1.2 功能清单的取舍逻辑我把完整功能清单简化成下面三档开发时严格按照优先级来做优先级功能模块具体内容P0 核心用户体系注册、登录、退出、个人资料、头像上传P0 核心版块与帖子游戏板块分类、帖子发布/编辑/删除、列表分页P0 核心互动行为评论、多级回复、点赞、收藏P1 增强内容组织帖子搜索、标签、热门排序、置顶P1 增强消息机制被评论、被回复、被点赞时站内通知P2 远期体验扩展签到、积分、关注用户、私信一期全部不做很多人看到这里会觉得“功能太少”。但我要说的是论坛系统的核心难点从来不在功能数量而在于每个功能都要和用户状态、权限、数据一致性绑在一起。P0 功能全部打磨好系统已经能作为一个完整作品展示了P1 的热门排序和通知会让系统有“活”的感觉P2 那些不做是为了避免在无限加功能的过程中把项目拖垮。1.3 需求阶段直接用故事板代替需求文档我没有写冗长的需求文档而是画了几条用户故事板游客在首页看到游戏版块列表 → 点进《原神》版块 → 看到帖子列表 → 点开帖子看攻略内容 → 想评论但发现未登录 → 注册/登录 → 回到原帖成功发表评论。注册用户进入个人中心 → 点击“发布攻略” → 填标题、选版块、写内容、传图片 → 点击发布 → 帖子出现在版块列表中并标记为最新。管理员登录后台 → 收到用户举报 → 在管理页面删掉违规帖子并通知作者。这三条链路贯穿了整个开发过程所有数据库表和视图函数都围绕它们展开。比空对空画用例图、画时序图要直观得多也更容易在写代码时保持方向感。2. Flask 和 Django 的框架分工选型不是二选一而是按业务切分“python-flask-django”这个标题在很多人看来可能是“或”的关系但这套系统里它们确实是“和”的关系。这里我多说几句选型逻辑因为这是整篇内容最容易被忽略、但实际最影响开发效率的部分。2.1 两个框架在社区场景下的真实差异Django 和 Flask 的底层都是 Python但设计哲学完全不同Django 是“全家桶”。自带 ORM、Admin 后台、认证系统、表单处理、中间件、信号机制。对论坛这类 CRUD 占比极高的业务系统来说Django 的 Model、View、Template 三层结构能把代码组织的非常规整。项目里最明显的好处是后台管理功能几乎不用自己写Django Admin 加几行注册代码就能管理用户、帖子、评论版主后台需求直接被覆盖省下了大量重复劳动。Flask 是“微框架”。它只给你路由和请求处理的最小内核数据库、表单、登录都要自己拼。优势是灵活、进程极少依赖、内存占用低非常适合跑“旁路任务”。后续我做的游戏资讯采集脚本如果用 Django 启动需要加载整个项目配置而用 Flask 写一个轻量服务只要几行代码就能把数据整理好再通过 HTTP 或数据库交给主站。但如果你要在二选一里直接做社区系统我反倒推荐 Django。原因很简单社区类系统几乎离不开后台管理、用户认证、ORM 关系映射这三座大山Django 全都内置了Flask 不是不能做而是这些基础能力都要自己找方案并拼装起来同样功能实现成本会高出不少。2.2 最终架构Django 主站 Flask 辅助服务在调研了游戏的资讯发布机制后我确定了双服务架构Django 应用提供论坛主站包含用户访问的所有页面和 API。运行在 Gunicorn 上监听 8000 端口。Flask 应用只跑一个独立服务负责抓取部分游戏新闻、按小时生成热门攻略榜单、提供内部管理接口。运行在另一个 Gunicorn 实例上监听 8001 端口。Django 主站不直接依赖 Flask。Flask 把采集到的资讯清洗后写入同一张资讯表如果是需要人工确认的内容则写入待审核表。用户请求主站页面时Django 从数据库直接读取数据展示。两个服务完全解耦Flask 进程挂了主站不受任何影响最多就是资讯更新停摆。这个架构的另一个优势是资源隔离。爬虫脚本如果放在 Django 进程里一旦遇到性能极差的第三方站点主站响应也会被拖慢。拆到独立 Flask 服务后超时、重试、并发参数都可以单独调优而且不会占用主站的数据库连接池。2.3 完整技术栈清单项目最终跑起来的技术栈如下Python 3.10Django 4.2Flask 2.3MySQL 8.0字符集 utf8mb4支持表情符号Redis 7缓存热点数据、存储通知未读数Bootstrap 5 简单原生 JavaScript界面层不做前后端分离Gunicorn Nginx部署宝塔面板辅助管理服务器这里要说一句很多教程喜欢一上来就推前后端分离加 Vue但对一个以内容展示和 SEO 为重的攻略社区来说服务端渲染反而更有优势搜索引擎能直接抓取帖子内容帖子详情页的打开速度也更快。前后端分离适合管理后台和频繁交互的模块而社区帖子的浏览链路更适合传统服务端渲染。我实际采用了混合方案前台页面用 Django 模板渲染部分异步操作点赞、收藏、通知加载用小接口返回 JSON。3. 工程目录与数据模型设计阶段必须盯紧的细节3.1 项目目录划分双服务架构下目录结构一开始就要划分好不然后面部署时会找不着北。我用的是多项目共存方案根目录下放两个独立子项目game_community/ ├── django_site/ # Django 主站项目 │ ├── manage.py │ ├── config/ # 项目配置目录 │ │ ├── settings/ │ │ ├── urls.py │ │ └── wsgi.py │ └── apps/ │ ├── users/ # 用户模块 │ ├── boards/ # 版块模块 │ ├── posts/ # 帖子模块 │ ├── comments/ # 评论模块 │ └── interactions/ # 点赞、收藏模块 ├── flask_service/ # Flask 辅助服务 │ ├── app.py # Flask 应用入口 │ ├── collector/ # 资讯采集 │ ├── hot_rank.py # 热门榜单生成 │ └── api.py # 内部管理接口 ├── requirements.txt └── deploy/ ├── gunicorn.conf.py └── nginx.confDjango 这边我用的是多 app 结构而不是单 app 堆文件。好处在于模块边界清晰users 里的代码不会和 posts 混在一起后期要拆成微服务时每个 app 都能快速独立。Flask 服务目录保持极度精简因为辅助服务不需要复杂的 ORM 和模板引擎。采集脚本、热榜生成、管理 API 分别独立别人接手时扫一眼目录就知道它承担什么职责。3.2 核心数据模型代码数据模型是论坛系统的地基。我简化了部分字段保留最能体现设计思路的核心模型from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): avatar models.ImageField(upload_toavatars/, blankTrue) bio models.CharField(max_length200, blankTrue, verbose_name个人简介) # 扩展 Django 自带的 User避免另起炉灶 class Meta: db_table user class Board(models.Model): name models.CharField(max_length50, uniqueTrue, verbose_name版块名) slug models.SlugField(uniqueTrue) description models.TextField(blankTrue) icon models.CharField(max_length100, blankTrue) sort_order models.IntegerField(default0, verbose_name排序权重) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table board ordering [sort_order] class Post(models.Model): STATUS_CHOICES ( (draft, 草稿), (published, 已发布), (reviewing, 待审核), (deleted, 已删除), ) title models.CharField(max_length200, verbose_name标题) content models.TextField(verbose_name内容) board models.ForeignKey(Board, on_deletemodels.CASCADE, related_nameposts, verbose_name所属版块) author models.ForeignKey(User, on_deletemodels.CASCADE, related_nameposts, verbose_name作者) status models.CharField(max_length20, choicesSTATUS_CHOICES, defaultpublished) is_top models.BooleanField(defaultFalse, verbose_name是否置顶) view_count models.IntegerField(default0, verbose_name浏览量) like_count models.IntegerField(default0, verbose_name点赞数) comment_count models.IntegerField(default0, verbose_name评论数) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) class Meta: db_table post ordering [-is_top, -created_at] indexes [ models.Index(fields[board, -created_at]), models.Index(fields[-created_at]), ] class Comment(models.Model): post models.ForeignKey(Post, on_deletemodels.CASCADE, related_namecomments, verbose_name所属帖子) author models.ForeignKey(User, on_deletemodels.CASCADE, related_namecomments, verbose_name评论者) parent models.ForeignKey(self, on_deletemodels.CASCADE, nullTrue, blankTrue, related_namereplies, verbose_name父评论) content models.TextField(verbose_name评论内容) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table comment ordering [created_at] class Favorite(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, related_namefavorites) post models.ForeignKey(Post, on_deletemodels.CASCADE, related_namefavorited_by) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table favorite constraints [ models.UniqueConstraint(fields[user, post], nameunique_user_post_fav) ]这几个模型基本撑起整个系统。用户模型继承 AbstractUser 而不是新建独立 User可以复用 Django 自带的密码哈希、权限组、登录状态管理少写很多安全相关的代码。Post 表里冗余了 like_count 和 comment_count虽然违反了一点“数据库范式”但这是为了列表页展示时不用实时聚合统计性能好得多。3.3 关系设计里的四个关键决策第一帖子状态字段比直接删除更安全。用户在社区里的帖子如果直接物理删除评论、点赞关系全断管理员也看不到违规证据。我所有“删除操作”都不会触发数据库 delete而是把 status 置为 deleted列表查询自动过滤。第二嵌套评论使用 parent 自关联而不是单独回复表。一级评论 parent 为 null二级评论 parent 指向一级评论。查询时一次取回所有数据再用递归在内存里构建树。这样表结构简单数据量不大时性能完全够用。第三版块和帖子用外键关联但查询时用 select_related 控制查询次数。Django ORM 默认惰性查询如果在列表页直接遍历 post.board.name会产生 N1 查询问题。这也是新手最容易犯的错后面踩坑部分我专门展开。第四热搜词、标签关系不单独建表。初期标签直接存在 Post 表里的 JSON 字段中查询用字符串匹配。等帖子量级到十万以上再考虑标签表和中间表。凡是能延后的复杂设计都延后这是项目能按期上线的关键。4. 核心功能实现从账号体系到热门排序4.1 用户注册登录与权限校验用户模块我直接用 Django 内置的认证视图和表单再覆盖掉默认的 User 模型增加头像和个人简介字段。注册逻辑需要注意两点密码不能明文存储。Django 的 AbstractUser 自带 PBKDF2 密码哈希只要不自己写存密码的逻辑就行。注册成功要自动登录。不然用户注册完还要去登录页输一遍账号密码体验很割裂。权限校验我封装了两个 mixinfrom django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin class UserLoginRequired(LoginRequiredMixin): login_url /login/ redirect_field_name next class AuthorOrModeratorRequired(UserPassesTestMixin): def test_func(self): post self.get_object() user self.request.user if post.author user: return True if user.is_staff or user.is_superuser: return True if user.is_authenticated and user.has_perm(boards.can_manage_board, post.board): return True return False用 LoginRequiredMixin 保证所有发布类操作必须登录用 AuthorOrModeratorRequired 保证只有作者、版主、管理员能编辑和删除帖子。装饰器写在类视图上代码非常干净。Flask 服务的鉴权我不走登录态直接用 Token。内部管理接口在请求头校验一个固定的 API TokenDjango 主站调用时带上这个 Token 即可。这样两套服务完全不需要共享 Session避免跨域和 cookie 域名的麻烦。4.2 帖子发布、富文本编辑与图片上传攻略帖不是普通微博内容需要代码块、标题分级、图片所以发布页必须用富文本编辑器。我选了轻量级的 wangeditor配合一个简单的图片上传接口。这里水非常深最主要的问题是XSS 注入。如果直接把富文本编辑器输出的 HTML 存进数据库再原样渲染到页面上用户可以随便插 script 标签往你网站上跑脚本。我的处理方式是在后端做三层过滤入库前用 bleach 库把脚本标签、事件属性、iframe 全部清洗掉只保留白名单标签。输出时模板自动转义 HTML只在需要渲染富文本的位置用|safe过滤器。上传的图片重命名不允许 SVG 格式SVG 里可以放脚本尽量只允许 jpg、png、webp。图片上传的实现要点是文件大小限制和路径分离def upload_image(request): if request.method POST and request.FILES.get(file): img request.FILES[file] if img.size 5 * 1024 * 1024: return JsonResponse({error: 图片不能超过 5MB}, status400) ext os.path.splitext(img.name)[-1].lower() if ext not in [.jpg, .jpeg, .png, .webp]: return JsonResponse({error: 不支持的图片格式}, status400) filename fpost_{int(time.time())}_{uuid.uuid4().hex[:8]}{ext} filepath os.path.join(settings.MEDIA_ROOT, post_images, filename) with open(filepath, wb) as f: for chunk in img.chunks(): f.write(chunk) url settings.MEDIA_URL post_images/ filename return JsonResponse({url: url})文件名用时间戳加 uuid 随机串绝不使用用户原始文件名避免路径穿越和中文乱码。4.3 嵌套评论的实现与计数冗余评论区我采用“一次查出全部评论在内存中构建树形结构”的策略。这样数据库只需一次查询渲染时再递归生成 HTMLdef build_comment_tree(comments): tree [] mapping {} for c in comments: c.children [] mapping[c.id] c for c in comments: if c.parent_id and c.parent_id in mapping: mapping[c.parent_id].children.append(c) else: tree.append(c) return tree这套实现的关键是所有二楼回复都挂在对应一级评论下前端通过缩进表示层级。评论计数不靠实时 count而是发布评论后对 Post 表的 comment_count 做 F 表达式原子更新def post_comment(request, post_id): post Post.objects.select_for_update().get(idpost_id) comment Comment.objects.create( postpost, authorrequest.user, parent_idrequest.POST.get(parent_id) or None, contentrequest.POST.get(content) ) Post.objects.filter(idpost.id).update( comment_countmodels.F(comment_count) 1 ) # 触发通知见 4.4 return JsonResponse({ok: True, comment_id: comment.id})用 F 表达式而不是先读后写是为了避免并发评论时计数丢更新。select_for_update 加行锁保证评论和计数一致。评论数只是展示用的冗余值即使极端情况下有小误差也不会影响主业务。4.4 搜索、热门排序与消息通知搜索我分了两层实现。标题和内容的关键词搜索首页用 Django ORM 的 Q 对象组合查询先满足 90% 的需求results Post.objects.filter( models.Q(title__icontainskeyword) | models.Q(content__icontainskeyword), statuspublished ).select_related(author, board)[:30]当帖子量级上来后再用 MySQL 的全文索引或引入 Elasticsearch。关键是不要在初期就背着 Elasticsearch 这个重包袱先用简单方案跑通。热门排序是社区类系统的灵魂。我设计了一个热度公式热度分 浏览量 * 0.3 点赞数 * 3 评论数 * 5最后乘一个时间衰减系数。具体实现是让 Flask 服务每小时跑一次榜单任务把结果写进 RedisDjango 首页直接从 Redis 取榜单热度分 (view_count * 0.3 like_count * 3 comment_count * 5) / pow((当前时间 - 发布时间).days 2, 1.2)时间衰减用平方根指数老帖子不会永远占据首位也不会瞬间掉出榜单。这个公式调到“新帖有机会冲到前排高质量老帖也有保留”的状态大概花了三天时间。消息通知我用 Django 的 signal 机制实现。Comment 发送 post_save 信号后判断目标用户不是当前用户就生成一条 Notification 记录。列表页通过 Redis 缓存未读数用户点开消息中心时再一次性查询并清零。receiver(post_save, senderComment) def notify_comment(sender, instance, created, **kwargs): if not created: return post instance.post if instance.parent and instance.parent.author_id ! instance.author_id: target_uid instance.parent.author_id else: target_uid post.author_id if target_uid instance.author_id: return Notification.objects.create( user_idtarget_uid, typecomment, post_idpost.id, contentf{instance.author.username} 回复了你的帖子 )这里最关键的是判断“不要给自己发通知”。我一开始忘了这个判断自己回复自己的帖子也会收到通知体验非常差。5. 两个框架共存之后踩到的坑session、上传路径、查询性能这段是本文的核心价值之一。这些坑都不是网上教程能教给你的是我实际写代码、部署、压测时一步一步踩出来的。5.1 登录状态丢失与 Session 配置项目前期本地开发一切正常部署到服务器后却频繁出现“登录后一刷新就退出”的问题。一开始我以为是 cookie 生命周期设置错了后来排查发现是 Django settings 里的 SESSION_COOKIE_DOMAIN 和 CSRF_COOKIE_DOMAIN 问题。本地访问 localhost 时 cookie 域名是 localhost服务器访问域名是 game.example.com如果 settings 里强制写死了 SESSION_COOKIE_DOMAIN浏览器会把 cookie 发错地方。正确做法是生产环境配置里把域名设为你的真实域名开发环境不设置任何 domain 参数让浏览器自动按当前域名管理 cookie。另外遇到一个非常隐蔽的问题Django 默认 Session 存在数据库。请求量上来后每次请求都要查 session 表性能和稳定性都受影响。我最终把 Session 存储切到了 RedisSESSION_ENGINE django.contrib.sessions.backends.cache SESSION_CACHE_ALIAS default登录态直接缓存到 Redis查询速度比数据库快一个数量级。这里的前提是 Redis 服务必须稳定Redis 挂了整个站点就全部掉登录所以同时在 Redis 配置里做了持久化和告警。5.2 图片上传能写入但页面访问 404上传图片后在后台能看到文件确实写在 MEDIA_ROOT 里但浏览器访问图片 URL 永远 404。排查到最后发现是 Nginx 没有把 /media/ 路径代理到 Django 的媒体目录。Django 开发服务器 runserver 会自动处理 MEDIA_URL但生产环境 Nginx 托管静态文件时必须在配置里单独加一条 location 规则location /media/ { alias /www/game_community/django_site/media/; expires 30d; add_header Cache-Control public, immutable; }同时要注意alias和root的区别。配 alias 时路径要写全alias 后面跟的是文件系统里的绝对路径配 root 时 URL 路径会拼在路径后面。这里我一开始用 root 写错了路径结构白折腾了一个下午。5.3 深分页导致接口越来越慢帖子列表页一开始用的是 Django 自带的 Paginator点击第 50 页时接口响应时间从 80ms 涨到接近 1 秒。原因在于 Paginator 对 MySQL 执行的是LIMIT 20 OFFSET 980MySQL 要扫描前 980 行数据再丢弃OFFSET 越大查询越慢。我对分页策略做了调整热门帖子列表只显示前 10 页超过 10 页要求用户使用搜索或板块筛选对“最新帖子”列表改成“下一页”式游标分页用创建时间和主键 ID 做游标每次只查询上一个 ID 之后的数据latest_posts Post.objects.filter( statuspublished, created_at__ltlast_created_at ).order_by(-created_at)[:20]这才是真正能支撑大数据量的分页方案。对普通帖子列表来说前 10 页覆盖了百分之九十以上的用户访问量后面的内容交给搜索远比分页合理。5.4 跨服务的数据库连接和资讯采集一致性Flask 服务与 Django 共用 MySQL 数据库。Flask 里我没有直接使用 Django 的 ORM而是用 SQLAlchemy 连接同一套表。这里遇到的核心问题有两个一是连接配置不一致导致字符集乱码写入资讯里的中文在 Django 页面显示问号。排查后发现 Flask 的连接字符串缺少charsetutf8mb4参数而 Django 的 OPTIONS 里设置了 utf8mb4。两边 mysqld 环境一致还不够连接参数必须完全一致。二是两个服务的数据库连接池互相挤占。Flask 采集任务经常一次性批量插入几千条数据如果连接池配置最大连接数是 20同步写密集任务会把连接占满导致 Django 的查询排队。最后把 Flask 服务的数据写入改成批量提交并限制采集并发高峰期连接占用从 18 个降到了 4 个Django 侧完全不再受影响。6. 部署上线从本地开发到真实可访问6.1 环境隔离与依赖管理项目从本地搬到服务器第一步就是环境隔离。我用 Python virtualenv 创建独立环境并通过 requirements.txt 锁定关键依赖版本。Django 和 Flask 的应用分别部署两个服务各自独立创建虚拟环境。需要注意 Python 版本一致性本地是 3.10服务器也必须装 3.10否则可能出现某些依赖编译失败。Django 的 settings 我拆成了 base、dev、prod 三个文件通过环境变量切换。生产环境的 SECRET_KEY 通过环境变量注入DEBUG 设置 FalseALLOWED_HOSTS 写明服务器域名或 IP。这些配置如果写死在代码里一旦项目开源或代码泄露服务器安全性就完全不可控。6.2 Gunicorn Nginx 配置要点Django 和 Flask 都跑在 Gunicorn 上。Django 服务的 Gunicorn 配置gunicorn config.wsgi:application \ --bind 127.0.0.1:8000 \ --workers 3 \ --threads 2 \ --timeout 30 \ --max-requests 2000 \ --max-requests-jitter 100workers 数量不是越多越好我 2 核 4G 的 ECS 上跑 3 个 worker 加每 worker 2 个线程效果最好。workers 太多会导致内存吃紧和上下文切换开销max-requests 设置成 2000 是为了防止内存泄漏积累达到请求数后 Gunicorn 会自动回收 worker 进程。Nginx 做反向代理把用户请求转发给 Gunicorn。静态文件交给 Nginx 直接返回POST、动态页面才转发。Flask 辅助服务监听 8001 端口Nginx 配置/api/internal/路径转发到 Flask外部不直接暴露 8001只有 Django 内部调用。6.3 上线后的简单压测体验整个系统上线后我用 Apache Bench 做了压力测试首页并发 30 时QPS 约 850响应时间 P95 在 180ms 左右。帖子详情页启用 Redis 缓存后QPS 提升到 1300 以上。评论写入接口受数据库写锁影响QPS 约 300对社区论坛这个量级完全足够。压测结束后我发现数据库连接数成为最大瓶颈最终又在 Django settings 里限制了 CONN_MAX_AGE 为 60 秒让每个 worker 的数据库连接可以复用避免频繁建立新连接导致 MySQL 连接数飙升。这一步对并发提升的效果非常明显。写在最后的实际体会项目从开发到部署总共花了不到一个月。回头总结我最想强调的不是“用了什么框架”而是先做减法再做加法。如果一开始就把所有功能都加上这个项目大概率在第五天就烂尾了。把 P0 链路跑通系统已经有了完整可用性之后每一个增强功能都像是在已经运作的系统上面做锦上添花做起来心态完全不一样。如果你也要做类似系统我建议按这个顺序推进先用户登录再版块和帖子再评论然后点赞收藏最后才碰热门排序和通知。每完成一步都能在浏览器里真实操作一次这种感觉比写再多设计文档都让人安心。另外一个很实在的建议是把 Django 和 Flask 拆成两个服务绝不是为了炫技而是这个项目的需求驱动我们这么干。如果你的项目没有独立的采集任务老老实实只用一个 Django 就够了完全不必为了“多框架”而造复杂度。我的这个双服务架构只有在内容源、主业务、内部管理接口三者分离时才真正划算。最后分享一个让我印象深刻的经验发布功能上线后的第一周就有用户通过富文本编辑器往帖子里插了一段非常规格式的代码导致页面布局错乱。那一刻我才真正理解用户永远会用你没想到的方式操作你的系统。做社区类项目内容安全和输入的边界校验永远不能省哪怕多花两天也值得。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。