当前位置: 首页 > 后端技术 > Node.js

如何将你的ThinkJS项目部署到ZEIT

时间:2023-04-03 11:03:48 Node.js

什么是ZEITZEIT是一个免费的云平台,支持部署静态网站和Serverless功能。Serverless是近几年比较流行的一个概念。简单理解就是你只需要实现具体的业务逻辑,而最终服务相关的服务器和HTTP服务则由第三方管理。无服务器也称为FaaS(功能即服务)。因为业务粒度非常细,非常方便动态扩容等自动化运维任务。//一个简单的基于Node.js的Serverless函数module.exports=function(req,res){const{name='World'}=req.queryres.send(`Hello${name}!`)}通过ZEIT现在提供的CLI工具,我们可以通过一条命令将Node.js、Golang、Python、Ruby、PHP、Rust等语言应用部署到ZEIT。如果您想了解更多关于ZEIT公司的信息,也可以阅读这篇知乎回答了解更多。如何使用ZEIT注册非常方便。打开https://zeit.co,点击右上角“免费加入”。使用您的Github或Gitlab帐户登录后,您将自动注册。当然,您也可以使用您的邮箱进行注册,系统会向您的邮箱发送一封确认邮件。登录后会要求填写昵称、头像、唯一ID等配置。选择Continue后,如果通过邮箱登录,会询问是否需要绑定Github账号,可以让Github和ZEIT的持续集成更加方便。当然你也可以选择SKIP跳过。最后一步将指导您如何创建项目。它提供了许多快速创建的模板,例如Next.js、React、Vuepress、Gatsby、Docz、Nuxt.js、Svelte、Angular。按照示例使用npminstall-gnow安装CLI工具。初始化项目后,使用now命令将其发布到ZEIT。整个过程非常简单。部署Koa.js服务通过刚才的例子,我们可以了解到它的本质是将HTTP请求的request和response传入方法,处理后返回给HTTP,所以也完全支持Koa.js除了Serverless功能和基于Koa.js的ThinkJS服务部署。我们先来看看如果我们要部署一个Koa.js服务应该怎么做。fork快速部署由于ZEIT正式推广Serverless服务,去掉了Node.js的脚手架模板,所以我们只能自己创建项目。为了方便,我提供了一个DEMO仓库https://github.com/lizheming/now-koa-demo。如果刚才在注册过程中绑定了Github账号,可以选择直接fork仓库,过一会你会收到ZEIT的Github通知,告诉你网站已经部署成功,并在commit中提供部署地址之后。命令行部署如果没有绑定Github账号也没关系,我们可以通过命令行来部署服务。克隆DEMO仓库后,直接使用now命令。部署成功后,ZEIT会返回一个当前提交版本的唯一地址。比如打开https://now-koa-demo-pac7dbxrf.now.sh/后,会看到Hellofromkoa.js!的返回信息。注意index.js文件的内容与普通的Koa.js项目代码相同。唯一不同的是,最终的项目并没有直接调用app.listen()方法进行监听,而是使用module.exports=app.callback()将最终的回调方法返回。我们知道app.callback()方法返回一个函数,该函数接受请求和响应对象作为参数,这让我们回到文章开头的例子。我们再看一下now.json的内容。此JSON文件用于告诉now服务需要使用@now/node运行时执行index.js文件,并且需要将所有请求转发到index.js文件。听起来很像Nginx上的东西?{“版本”:2,“构建”:[{“src”:“index.js”,“使用”:“@now/node”}],“路线”:[{“src”:“/(。*)","dest":"/index.js"}]}部署ThinkJS服务成功部署Koa.js服务后,让我们看看如何为您的ThinkJS服务寻找空闲空间进行部署!为了方便,我也提供了一个DEMO仓库https://github.com/lizheming/now-thinkjs-demo,fork这个仓库可以快速体验Now部署ThinkJS服务。fork成功后,部署成功后会有提示,同时告知你部署后的唯一地址,如https://now-thinkjs-demo-hrmqxxv2p.now.sh/。然而,这只是我成功的结果。直接部署基于ThinkJS的服务并不像部署Koa.js服务那么简单,这主要是由ThinkJS框架本身的特性决定的。下面我介绍一下需要注意的点,方便其他已有服务的迁移。我们先来看看ZEIT平台的ThinkJS启动文件的内容。然后我们主要描述遇到的问题以及为什么要基于这个文档来做。constpath=require('path');constApplication=require('thinkjs');constLoader=require('thinkjs/lib/loader');classNowLoaderextendsLoader{writeConfig(){}}constapp=newApplication({ROOT_PATH:__dirname,APP_PATH:path.join(__dirname,'src'),VIEW_PATH:path.join(__dirname,'view'),proxy:true,//使用代理env:'now',external:{log4js:{stdout:path.join(__dirname,'node_modules/log4js/lib/appenders/stdout.js'),console:path.join(__dirname,'node_modules/log4js/lib/appenders/console.js')},static:{www:path.join(__dirname,'www')}}});constloader=newNowLoader(app.options);loader.loadAll('worker');module.exports=function(req,res){返回think.beforeStartServer().catch(err=>{think.logger.error(err);}).then(()=>{constcallback=think.app.callback();returncallback(req,res);}).then(()=>{think.app.emit('appReady');});};服务启动问题刚刚部署Koa.js的时候我们知道了,ZEIT运行时接受的文件需要返回一个函数,在Koa.js中是app.callback(),在ThinkJS中是think.app.callback()。但是我们不能这样直接返回,因为从源码中我们可以知道ThinkJS服务启动做了以下事情:初始化Loader实例,在相应的进程上加载需要的文件,包括config,middleware,controller,logic,model,service等。在服务启动前执行beforeStartServer()启动hook,服务启动后全局发送appReady事件。目前ThinkJS服务中还没有纯非启动方法来包含这些内容,所以我选择在启动脚本中模拟正常的启动过程。自定义启动进程的方式。由于多进程逻辑稍微复杂一点,我直接用单进程模式模拟。实例化Loader,使用loader.loadAll('worker')加载所有依赖文件,在回调中执行beforeStartServer(),在启动hook前执行callback(),启动服务,向全局文件引用问题发送appReady事件。我们知道工程文件的相对引用ThinkJS的本质是文件夹是路由模式。Controller、Model、View等文件按照一定的文件夹规则放置,通过动态读取文件找到对应的文件并加载执行。这在正常的项目中是没有问题的,但是为了节省空间,ZEITNow平台会忽略入口文件中不显示依赖关系的文件。我们正常的启动文件中只定义了APP_PATH,而在src/config/adapter.js和src/config/middleware.js中定义了VIEW_PATH甚至是静态资源目录。而这两个文件是通过动态读取文件的方式导入的,导致上传时因为没有显式依赖文件而上传不上文件。所以为了解决这个问题,我选择在启动文件中重新显示和声明需要加载的文件。当然,这些配置对于ThinkJS来说是没有用的。从依赖文件的相对引用可以看出,除了正常工程文件的引用外,我还写了两个log4js文件的引用。为什么?主要是为了节省体量,ZEIT不仅会限制只上传需要的文件,还会使用webpack对入口文件进行打包。用webpack打包后,所有的依赖都在入口文件中,不需要再上传一个庞大的node_modules文件夹,可以大大减小体积。ZEIT开源了Node.js项目打包成单个文件的打包工具https://github.com/zeit/ncc如果项目需要打包成单个文件以减小体积,也可以使用。在很早的log4js版本中,以require(./${type})的形式加载对应的logoutputter。由于打包后目录结构发生变化,打包后当前文件夹中没有对应的文件,所以执行时会报找不到文件的错误。所以为了解决这个问题,还需要在入口文件中显式声明这些文件的依赖关系。去年二月,一个用户通过显式依赖所有加载器,然后选择它们来解决这个问题。所以这个问题在新版本的log4js中已经不存在了,不过我还是在这里说明一下,因为项目中引用的其他依赖也可能存在这个问题,还是需要注意一下。写权限问题除了上面的问题,我在部署的时候还遇到了没有写文件权限的问题。由于ZEITNow提供的是无状态服务,ZEIT中禁止写文件等副作用操作。如果有文件写入操作,会在控制台提示写入失败并抛出错误。在ThinkJS中,由于配置文件比较多,为了方便排错,会在加载配置文件后调用writeConfig()方法写入一个最终合并后的配置到runtime目录下,比如runtime/config/production.json文档。这种情况下ZEIT平台会报错,无法正常启动服务。但是ThinkJS目前没有提供可以取消写这个配置文件的操作的配置。所以我提供的方案是通过继承重写writeConfig()方法来组织文件写入操作。当然,这是ThinkJS本身的文件写入操作。如果你的项目中还有其他的文件写入操作,你也需要做相应的操作。比如logger日志的配置可以输出到控制台,文件上传等必须写入文件的可以写入系统临时目录/tmp。不同系统的临时目录可能不同。在Node.js中,推荐通过require('os').tmpdir()获取。后记通过ZEIT平台,部署Node.js服务的成本大大降低,不仅是机器成本,还有维护成本。其实部署一个普通的Node.js项目是很方便的。主要原因是ThinkJS的依赖引用不显式,导致打包有些困难。其他的还是很方便的。如果您有任何其他问题,请随时与我们沟通。参考:如何通过ZEIT快速轻松地部署免费的Node.js项目?