前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >搬运向 | 浅析serverless架构与实践

搬运向 | 浅析serverless架构与实践

作者头像
Rainbond开源
发布2018-05-31 13:27:45
2.5K0
发布2018-05-31 13:27:45
举报
文章被收录于专栏:Rainbond开源「容器云平台」

Serverless ,不是没有server,而是不用去担心维护server 这件事,

不管是在部署还是开发,都是以一个个function 为单位,

这带来了程式码上的高度decoupling,但同时也因为过大的弹性,

常常搞的我们无所适从,就像这张图一样:

serverless 更考验着我们对系统设计的思维,

这是一篇非常粗浅的文章,

目的在带领对serverless 有兴趣的人无痛的入门,

不管是在概念上,还是在实务的使用上。

假如你是懒得看文章的人,可以直接到我的github repo上面看 有哪里写错的话可以提个issue,觉得赞赞赞的话也可以给星星以兹鼓励。

试想当你是一个单枪匹马的开发者时,你绝对会希望能真正专心在开发,

而不是一天到晚担心机器有没有死掉,或者配置环境就花了大半时间。

我只是一个前端工程师,对于后端的知识甚是浅薄,

serverless 对我而言是个很合理的选择,

但这不代表我不在乎任何后端的专业性,

更不代表着后端工程师使用serverless 架构就是代表实力不够。

相反的,我认为后端工程师如果能从管理机器中解放,

设计出更好的serverless 架构以及更专注在程式本身的逻辑上,

那从serverless 上能获得的增益一定也是相当惊人的。

看着我们虚拟化的趋势=> VM => Container => Docker 的兴起 尽管做法略有不同,但方向是一致的, 都是想让程式开发者更能专注在程式本身,而不是管理机器上 话说回来,前端后端的分界点一直都是个有争议的问题, 不过就不在这里去讨论了

这篇会需要用到数个aws 的服务,不过为了让事情更单纯,

我只会用到IAM, DynamoDB, API Gateway, CloudWatch 以及Lambda,

都不熟悉这些也没有关系,因为我在写完这一段之前,

也只是大略的把文件扫过去,也不用担心缩写令人看不懂,

因为我最讨厌的就是这种缩来缩去的东西,

所以接下来都会在提到的地方解释我们正在处理的是什么。

以往都是直接用EC2 开一台机器, 要用什么直接当自己家的在上面装就是了。 (当然可以学一些东西自动化这流程: chef,不过这不是这篇的重点)

Introduction

这篇会着重在比较抽象化的概念上,

而不是去针对特定的功能作serverless 的实现,

但不要误会了,后面还是有一个简易restful api 的实作

我认为能掌握以下几个点,才是针对特定功能实现的基础:

  • Project 的架构
    • 对于设计一套serverless architecture 的抽象概念
    • 各个功能与api 间对应的关系
  • 资料的处理
    • 要能永久被储存
    • CRUD 操作
    • Schedule:定时或是routine 的去做一些事情(这一篇文章里面不会提到)
  • 部署
    • 有新功能时我们要能够部署上去
  • Log
    • 不然你debug 是要通灵吗

至于使用的语言会是nodejs。

优点

  • 不需要自己管机器,以及近乎无限能力的scale-out(你的财力够的话)
  • 相对便宜。因为我们是有执行function 才收费
    • 如果只是自己要使用或是小型专案,基本上都会落在free tier 区间
  • 高度的解耦及灵活的配置
    • 不管你是想要制作nano service 还是micro service 你都能灵活地去组合

有人说过,当你手上只有锤子时,那你看到的所有东西都会是钉子。

不过对于function这么general purpose的东西来说,

它的确能拿来解决一切计算相关的问题,端看你组合的方式对不对而已。

限制与风险

