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,一经查实,立即删除!

相关文章

python 小爱音箱集成_python控制小爱音箱自定义设备开关_修仙教程_小爱同学

send send_to_login(ipport,cookie,start_time,end_time)def play(): name info,播放 message_json {"action":"play","media":"app_ios"} path mediaplayer method player_play_operation send.sned_to_cmd(message_json,path,me…

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保存在区块头里。可以说不弄懂这种树的原理就没有办法真正明白以太坊…

python打开串口失败_python 如何防止串口通信失败?

python 对串口的操作我用的是“线程轮寻”方式。就是打开串口后,启动一个线程来监听串口数据的进入,有数据时,就做数据的处理(也可以发送一个事件,并携带接收到的数据)。我没有用到串口处理太深的东西。客户的原程序不能给你&…

java 调用scala 类_如何使用java类加载器调用带参数的scala函数?

我正在寻找一些将scala jar加载到java类加载器的指导。当我使用java jar文件时,下面的函数对我有效。其中,arr是一个java.net.URL数组,用于我需要加载到类加载器中的所有jar。val classLoader new URLClassLoader(arr, this.getClass().getClassLoader())val clazz classLoad…

python c4.5完整代码_python实现c4.5/Id3自我练习

import numpy as npclass DecisionTree:"""决策树使用方法:- 生成实例: clf DecisionTrees(). 参数mode可选,ID3或C4.5,默认C4.5- 训练,调用fit方法: clf.fit(X,y). X,y均为np.ndarray类型…

jdeveloper_适用于JDeveloper 11gR2的Glassfish插件

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

php接收get数组数据,来自HTTP的PHP注入GET数据用作PHP数组键值

我想知道在以下场景中是否存在可能的代码注入(或任何其他安全风险,如读取您不应该使用的内存块等等),其中来自HTTP GET的未经过处理的数据用于代码中PHP作为数组的键.这应该将字母转换为字母顺序. a到1,b到2,c到3 …. HTTP GET“字母”变量应该有值字母,但是你可以理解任何东西…

python绘制横向堆积柱状图_Python 堆叠柱状图绘制方法

本文介绍了Python 堆叠柱状图绘制方法,分享给大家,具体如下:>>文件: 堆叠直方图.py>>作者: liu yang>>邮箱: liuyang0001outlook.com>>博客: www.cnblogs.com/liu66blog#!/usr/bin/env python# -*- coding: utf-8 -*…

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不自动下载_Spring:自动接线或不自动接线

spring不自动下载自从使用Spring 2.5以来,我从基于XML的应用程序上下文切换到了注释。 尽管我发现那些非常有用且节省大量时间的人,但我始终觉得在灵活性方面我失去了一些东西。 特别是Autowired批注-或标准Inject-在我看来就像新的“新”,增…

php faker 中文,使用faker 生成中文测试数据

https://github.com/fzaninotto/Faker/blob/master/src/Faker/Provider/zh_CN/Address.php常用的类型都在里面。下面是一个实例。使用了laravel 框架的工厂模式向数据库填充测试数据。$factory->define(App\Models\Customer::class, function ($faker) {$faker Faker\Facto…

python课设总结_Python技术分享课总结:用Python模拟知乎自动登录

原标题:Python技术分享课总结:用Python模拟知乎自动登录Python语言是由Guido van Rossum大牛在1989年发明,它是当今世界最受欢迎的计算机编程语言之一,也是一门“学了有用、学了能用、学会能久用”的计算生态语言。为此&#xff0…

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

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

php 随机钱数,PHP 仿微信红包金额随机

博主寒冰最近闲来无事。就想研究一下微信红包的金额随机算法。早在微信红包刚出来的时候就研究过。始终不得要领。后来,通过查阅诸多资料。听说要实现“正态分布”。这个理论的东西不想深挖。恰好在网上一篇博客找到一个相对完整的算法。我经过试用确实不错。经过我…

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

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