基于 Serverless 重构编程学习小工具

发布于:2020 年 6 月 28 日 17:05

基于Serverless 重构编程学习小工具

很久之前,我做过一个在线编程的软件,目前用户量大概有几十万,通过这个 APP 不仅仅可以进行代码的编写、运行,还可以进行编程的学习。

我自己一直对 Serverless 架构情有独钟,恰好赶到这个 APP 学习板块被很多人吐槽难用,所以索性就对学习板块进行了重构,并将这个学习板块直接搬上 Serverless 架构。

直接基于 Serverless 架构重构,主要是出于两个方面考虑,第一是 Serverless 架构在很多时候会让个人开发者的运维工作变得简单,不用再关心服务器健康、不用关心流量洪峰;二是 Serverless 架构是按量付费,虽然在一定程度上,Serverless 架构会让所依赖的产品维度变多,但是实际,只要控制、评估得好,成本节约效果是非常显著的。

整体设计

数据库设计

之前,数据库设计是若干个大模块,现在我们把它们统一到一个模块中进行项目重构。

数据库中有四个基本模块:新闻文章、开发文档、基础教程以及图书资源。其中开发文档包括大分类、子列表以及正文等内容,表关联并没有使用外键,而是直接用的 id 进行表之间的关联。

说实话,这个数据库设计的并不是很好,初次构建数据部分时,绝大部分数据都是从其他站点采集而来的,当时为了快速上线,便直接按照原有格式存储,所以数据库中有很多表的字段其实是无效的,或者说针对这个项目是未被使用的。

后端设计

后端会整体部署到一个函数上,整体功能结构如下:

基于Serverless 重构编程学习小工具

整体功能就是云函数绑定 API 网关触发器,用户访问 API 网关指定的地址,触发云函数,然后函数在入口处进行功能拆分,请求不同的方法获得对应的数据。

需要额外说明的是,我把后端整体接口都部署在一个函数,是因为这个模块的使用量并不是特别频繁,部署到一个函数上也不会出现超过最大实例限制的情况,如果超出限制是可以申请扩容的;其次,所有的接口都是对数据库进行增删改查,放入到一个函数中,在一定程度上可以保证容器的活性,降低部分冷启动带来的问题,同时容器的复用,也可以在一定程度上降低后台数据库链接池的压力;除此之外,所有的接口功能都只需要最少的内存(64M)即可完整运行,不会因为个别接口的预估内存较大,进而影响整体的成本。

前端设计

前端设计,经过评估,我预计学习资源部分需要有 8 个页面,包括科技类新闻、教程、文档、图书等相关功能,通过墨刀绘制的原型图如下:

基于Serverless 重构编程学习小工具

前端项目开发采用了 Vue.js,并将其部署到对象存储中,通过腾讯云对象存储的静态网站功能对外提供服务。

项目开发

后端函数开发

后端函数开发主要包括三部分:

  • 部分资源的初始化:这部分需要在函数外进行,复用实例的时候不会再次建立连接,防止数据库连接池出现问题:
复制代码
def getConnection(dbName):
conn = pymysql.connect(host="",
user="root",
password="",
port=3306,
db=dbName,
charset='utf8',
cursorclass=pymysql.cursors.DictCursor,
)
conn.autocommit(1)
return conn
connectionArticle = getConnection("anycodes_article")
  • 数据库查询操作:这部分主要就是针对不同接口查询数据库,例如获取文章分类:
复制代码
def getArticleCategory():
connectionArticle.ping(reconnect=True)
cursor = connectionArticle.cursor()
search_stmt = ('SELECT * FROM `category` ORDER BY `sort`')
cursor.execute(search_stmt, ())
data = cursor.fetchall()
cursor.close()
result = {}
for eve_data in data:
if eve_data['pre_name'] not in result:
result[eve_data['pre_name']] = []
result[eve_data['pre_name']].append({
"id": eve_data["sort"],
"name": eve_data["name"]
})
return result

例如获取文章列表:

复制代码
def getArticleList(cid):
connectionArticle.ping(reconnect=True)
cursor = connectionArticle.cursor()
search_stmt = ('SELECT * FROM `article` WHERE `category` = %s ORDER BY `sort`')
cursor.execute(search_stmt, (cid,))
data = cursor.fetchall()
cursor.close()
result = [{
"id": eve_data["aid"],
"title": eve_data["title"]
} for eve_data in data]
return result
  • 函数入口:主要是实现功能分发和接口识别:
