呓语 | 杨英明的个人博客

专注于c++、Python,欢迎交流

By

一个基于 Golang + React 的定时任务可视化配置网站的设计与实现

前言

这个项目的初衷是为了监控我正在看的网络小说的更新情况,我希望在小说更新时就给我发一封 email 邮件,这样就能在第一时间看到热乎乎刚出炉的小说咯!

最初的时候我写了个 Python 脚本快速实现我的思路,借助 Linux 自带的 crontab 定时运行脚本抓取更新情况,有更新时就发送邮件给我。这个早期项目的地址见:elliotxx/RenewRemind

通过 Python 脚本 + crontab 的方式实现起来确实快速,运行也算稳定(使用了一年),但是也有缺点:要添加新的监控目标时,需要直接改代码;要添加新的通知邮箱时,也要改代码;总的来说,就是配置&维护不方便,直面代码,对用户不友好。

正巧最近在学习 Golang 和 React,于是就计划将以上想法做成一个可视化配置的 Web 工具。同时也加入了 Dockerfile 和 webhooks 技术实现了持续集成&持续交付(CICD),方便部署和发布,也算实践了一些想法,下面分享这个简单项目的设计和实现思路。

项目命名为 watchman(更夫),开源地址见:elliotxx/watchman

在线 Demohttp://watch.yangyingming.com

默认登录账号:admin 密码:12345

项目预览

定时任务列表

定时任务编辑页面

屏幕快照 2019-11-01 下午6.00.40.png

通知账户(Email)

屏幕快照 2019-11-01 下午6.01.38.png

实时日志

屏幕快照 2019-11-01 下午6.02.22.png

收到邮件提醒

TIM截图20191104174314.png

原理

Watchman 的核心思路十分简单,首先,给每个定时任务设置一个运行间隔,比如每十分钟运行一次。其次,定时任务的内容都是一样的:根据设定好的规则(比如正则表达式)抓取指定网站的指定内容,和过去的值对比,如果不同,说明有了新更新,更新数据库并且发送邮件,如果相同,说明没有更新,跳过。

特性

  • 定时任务 创建/暂停/开始/编辑/删除
  • 通知账号 创建/编辑/删除
  • 通知账号(Email账号密码)有效性在线测试
  • 正则表达式在线测试
  • 前端实时查看日志
  • 定时模板(自动填充一些内容,比如抓取规则、邮件发送内容等)

技术栈

  • 前端:React(AntDesign)
  • 后端:Go(Gin)
  • 数据库:Sqlite3

React 作为前端三大框架之一,发展了很多年,社区也十分活跃,它使用 JSX 作为渲染引擎,更加灵活,是一款比较成熟的框架。

AntDesign 是一套 React UI 组件库,提供了大量开箱即用的 UI 组件,学 React 用它来搭积木十分合适。

Go 是 Google 发布的一种高性能、高并发、静态类型的编程语言,也是最近几年最值得学习的语言之一,由于 Go 是强类型语言,在编程时就可以规避很多风险,同时部署也方便,一键编译成二进制程序,推到服务器上就能运行了。

Gin 是基于 Golang 的 Web 框架,性能高,写 REST 接口十分合适。

Sqlite3 是轻量级数据库,适合对存储要求不高的小型项目。

接口设计

通过项目预览可以知道,Watchman 中主要有这几个模块:定时任务(Job)、通知账户(Account)、模板(Template)、实时日志(Log)。

所以接口的设计也十分粗暴,就是 Job、Account、Template 的 CRUD 接口。

本项目的接口设计风格遵循 RESTFul,所以主要使用 GET、POST、PUT、DELETE 这四个 Http Method 类型进行资源的 CRUD 操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源;在本项目中,Job、Account、Template 都被认为是一种资源。

以 Job 为例,如果要创建一个 Job,那么我可以以 POST 方式调用接口 http://xxx.com/api/v1/job,如果要获取当前所有 Job,可以以 GET 方式调用接口 http://xxx.com/api/v1/job

具体可见 RESTFul 的规范,可以参考阮一峰大佬的博文《RESTful API 设计指南 - 阮一峰》,这里不再赘述。

架构图

首先,这个项目分为前端和后端程序(前后端分离),前端需要数据时就调用后端提供的 RESTFul 风格的 API 接口获取数据。

其次,前后端分别暴露 3000 和 8080 端口提供服务,我通过 nginx 做了域名转发,将 /api/v1 为前缀的 http 请求转发给 8080 端口(后端程序),其它 http 请求转发给 3000 端口(前端程序)。这样对外可以统一用 80 端口提供服务,只不过请求进来时,nginx 根据 url 转发给对应的响应程序。

Watchman 架构图见下:

watchman.svg

持续集成&交付(CICD)

一个项目开发时间久了,往往要面对这样一个问题:每进行一次迭代,都要花费相同的时间精力对项目进行测试、构建、部署到线上环境,这部分操作其实是重复的。

