Koa.js + 函数计算实现serverless后台

Posted DevStudio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Koa.js + 函数计算实现serverless后台相关的知识,希望对你有一定的参考价值。

本文介绍如何使用函数计算+Koajs+表格存储实现一个serverless web服务。

背景

偶然的机会,试用了阿里云的函数计算,经过数十天尝试,把新浪SAE的一堆web服务浩浩荡荡迁移到函数计算之后,才发现Serverless架构已经如此成熟,开始飞入寻常百姓家了。

言归正传

作为Serverless的重要一环,FaaS服务也是各大云厂商必争之地。函数计算 是阿里云推出一款FaaS服务。它的优点是无运维、动态扩容、按量计费;缺点是目前支持的平台还不够多(Nodejs、python、php、java)。对于我这种屌丝而言,这几个缺点已经可以忽略了,而它的优点简直让人无法拒绝。


实现

技术栈

  • koajs,轻量、成熟,特别适合FaaS

  • 函数计算(FaaS),廉价、稳定

  • 表格存储(BaaS),廉价、稳定,高性能,FaaS的完美搭


配置

函数计算弱化了服务器的概念。要部署一个web服务,我们只需把业务代码上传到阿里云,配置访问入口,就完成了。不需要计算几核几G,也不需要考虑容灾报警,这些琐事都交给阿里云。

函数计算的部署主要有两种方式

  • 借助阿里官方的fun cli,可以一键部署

  • 使用阿里云控制台手动上传代码,手动填写配置

    • 代码的上传方式有:在线编辑,OSS上传,代码包上传,文件夹上传

    • 需要配置内容有:函数名、运行环境(nodejs8)、超时时间、环境变量、触发器……

在调试初期,我用的是阿里云控制台,上手比较简单,流程跑通后可以导出配置文件(一个template.yml文件),再改为funcli部署。

需要注意的是,只有配置了Http触发器才能实现公网的http访问,而且只有绑定了自定义域名才能像网站一样打开页面,否则原始url的http响应头有限制,浏览器是无法正常打开页面的。


函数

函数计算的入口是一个js函数,它的参数根据不同的触发器会有所不同。

http出发器的入口参数有req,resp,context。基于这些参数即可完成一次http请求的响应,但是它们与Nodejs的HttpServer并不完全兼容。不过前人帮我们做了一些转换,可以直接拿来用 --- @webserverless/fc-express

从名字fc-express即可看出,它是为express.js做的,还好Koa和express很有渊源,想要兼容也很简单,下面是代码。

index.js:

 
const { Server } = require('@webserverless/fc-express')const Koa = require('koa')const app = require('./src/server');let serverconst app = new Koa();module.exports.init = function (context, cb) { function callback() { cb(null, 'finish init'); } if (!server) { server = new Server(app.callback(), callback); server.startServer() }}// http trigger entrymodule.exports.handler = async function (req, res, context) { server.httpProxy(req, res, context);};


注意,函数计算会涉及到冷启动问题,exports.init 就是为了解决这种问题而存在的。冷启动时,只有cb(null, 'finish')执行完成后,才会进入exports.handler执行。


业务


 
wechatRouter.get('/:privatekey/wechat-token', async function (ctx) { if (!ctx.request.query) { ctx.request.query = {} } const { privatekey } = ctx.params; const { echostr, signature, timestamp, nonce } = ctx.request.query; if (check([genToken(privatekey), timestamp, nonce], signature)) { ctx.body = echostr; } else { ctx.status = 400; ctx.body = 'invalid req.' }})
wechatRouter.post('/:privatekey/wechat-token', async function(ctx) { if (!ctx.request.query) { ctx.request.query = {} } const { privatekey } = ctx.params; const { signature, timestamp, nonce } = ctx.request.query; if (check([genToken(privatekey), timestamp, nonce], signature)) { await wechatMsgHandler(ctx, privatekey); } else { ctx.status = 400; ctx.body = 'invalid req.' }})



其中get接口处理微信公众平台的校验,post接口处理消息响应。


存储

FaaS需要搭配BaaS才能实现完整的web服务,BaaS往往是一些状态存储相关的服务,如表格存储、oss、消息队列、缓存服务等。

大部分情况下,阿里云的TableStore可以取代传统的SQL数据库。相比目前SQL数据库服务,TableStore能够按量付费(近似bai piao)。它也为Nodejs平台提供了简单的SDK。


 
const { client } = require('../lib/tablestore');const TableStore = require('tablestore');const log = require('../lib/log');async function putUser(appName, openid, enabled) { const currentTimeStamp = Date.now(); const params = { tableName: 'user', condition: new TableStore.Condition(TableStore.RowExistenceExpectation.IGNORE, null), primaryKey: [{ openid, appName } ], attributeColumns: [ { enabled, 'timestamp': currentTimeStamp }, ], returnContent: { returnType: TableStore.ReturnType.Primarykey } }; try { const result = await client.putRow(params); } catch(e) { log.error(e) } return true;}async function putUnsub(appName, openid) { const currentTimeStamp = Date.now(); const params = { tableName: 'unsub_log', condition: new TableStore.Condition(TableStore.RowExistenceExpectation.IGNORE, null), primaryKey: [{ openid }, { appName }, { id: TableStore.PK_AUTO_INCR } ], attributeColumns: [ { 'time': currentTimeStamp }, ], returnContent: { returnType: TableStore.ReturnType.Primarykey } }; try { const result = await client.putRow(params); } catch(e) { log.error(e) } return true;}




部署

使用funcli可以实现快捷的函数计算部署,下面是一个配置文件demo

template.yml

 
ROSTemplateFormatVersion: '2015-09-01'Transform: 'Aliyun::Serverless-2018-04-03'Resources: wx.roughwin.com: Type: 'Aliyun::Serverless::CustomDomain' Properties: Protocol: HTTP,HTTPS CertConfig: CertName: 'Cert1' PrivateKey: './cert/privkey.pem' Certificate: './cert/cert.pem' RouteConfig: routes: '/*': ServiceName: app FunctionName: api app: Type: 'Aliyun::Serverless::Service' Properties: Description: This is app service Role: 'acs:ram::1234:role/fc-log-role' LogConfig: Project: logtest Logstore: backend-log VpcConfig: VpcId: vpc-123 VSwitchIds: - vsw-123 SecurityGroupId: sg-123 InternetAccess: true api: Type: 'Aliyun::Serverless::Function' Properties: Initializer: index.init InitializationTimeout: 3 Handler: index.handler Runtime: nodejs8 Timeout: 10 MemorySize: 128 CodeUri: ./web Events: httpTrigger: Type: HTTP Properties: AuthType: ANONYMOUS Methods: - GET - POST


总结

serverless是为云计算而生的一种技术架构,优势明显,前景不错。目前各大云厂商提供的serverless技术方案都已经非常成熟、易用。对创业团队来说,将现有技术和业务迁移到serverless已经完全可行。serverless的成本、效率优势也十分可观。



以上是关于Koa.js + 函数计算实现serverless后台的主要内容,如果未能解决你的问题,请参考以下文章

Serverless Computing(函数计算) 在百度云的实现

函数计算云平台设计与实现

函数计算搭建 Serverless Web 应用- HTTP 触发器

从函数计算架构看 Serverless 的演进与思考

从函数计算架构看 Serverless 的演进与思考

节省 58% IT 成本,调用函数计算超过 30 亿次,石墨文档的 Serverless 实践