复制代码
def main_handler(event, context):
try:
result_data = {
"error": False
}
req_type = event["pathParameters"]["type"]
if req_type == "get_book_list":
result_data["data"] = getBookList()
elif req_type == "get_book_info":
result_data["data"] = getBookContent(event["queryString"]["id"])
elif req_type == "get_daily_content":
result_data["data"] = getDailyContent(event["queryString"]["id"])
elif req_type == "get_daily_list":
result_data["data"] = getDailyList(event["queryString"]["category"])
elif req_type == "get_dictionary_result":
result_data["data"] = getDictionaryResult(event["queryString"]["word"])
elif req_type == "get_dev_content":
result_data["data"] = getDevContent(event["queryString"]["id"])
elif req_type == "get_dev_section":
result_data["data"] = getDevSection(event["queryString"]["id"])
elif req_type == "get_dev_chapter":
result_data["data"] = getDevChapter(event["queryString"]["id"])
elif req_type == "get_dev_list":
result_data["data"] = getDevList()
elif req_type == "get_article_content":
result_data["data"] = getArticle(event["queryString"]["id"])
elif req_type == "get_article_list":
result_data["data"] = getArticleList(event["queryString"]["id"])
elif req_type == "get_article_category":
result_data["data"] = getArticleCategory()
return result_data
except Exception as e:
print(e)
return {"error": True}

函数部分完成之后,可以配置 API 网关部分:

基于Serverless 重构编程学习小工具

学习功能模块几乎都是对数据库进行查询的操作,所以在整个开发过程中,没有遇到太大的问题,完成得比较顺利。

效果预览

整个项目共包括十几个页面,这里截取了 8 个主要页面做效果展示:

基于Serverless 重构编程学习小工具

整个页面基本还原了设计稿的样子,并与原有项目进行了部分整合,无论是列表页面还是图书页面等,数据加载速度表现良好。

通过 PostMan 进行基本测试:

基于Serverless 重构编程学习小工具

对接口进行 1000 次访问测试:

基于Serverless 重构编程学习小工具

接口表现良好,并未出现失败的情况,对该测试结果进行耗时的可视化:

基于Serverless 重构编程学习小工具

其中最大的时间消耗是 219 毫秒,最小是 27 毫秒,平均值是 35 毫秒,整体的效果还是非常不错。

项目开发完成,上线之后,前端部分会被放到对象存储中,后端业务被放到函数计算中,触发器使用的是 API 网关,在监控层面,函数计算有着比较不错的监控纬度:

基于Serverless 重构编程学习小工具

而函数并发,弹性伸缩等问题都由云厂商来解决。可以这样说,自从这个组件部署到了 Serverless 架构上,我所做的操作就只剩下了当业务代码有问题,进行简单修复和简单维护。

通过按量付费,可以看到我后端服务产生的费用:
基于Serverless 重构编程学习小工具

由于云函数没办法看到单个资源的费用,所以在计算时选择了整体花费,一个月的花费要比使用服务器便宜很多。当然,API 网关和对象存储的费用要不能忘记:

基于Serverless 重构编程学习小工具

项目中的 API 网关包括了很多服务,不仅仅 Anycodes 一个服务产生的,但是整体加一起 2 月份只有 1 元钱,相对来说也是蛮低的。

总结

通过个人项目中的一个子模块重构过程,将该项目部署到 Serverless 架构上:

  • 整个开发过程中是比较轻松的,一方面自己不需要在服务器中安装各类软件,也不需要搭建 web 服务,不需要对 web 服务进行优化,做的只是读取数据库,按照一定的格式进行 return,而 web 服务等相关模块交给 API 网关来实现,整个后端开发大概耗时一个多小时;前端开发是比较耗时的,因为我个人不是专业做前端的,所以无论是布局还是逻辑开发都有点障碍,不过也只用了 2 天时间。所以,整个模块从开发到上线只用了 2 天时间;

  • 项目在部署的时候非常流畅,基于 Serverless Framework 的开发者工具一键部署,后期更新维护,只需要重新部署即可,线上也是无缝切换,不会出现更新服务造成的服务中断,也不用为更新服务可能造成服务中断而做额外的操作,后期更新过程快速且简单易用;

  • 资源消耗部分是使用按量付费,通过一个月的观察,整个资源消耗是蛮低的,在整体性能保证的同时,也逐渐降低了成本,对于个人开发者来说,确实是一个福音。

阅读数:2 发布于:2020 年 6 月 28 日 17:05

评论

发布
暂无评论