由上节架构图也可以知道,将这个项目部署起来是需要一些操作的,比如编译 Golang 程序、打包前端 React 程序、配置 Nginx 等等。那么可不可以将这些部署操作记录下来,下次自动运行呢?答案是可以的。将它们写成一个 Dockerfile 就可以了,Dockerfile 可以告诉 Docker 如何将代码和环境统一打包,Docker 会一步一步的运行 Dockerfile 中的步骤,一切顺利的话,最后会生成一个 Docker 镜像,镜像中包含运行项目的一切环境和代码。这个时候,就可以一键将项目启动起来。

以上步骤可以认为是一次持续集成&持续交付(CICD)流程,它可以极大方便运维操作,避免一些重复劳动。为了实现 CICD,我利用容器和 webhook 技术对该项目进行改造。

最终的效果是我只要向代码仓库中推送一个名为 deploy 的 tag,就可以自动拉取代码构建成 Docker 镜像,并将镜像自动部署到云服务器上跑起来(线上环境)

具体的部署图见下:

watchman-docker.svg

我做的改造具体有:

  • Dockerfile,设定部署该项目需要哪些步骤;
  • 利用阿里云容器镜像服务,实现持续集成。在检测到代码仓库有指定的提交时,自动拉取代码,并构建成镜像;
  • 自建 Webhook Server,实现持续交付。阿里云容器镜像服务成功构建好镜像后,会推送一个事件到云服务器上的 Webhook Server,触发部署操作(自动拉取最新镜像,并运行它);

这里要自建 Webhook Server 的原因是阿里云容器镜像服务(以下简称 镜像服务)没有提供持续交付的功能,也就是说,在镜像构建完毕后,云服务器无法马上知道一个新的镜像构建好了,也就无法在服务器上触发自动部署,而 Webhook Server 可以实现这个功能。

Webhook Server 的原理实际上就是实现一个 web 接口,把这个接口(webhook url)填写到客户方即可生效,比如阿里云容器镜像服务,在构建好镜像后会主动调用 webhook url 通知 webhook server,就说我这边好了,跟你说一声,webhook server 收到通知后,会解析这个通知包含的具体信息,再判断是否触发后续操作(比如部署动作)。

实现 webhook server 的关键是能解析阿里云推送过来的通知内容,我找了下 github 没有能解析阿里云容器镜像服务现成的库,就自己实现了一个(很简单):elliotxx/webhooks

把这块拼图加上之后,借助阿里云容器镜像服务就可以实现简单的 CICD 流程,即本地做好一个代码变更后,本地 push 一个 tag,云上便会自动帮我做拉取代码、编译、测试、构建镜像、部署、启动服务等操作,然后什么也不用做等待 5 分钟,最新鲜热乎的项目就在线上跑起来啦。

阿里云容器镜像服务正在构建一个新的镜像:

屏幕快照 2019-11-03 下午5.53.29.png

镜像构建结束后,线上环境跑起来了一个新的容器:

屏幕快照 2019-11-03 下午5.58.24.png

访问控制

项目实现了基本功能之后,还需要做访问控制,不然谁都能访问这个 Web 应用,随意创建和删除定时任务,太不安全。

Watchman 使用一种简单的认证方式:BasicAuth,它的优点就一个:简单。

BasicAuth 不需要数据库,也不需要 cookie,在代码中配置好 BasicAuth 之后,访问接口网站会弹出一个认证窗口,输入用户名(user)和密码(password)后,会在请求头中加入一个 Authorization字段,它的值是用 base64 加密过的 user@password 字符串。后端接受到加密字符串后,会自动解密和认证,如果通过,会正常返回内容。

第一次访问,会弹出认证窗口(在未认证之前,是无法拿到数据的):

屏幕快照 2019-11-03 下午5.59.46.png

请求头中的 Authorization字段:

屏幕快照 2019-11-03 下午6.15.03.png

参考资料

【BasicAuth】

Using BasicAuth() middleware - github

https://github.com/gin-gonic/gin#using-basicauth-middleware

BasicAuth认证与Go

https://blog.csdn.net/puss0/article/details/81003400

http协议基本认证 Authorization

https://blog.csdn.net/u011181633/article/details/43229387

Go 教程:如何在 Gin 中实现 HTTP Basic Auth?

https://learnku.com/golang/t/24996

【websocket】

WebSocket 教程 - 阮一峰

http://www.ruanyifeng.com/blog/2017/05/websocket.html

前端vue后端go-gin实现websocket

https://juejin.im/post/5c36ea676fb9a049c30b71b6

Websocket 执行原理(看完就彻底理解了)

https://blog.csdn.net/word_joke/article/details/80222634

gin 框架中结合 gorilla 实现 webSocket

https://hacpai.com/article/1524469717858

原创声明

转载请注明:呓语 » 一个基于 Golang + React 的定时任务可视化配置网站的设计与实现