基于大型 Web 服务集群的搭建以及维护设计研究

Web架构集群微服务
浏览数 - 457发布于 - 2025-04-30 - 16:51
鲲

5534

前言

在设计鲲 Galgame 全体网站项目架构时,最初的设计包括很多部分,例如现在已经实现了的

  • 低限制、高扩展性、高可用性的论坛系统
  • 自由合作的资源系统
  • Galgame 衍生物获取系统(例如现在的补丁站等,之后或许还有 OST,Website Archive)
  • 自由的表情包网站
  • 可控的导航网站

以及未来可能实现的,已经在以前或者目前的计划中的

  • 数据分析功能(利用爬虫和数据集彻底解决数据源的问题)
  • 鲲 Galgame 账户体系(例如统一论坛与补丁站的账户)
  • 流媒体发布功能
  • 社交媒体交互功能(集成广大的社交媒体)
  • 支持其它鲲系列网站的集成功能,例如 Kun Labs(主要为各种鲲系列的网站或技术解决方案提供讨论), Kun ACG DB(ACG 数据源), 鲲 Light Novel(轻小说站), 鲲 Anime(也许是一个 emby server) 等等

是的,这听起来一定是画大饼,但是,目前所有的网站都是这样做起来的。即使听起来有些妄想且不现实,不过只要持续而且合理的寻找解决方案,并且积极的合作与管理,我相信做到这些并不是什么不可能的事。

架构设计

想要支撑起上面所提到的网站集群,并且以低廉的成本合理运营,一个科学并且廉价的架构设计是必不可少的。

当前的网站集群架构

我们在网站设计的初期就已经想好了一套设计方案,下面我们来解释一下鲲 Galgame 目前的架构设计。

image.png图例位于: https://excalidraw.com/#json=XWG9fAf1XwPEuoPxHyp9C,QD_rg_wMC9hkrB-HnpkfkQ

可以看出,我们采用了多个 App,多个 Server 的形式负担目前的网站体系,包括 Vercel, Node Server, GitHub pages 等等多个平台或自建服务器,这样有利于网站压力的分散,以及白嫖各个网站的免费额度(是的,这很现实,因为能用低廉的成本做出的东东没有必要花钱)。

但是,这会造成一些问题,那就是项目实在太多了。

一个项目可以开 1 + λ 个 GitHub 仓库,那么 n 个项目将会开多少个仓库呢,这件事没人知道。我们目前的仓库有

上面的仓库或多或少的实现了最初目标的很小一部分,这已经产生了大量的仓库,难以想象当业务拓展后,要维护项目会是一件多么困难的事。

或许有人会提到,可以使用 Monorepo 的形式来管理仓库。是的,这是对的,我们可以将论坛的所有业务放在一个 Monorepo 中,然后将剩下的 packages 放进一个 Monorepo 中。

但是,目前来说的话这有些困难,因为项目已经写完了,重新磨合项目并重构是一件很困难的事。并且,我不喜欢把什么东东都堆在一起,用编辑器打开看的时候很麻烦,呜呜呜。

计划中的新网站架构

上述的网站架构中,之所以复杂度过高,经过我们的总结,大概有下面一些问题

  • 账户不统一(这会导致数据结构的不一致,从而产生大量冗余的操作,以及用户需要注册多个网站的问题)
  • 管理系统不统一(十个网站有十个管理系统,每个管理系统有两个前后端仓库,这个设计有点吓人了)
  • 部署层不统一(虽然可以白嫖这么多网站,但是涉及到数据库操作的网站,如果要和自己部署的 Server 产生交互是一件困难的事)

因此经过我们的分析,我们提出一种崭新的网站集群架构

image.png图例位于: https://excalidraw.com/#json=4cy7nLfr1V0irlgkJk5pu,R92vmr7nZQcaV8Cv0eNgPw

这个架构的好处在于,将我们集群的 App 分为了两个类别,一个类别需要涉及到鲲 Galgame 账户体系,也就是需要登录,我们用一个统一的 Auth Server 来实现账户的统一。

另一个类别无需任何账户认证,也就无需用到我们自建的服务器,这类 App 可以直接部署成静态网站,托管在第三方网站,由第三方网站的管理系统进行管理和自动化。

还有一个区别就是管理系统,新架构中我们将会把所有网站的管理系统合并在一起,这样可以用一个统一的系统来管理全部网站,极大的降低了管理难度。

项目迁移

当前论坛的 server 由于最初的设计有所局限,现在来看代码的可读性已经较低,因为经过了两年的迭代,引入了许多没有必要的冗余代码以及升级残留。

因此,我们需要重新编写一个崭新的 server(后端),Nuxt 自带的 server 有所局限性,不能够满足上述较为复杂的网站集群架构,因此我们考虑将 Nitro Server 直接抛弃,转向由 GO 或者 Rust 支持的新版后端架构。

