-
服务端源代码 github.com/gowvp/gb28181
介绍
GoWVP (Golang Web Video Platfrom) 是一个 Go 语言实现的,基于 GB28181-2022 标准实现的网络视频平台,负责实现核心信令与设备管理后台部分,支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。支持国标级联,支持rtsp/rtmp等视频流转发到国标平台,支持rtsp/rtmp等推流转发到国标平台。
技术栈
Golang v1.23, Goweb v1.x, Gin v1.10, Gorm v1.25 …
React 19, Vite 6.x, Typescript, React-Router v7, React-Query v5, shadcn/ui …
第二篇 搭建服务端
打包前端
在开始之前,我们将前端剩余一些工作完成。
此项目前后端合并部署,由 Go 服务端提供静态资源访问,实际部署无所谓,前端静态资源也可以用 nginx 部署,只是本文将按照前后端合并的方式部署。
前端打包后的静态文件,使用公共前缀路由能够很好约束并明确,此时请求的内容是静态资源还是接口。
例如以 /web
开头的路由前缀,均为前端静态资源。
编辑 vite.config.ts
文件
增加 base: mode === "development" ? "/" : "/web/"
,表示开发模式还是根目录,生产模式以 /web/
为公共前缀。
vite 官方文档 有提到可以使用 ./
设置相对基础路径,实际在开发模式中使用 base: "./"
,会出现路径访问不到资源的情况。
**编辑 react-router.config.ts
文件 **
增加 basename: import.meta.env.MODE === "development" ? "/" : "/web/",
。
在终端执行 yarn build
即可打包成功,可在 build/client
目录中查看,我们将 client
重命名为 www
,放到服务端项目目录下。
搭建服务端
Golang 服务端采用 GoWeb 模板,GoWeb
是一个专注于 REST API 的完整 CURD 解决方案,支持代码生成,整洁架构。
打开 goweb 仓库,选择使用此模板,创建自己的项目仓库后,克隆到本地。
运行项目
配置目录 configs
在项目根目录,main
函数入口在 cmd/server
,编辑器默认运行会在 main.go
同级创建可执行文件,会导致读取不到配置,所以我们调整一下编辑器。
vscode
配置 launch.json
,让可执行程序生成在项目根目录,运行 f5,
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/server",
"cwd": "${workspaceFolder}",
"output": "__debug_${workspaceFolderBasename}",
}
]
}
项目运行后,在终端执行 curl -s localhost:8080/health | jq
,可以看到如下图所示。/health
是 goweb 默认提供的健康检查接口,在 web/api/api.go
中定义。
windows 电脑可以用 git-bash
终端,我觉得记忆两套操作系统的命令还挺麻烦的,不如都用 linux 命令操作。无法操作终端时,浏览器访问是一样的效果哈。
接入前端
我们以 /web
为网页静态资源前缀,使用 gin 框架,定义后台静态资源组,使用 gzip.Gzip
压缩加快前端资源下载速度。
在上面打包前端资源后,已将 www
移动到项目根目录下,使用 Static
提供对 www
资源的访问,增加 /
重定向到静态资源。
最后,前端打包使用的是浏览器路由,而非 hash 路由(/#/),此路由 Golang 服务端是不知道的,所以将 /web
前缀全部提供访问到 index.html
,由静态资源处理这些前端路由。
代码如下:
const staticPrefix = "/web"
const staticDir = "www"
admin := r.Group(staticPrefix, gzip.Gzip(gzip.DefaultCompression))
admin.Static("/", filepath.Join(system.Getwd(), staticDir))
r.NoRoute(func(c *gin.Context) {
// react-router 路由指向前端资源
if strings.HasPrefix(c.Request.URL.Path, staticPrefix) {
c.File(filepath.Join(system.Getwd(), staticDir, "index.html"))
return
}
c.JSON(404, "来到了无人的荒漠")
})
// 访问根路径时重定向到前端资源
r.GET("/", func(ctx *gin.Context) {
ctx.Redirect(http.StatusPermanentRedirect, filepath.Join(staticPrefix, "index.html"))
})
打开网页,访问 http://localhost:8080/
,即可看到登录页面。
前后端接口对接,向用户展示系统资源
gopsutil **** 是一个 Go 语言的开源库,主要用于系统和进程监控。它为开发人员提供了方便的接口,用于获取系统级别的各种信息,如 CPU、内存、磁盘、网络、进程等方面的信息,并且跨平台支持 Linux、Windows、macOS、FreeBSD 等操作系统。
我们使用 gopsutil
库获取 Disk/CPU/Memory/Network 数据。
定义一个循环队列,定时通过 gopsutil
获取服务主机数据,写入循环队列,提供接口给前端访问。(略)
解决跨域
回到前端项目,我们使用 axios
向服务端发出请求。
axios
是一个基于 Promise 的 HTTP 客户端库,主要用于在浏览器和 Node.js 环境中进行 HTTP 请求。
在终端执行命令:
yarn add axios
在 app/service/http.ts
定义请求封装。
const headers = {
"Content-Type": "application/json",
};
export const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 60000,
headers: headers,
responseType: "json",
});
其中的import.meta.env.VITE_API_BASE_URL
是环境变量,可以在项目目录下创建 2 个文件
.env.development
开发模式环境变量,写入VITE_API_BASE_URL=/api
.env.production
生产模式环境变量
在没有更多配置的情况下,直接请求会导致跨域资源无法访问,在 vite.config.ts
中定义如下内容,由 vite 做反向代理,替我们访问资源。
server: {
proxy: {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
接口联调
在 app/service/model/stat.ts
定义 HTTP 接口响应模型,使用 postman/apifox/apipost
之类的工具,定义接口文档后,可以快速生成相关代码。
在 app/service/api/stat.ts
定义接口请求。
export async function FindStats() {
return await GET<FindStatResponse>(`/stats`);
}
在任意页面,使用 react-query 每 5 秒请求一次服务端,获取最新服务主机资源状态。
const query = useQuery({
queryKey: ["dashboard"],
queryFn: FindStats,
refetchInterval: 5000,
throwOnError: (error, query) => {
return false;
},
});
打开相关页面,F12
查看 网络
,可以看到每 5 秒获取一次数据。