3月来了,给自己做一个简单的nodejs后端技术总结

3月来了,给自己做一个简略的nodejs后端技能总结

  • 3月来了,给自己做一个简略的nodejs后端技能总结
    • 完全重构
      • 数据库切换迁移
      • Why Nestjs?
      • prisma or typeorm?
      • serverless 函数辅佐
      • GraphQL
      • Github Action CI/CD
      • 布置 tensorflow 模型

我又滚回来写文章了,从上一年11月底到今年2月底,算起来整整1/4年没有写博客了,自己的博客站都长满了坟头草,我在上面跳舞。在这3月来临之际,咱们仍是来聊聊技能,就当给自己做个阶段性总结。

完全重构

这几个月我阅历了一次对自己前后端栈的一次完全的重构。由于日益增长的需求,旧有的技能架构许多变成了技能债,拖累着后续的开发保护作业,来看看我详细做了哪些吧:

  1. 数据库切换 mongodb -> postgres
  2. 后端结构切换 koajs -> nestjs
  3. mongodb native client -> typeorm
  4. serverless -> serverless & docker compose swarm mode
  5. restful API -> restful API & graphQL
  6. github action CI/CD
  7. tensorflow 内容合规性查看(说白了便是鉴黄)

数据库切换迁移

mongodb 是个好东西,许多场景下运用起来十分便利。可是一天,我阅览几个月前写的代码的时分,突然感觉它很丑恶,尤其是聚合查询,一个aggregate管道,中心还夹杂着几个 $facet 几个 $replaceRoot,里边作为参数的索引字符串里,还套了许许多多的$与预留关键字。

阅览完后,我开始思考人生,我想起了那天在落日下的感叹,啊!那个少年竟然写出了如此精妙的聚合代码!而现在再与它两眼相望,竟无语凝噎。所以我决断把这段狗屎代码注释了。

当然我知道这也不怪 mongodb,这马是好马,只是骑的人没本事而已。经过考虑之后,为了代码的 TypeStrong 考虑,仍是挑选了 RDS。所以选用了postgres,依托之前一定的 MS SQL Servermysql 的运用经历,仍是十分好上手的。加上运用了ORM结构,躲藏了一部分直接写SQL的数据库操作。也写了一些脚本,定时pg_dump备份啥的,暂时够用,其他高阶用法,比方像 supabase 里什么行数据权限啥的就不是很了解了。

Why Nestjs?

把后端结构换了,为啥换 Nestjs 而不是根据koaegg,midway?究其根本是生态好,加上不是KPI项目。

生态好,体现在高质量的官方包与丰厚的第三方最佳实践,别的其他开发人员踩过的坑多,好学习(抄)也是一个重要因素。

别的,我以前写 ASP.NET MVC/Core 的时分,就很喜爱 IOC DINestjs的核心便是这套机制。何况即使我能够运用 inversify 自己设计一套,但这哪有白嫖的香呢?

而且比较koa-compose那种,遇事不决middleware的开发办法。Nestjs提供了更多细粒度的切面办法,协助咱们更好的 aop,开发体会适当舒服。

prisma or typeorm?

当时在选型的时分,两头都看了一下文档,而且也都forkexamples项目跑了一下,终究挑选 typeorm 了,原因有以下几点:

  1. prisma 界说实体对象,靠的是它自己的DSL。虽然这个DSL通俗易懂,可是schema.prisma这玩意实际上和 schema.gql 这类相同,在运用调试和测试的时分,都需求修修补补的。与其多个 DSL 一同修修补补,不如我一份 entity 运用 meta data 一起生成 db schemagql schema
  2. typeormnestjs 的适配性适当的好
  3. prisma client 需求初始化,一起在 schema 产生改变后,需求从头生成 (prisma generate)。

出于这些原因,挑选了 bug 仍然大堆的 typeorm,还妄想着经过界说一份 entity,能够派生出 dto,input,model…这些其他的对象。实践事实证明,是可行的,可是需求你对元数据有比较准确的掌控,比方:

import { OmitType, PartialType, PickType, IntersectionType } from '@nestjs/mapped-types';
import { OmitType, PartialType, PickType, IntersectionType } from '@nestjs/swagger';
import { OmitType, PartialType, PickType, IntersectionType } from '@nestjs/graphql';

这些办法,它们各自取,或许说运用的元数据(meta data)是不咋相同的,(根本的原因来自于取的元数据的key的不同,这个藏在每个包的 *.constants.ts文件中),你需求根据你的需求,来调用对应包的中的对应办法。

serverless 函数辅佐

替换后端结构的背后,也付出了一些价值。

当我把 Nestjs 运用布置到阿里云函数计算/腾讯如此函数的时分,这个冷启动时刻远超 koa/express 这类运用,后来我又把整个 Nestjs app 经过 webpack进行打包压缩,再次进行布置测试。成果也只是稍稍优化了一点点冷启动时刻,总的时刻仍是差强人意。这也是我挑选自己买服务器布置的原因所在了。

关于现在 serverless 状况,许多实践还都是把传统的 web server, 或许经过http triggereventshim,或许经过端口监听,又或许经过 docker image 布置的办法布置到单个函数的。这种遇到事务规模,体量稍微大点的,就很难满足实时性的需求。

