掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务

如何在AWS Lambda上搭建无服务器NodeJS


 在这篇文章中,我将会分享我的经验,讲解如何使用“Serverless”,并且使用AWS LambdaAPI GatewayDynamoDB搭建一个CRUD API微服务。

准备工作


假设你已经有了AWS账户,并且已经安装了NodeJS

下一步,你需要安装Serverless npm package,它能够让你轻松的创建、编辑和部署微服务:

 npm install -g serverless

之后按照亚马逊的指导,创建一个IAM用户,然后使用那些证书配置Serverless

指定新项目的储存路径,然后运行:

 serverless create --template aws-nodejs --path pets-service

现在我们要在项目中审定linting。这篇文章并不是为了介绍ESLint,所以我不会说的太详细,但是我建议你现在安装,并且用下面的方式设定.eslintrc

 {

  “plugins”: [“node”],

  “extends”: [“eslint:recommended”, “plugin:node/recommended”],

  “env”: {

    “node”: true,

    “mocha”: true

  },

  “rules”: {

    “no-console”: 0,

    “node/no-unsupported-features”: [2, {“version”: 4}]

  }

}

创建Handler


使用npm安装aws-sdklodash

 npm i -S aws-sdk lodash

转到handler.js,在文件顶部添加依赖:

 const aws = require(‘aws-sdk’);

const _ = require(‘lodash/fp’);

请注意,我们使用的是Iodash“functional programming”变体,因为它的合并功能不会改变原始对象。

下面,使用DynamoDB设定用于沟通的文件客户端:

 const dynamo = new aws.DynamoDB.DocumentClient();

现在来创建create()函数,目的是在数据库中建立一个新的Pet

 exports.create = function(event, context) {

  const payload = {

    TableName: 'Pets',

    Item: event.body

  };

  const cb = (err, data) => {

    if (err) {

      console.log(err);

      context.fail(‘Error creating pet’);

    } else {

      console.log(data);

      context.succeed(data);

    }

  }

  dynamo.put(payload, cb);

};

这段代码其实并不难理解:

我们获得了一个事件对象,对象中有一个key body,里面是我们想要储存的数据。

我们还提供了一个callback,它有两个重要的目的:

如果出现错误,我们运行context.fail(),它基本上就是一个AWS提供的onError callback

如果项目创建成功,我们则运行context.succeed(),数据中的传递会被作为Lambda函数的结果被返回。

DynamoDB有一个要求,那就是在创建的时候,我们必须要提供主要的key。在这个例子中,我们必须要在event.body对象中包含petId,将它作为key

CRUD的操作上,我们也会使用相同的方式:

 exports.show = function(event, context) {

  const payload = {

    TableName: 'Pets',

    Key: {

      petId: event.params.path.petId

    }

  }

  const cb = (err, data) => {

    if (err) {

      console.log(err);

      context.fail('Error retrieving pet');

    } else {

      console.log(data);

      context.done(null, data);

    }

  }

  dynamo.get(payload, cb);

};

exports.list = function(event, context) {

 const payload = {

  TableName: 'Pets'

 }

  const cb = (err, data) => {

    if (err) {

      console.log(err);

      context.fail('Error getting pets');

    } else {

      console.log(data);

      context.done(null, data);

    }

  }

  dynamo.scan(payload, cb);

}

exports.update = function(event, context) {

  const payload = {

    TableName: 'Pets',

    Key: {

      petId: event.params.path.petId

    }

  };

  dynamo.get(payload, (err, data) => {

    if (err) {

      console.log(err);

      context.fail('No pet with that id exists.');

    } else {

      const item = _.merge(data.Item, event.body);

      payload.Item = item;

      dynamo.put(payload, (putErr, putData) => {

        if (putErr) {

          console.log('Error updating pet.');

          console.log(putErr);

          context.fail('Error updating pet.');

        } else {

          console.log('Success!');

          console.log(putData);

          context.done(null, item);

        }

      });

    }

  });

}

