Egg框架学习笔记
解决问题的技巧
0、首先,你得有一台跑得飞快的机器,能让编译丝般顺滑
1、单步调试(比如 npm run tsc)
2、最简原则(起一个最简单的 egg 项目,对比自己的项目,从差异点找问题原因)
3、(二分)删除法
TypeScript 改造
Egg 结合 TS 开发,有一些不便利性,可以参考这个文章的思路进行改造。
Egg 官方也已经进行过一些对 TS 的适配了,详见官方文档。
整合 TypeScript 和 MySQL
egg 有提供 egg-mysql 的组件,但是如果项目中用了 TypeScript,那么使用 mysql 的时候,一定要注意,项目必须通过如下命令进行初始化:
1 | |
我踩过一个坑,就是我将一个之前的项目复制过来,然后安装 egg-mysql,按照网上的经验,在 typings/index.d.ts 中通过 declare 给 Application 追加了 mysql 属性,结果发现死活跑不通,一直报错:
1 | |
后来我通过npm init egg --type=ts初始化一个最基本的项目,就没有问题了。
MySQL 常用操作
官方文档已经描述得很清晰了,现在各种框架的数据库操作都大同小异,注意下注入问题,使用绑定的方式传入参数即可。
另外,开发阶段如果想要打印出执行的 SQL 语句,只需要在 config.default.ts 中,配置 SQL 连接参数的地方加上 debug=true 即可:
1 | |
定时任务
二种执行时机的配置模式:
interval 和 cron
二种运行模式:
- worker: only one worker per machine executes this scheduled task, the worker to execute is random.
- all: each worker on each machine executes this scheduled task.
开机执行:immediate
手动执行任务
1 | |
日志
注意日志文件的位置:
Schedule log will be written to ${appInfo.root}/logs/{app_name}/egg-schedule.log, but won’t be logged to terminal by default, you could customize via config.customLogger.scheduleLogger.
获取 HTTP 参数
详见官方文档。
注意取 url 中的参数和取 post 中的参数是不一样的,区分处理下。
后面我可以将这个做下优化,封装一层,应用的时候不用管参数是来自 url 还是 post 的实体。
中间件 middleware
文件名必须以小写字母开头
因为 typing 下生成的文件,默认会把 middleware 的首字母转为小写,如果你以大写字母开头来命名 middleware,会导致在调用的地方报该中间件不是一个 function 的错误。
单元测试
Mocha + power-assert
https://www.eggjs.org/intro/quickstart#adding-unit-testing
https://www.eggjs.org/zh-CN/core/unittest
脚本
通过单元测试的方式来执行
常见问题
老项目发布遇到的问题汇总
PS:我们还是得搞一个稳定可靠的镜像才行。
TypeScript 编译报错:升级容器的 Node 到 12.18.3
(健康检查)脚本不执行:将脚本的文件格式从 windows 改为 Unix
启动后触发 BackOff 重启:主动在 init 脚本中挂起进程
nginx 的配置文件没 copy 过去,也没启动 nginx:参考 exhibition 项目,主动在 bootstrap.sh 中拷贝 nginx 配置并在 init 脚本中启动 nginx
Dockerfile 中的 CMD 命令不执行:详见”docker 常用命令”这个博客,这是因为没弄清楚 CMD 命令执行顺序导致的,前面挂起了程序,最终的 CMD 走不到。
Illegal instruction /usr/local/openresty/nginx/sbin/nginx:容器平台的问题(宿主机太旧),重新发布
invalid csrf token
一般是因为开启了安全模块,但是发上来的请求并未带上 token 导致的。
可以选择带上 token,或者在config.default.ts中临时关闭安全验证来解决:
1 | |
Please set config.keys first
单纯看这个报错,是提示我们没有设置 config.keys,但是实际上我们程序中一般都会在 config.default.ts 中设置这个属性,然后其他的环境可以自动继承到该属性。
所以出现这个问题,往往是因为我们在 package.json 的 script 中,在启动程序的命令里面没有加上编译 ts 代码的功能,比如:
1 | |
正确的应该是这样的:
1 | |
需要在命令前面加上npm run tsc
编译时用到的 tsconfig.json,可以参考下面这个:
1 | |
程序起来后默认会创建 redis 连接
这是因为我们在 config/plugin.ts 中配置了 redis,如果不需要,将其注释掉,或者 enable 设置为 false 即可。
session 过大导致无法正常传递
我们经常会将用户信息(比如权限)放在 session 中,但是有如下问题:
1、如果用户信息太多,会导致 session 太大,而 egg 的 session 默认是通过 cookie 传递的,可能导致 cookie 达到 4KB 的浏览器存储上限;
2、如果 cookie 很多,则会使 header 大小超过服务器的处理的限制,会导致服务端 nginx 报错。
遇到这种问题,排查的时候一定要先看日志,包括 Nginx 的日志和 Egg 的控制台报警。
日志写入报错“too many open files”
这是因为我们对日志进行了自定义,但是我们是将自定义日志挂在了Context上,即请求级别的日志。这样如有多个请求就会打开多个文件句柄。而且我们程序中还有一些循环操作中,也记录了日志,导致日志写入较多。
后面我们将自定义日志改为了应用级别,并清理掉了冗余的日志信息,准备发布上去看看能否解决这个问题。
1 | |
参考文章:
https://github.com/eggjs/egg/issues/3331
代码正常,但是访问接口报 404
注意检查下是不是 controller 中的方法名和 controller 基类的方法重名了。
比如我出现过一次,在 controller 中定义了一个名为 config 的接口,而在 controller 基类中已经有名为 config 的方法了。
这种情况 Egg.js 是不会给用户进行明确的错误提示的,需要自己注意下。
设置 cookie 失败
查看触发了设置 cookie 的请求,从响应头可以看到这个信息:
1 | |
经过排查,是因为程序中设置了 config.cookies = { secure: true },该配置的 Egg 官方文档如下:
1 | |
我们的请求是通过 http 协议发上来的,不是一个 secure 的请求;自然就无法设置成功了。
考虑到我们现在新的域名是内网域名,只有 http 协议,因此我把 secure 修改为 false 以解决该问题。
删除 cookie 失败
类似上一个问题,经排查,发现是因为设置了 cookie 的 sameSite = ‘None’导致的,去掉这个设置即可。
容器发布后,不断 BackOff 重启
如果发现日志末尾有这几行,那多半是因为没有主动挂起导致的:
1 | |
需要在 init 脚本中增加挂起功能:
1 | |