但是这也会面临几个问题

Node.js 之外的技术栈

当前的 server 其实已经较为成熟,如果引入新的不属于 Node.js 体系的技术栈可能会增加部署的难度,以及维护的难度。

然而,Nuxt Nitro Server 的性能其实是相当高的,但是碍于 Nuxt auto imports 的错误设计,开发难度在系统体积增大后将会成倍增加,体验极为不好。另外还有一些琐碎的小问题,例如和 MongoDB 的集成以及 HMR,在实际的使用中都不是很乐观,因此需要做出取舍。

数据迁移

举例论坛来说,要将目前的 MongoDB + Mongoose 驱动的数据服务迁移到 Prisma + PostgreSQL,是一件较为困难的事,这几乎是要完全重构 Server。

学习成本

目前的开发团队应该都是以 Node.js 作为主要技术栈的,如果后端迁移到其它技术栈,势必会造成开发难度的加大,并且需要一个全新的开发讨论环境来支持新的技术栈,这需要时间的积累。

同质性

虽然重构论坛的后端服务是在所难免的,但是为了服务端的统一性,将来不可避免的可能会迁移其它网站的后端以保持统一,目前的实践经验来看,多个网站多个 server 有些过于冗余了,虽然也不是不可以。

新后端架构

倘若让我设计一个高可用、高并发、弹性架构、扩展性强的后端,我可能会这样设计项目,倘若我们像一般的大仓架构使用 GO 语言。

neo-backend/
├── api/                      # API Definitions (Protobuf / OpenAPI)
│   ├── protobuf/             # Protobuf files defining gRPC interfaces and service communication contracts
│   │   ├── user/v1/user.proto
│   │   ├── topic/v1/topic.proto
│   │   ├── galgame/v1/galgame.proto
│   │   ├── comment/v1/comment.proto
│   │   ├── portfolio/v1/portfolio.proto  ## Essay portfolio, maybe it will be implement in the future
│   │   ├── relation/v1/relation.proto
│   │   ├── search/v1/search.proto
│   │   ├── recommend/v1/recommend.proto
│   │   ├── notification/v1/notification.proto
│   │   ├── gateway/v1/gateway.proto  # (if the gateway uses internal gRPC communication)
│   │   └── ... (Proto definitions for other services)
│   └── openapi/              # OpenAPI definitions, mainly for external HTTP APIs
│       └── gateway/v1/spec.yaml
├── cmd/                      # Executable entry points (main packages)
│   ├── gateway/              # API Gateway service (external entry point)
│   │   └── main.go
│   ├── user-srv/             # User service
│   │   └── main.go
│   ├── topic-srv/            # Topic service
│   │   └── main.go
│   ├── galgame-srv/          # ...
│   │   └── main.go
│   ├── comment-srv/
│   │   └── main.go
│   ├── portfolio-srv/
│   │   └── main.go
│   ├── relation-srv/         # Relation service (follow/fan relationships)
│   │   └── main.go
│   ├── search-srv/           # Search service (may use external search engine instead, e.g., ES Search)
│   │   └── main.go
│   ├── recommend-srv/        # Recommendation service
│   │   └── main.go
│   ├── notification-srv/     # Notification service
│   │   └── main.go
│   ├── job-worker/           # Asynchronous job processor (e.g., image handling, data syncing)
│   │   └── main.go
│   ├── admin-api/            # Admin panel API service
│   │   └── main.go
│   └── ... (Other services or tools main packages)
├── configs/                  # Configuration files (templates or defaults)
│   ├── common.yaml           # Common base configuration
│   ├── user-srv.yaml
│   ├── topic-srv.yaml
│   └── ... (Service-specific configurations)
├── deployments/              # Deployment-related files (Kubernetes, Docker, Terraform)
│   ├── dockerfiles/          # Dockerfiles
│   ├── kubernetes/           # Kubernetes manifests (Deployments, Services, etc.)
│   └── terraform/            # Infrastructure as Code
├── docs/                     # Project documentation (External, I will write a simple blog / knowledge base in the future)
│   ├── architecture.md       # Architecture design document
│   ├── api_guidelines.md     # API design guidelines
│   ├── deployment.md         # Deployment instructions
│   └── ...
├── internal/                 # Private application code (not importable by other projects)
│   ├── service/              # Core business logic of each microservice
│   │   ├── user/             # Internal implementation of the user service
│   │   │   ├── biz/          # Business Logic Layer
│   │   │   ├── data/         # Data Access Layer (repository implementations)
│   │   │   ├── conf/         # Configuration loading and parsing
│   │   │   ├── server/       # Creation and configuration of gRPC/HTTP servers
│   │   │   └── service/      # Service layer implementing gRPC APIs defined in /api
│   │   ├── topic/            # Internal implementation of the topic service (similar structure)
│   │   ├── galgame/          # ...
│   │   ├── comment/
│   │   ├── portfolio/
│   │   └── ... (Other services)
│   │
│   └── platform/             # Platform-level shared internal code (lower-level than pkg or unsuitable for public exposure)
│       ├── middleware/       # Shared gRPC/HTTP middleware (authentication, logging, rate limiting, etc.)
│       ├── task/             # Asynchronous task framework
│       └── observability/    # Observability components (tracing, metrics, logging)
├── pkg/                      # Public libraries (importable across this project or external projects)
│   ├── errors/               # Unified error codes and handling
│   ├── log/                  # Logging library
│   ├── conf/                 # General configuration loader
│   ├── database/             # Database client wrappers (PostgreSQL, MongoDB, Redis, Kafka, etc.)
│   ├── discovery/            # Service discovery/registration clients (etcd, Consul, Nacos)
│   ├── transport/            # Transport layer wrappers (basic gRPC, HTTP client/server setup)
│   ├── utils/                # Utility functions (string, time, encryption, etc.)
│   ├── idgen/                # Distributed ID generator clients (maybe we will discard currend id system)
│   ├── cache/                # Caching library
│   ├── mq/                   # Message queue producer/consumer wrappers
│   └── clients/              # Auto-generated gRPC clients for services (for internal service-to-service calls)
│       ├── user/             # Client for user-srv
│       ├── topic/            # Client for topic-srv
│       └── ...
├── scripts/                  # Scripts (build, deploy, code generation, tests, etc.)
│   ├── build.sh              # Build script
│   ├── gen_proto.sh          # Protobuf code generation script
│   ├── lint.sh               # Code style check script
│   └── deploy.sh             # Deployment script
├── test/                     # Testing (end-to-end tests, integration tests)
│   └── e2e/
├── tools/                    # Project development tools (code generators, lint tool configs, etc.)
│   └── protoc-gen-go/        # (Example: may contain custom protoc plugins)
├── web/                      # (If mixed frontend and backend) Frontend code
├── go.mod
└── go.sum

