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

news2025/1/17 4:05:57

Image

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 后运行良好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/378792.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用canvas画一个炫酷的粒子动画倒计时

前言 😆 这是一篇踩在活动尾声的文章,主要是之前在摸鱼社群里有人发了个粒子动画的特效视频,想着研究研究写一篇文章出来看看,结果这一下子就研究了半个多月。 😂 下面就把研究成果通过文字的形式展现出来吧&#xf…

Compact 调优实例

1.问题描述 10月27号,用户反馈 g_feature 资源组的回溯任务在夜间的耗时比较大。在00:49——04:16期间,查询的平均耗时是大于100ms的。 2. 分析原因 根据问题现象,在夜间的耗时比较大,白天的耗时比较小,首先想到的就…

【电子通识】为什么产品出厂前要进行高温老化?

产品在出厂时往往会进行高温老化测试,那出厂前高温老化的目的是什么?可靠性定义首先我们要知道产品的可靠性,可靠性的定义是指产品在规定的条件下能够正常运行达到产品寿命的概率。如下公式表示,其中,λ表示固有故障率…

一起了解井用污水采样器——让井下污水采样更人性化

井用采样器的工作环境比较特殊。比如:雨水管网、窨井、污水井、排污口、下水道,海洋、河流、沟渠等恶劣狭小的环境。这就要求采样设备小巧灵活,方便环境检测执法检查人员在排污井、检查井、雨水管网等特殊环境中进行水质采样。 **井用采样器主…

二叉树路径查找

题目描述:给定一棵二叉树(结构如下),其中每个节点值为整数。给定一个值 K,求所有满足如下条件的路径并将路径上节点的值打印出来: 1、路径方向必须向下,即只能从父节点指向子节点 2、路径并不是必须从根节点开始或在叶…

21- 神经网络模型_超参数搜索 (TensorFlow系列) (深度学习)

知识要点 fetch_california_housing:加利福尼亚的房价数据,总计20640个样本,每个样本8个属性表示,以及房价作为target 超参数搜索的方式: 网格搜索, 随机搜索, 遗传算法搜索, 启发式搜索 函数式添加神经网络: model.add(keras.l…

Python可视化界面编程入门

Python可视化界面编程入门具体实现代码如所示: (1)普通可视化界面编程代码入门: import sys from PyQt5.QtWidgets import QWidget,QApplication #导入两个类来进行程序界面编程if __name__"__main__":#创建一个Appl…

探索ChatGPT背后的网络基础设施

ChatGPT是OpenAI公司开发的一款聊天机器人应用,自2022年11月推出以来以迅雷不及掩耳盗铃之势火爆全球。ChatGPT不仅可以模仿人类对话,还可以创建音乐、电视剧、童话故事和学生论文,甚至是编写和调试计算机程序。 截至2023年1月,C…

如何打造自己的小程序生态?

2021 年全网小程序数量就已超 700 万,从微信开始,到其他各大平台,如抖音、支付宝,小程序发展迅猛,2023年小程序仍有着巨大的发展潜力。 现在。人们逐渐发现,日常的生活、出行、购物各个方面都越来越离不开…

CAN工具-VSpy(ValueCAN) - Panel面板

在介绍CANoe工具的时候,有介绍过Panel面板的使用,同样,在VSpy软件工具中,也有同类型的工具可供使用 - Graphical Panels,同样也能提供一个控制面板,然后我们通过连接信号实现不同的控件,已达到我…

极验4参数分析

目标链接 aHR0cHM6Ly9ndDQuZ2VldGVzdC5jb20v接口分析 开发者人员工具进行抓包,刷新页面,抓到了一个名为 load?captcha_idxxx 的包,Query String Parameters 包含了一些参数 captcha_id:验证码 id,固定值&#xff0c…

如何使用AzureGraph通过Microsoft Graph收集Azure活动目录信息

关于AzureGraph AzureGraph是一款针对Azure活动目录的信息收集工具,该工具基于Microsoft Graph实现其功能。多亏了Microsoft Graph技术,AzureGraph才能从Azure活动目录获取各种信息,如用户、设备、应用程序、域等。 此应用程序允许我们通过…

一次性搞定 `SHOW SLAVE STATUS` 的解读

一次性搞定 SHOW SLAVE STATUS 的解读 解析日志文件的位置 诚然, GTID(全局事务标识符)已经在 MySQL 5.6中得到支持, 此外,还可以通过 Tungsten replicator 软件来实现(2009年以后一直有谷歌在维护,不是吗?)。 但有一部分人还在使用MySQL 5.5的标准副本方式, 那么这些二进制日…

20道经典自动化测试面试题

概述 觉得自动化测试很难? 是的,它确实不简单。但是学会它,工资高啊! 担心面试的时候被问到自动化测试? 嗯,你担心的没错!确实会被经常问到! 现在应聘软件测试工程师的岗位&…

前端经典react面试题及答案

为什么 React 元素有一个 $$typeof 属性 目的是为了防止 XSS 攻击。因为 Synbol 无法被序列化,所以 React 可以通过有没有 $$typeof 属性来断出当前的 element 对象是从数据库来的还是自己生成的。 如果没有 $$typeof 这个属性,react 会拒绝处理该元素。…

docker搭建redis集群模式

目录docker 安装redis1.创建redis.conf开启redis验证(开启密码)允许redis外地连接后台启动开启redis持久化2.启动redis容器3.进入容器redis集群3主3从1.新建6个redis容器2.构建主从关系3.查询集群信息4.主从扩容5.主从缩容docker 安装redis 1.创建redis.conf 开启redis验证(开…

第四阶段-12关于Spring Security框架,RBAC,密码加密原则

关于csmall-passport项目 此项目主要用于实现“管理员”账号的后台管理功能,主要实现: 管理员登录添加管理员删除管理员显示管理员列表启用 / 禁用管理员 关于RBAC RBAC:Role-Based Access Control,基于角色的访问控制 在涉及…

Feign Ribbon Hystrix 三者关系

在微服务架构的应用中, Feign、Hystrix,Ribbon 三者都是必不可少的,可以说已经成为铁三角。 Feign 介绍 Feign 是一款Java语言编写的 HttpClient 绑定器,在 Spring Cloud 微服务中用于实现微服务之间的声明式调用。Feign 可以定…

IIC子系统

文章目录引言一、I2C 总线驱动框架二、I2C驱动框图(重点)三、I2C 子系统软件框架3.1 I2C子系统的4个关键结构体3.2 I2C总线与平台总线的结合3.3 在设备树信息添加i2c从设备3.4 新增加i2c从设备四、i2c driver驱动的编写4.1 陀螺仪和加速度工作原理4.2 mpu6050的寄存器信息和设置…

Synchronized的锁升级过程

Synchronized的锁升级过程 synchronized锁升级过程:在synchronized中引入了偏向锁、轻量级锁、重量级锁之后,当前具体使用的是synchronzed中的那种类型锁,是根据线程竞争激烈程度来决定的。 偏向锁:在锁对象的对象头中记录一下当…