讲了这么多好处,现在当然要来讲它的限制。

  • 有限的记忆体
  • timeout
    • 目前最多只能运算300 秒,就会被强制结束掉
  • 高度的解耦
    • 这看起来是好处,但必须要用跟以前不一样的想法来设计程式,因为我们每次function 运行完之后,就会把所有资源释放出去
  • Latency
    • 因为我们是需要计算时,才会去要资源来运算,每次都算是一个cold start,所以对latency 完全无法容忍的服务,可能不适合。
    • 实际上透过schedule 可以一定程度的解决这问题
  • 风险
    • 我说一个字大家就懂了:Parse
    • 当事情走到这一步的时候,基本上就没啥救了,这就是我们冒着最大的风险
    • 但就如同前面所言,我认为serverless 是未来大势所趋,也许不会所有的project 都如此,不过大多数的中小型专案都会转向朝这一架构迈进。
    • 因为我们以function 为单位的高解耦,所以更换API,不是一个让人全面崩溃的风险
    • 坦白说,如果是考虑到有没有办法scale-out,那我想大部分情形,aws 都是没问题的
    • Scale-out
    • API 更换
    • 服务被停用

Why serverless framework

  • 过度的自由,失控的decoupling
    • 框架给了我们更好结构化project 的方式
  • Config 的设置以及部署function 简化
  • 文件和plugins
  • 社群或公司支持
    • Serverless 的官网上有说到,现在是由一群工程师全职在维护这个framework
    • gitter 上问问题也几乎马上就能得到回答
  • Apex?
    • TJ 的产品,目前还在观望中,但serverless 看起来相对较稳定、成熟
    • 不过光是TJ 这个名字,就很值得一试
    • 就像我前面说的,因为高度解耦的关系,其实要迁移过来「理论上」不是太难的事

Setup 开发环境的建置

我不认为一个环境的建置,是在把东西装一装之后就结束了,

因为东西装一装之后,通常后续只会有更多的问题,

而且一个project 本来就需要在一开始就做好deploy 的准备了。

不部署的话干嘛要用aws 啊?囧

完整一点的setup 应该要包含了从建置基本设定=> 部署

才算是真的结束,

所以这一小节会从配置到部署都走过一次。

AWS 的介面可能会因为时间的关系,与下方略有不同, 但估计变动不会太大,知道要使用什么功能比较重要, 故我不会把操作介面的图片放上来。

为你的api 建立一个「role」

  • 跟以往一样,我认为建环境是最困难的部分
  • 首先要建一个IAMrole

IAM(Identity and Access Management) IAM 的功用就是让你能够管理使用者对于服务和资源所拥有的「权限」 可以针对不同的使用者,制定不同的角色, 举例来说,如果你今天的api 只想让user 从s3 的bucket 里面读一些静态资源 你就不会想要让他拥有access DynamoDB 的权限,懂? IAM是免费的

到aws 选取services,在拉下来一狗票的服务中,

选择IAM

建立一个新的User,名字就输入:serverless-admin

建立好之后,

把拿到的Access Key IdSecret Access Key给记下来,

待会会用到。

接着选择刚刚建立的那个user:serverless-admin

在permissions 的地方加上新的policy,

这里aws 相当贴心的提供我们超大一坨的policies 可供选择,

为了方便,我们直接选择AdministratorAccess

当在production 环境时,这样处理permissions 不会是一个好主意XD 坦白说我觉得permissions 会是一个令人头痛的点

Create Project

我们选择了serverless-framework这一套serverless framework。

npm i -g serverlessserverless project create

会要你输入名字以及刚刚的access key id 跟secret access key。

接着还要选择你想要你的project 运行服务在的地区。

再来稍后三分钟之后, project 就会建好了。

会生成一大堆东西,下面列出简易版的解释, 看不懂也没关系,之后在实作中就会碰到很多次了:

├── _meta // (.gitignored) 就是个存meta data 的地方(config 之类的├── admin.env // (.gitignored)刚刚create function 时的AWS Profiles├── functions│ └── function1│ ├── event.json│ ├── handler.js│ └── s-function.json├── package.json // 就是npm 的那个├── s-project.json // serverless 的套件管理└── s-resources-cf.json // 就是上述讲到CloudFormation 的描述档

