跳到主要内容

使用Node.js, Pm2和Docker构建Rest Api

我们开始上课吧!

我们开始上课吧!

大家好,这是一个初学者级的动手教程,但强烈建议您已经接触过javascript或一些带有动态类型的解释语言。


我能学到什么?
如何使用Express创建一个Node.js Rest API应用程序。
如何运行一个Node.js Rest API应用程序的多个实例,并使用PM2平衡它们之间的负载。
-如何构建应用程序的映像并在Docker容器中运行。


需求
-基本了解javascript。
- Node.js版本10及以上-https://nodejs.org/en/download/
- npm版本6或更高版本- Node.js安装已经解决了npm依赖。
—Docker 2.0及以上版本-https://www.docker.com/get-started


构建项目的文件夹结构并安装项目的依赖项

警告:
本教程使用MacOs构建。在其他操作系统中,有些事情可能会出现分歧。


首先,你需要为项目和npm项目创建一个目录。在终端中,我们会创建一个文件夹并在里面导航。

mkdir rest apicd rest api

现在我们要启动一个新的npm项目,输入以下命令,并按enter键让输入为空:

npm init

如果我们看一下目录,我们可以看到一个名为' package.json '的新文件。这个文件将负责管理我们项目的依赖项。


下一步是创建项目的文件夹结构:

——Dockerfile——process.yml——rest-api.js——库——user-mock-repository——index.js——线路——index.js——处理程序——用户——index.js——服务——用户——index.js——模型——用户——index.js——共享——日志记录器——index.js

我们可以通过复制和粘贴以下命令轻松地做到这一点:

mkdir路线Mkdir -p handlers/userMkdir -p services/userMkdir -p repository/user-mock-repositoryMkdir -p models/userMkdir -p commons/logger触摸Dockerfile触摸process.yml触摸rest-api.js触摸路线/ index.js/ user / index.js触摸处理程序服务/ user / index.js联系库/ user-mock-repository / index.js联系/ user / index.js接触模型共享/日志/ index.js联系

现在我们已经构建了项目结构,是时候用节点包管理器(npm)安装项目的一些未来依赖项了。每个依赖项都是应用程序执行中需要的模块,并且必须在本地机器中可用。我们需要使用以下命令安装以下依赖项:

NPM安装express@4.164NPM安装winston@3.21NPM安装body-parser@1.183.Sudo NPM安装pm2@3.50- g

“-g”选项表示将全局安装依赖项,“@”后面的数字是依赖项的版本。


请打开您最喜欢的编辑器,因为是时候编写代码了!

首先,我们将创建记录器模块,以记录应用程序的行为。

rest api /共享/日志/ index.js

//获取winston模块。常量温斯顿=需要“温斯顿。”//创建一个记录器,将在控制台中打印应用程序的行为。常量logger = winston.createLogger({传输:[winston.transports.Console ()});//导出logger对象,供整个应用程序作为模块使用。模块。出口=记录器

在使用动态类型语言时,模型可以帮助您识别对象的结构,因此让我们创建一个名为User的模型。

rest api /模型/ user / index.js

//一个名为User的方法,每次调用时返回一个具有预定义属性的新对象。常量User = (id, name, email) => ({id,的名字,电子邮件})//导出模型方法。模块。exports =用户

现在让我们创建一个伪存储库,它将负责我们的用户。

rest api /仓库/ user-mock-repository / index.js

//导入User模型工厂方法。常量用户=需要“. . / . . /模型/用户”//创建一个虚假的用户列表,以消除数据库查询。常量mockedUserList = [用户(1“约翰·史密斯”“john.smith.com”),用户(2“丹尼尔Ackles”“daniel.ackles.com”),用户(3.“菲尔大门”“phill.damon.com”//创建一个返回mockedUserList的方法。常量getUsers = () => mockedUserList//导出存储库模块的方法。模块。出口= {getuser

现在是时候用它的方法构建我们的服务模块了!

rest api /服务/ user / index.js

