12306售票系统设计方案
本人的工程实践项目是设计一个类似12306的网上售票系统,本文将分析该项目的同时对软件架构进行初步设计。
项目信息
题目基本要求
-
参考12306站点进行售票系统建模设计,尽可能接近覆盖真实线上系统,实现的功能有但不限于:
- 用户信息注册
- 查询余票: 根据时间,车次,站点区间,座次(一等座,二等座,硬卧,硬座…)查询余票
- 售票: 支持一次购买同一车次的多张车票(多人),支持订单30分钟内锁定,超时释放。支付接口可以mock。
- 退票: 支持一个用户账户下的批量退票
- 改签: 同一用户一张车票只能改签一次
-
所有读写接口延迟要求 <= 500ms
-
单机支持到500qps的并发请求
软件架构
MVC
MVC架构,即 Model-View-Controller(模型-视图-控制器)。
- 控制器:负责转发请求,对请求进行处理。
- 视图:图形界面设计。
- 模型:程序应有的功能(实现算法等)、数据管理和数据库设计。
随着时代的发展,前端也越来复杂,为了更好地工程化,前后端分离的架构更加流行。可以认为是把 V 层从 MVC 中抽离出来成为单独的项目,后端就只剩下 M 层和 C 层。本项目中,前端为移动客户端,包括安卓和 iOS 两部分,后端包括模型和控制器两层。
对于后端部分,可以继续细分,分为三层。
- Controller:控制器,负责处理路由、参数校验、请求转发。
- Service:服务层,定义业务逻辑和业务流程。
- DAO:负责数据的持久化工作。
微服务
在模块化思想的指导下目前主要有两种软件架构模式,即传统单体集中式(Monolithic)架构与微服务(Microservice)架构。本项目使用微服务架构,整个系统由一系列独立的微服务共同组成。每个微服务单独部署,跑在自己的进程中,也就是说每个微服务可以有一个自己独立的运行环境和软件堆栈。
按照功能,本项目可以划分为以下几个微服务。
- 用户服务
- 购票服务
- 订单服务
- 支付服务
软件架构风格与策略
C/S
Client/Server(C/S)和Browser/Server(B/S)是我们常用的对软件的网络结构特点的表述方式,但它们背后蕴含着一种普遍存在的软件架构风格,即客户-服务模式的架构风格。
在客户-服务模式中,客户是主动的,服务是被动的。例如在本项目中,用户使用客户端查询车票,客户端向服务器发送查询请求,服务器处理好请求后,将结果传回客户端。这种方式具有典型的模块化特征,降低了系统中客户端和服务端之间的耦合度,提高了服务构件的可重用性。
CRUD
CRUD是 Create、Retrieve、Update、Delete 的缩写,是四种数据库持久化信息的基本操作。同时 CRUD 也是一种围绕中心化管理系统关键数据的软件架构风格。
在本项目中,核心数据根据功能需求可以分为用户信息、车次信息、车票信息等,购票系统的功能都需要围绕这些关键数据进行开发。所以,本项目也符合 CRUD 架构风格,软件整体以数据为中心。
接口API
本项目主要提供 HTTP 接口,接口定义遵循 RESTful 设计风格。
本项目中部分接口 API 如下:
接口名称 | 接口地址 | 请求方式 | 请求参数 | 响应信息 |
---|---|---|---|---|
用户注册 | /user/register | POST | username, password | code, msg 是否注册成功 |
用户登录 | /user/login | POST | username, password | code, msg 是否登录成功 |
获取所有站点信息 | /search/allstations | GET | ||
查询余票 | /search/remainder | GET | startCity, endCity, date, type | 余票信息 |
项目的不同视图
软件架构模型是通过一组关键视图来描述的,同一个软件架构,由于选取的视角不同可以得到不同的视图,这样一组关键视图搭配起来可以完整地描述一个逻辑自洽的软件架构模型。一般来说,我们常用的几种视图有分解视图、依赖视图、泛化视图、执行视图、实现视图、部署视图和工作任务分配视图。
分解视图
实现视图
项目实现的代码目录结构大致如下
├── rpc grpc相关的接口和协议文件
│ ├── pay pay服务器的rpc代码, 同理如果是user服务应该在该文件夹下建立user文件夹
│ ├── proto .proto文件存放
│ ├── client grpc客户端, grpc服务再server中自己实现
├── server 每个微服务项目
│ ├── candidate 候补服务器
│ ├── controller 控制层,数据的接受的校验
│ ├── service 服务层,业务逻辑
│ ├── model 模型层,与数据库连接
│ ├── redis 缓存连接
│ ├── setting 配置服务
│ ├── config 配置文件存放
│ ├── pay 支付服务器
├── reticket 退票服务器
├── search 搜索
│ ├── dynamic 动态搜索
│ ├── static 静态搜索
├── ticket 购票服务器
├── user 用户服务器
├── ticketPool 线程池服务,主要是对内提供服务
部署视图
数据库设计
用户
属性名 | 类型 | 注释 |
---|---|---|
id | int | 用户ID |
username | varchar | 用户名 |
password | varchar | 密码 |
name | varchar | 姓名 |
id_type | int | 证件类型 |
id_number | int | 证件号码 |
sex | int | 性别 |
varchar | ||
passenger_type | int | 乘客类型(成人/学生) |
register_time | datetime | 注册时间 |
联系人
属性名 | 类型 | 注释 |
---|---|---|
id | int | 联系人信息ID |
user_id | int | 用户ID |
contact_name | int | 联系人姓名 |
contact_number | varchar | 联系人电话 |
contact_id_type | int | 联系人证件类型 |
contact_id_number | varchar | 联系人证件号码 |
contact_passenger_type | int | 联系人乘客类型 |
车次
属性名 | 类型 | 注释 |
---|---|---|
id | int | 车次ID |
name | varchar | 车次名称 |
train_type | int | 车次类型(K/D/G) |
starting_station_id | int | 起点站ID |
terminal_station_id | int | 终点站ID |
starting_station_name | int | 起点站名称 |
terminal_station_name | int | 终点站名称 |
starting_time | datetime | 车次出发时间 |
terminal_time | datetime | 车次到达时间 |
站点
属性名 | 类型 | 注释 |
---|---|---|
id | int | 站点ID |
name | varchar | 站点名称 |
city | varchar | 城市名称 |
车次区间
属性名 | 类型 | 注释 |
---|---|---|
id | int | 区间ID |
train_id | int | 车次ID |
station_id | int | 站点ID |
order | int | 站点顺序 |
订单
属性名 | 类型 | 注释 |
---|---|---|
id | int | 订单ID |
user_id | int | 订单创建者ID |
train_id | int | 车次ID |
train_date | date | 车次出发日期 |
starting_station_id | int | 出发站ID |
terminal_station_id | int | 到达站ID |
price | int | 总金额(分) |
is_paid | int | 是否已支付 |
paid_time | datetime | 支付时间 |
created_at | datetime | 创建时间 |
订单详情
属性名 | 类型 | 注释 |
---|---|---|
id | int | 订单详情ID |
order_id | int | 订单ID |
passenger_name | int | 乘客姓名 |
passenger_id_type | int | 乘客证件类型 |
passenger_id_number | varchar | 乘客身份证号 |
passenger_type | int | 乘客类型 |
seat_type | int | 座位类型 |
carriage_number | int | 车厢号 |
seat_number | int | 座位号(排) |
seat_location | char | 座位位置(ABCDEF) |
车票信息
属性名 | 类型 | 注释 |
---|---|---|
id | int | 车票ID |
train_id | int | 车次ID |
starting_station_name | varchar | 出发站名称 |
terminal_station_name | varchar | 到达站名称 |
starting_time | datetime | 出发日期时间 |
seat_type | int | 座位类型 |
carriage_number | int | 车厢号 |
seat_number | int | 座位号(排) |
软件运行环境及技术选型
运行环境:使用 Docker 容器化技术部署。
主要技术:
- Gin:Go 语言轻量级 Web 框架
- gRPC:基于 Protobuf 的跨语言开源 RPC 框架
- MySQL:关系型数据库
- Redis:Key-Value 数据库
概念原型核心工作机制示例
- 用户未登录时,可以以游客身份使用系统,此时只可以进行查询操作。
- 用户注册后,成为注册用户。
- 注册用户登录后,可以使用用户信息管理、查询、购票、改签、退票等功能。
- 购票操作需要填写乘客信息和一些选项,购买成功后进入待支付状态。
- 支付成功后,订单完成,出票。