Create First function

先让我们focus在function上,这些config真的都可以先放着没关系。

这不代表他们不重要,只是晚点再回来看他们是在做什么 如果你真的现在就等不及,也可以到serverless 的官方文件看 Project structure

serverless function create functions/posts

选择nodejs => Create Endpoint

接着就可以看到多了一个functions资料夹,

并且里面跟着一个posts以及一些东西了。

一样我们只要知道自己现在建立了一些基础建设,稍后再来回头看这是什么。

Deployment

serverless dash deploy

function - postsendpoint - posts - GET

这两个都记得要选才会把东西部署上去aws-lambda。

选择deploy 之后稍待几秒钟,就可以看到回传一个网址给你。

这就是能够执行我们刚刚部属上去的posts的地方。

如果你没做任何更改,点进去后应该能看到

{"message": "Go Serverless! Your Lambda function executed successfully!"}

到这里为止,我们才能不心虚的说:环境建完,可以继续了。

Abstraction

Overview

前面一直说到serverless 架构是以function 为单位去部署和开发,

现在来对「lambda function」有个具体的抽象概念。(欸?

先来个大略的概观,你可以跟刚刚create 的project 对照着看:

  • 每个function 可以有许多个endpoint(进入点)
  • 每个endpoint 可以有许多个method( GET, POST…)
  • Handler则是aws lambda执行的进入点(就是handler.js)

来看一下handler.js

module .exports.handler = function ( event, context, cb ) {// empty}

实际上我们运行的function 就是长下面这个样子,

在开始讨论其他配置,和aws 要怎么运行到这里之前,

先搞清楚到底在谈论什么东西:

function ( event, context )

可以有第三个参数cabllback, 不过其实只要这两项就可以运作的很好了, 而且callback 实在不是一个好事

Source event

source event,可以是push 或pull model。

假设S3 上面资料新增,lambda function 会接收到event 去做事情,

那这就是一个push model。

假设今天是lamda function 去扫了一遍DynamoDB ,

发现有事情要根据上面的资料去做,

这就是一个pull model。

而source event 也可以很单纯的来自http request。

Context

context 是一个object,

里面包含了当前lambda 运行环境的讯息,

以及一些method。

有三个methods 是一定要知道的:

这里的参数是可选的,我们可以只让function做事, 没有一定要强制回传结果。

  • context.succeed(Object result)
    • 可以在执行成功时回传东西: context.succeed(someObject)
    • 注意这里的result必须要能够被JSON.stringifyu转成字串
  • context.fail(Error error)
    • 在失败时回传东西
  • context.done(Error error, Object result)
    • 这个就有点奇葩了,有了成功和失败为什么还要存在个done 呢?
    • 如果error 不为null,这次的lamda function 就会被认定为执行失败

再来是可以看到目前执行剩余时间:

context.getRemainingTimeInMillis()

这里所谓的看到当然是指在function 执行时我们能利用啦!

不过要注意的是如果归零,

AWS lambda 就会强制终止我们的lambda function 了。

handler.js

前面有提到过这里就是aws 运行的进入点,

要在s-function.json里面设定,

这里看到我们只在handler那个属性打上: handler.handler

这有两件事情值得注意:

  • 对应执行的就是handler.js这个module底下的handler

// in handler.jsmodule .exports.handler = function ( event, context ) {// This be implemented}

第二件事就是这个hanlder 属性还隐含着我们目前能作用的scope,

假如我们是:function1/handler.handler

就把上层的parent folder 给包含进去,

所以他就吃得到我们在根目录安装的npm 套件。

比如说你安装了react,那你就可以: require('react')

理解到这样的程度,就已经足够进行下去了,

直接来实作吧!

Implementation: Simple RESTful api

直接看文件时,总会有种雾里看花的感觉,

不过等到实际开始做之后,你会发现其实概念只要mapping 过去,

并没有想像中的困难。

这个是完成后的github repo, 如果你中途发现有什么错误的话,可以在上面查看是否有哪里不一样。

Why

底下会包含基本的CRUD 以及list,

大多数的应用程式都不脱这五种操作,

就算需要更特殊的操作,

也总是要熟悉这些基础后才能继续前进,

包含着如何储存资料以及debug 的概念。

至于资料夹的结构或是workflow 的顺序,

你都可以依照个人的喜好去调整,不一定要照我写的走。

Log

  • 没错,我们先来看看要怎么找出错误,从犯错中学习,是新手成长最快的方式
  • 来修改一下functions/posts/hanlder.js

contextevent是我们在lambda中要好好处理的东西没错,

不过这里先专注在出bug 时要怎么解决:

'use strict'console .log( 'Loading function' )function display ( object ) {return JSON .stringify(object, null , 2 )}module .exports.handler = (event, context) => {console .log( 'Event: ' , display(event))console .log( 'Context: ' , display(context))context.succedd({message: 'ok, it works'})}

这里的程式码有个明显的错误,待会我们会除错并且学习如何看log

稍做一些更改之后我们就可以再次部署了:

serverless dash deploy

再到刚刚的网址,会发现出现错误了!

幸好这里加上了许多console.log

假如你曾经写过JavaScript 对这样的除错技巧一定不陌生,

但,这里的log 不会在console 印出来,会到哪里呢?

这里就要使用aws 上的另个服务:CloudWatch 了。

到services 点CloudWatch,选取logs,

就会看到这里有个log groups 就是我们刚刚建立的functions。

选进去后会很神奇地发现我们之前call 的纪录都在这里。

在log 中我们可以看到:

...(一些日期和系统资讯) TypeError: context.succedd is not a function at module.exports.handler (/const/task/handler.js:12:11)

我们出了一个typo 的错误,改正过来以后就成功啦!

context.succeed({message: 'ok, it works'})

Create an item

要存资料库前,必须先在DynamoDB建一张Table。

DynamoDB 是一个no sql 的资料库 为了scale-out ,它在使用上有一些限制, 但在这个简单的示例中,并不会需要考量到这些, 假如有兴趣深入的话,可以看补充资料的地方 解析DynamoDB

  • 到aws上选择DynamoDB
  • Create table
  • table name 输入 posts
  • primary key 名称设定为 id
  • 下面的default setting 取消勾选,然后将Read capacity units 以及Write capacity units 都调成 1
  • 我们就有一个很阳春的table 了

接着是在handler里面的更动,

首先要安装两个package

npm i -S dynamodb-doc node-uuid

前面有说过lambda function 其实就是根据source event,

去执行对应的动作:

const DOC = require ( 'dynamodb-doc' )const dynamo = new DOC.DynamoDB()module .exports.handler = (event, context) => {console .log( 'Event: ' , display(event))console .log( 'Context: ' , display(context))const operation = event.operationif (event.tableName) {event.payload.TableName = event.tableName}switch (operation) {case 'create' :const uuid = require ( 'node-uuid' )event.payload.Item.id = uuid.v1()dynamo.putItem(event.payload, () => {context.succeed({"id" : event.payload.Item.id})})breakdefault :context.fail( new Error ( 'Unrecognized operation "' + operation + '"' ))}}

其实蛮像我们平常在redux中处理对应的action type的reducer

这里建立了一个DynamoDB的client,简单的来说,我们会把event.payload这个object,

新增成Table里的一个新item,并且给它一个唯一的id

毕竟是Primary key 嘛!

如果你不熟悉Database 的基础理论,Primary key。 Primary key 就是我们拿来识别这个item 在这个表中是唯一的「身分证」, 在这里我们是用id来作为我们的Primary key。

那这个event又是怎么来的呢?

首先我们要了解的是Create这个动作对应到的http method是POST

所以当我们在对同一个url执行GETPOST时,

虽然call 的是同个function(或者更精确地说,是同一个Endpoint)。

posts资料夹底下,可以看到一个s-function.json

这个档案中放着的是关于我们在进入handler.js时相关的config。

当然也包括了前面说到的event

先直接看到endpoints这个attribute,里面有许多个物件,

预设的是这个:

{"path" : "posts" ,"method" : "GET" ,"type" : "AWS" ,"authorizationType" : "none" ,"authorizerFunction" : false ,"apiKeyRequired" : false ,"requestParameters" : {},"requestTemplates" : {"application/json" : ""},"responses" : {"400" : {"statusCode" : "400"},"default" : {"statusCode" : "200" ,"responseParameters" : {},"responseModels" : {"application/jsoncharset=UTF-8" : "Empty"},"responseTemplates" : {"application/jsoncharset=UTF-8" : ""}}}}

这里有好多东西,

假如我们要在里面定义我们对每个endpoint 的长相,谁不发疯呢?

眼尖的你应该看到了有template这个字眼,

而刚刚送进来的event正是一个http request,

所以我们要做的事情已经呼之欲出了,就是在requestTemplates加上我们指定的template名称,

就能根据这个template 生出我们想要的event 。

endpoints中加上了这个新的object:

{"path" : "posts" ,"method" : "POST" ,"type" : "AWS" ,"authorizationType" : "none" ,"authorizerFunction" : false ,"apiKeyRequired" : false ,"requestParameters" : {},"requestTemplates" : "$${requestCreatePostTemplate}" ,"responses" : {"400" : {"statusCode" : "400"},"default" : {"statusCode" : "200" ,"responseParameters" : {},"responseModels" : {"application/jsoncharset=UTF-8" : "Empty"},"responseTemplates" : {"application/jsoncharset=UTF-8" : ""}}}}

当进入这个api 时(path 没有改变),使用POST method时,

我们的request会照着requestCreatePostTemplate这个template走

$${requestCreatePostTemplate} 是特殊的语法, 让serverless 知道这是个template 名字,而不是一般的string。

所以我说,那个tempalte 呢?

这里要在posts底下新增s-templates.json

所有的关于lambda function 的template 都会放在这里。

接下来我们就可以设计我们的request(event)的长相了:

{"requestCreatePostTemplate" : {"application/json" : {"operation" : "create" ,"tableName" : "posts" ,"payload" : {"Item" : {"content" : "$input.json('$')"}}}}}

这里比较让人疑惑的是$input.json('$')是什么,

这其实是跟API Gateway 比较有关系的template 语法,

而不是serverless 这个框架底下的。

This function evaluates a JSONPath expression and returns the results as a JSON string. For example, $input.json('$.pets') will return a JSON string representing the pets structure.

简单的说,他会将input 转成一个json-like string,

更棒的地方是他可以像我们平常access 底下的attribut 那样去找底下的东西:

(就是所谓的json path

像是$.pets就是将我们吃到的input object底下pets对应到的东西,

转成string。

Amazon API Gateway: Mapping template reference 想了解更多关于Template 的话可以参考serverless framework 的文件: Template & Variable

接着回到一开始的handler.js

就可以把跟event有关的东西与我们前面template里面所做的config连接起来了:

module .exports.handler = (event, context) => {console .log( 'Event: ' , display(event))console .log( 'Context: ' , display(context))const operation = event.operationif (event.tableName) {event.payload.TableName = event.tableName}switch (operation) {case 'create' :const uuid = require ( 'node-uuid' )event.payload.Item.id = uuid.v1()console .log( 'Payload: ' , display(event.payload))dynamo.putItem(event.payload, () => {context.succeed(event.payload.Item)})breakdefault :context.fail( new Error ( 'Unrecognized operation "' + operation + '"' ))}}

这时候可以部署了!

部署完成之后我们需要试试有没有成功,必须要打开API Gateway,

一进去就可以看到对应project 名称的api,

点进去能看到我们现在有哪几个api 可以用(url)。

可以把API Gateway想像成我们平常使用的router

Gateway 会把要执行的endpoint 接到对应的url 上。

点击/posts底下POSTmethod的integration request ,

在Body Mapping Templates 可以看到对应的template:

{ "operation" : "create" , "tableName" : "posts" , "payload" :{ "Item" :{ "content" :$input.json( '$' )}}}

那,要怎么测试呢?

我习惯用postman,算是一个测api 相当好用的工具,

找到serverless-demo这project底下对应的stages

选择当前对应的stage(预设应该是dev),

然后选择Export as Swagger + Postman Extensions这个选项,

会下载一个json ,里面把你所有建立的request 都包好好的。

接着就能在postman 中import ,就能直接使用了。

首先当然是先测试原先的GETmethod,理论上来说应该要丢出error,

因为送进来的request(event),它的operationundefined

{"errorMessage" : "Unrecognized operation \"undefined\"" ,"errorType" : "Error" ,"stackTrace" : ["module.exports.handler (/const/task/handler.js:28:26)"]}

非常的好。

接着是POST

{"errorMessage" : "Process exited before completing request"}

居然喷错了,所以我们要再度到CloudWatch 去看一下log,

看起来event的样子是对的,但往下一看就找到了这个错误:

Cannot find module 'node-uuid'

我们在根目录虽然有package.json

但是目前对于底下的handler.js而言,

它对根目录是完全一无所知的,那该怎么做呢?

s-function.json中的handler改成functions/posts/handler.handler

我们能在这里决定function 要对整个project 的权限到哪里,

像这里就会一直延伸到根目录,所以我们在根目录所安装的package,

自然到了posts底下也吃得到了。

假如仍然没有办法动到dynamodb 的话,

就要到s-resources-cf.json更改设定

IamPolicyLambda.Properties.PolicyDocument.Statement底下加上:

{"Effect" : "Allow" ,"Action" : [ "*" ],"Resource" : "arn:aws:dynamodb:${region}:*:table/*"}

再去Postman 执行一次,

DynamoDB 的Table 里面就会出现新一笔的资料了(一个新的Item)。

Read an item

  • 我们刚刚已经可以在DynamoDB 里面新增资料,自然要有办法拿出来才是。

第一步一样是从handler.js里面直接去做更改:

为什么每次都从handler.js开始是因为这边是最符合逻辑的地方, 其他都比较特定的config 问题

switch (operation) {case 'create' :const uuid = require ( 'node-uuid' )event.payload.Item.id = uuid.v1()console .log( 'Payload: ' , display(event.payload))dynamo.putItem(event.payload, () => {context.succeed(event.payload.Item)})breakcase 'read' :dynamo.getItem(event.payload, context.done)breakdefault :context.fail( new Error ( 'Unrecognized operation "' + operation + '"' ))}

接着要到s-function.json里面去加上对于parameter的设定,

以及加上template:

在GET method 的底下

"requestParameters" : {"integration.request.querystring.id" : "method.request.querystring.id"},"requestTemplates" : "$${requestReadPostTemplate}"

最后则是template:

"requestReadPostTemplate" : {"application/json" : {"operation" : "read" ,"tableName" : "posts" ,"payload" : {"Key" : {"id" : "$input.params('id')"}}}}

假如你好奇为什么要用Key的话, 可以参考DynamoDB js sdk的github 与mongodb 的query 非常相似

因为我们在handler中用了context.done

这里其实是个callback function,等到getItem结束后,

才会执行context.done

并且会依序传入errordata两个object,

所以回传的response 会是像这样的一整个item:

{"Item" : {"id" : "3caaeb80-1ebf-11e6-81a9-21cf9c171332" ,"content" : {"message" : "Hello world again!"}}}

有时候我们并不想让使用者知道这么多,

所以可以使用response template,

这里就能看到前面说的json path 的用处:

// s-function.json"responseTemplates" : "$${responseReadPostTemplate}"

// s-templates.json"responseReadPostTemplate" : {"application/json" : {"post" : {"id" : "$input.path('$').Item.id" ,"content" : {"message" : "$input.path('$').Item.content.message"}}}}

Update an item

Update 跟Read 的做法其实已经大同小异,

一样是把查询用的Key放在params中,

这里我们一样把整包payload 都丢进来。

dynamo.putItem(event.payload, (err, data)=> {context.succeed(event.payload)})

看起来只是改成使用putItem而已,

但其实这边的template 有点小小的改变。

"requestUpdatePostTemplate" : {"application/json" : {"operation" : "update" ,"tableName" : "posts" ,"payload" : {"Item" : {"id" : "$input.params('id')" ,"content" : "$input.json('$')"}}}}

这样子的好处就是在更新时,只要在params输入指定的id

其余要更新的部分就是放在body里面。

这里的PUT并不是partial的更新, 而是整个会替换掉,符合它原本HTTP method 对应的行为

至于s-function.json里面要怎么改,这有点太trivial ,

就不放上来了。

Delete an item

删除一个item,要做的事情比update 单纯多了,

基本上只要指定好Key,一切就已经结束了:

dynamo.deleteItem(event.payload, context.done)

"requestDestroyPostTemplate" : {"application/json" : {"operation" : "destroy" ,"tableName" : "posts" ,"payload" : {"Key" : {"id" : "$input.params('id')"}}}}

List items

除了以上的CRUD 之外,

列出一定数量的items 也是一个相当常见的需求。

dynamo.scan(event.payload, context.done)

"requestListPostTemplate" : {"application/json" : {"operation" : "list" ,"tableName" : "posts" ,"payload" : {}}}

最后的Response template会用到foreach语法,

坦白说这里我压根不想去理解这里的意义是什么,

我宁愿在需要的时候再去查文件就好,

因为我相信这种夭寿的语法迟早会被改掉的:

"responseListPostTemplate": "{\"posts\" : [#foreach($post in $input.path('$').Items){\"id\" : \"$post.id\",\"content \" : { \"message\":\"$post.content.message\" }}#if($foreach.hasNext),#end #end ] }"

Conclusion

现在大概知道,

为什么当初开始学的时候网路上没什么好的教学文了,

因为config 的设置真的是挺复杂的,

不过我想这一篇这样记录下来,应该能让许多人省下走冤枉路的时间。

对于一个程式开发者来说,学习东西的时间就是最大的成本,

我想serverless 不管对于前后端来说,

都是一项很超值的投资。

因为大部分时候,我们都不需要开一整台机器来完成你想做的事情。

在完成这篇之后,可以做什么练习呢?

你可以试着把你原本在EC2 上host 的服务,

转移成serverless 架构。

光想就觉得超难的

或者是把一些routine 的工作,用serverless 的方式去做,

当你越过前面那些xxxconfig 后,

你会发现开发和部署上带来的效率令你吃惊。

作者:Tsung-Chen Ku

原文链接:http://denny.qollie.com/2016/05/22/serverless-simple-crud/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-12-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rainbond 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Introduction
    • 优点
      • 限制与风险
        • Why serverless framework
        • Setup 开发环境的建置
          • 为你的api 建立一个「role」
            • Create Project
              • Create First function
                • Deployment
                • Abstraction
                  • Overview
                    • Source event
                      • Context
                        • handler.js
                        • Implementation: Simple RESTful api
                          • Why
                            • Log
                              • Create an item
                                • Read an item
                                  • Update an item
                                    • Delete an item
                                      • List items
                                      • Conclusion
                                      相关产品与服务
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档