view如何接受json_如何将你的 ThinkJS 项目部署到 ZEIT 上

编者按:本文作者奇舞团前端开发工程师李喆明。

什么是 ZEIT

ZEIT(https://zeit.co) 是免费的云平台,支持部署静态网站以及 Serverless 函数。Serverless 是近几年比较火的概念,简单去理解就是你只需要去实现具体的业务逻辑,而与最终服务相关的服务器、HTTP 服务等则由第三方管理。Serverless 又被称为 FaaS(函数即服务),由于业务粒度非常细,所以非常方便做动态扩容等自动化运维任务。

//一个最简单的基于 Node.js 的 Serverless 函数

module.exports = function(req, res) {

  const { name = 'World' } = req.query

  res.send(`Hello ${name}!`)

}

通过 ZEIT 提供的 CLI 工具 now,我们可以一条命令将 Node.js, Golang, Python, Ruby, PHP, Rust 等语言的应用部署到 ZEIT 上。如果你想了解更多关于 ZEIT 这个公司的知识也可以看这篇知乎回答(https://www.zhihu.com/question/59278159/answer/163585410)了解更多。

如何使用 ZEIT

注册非常方便,打开 https://zeit.co 点击右上角的 "Join Free",使用 Github 或者 Gitlab 账号登录后会自动注册。当然你也可以使用邮箱注册,会发送一封确认邮件到你的邮箱。登录后会让你填写昵称、头像和唯一 ID等配置。

e086397429569588ea5e2cd6f082ccb1.png

选择 Continue 之后如果是通过邮箱登录进来的会问你是否需要绑定 Github 账号,可以让 Github 与 ZEIT 之间的持续集成更加方便,当然你也可以选择 SKIP 跳过。最后一步则会指导你如何创建项目,它提供了很多快速创建的模板,例如 Next.js, React, Vuepress, Gatsby, Docz, Nuxt.js, Svelte, Angular。

cbe0e092d265be20dd2629352eda62e8.png

按照示例使用 npm install -g now 安装 CLI 工具,初始化项目后直接使用 now 命令即可发布到 ZEIT 上,整体流程非常简单。

部署 Koa.js 服务

通过刚才的示例我们可以了解到其实它的本质就是将 HTTP 请求的 request和response传入方法中,处理后再返回给 HTTP,所以它除了 Serverless 函数之外也是完全支持 Koa.js 以及基于 Koa.js 的 ThinkJS 服务部署的。我们先来看看如果要部署一个 Koa.js 服务应该怎么做。

Fork 快速部署

由于 ZEIT 官方主推 Serverless 服务,所以把 Node.js 的脚手架模板去除掉了,所以我们只能自己创建项目了,为了方便我提供了一个 DEMO 仓库 https://github.com/lizheming/now-koa-demo。如果在刚才的注册流程中你绑定了 Github 的账号的话你可以选择直接 Fork 该仓库,等一小会儿之后就会收到 ZEIT 的 Github 通知告诉你网站已经部署成功,并在 commit 中提供部署后的地址。

61ae6d894d1f53595a22aa919e63801b.png

命令行部署

如果没有绑定 Github 账户也没关系,我们可以通过命令行部署服务。将 DEMO 仓库克隆下来后直接使用 now 命令就可以了。部署成功后 ZEIT 会给我们返回一个当前提交版本的唯一地址,比如说 https://now-koa-demo-pac7dbxrf.now.sh/ 打开之后就会见到 Hello from koa.js! 的返回信息。

b6a5f639d3af133496513ad4d5adc6ba.png

注意事项

index.js 文件内容与正常的 Koa.js 项目代码无异,唯一的区别是最终项目没有直接调 app.listen() 方法进行监听,而是使用 module.exports = app.callback() 将最终的 callback 方法进行了返回。我们知道 app.callback() 方法返回的是接受 request 和 response 对象作为参数的函数,这就回到了文章最开始的示例了。

我们再来看看 now.json 的内容。该 JSON 文件用于告诉 now 服务 index.js 文件需要使用 @now/node 运行时执行,而所有的请求需要转发到 index.js 文件上。听起来是不是非常像 Nginx 上的内容?

{

  "version": 2,

  "builds": [

    { "src": "index.js", "use": "@now/node" }

  ],

  "routes": [

    { "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 启动文件有哪些内容。然后我们基于该文件主要讲述下碰到的问题以及为什么需要这么做。

const path = require('path');

const Application = require('thinkjs');

const Loader = require('thinkjs/lib/loader');

class NowLoader extends Loader {

  writeConfig() {}

}

const app = new Application({

  ROOT_PATH: __dirname,

  APP_PATH: path.join(__dirname, 'src'),

  VIEW_PATH: path.join(__dirname, 'view'),

  proxy: true, // use proxy

  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')

    }

  }

});

const loader = new NowLoader(app.options);

loader.loadAll('worker');

module.exports = function (req, res) {

  return think.beforeStartServer().catch(err => {

    think.logger.error(err);

  }).then(() => {

    const callback = think.app.callback();

    return callback(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() 启动前钩子

  • 启动服务

  • 启动后向全局发送 appReady 事件

目前 ThinkJS 服务中并没有纯粹的非启动方法包含这些内容,所以我选择了在启动脚本中模拟正常的启动流程自定义启动过程的方式。由于多进程逻辑稍微复杂点,所以我直接按照单进程模式模拟。

  • 实例化 Loader,使用 loader.loadAll('worker') 加载所有的依赖文件

  • 在回调中执行 beforeStartServer() 启动前钩子

  • 执行 callback() 启动服务

  • 启动后向全局发送 appReady 事件

文件引用问题

项目文件相对引用

我们知道 ThinkJS 的本质是文件夹即路由的模式,Controller, Model, View 等文件按照一定的文件夹规则放置,通过动态读取文件的形式找到对应的文件并加载执行。这在正常的项目中本来不存在什么问题,但是 ZEIT Now 平台为了节省空间,会对在入口文件中没有显示依赖的文件进行忽略。

我们正常的启动文件中只会定义 APP_PATH ,而 VIEW_PATH 甚至是静态资源目录是在 src/config/adapter.js 以及 src/config/middleware.js 中定义的。而这两个文件又是动态读取文件引入的,导致在上传的时候由于没有显式依赖该文件而不上传该文件。所以为了解决这个问题,我选择了在启动文件中再次显示声明一下需要加载的文件。当然这些配置对 ThinkJS 来说是没有用的。

依赖文件相对引用

可以看到,除了正常的项目文件的引用之外,我还写了两个 log4js 文件的引用,这又是为什么呢?

主要还是因为 ZEIT 为了节省体积,除了会限制只上传需要的文件之外,还会针对入口文件使用 webpack 进行打包。使用 webpack 打包后所有的依赖都在入口文件中了,这样就不用上传硕大的 node_modules 文件夹,可以极大的减小体积。ZEIT 将该针对 Node.js 项目打包成单文件的打包工具开源出来了 https://github.com/zeit/ncc 如果项目中有需要打包成单文件减小体积的需求也可以使用。

而 log4js 非常早期的版本中是通过 require(./${type}) 的形式将对应的日志输出器加载进来的。由于打包后目录结构发生变化,打包后当前文件夹并没有对应的文件,所以会导致执行的时候报文件找不到的错误。所以为了解决这个问题则同样需要在入口文件中显式的声明这些文件的依赖。

去年2月份就有用户针对这个问题提了 Commit 将所有的加载器显式依赖后再进行选择解决了这个问题。所以在新版 log4js 的中已经不存在这个问题了,不过我还是在这里说明一下,是因为可能项目中引用的其它依赖会有这个问题,还是需要注意一下的。

写入权限问题

除了上面的问题之外,部署的时候我还碰到了文件写入无权限的问题。由于 ZEIT Now 提供无状态服务,所以写入文件等副作用操作在 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 項目?

关于奇舞周刊

《奇舞周刊》是360公司专业前端团队「奇舞团」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。

3c477fa44b69aee4ef190d366a7e47e9.png

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

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

相关文章

Spring MVC和REST中@RestController和@Controller注释之间的区别

Spring MVC中的RestController注释不过是Controller和ResponseBody注释的组合。 它已添加到Spring 4.0中,以简化在Spring框架中RESTful Web Services的开发。 如果您熟悉REST Web服务,您就会知道Web应用程序与REST API之间的根本区别在于,Web…

java patriciatrie_明明白白以太坊Merkle Patricia Trie

在以太坊数据结构中,Merkle Patricia Trie始终是个绕不过去的坎,世界状态,交易,交易收据等都是以这种树的形式存储在区块链数据库中,并将树root hash保存在区块头里。可以说不弄懂这种树的原理就没有办法真正明白以太坊…

jdeveloper_适用于JDeveloper 11gR2的Glassfish插件

jdeveloper众所周知, ADF Essentials是使用Java构建Web应用程序的绝佳框架,它可以自由开发和部署。 您在Glassfish(3.1)服务器上部署ADF Essentials应用程序。 但是,JDeveloper并不带有嵌入式Glassfish服务器&#xff…

Spring Cloud教程– Spring Cloud Config Server简介

问题 SpringBoot在通过属性或YAML文件外部化配置属性方面提供了很大的灵活性。 我们还可以使用特定于配置文件的配置文件(例如application.properties , application-dev.properties , application-prod.properties等)分别为每个环…

数字孪生体技术白皮书_基于Flownex的数字孪生体解决方案 系列介绍之二:数据中心应用实例...

致力于数字孪生体技术的研究与发展通过解决方案和工程化应用造福人类来源:数字孪生体实验室原创作者:王永康转载请注明来源和出处导 读《基于Flownex的数字孪生体解决方案》是我们最近完成的系列落地方案之一。该方案适用于热力系统、冷却系统、通风空调…

node php聊天室,最简单的Nodejs聊天室示例

今天群里一个同学找我要一个nodejs聊天室的demo。给他了一个简单的例子,顺便记录下:准备工作(前提是已经装好了nodejs):mkdir nodejs-democd nodejs-demo安装express : npm install express安装socket.io : npm install socket.io安装foreve…

neo4j安装_neo4j 社区版win10 下安装

准备工作:Neo4j下载网址:https://neo4j.com/download-center/#releasesava jdk官网下载:https://www.oracle.com/technetwork/java/javase/downloads/index.html安装 查看是否有用旧版本的java jdk ,如果有请在设置“应用和功能”卸载 旧的ja…

php网站 qq登陆,php写的插件网站接入QQ登录,QQ互联

qq按钮这里的链接是入口,调用你的apiapi_qq.php前端直接链接到此/*** 这个QQ登录简单实用,只要大家看我写的注释会一目了然,请注意看哦。* 带有"todo"这样注释的地方都是要你去改成你自己的逻辑* 这个php怎么进来呢?这是…

Spring MVC中@RequestParam和@PathVariable批注之间的区别?

Spring MVC框架是在Java世界中开发Web应用程序最流行的框架之一,它还提供了一些有用的注释,可以从传入的请求中提取数据并将请求映射到控制器,例如 RequestMapping, RequestParam和PathVariable。 即使将RequestParam和ParthVari…

excel 两列模糊匹配给出结果_北大硕士给大脑植入Excel病毒,工作效率提升了好几倍...

在工作中,我们经常会碰到这样的同事,他们是这样完成工作的:先用计算器算好结果,甚者动用手指头在电脑屏幕上数数,然后把数据填写到Excel表格中。结果可以预见,原本可以在上班时间完成的工作,愣是…

java ee cdi_Java EE CDI Producer方法教程

java ee cdi这是CDI Producer方法的教程。 在CDI中,生产者方法生成一个对象,然后可以将其注入。 当我们要注入本身不是bean的对象,要注入的对象的具体类型在运行时可能有所不同,或者当对象需要一些bean构造函数不执行的自定义初始…

qnap nas web php,如何在QNAP NAS上建立并使用 iSCSI Target

本帖最后由 小Q 于 2015-2-5 13:30 编辑在QNAP Turbo NAS上建立并使用iSCSI Target,快速、便利且便宜建置网络储存系统之方式内容:l 在Windows中使用Microsoft iSCSI启动器来连接iSCSI装置什么是iSCSI且它有什么好处?iSCSI(Internet Small Computer Sy…

openssl php api,PHP7使用openssl解密易班API中的用户数据

PHP7使用openssl解密易班API中的用户数据一、mcrypt扩展解密自从PHP版本更新到了7.1以上以后,mcrypt扩展被废弃,使用mcrypt扩展会出现如下图的报错。只能使用openssl来代替。然而易班轻应用提供的还是旧版本的mcrypt扩展,这将导致php版本升级…

Spring MVC的DispatcherServlet – Java开发人员应该知道的10件事

如果您使用过Spring MVC,那么您应该知道什么是DispatcherServlet? 它实际上是Spring MVC的心脏,确切地说是MVC设计模式或控制器的C语言。 应该由Spring MVC处理的每个Web请求都通过DispatcherServlet处理。 通常,它是Front Contro…

运行时错误7内存溢出_分别从运行时和GC的角度看JAVA8内存管理

运行时区域1.程序计数器程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机概念模型里(概念模型,各种虚拟机可能会通过一些更高效的方式实现&#…

极域课堂管理系统软件如何取消控制_微缔电子组装业MES系统软件六大功能组成...

电子组装业MES系统软件六大功能组成MES系统软件是制造执行系统的英文简称,MES系统软件在整个企业信息集成系统中承上启下,是生产活动与管理活动信息沟通的桥梁,MES系统软件在产品从工单下发到生产成成品的整个过程中,扮演着促进生…

多个cuda 被单进程沾满_报名 | 提高GPU利用率,听英伟达专家分享这个CUDA工具

随着 NVIDIA GPU 计算性能的不断提升,如何提升 GPU 利用率是开发者普遍关心的问题之一。从 Kepler 架构开始,NVIDIA GPU 支持多个 CUDA kernels 函数的并发执行,称为 Hyper-Q 技术。Hyper-Q 技术支持多个 CUDA streams、多个 CPU threads 或者…

usb转ttl模块与matlab,USB接口转TTL小板的自检测试

现在电脑基本上都不会配置DB9串行数据端口了,这给一些喜欢折腾刷机和单片机加载程序的朋友带来了诸多的不便。还好,随着技术的发展,USB接口转TTL的产品越来越成熟,而这种产品主要以采用PL-2303HX芯片作为主控器的居多,…

matlab 误差椭圆,求3倍标准差误差椭圆分析的程序

根据《白话空间统计之九:方向分布(标准差椭圆)修正版》(有些地方没有理解清楚),写了下面的程序。但是好像结果不对Zmvnrnd([0.5 1.5], [0.025 0.03 ; 0.03 0.16], 50);XZ(:,1); YZ(:,2);mean_Xnanmean(X); mean_Ynanmean(Y); %椭圆圆心%确定长短半轴…

java ee cdi_Java EE CDI处理程序方法示例

java ee cdi这是CDI Disposer方法的教程。 在CDI中,由于Producer方法生成的对象随后可以注入到应用程序中,因此使用Disposer方法,以便在其工作完成时将其删除。 Disposer方法始终与Producer方法匹配。 Disposer方法使用的一个示例是当应用程…