exports.delete = function(event, context) {

  const payload = {

    TableName: 'Pets',

    Key: {

      petId: event.params.path.petId

    }

  };

  const cb = (err, data) => {

    if (err) {

      console.log(err);

      context.fail('Error retrieving pet');

    } else {

      console.log(data);

      context.done(null, data);

    }

  }

  dynamo.delete(payload, cb);

}

有两点需要指出的是:

我们需要获得部分省级的能力,也就是说你不需要随变更一起发送整个Pet对象,只需要发送变更就好。为了实现这个目的,我们需要在update()函数中先调用一个get,然后将变更合并进这次操作的结果中。

petId被当做一个parameter被传递进了API Gateway,之后为通过event.params.path.petId出现在Lambda中。如果你喜欢的话,你也可以使用查询字符串。

配置Serverless


马上就要完成了,我们现在就来搞定Serverless配置文件。打开serverless.yml,然后按照下面的方式对其进行编辑:

 service: pets-service

provider:

  name: aws

  runtime: nodejs4.3

defaults:

  stage: dev

  region: us-west-2

functions:

  createPet:

    handler: handler.create

    events:

      - http:

          path: pets

          method: POST

  showPet:

    handler: handler.show

    events:

      - http:

          path: pets/{petId}

          method: GET

  listPets:

    handler: handler.list

    events:

      - http:

          path: pets

          method: GET

  updatePet:

    handler: handler.update

    events:

      - http:

          path: pets/{petId}

          method: PUT

  deletePet:

    handler: handler.delete

    events:

      - http:

          path: pets/{petId}

          method: DELETE

这个配置很好理解。首先指定Lambda函数的名称,然后将他们maphandler.js函数,以及所需的HTTP方法和路径上。

我将默认的region更改成了 ‘us-west-2’,而stage还是默认的’dev’

API Gateways的名称中不应该提及环境信息,因为它可以处理多种环境或是“stage”

部署以及测试服务


现在开始部署了。要做的只有运行下面这条命令:

serverless deploy

大概一分钟左右,你的Lambda就应该部署好了,API Gateway也创建好了。

创建一个名为‘Pets’DynamoDB table,找到 ‘dev-pets-service’,然后将其导航到POST方法。

点击TEST,对API进行测试,你需要下面这些数据:

 { petId: "029340", name: "Fido", type: "dog" }

现在你应该已经在数据库中成功创建了一个新的项目。

下一步


你下一步或许想要为你的资源打开CORS,给你的API使用自定义域名,以及对你的前端应用进行设定,让它们与这些endpoint进行对话。

这些操作都不在本文的讨论范围内,幸运的是这些操作都不难。

编辑


Reddit用户jcready提出了一个意见,可以改进方法的升级:

 exports.update = function(event, context) {

  const payload = _.reduce(event.body, (memo, value, key) => {

    memo.ExpressionAttributeNames[`#${key}`] = key

    memo.ExpressionAttributeValues[`:${key}`] = value

    memo.UpdateExpression.push(`#${key} = :${key}`)

    return memo

  }, {

    TableName: 'Pets',

    Key: { petId: event.params.path.petId },

    UpdateExpression: [],

    ExpressionAttributeNames: {},

    ExpressionAttributeValues: {}

  })

  payload.UpdateExpression = 'SET ' + payload.UpdateExpression.join(', ')

  dynamo.update(payload, context.done)

}

我们当前的部署方法存在一个问题,那就是如果两个更新请求同时被发送,一个用户可以覆盖另一个用户的变更。

DocumentClient提供了一个更新方法,让我们可以声明需要升级哪个field,但是它的语法有一点古怪,而且需要生成一个“UpdateExpression”才能实现这些变更。上面的这段代码可以解决这个问题。


原文来自:SDK.cn

声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com

0512-88869195
数 据 驱 动 未 来
Data Drives The Future