假如你说保存实例,那我为啥不自建? 别的多函数布置运维的serverless结构也有,可是投入这么大精力搞这些玩意,效果和产出还不如用k8s,你请得起高薪的程序员,难道不舍得花钱买十几二十台服务器吗?

可是这不意味着我要抛弃 serverless,只是让它从打二号位,转移到了五号位,打打辅佐或许快速成型仍是适当好用的。何况只需大家还在一个 VPC(私有网络) 里边,通信是比较便利的,打打辅佐更是称心如意。别的我没有上 k8s 的原因,核心原因是swarm够用,根本原因是我不会。

GraphQL

这次总算把 GraphQL 加上了,前端爽歪歪,后端爽歪歪,什么?前端后端都是我?这下苦逼了。

首要,由于 GraphQL SDL schema 会按照图这样的数据结构进行分析来调用咱们界说的字段节点处理办法,导致咱们在界说通信 model的时分,一旦遇到相互关联的其他 model,就需求界说这个关系的处理办法,即 ResolveField Function。可是这种处理办法会带来一个问题,原先咱们写 restful的时分,去取有关联的数据,几个 LEFT JOIN 就完事了,可是到了 GraphQLresolver 里边,由于 ResolveField Function 的独立性,它从一次数据库查询,变成了屡次数据库查询,获取成果后再进行处理组合,变成前端想要的对象结构。

在这种状况下,数据缓存就显得及其重要了,别的缓存的key也有必要很好的体现数据的特征。所以我决断拉取了 redis/redis-stack-server:latest,仍是适当好用的。

别的有一点,个人不是很了解,感觉应该分隔界说有可能很大的字段在不同的 model 里。举个例子,数据库有张表,表里边有个字段,每一数据行,这个字段的数据大小都超过了 1MB。然后后端去取数据的时分,又是 SELECT *,这很明显数据一多,内存就爆掉了呗。可是关于前端来说,我又没有去取你这个字段,你为啥要去取这个字段呢?所以后端也应该根据前端传过来的 gql句子,去预估数据的projection

Github Action CI/CD

现在我用的是云主机布置的,代码在 Github 私有库房,那么有啥办法能够稍稍优化一下现在的布置办法呢?

这儿我的做法不是很好,可是我有必要说出来,这样大牛看到我的方案之后,才能对我进行降维冲击。

首要我的前后端布置方案,都是依赖各个云的,出于前史原因,我的前端OSS,CDN这些在腾讯云上。我的云函数,域名,服务器,还有一些其他在阿里云上。别的手里还有一台华为如此主机。

首要是后端布置,我必定不会在云服务器把后端的代码全部拉下来,打包编译运行的,这样又蠢又笨重。

在后端的代码库房里,我只是保存 Dockerfiledocker-compose.yml,一个用来打镜像,一个用来起本地配套的服务,比方我会拉 redispostgres 的镜像,本地起几个用来开发和调试,还有数据库 schema 的同步和本地 migration 的预演。

别的我还有一个布置库房,里边用来寄存装备,比方 nginx.conf,各种ssl证书,环境变量,加上一大坨 shell脚本和不同的 compose文件,这些 compose 会映射各种容器卷这个目录下。

这样,我拿到一台机器之后,首要要做的,便是生成 ssh key,然后把 key 注册进 github,给它对应库房的拜访权限。拉下来之后,直接 docker compose up -d 就布置成功了。

当然,状况必定不这么简略的,因为布置库房里,寄存的compose文件,里边都写的都是阿里云私有镜像的地址,所以还要 login 一下。

别的我还运用 Github Action 来进行镜像的打包,并推送到私有库房,这一块十分简略,懒得说了。还有一个留意点便是,每次推送镜像,都会生成一个新的镜像 tag。那如何在推送之后,动态改变别的那个布置库房的 IMAGE_TAG 呢? 实际上也很简略,在布置库房也建立一个 Github Action 用来被动的接纳,后端库房推送完成后的 repository_dispatch里的参数就行。

参数(payload)里,寄存着推送完成后的 tag,然后执行一行:

sed -i.bak '/^IMAGE_TAG=/s/=\w*/='${{ github.event.client_payload.tag }}'/' .env

然后 git commit 就搞定了。这样每次后端库房镜像一推送,布置库房的IMAGE_TAG就实时改变,云主机只需从头 git pull 然后 docker compose up -d 布置就完成了。

至于前端布置,这就看是 ssr,仍是 spa/ssg 这类的,这块之前搞 vue/react 运用开发,用 nuxt/next的比较多,这儿就不继续说了。

布置 tensorflow 模型

之前我的技能群里,有小伙伴说它的小程序因为涉黄被封了,原因是用户自己上传了黄色图片,然后自己截了图,举报了这个小程序,然后就被微信封了。

所以我决议仍是要防一手的,咱练习模型不会,工程化才能仍是有一点点的,所以起码用这种仍是十分简单的。

简略到,在 github 里找到并下载模型,然后本地加载调用就ok了。不过我在运用中,遇到了一个坑点便是,不能运用 alpine 作为基座,详细原因能够看这个 issue/1425,所以替换成了 debian-bullseye-slim 后运行杰出。