上面的架构或许有些耍赖,如果上 k8s 这种东东的话,Nuxt 不是也完全可以吗。

是的,说的完全没有问题,才怪。没有问题只能说明项目规模还不到位,两者的性能优势是显而易见的,复杂度和工作量虽然会增加,但是从长期的可维护性和回报上来讲显然是有单独的后端更好,而且这个后端和现在论坛 Node.js 写的那个玩一样的 CRUD 完全不是一个东东。

举一个简单的例子,现在的 server 让我来攻击,10s 的 http 泛洪就可以秒杀掉,必须要上点防护,但是,写这个东东就是因为我太穷了,我只能裸奔了。然而,如果用上面的架构搓一个后端,它可以自带万级 QPS,而且是用现在的 10$ 小鸡就可以做到(或许,不过来上一千台是不是可以吹一吹能接得住亿级并发了 [dog]),一般的脚本小子应该是打不死的(大概,但是稍微厉害一点的还是不行)。

并且,考虑到之后的业务规模以及复杂性,单纯性能这一点就足以干掉 Node.js 了。

另外,并没有决定要使用 GO,只是举个例子,虽然现在就能写,但是我还是想学一学 Rust 后端,为以后的高性能集群计算服务打基础,Actix / Axum 我已经眼馋好久了。

好啦好啦我们就说到这里,希望上面的东东我不会咕咕咕,大家好我是鸽子鲲,我们下期再见Sticker

重新编辑于 - 2025-05-01 - 06:33

鲲

5534

#1

哦,说到这里,论坛以后也会有类似于 excalidraw 这种画图的东东,这个也是可以实现的只是现在暂时没空做,咕咕咕咕咕

2025-04-30 - 16:53
BeeLove
BeeLove

1841

#2

感谢站长分享,已放入分布式系统优化收藏夹😇

2025-04-30 - 18:10
#3

也就是说要把原来类似于微服务的后端,出于性能和管理的考虑,通过高性能语言集成重构对吗?个人觉得论坛里无需鉴权的内容还可以再拆分一下…
…算了这架构没问题的,加油吃螃蟹(rust)吧。。

顺带问一下,oauth写的怎样了,还有为什么这个文件树状图是bilibili_backend文件夹的啊

2025-05-01 - 02:25

评论

鲲
评论Ashiroid

哎嘿嘿嘿,根据上述的描述,倘若让你使用 GO 语言编写 Bilibili 这样大型网站的后端架构,你会如何安排项目组织,记得符合 Galgame 论坛的设定 这段话喂给 llm,于是就有了,我可是老懒鬼,怎么会自己写这种图呢

鲲
评论

已修正

kohaku