// Id大于其他Id时返回的方法。常量sortById = (x, y) => x.id > y.id//返回匹配特定Id的用户列表的方法。常量getUserById = (repository, id) => repository. getusers()。过滤(user =>用户。=== id).sort(sortById)//添加一个新用户到假列表并返回更新后的假列表的方法,注意没有任何持久性,//这样以后调用该方法返回的数据将始终是相同的。常量insertUser =(存储库,newUser) => {常量usersList = […repository.getUsers (),分类列出返回usersList.sort (sortById)//该方法更新假列表中存在的用户,并返回更新后的假列表,注意没有任何持久性,//这样以后调用该方法返回的数据将始终是相同的。常量updateUser =(存储库,userToBeUpdated) => {常量usersList = […repository.getUsers()。过滤(user =>用户。== userToBeUpdated.id),userToBeUpdated返回usersList.sort (sortById)//从假列表中删除一个存在的用户并返回更新后的假列表的方法,注意没有任何持久性,//这样以后调用该方法返回的数据将始终是相同的。常量deleteUserById = (repository, id) => repository. getusers()。过滤(user =>用户。== id).sort(sortById)//导出服务模块的方法。模块。出口= {getUserById,insertUser,updateUser,deleteUserById

让我们创建请求处理程序。

rest api / user / index.js /处理程序

//导入之前创建的模块。常量userService =需要“. . / . . /服务/用户”常量库=需要“. . / . . /仓库/ user-mock-repository”常量记录器=需要“. . / . . /共享/记录器”常量用户=需要“. . / . . /模型/用户”//处理程序负责管理请求和响应对象,并将它们链接到将完成艰苦工作的服务模块。//下面的每个处理程序都有req和res参数,它们代表请求和响应。//这个模块的每个处理程序都代表一个HTTP动词(GET, POST, PUT和DELETE),将来会通过路由器链接到它们。/ /得到常量getUserById = (req, res) => {试一试常量users = userService。getUserById(存储库,方法(req.params.id))logger.info (“用户检索”res.send(用户)(err) {记录器。错误(err.message)res.send (err.message)/ /发布常量insertUser = (req, res) => {试一试常量user =用户(req.body。id,req.body.name, req.body.email)常量users = userService。insertUser(存储库,用户)logger.info (“用户插入”res.send(用户)(err) {记录器。错误(err.message)res.send (err.message)/ /把常量updateUser = (req, res) => {试一试常量user =用户(req.body。id,req.body.name, req.body.email)常量users = userService。updateUser(存储库,用户)logger.info (“用户更新”res.send(用户)(err) {记录器。错误(err.message)res.send (err.message)/ /删除常量deleteUserById = (req, res) => {试一试常量users = userService.deleteUserById(存储库,parseInt(req.params.id))logger.info (“用户删除”res.send(用户)(err) {记录器。错误(err.message)res.send (err.message)//导出处理程序。模块。出口= {getUserById,insertUser,updateUser,deleteUserById

现在,我们要设置HTTP路由。

rest api /线路/ index.js

//导入处理器模块。常量userHandler =需要“. . /处理/用户”//导入一个快速对象,负责将请求从url路由到处理程序。常量路由器=需要“表达”) .Router ()//添加路由到路由器对象。router.get (“/ user /: id”userHandler.getUserById)router.post (/用户的userHandler.insertUser)router.put (/用户的userHandler.updateUser)router.delete (“/ user /: id”userHandler.deleteUserById)//导出配置的路由器对象。模块。导出=路由器

最后,是时候构建应用程序层了。


rest api / rest-api.js

//导入Rest API框架常量表达=需要“表达”//导入将请求体转换为JSON的模块。常量bodyParser =需要“体”//导入日志模块常量记录器=需要”。/共享/记录器”//导入路由器对象常量路由器=需要“/路线。”//接收请求的端口常量restApiPort =3000//初始化Express框架常量App = express()//保持秩序,这很重要应用程序。使用(bodyParser.json ())应用程序。使用(路由器)//让我们的Rest API监听端口3000上的请求app.listen(restApiPort, () => {logger.info(' API监听端口:${restApiPort} ')})

运行应用程序

在' rest-api/ '目录中输入以下代码来运行我们的应用程序:

节点rest-api.js

您应该在终端窗口中看到如下消息:

{"message":"API监听端口:3000","level":"info"}


上面的消息意味着我们的Rest API正在运行,所以让我们打开另一个终端,使用curl进行一些测试调用:

旋度localhost:3000/ user /1curl -X POST localhost:3000/ user - d{"id":5, "name":"Danilo Oliveira", "email": "danilo.oliveira@email.com"}- h“application / json内容类型:curl -X PUT localhost:3000/ user - d{"id":2, "name":"Danilo Oliveira", "email": "danilo.oliveira@email.com"}- h“application / json内容类型:curl -X DELETE localhost:3000/ user /2

配置和运行PM2

既然一切正常,现在可以在应用程序中配置PM2服务了。要做到这一点,我们需要转到本教程开始时创建的rest-api/process文件。并实现如下配置结构:

应用程序:—脚本:rest-api.js#应用程序的启动文件名实例:4#必须并行运行的进程数,如果需要可以更改exec_mode:集群#执行模式

现在,我们将打开我们的PM2服务,在执行以下命令之前确保我们的Rest API没有在任何地方运行,因为我们需要开放端口3000。

Pm2启动process.yml

你应该看到一个表显示一些实例与'应用程序名称= rest-api '和'状态=在线',如果是这样,是时候测试我们的负载平衡。为了进行这个测试,我们将输入以下命令并打开第二个终端来发出一些请求:

终端1

pm2日志

终端2

旋度localhost:3000/ user /1curl -X POST localhost:3000/ user - d{"id":5, "name":"Danilo Oliveira", "email": "danilo.oliveira@email.com"}- h“application / json内容类型:curl -X PUT localhost:3000/ user - d{"id":2, "name":"Danilo Oliveira", "email": "danilo.oliveira@email.com"}- h“application / json内容类型:curl -X DELETE localhost:3000/ user /2

在“Terminal 1”中,你应该注意到你的请求是通过我们应用程序的多个实例来平衡的,每行开始的数字是实例id:

2|rest api | {“消息”“用户更新”“水平”“信息”3.|rest api | {“消息”“用户更新”“水平”“信息”0|rest api | {“消息”“用户更新”“水平”“信息”1|rest api | {“消息”“用户更新”“水平”“信息”2|rest api | {“消息”“用户删除”“水平”“信息”3.|rest api | {“消息”“用户插入”“水平”“信息”0|rest api | {“消息”“用户检索”“水平”“信息”

由于我们已经测试了PM2服务,让我们删除正在运行的实例来释放端口3000:

Pm2删除rest-api

使用码头工人

首先,我们需要实现应用程序的Dockerfile:

rest api / rest-api.js

#基本图片节点:苗条#在base image中创建一个目录并定义为base directoryWORKDIR /应用程序#复制根目录的文件到基目录添加。/应用程序#安装项目依赖项运行npm install执行npm install pm2@3.50- g#启动pm2进程并保持docker容器激活CMD pm2启动进程。Yml && tail -f /dev/#暴露RestAPI端口暴露3000

最后,让我们构建应用程序的镜像并在docker中运行它,我们还需要将应用程序的端口映射到本地机器的端口并测试它:

终端1

Docker映像构建。——标签rest api /当地:最新Docker run -p30003000- d rest api /当地:最新docker exec -it{上一个命令返回的containerId} bashpm2日志

终端2

旋度localhost:3000/ user /1curl -X POST localhost:3000/ user - d{"id":5, "name":"Danilo Oliveira", "email": "danilo.oliveira@email.com"}- h“application / json内容类型:curl -X PUT localhost:3000/ user - d{"id":2, "name":"Danilo Oliveira", "email": "danilo.oliveira@email.com"}- h“application / json内容类型:curl -X DELETE localhost:3000/ user /2

正如前面所发生的,在“Terminal 1”中,您应该注意到您的请求正在通过应用程序的多个实例进行平衡,但这次这些实例在docker容器中运行。

结论

Node.js和PM2是一个强大的工具,这种组合可以在许多情况下作为工作者、api和其他类型的应用程序使用。将docker容器添加到等式中,它可以极大地降低成本并提高堆栈的性能。

这就是所有的朋友们!我希望你喜欢这个教程,如果你有任何疑问,请告诉我。


你可以从以下链接获得本教程的源代码:

https://github.com/ds-oliveira/rest-api


见到你!

©2019 Danilo Oliveira

Baidu