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 server
const 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 entry
module.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(函数计算) 在百度云的实现