不久前,我们发布了 Fun 的 2.0 版本。欢迎大家使用!
Fun 是什么
Fun 是 have Fun with Serverless
的缩写,是一款 Serverless 应用开发的工具,可以帮助用户定义函数计算、API 网关、日志服务等资源。
为什么需要 Fun
Serverless 的出现,为我们的开发效率带来了巨大的提升。我们可以将 Serverless 看成是一种 "OpsLess" 的开发方式。利用 Serverless 产品开发出的应用,先天就是高度弹性可扩展的、按使用量付费的。
Serverless 已经极大的提升了开发效率以及应用发布后的运维效率。但在使用传统方式开发 Serverless 应用时,因为生态的相对不成熟,还是面临一些问题:
管理困难。FaaS 作为 Serverless 平台体系中的一部分,提供了一种更小粒度的开发模型。实现一个函数,就和我们过往开发中实现一个接口方法类似。因此,在一个 Serverless 应用中,往往包含多个函数,一部分函数可以用于功能的实现,一部分函数可以用于云服务间的粘合,这就导致了要管理非常多的的资源数量,如果再考虑到需要将函数部署在多个 region 中,那么要管理的资源数量还要乘上 region 数量。
交付困难。应用开发完成后,除了代码本身,还要附带详细的配置文档。
移植困难。在不同的 Region 下配置相同的应用,如果是手动操作,操作十分繁琐,也不利于维护。
Fun 就是致力于解决这些问题的一款工具。
全新的 Fun 2.0
2.0 我们依旧致力于为客户提供一款更好用的、提升开发效率的工具。2.0 的新功能参见。
2.0 相对于之前的版本,主要两个变化:
1. 定义了全新的 Serverless Application Model
为了解决传统方式开发 Serverless 应用面临的问题。Fun 2.0 版本引入了全新设计的 Serverless Application Model(SAM) 规范。
SAM 作为一种基础设施即代码(Infrastructure as Code),允许用户描述函数计算及其相关云资源,可以使用同一份模板文件,进行跨 region 或者账户部署您的云应用。描述云资源的模板文件,也会成为项目代码的一部分,在不同开发者之间共享。这极大的降低了 Serverless 应用的交付难度、管理难度、移植难度。
SAM 在设计之初,就考虑到对 ROS 的兼容,在未来,我们会将 ROS 已有的能力纳入进来,与 ROS 相互赋能。
2. 对其描述能力进行了增强
除了 1.0 支持的函数、API 网关的配置,我们在 2.0 中:
- 增强了对函数的描述能力:环境变量、日志服务、角色属性、VPC 属性等。
- 支持配置新的应用资源,比如 Table Store、日志服务等。
- 代码上传可以指定文件、目录、压缩包以及 OSS 路径。
- 更多的 API 网关参数配置
- 等等
Fun 使用示例
下面我们用 nodejs 编写一个结合函数计算、表格存储以及 API 网关的 demo,该 demo 可以用于统计网站的访问者数量。
首先,创建一个名为 index.js 的文件,内容为:
'use strict';
const TableStore = require('tablestore');
const Long = TableStore.Long;
async function getClient(context) {
return new TableStore.Client({
accessKeyId: context.credentials.accessKeyId,
secretAccessKey: context.credentials.accessKeySecret,
stsToken: context.credentials.securityToken,
endpoint: process.env['Endpoint'],
instancename: process.env['InstanceName']
});
}
async function getCount(client) {
var params = {
tableName: process.env['TableName'],
primaryKey: [{ 'count_name': 'views' }],
maxVersions: 1
};
const tableName = process.env['TableName'];
const response = await client.getRow(params);
const row = response.row;
if (row && row.primaryKey) {
return row.attributes[0].columnValue.toNumber();
}
return null;
}
exports.handler = function(event, context, callback) {
(async () => {
let views = null;
const client = await getClient(context);
let success = false;
do {
views = await getCount(client);
if (views) {
try {
await client.updateRow({
tableName: process.env['TableName'],
condition: new TableStore.Condition(TableStore.RowExistenceExpectation.IGNORE, new TableStore.SingleColumnCondition('count', Long.fromNumber(views), TableStore.ComparatorType.EQUAL)),
primaryKey: [{ 'count_name': 'views' }],
updateOfAttributeColumns: [
{ 'PUT': [{'count': Long.fromNumber(views + 1)}]}
],
returnContent: { returnType: TableStore.ReturnType.Primarykey }
});
success = true;
} catch (ex) {
if (ex.code !== 403) {
callback(ex, null);
}
}
} else {
try {
views = 1;
const res = await client.updateRow({
tableName: process.env['TableName'],
condition: new TableStore.Condition(TableStore.RowExistenceExpectation.EXPECT_NOT_EXIST, null),
primaryKey: [{ 'count_name': 'views' }],
updateOfAttributeColumns: [
{ 'PUT': [{'count': Long.fromNumber(views)}]}
]
});
success = true;
} catch (ex) {
console.log(ex);
}
}
} while(!success);
var response = {
isBase64Encoded: false,
statusCode: 200,
body: `You are the ${views}th visitor!`
};
callback(null, response);
})();
};
这里我们需要处理表格存在、表格不存在的两种情况,分别保证他们的操作的原子性。
可以看到,这里我们对于表格存储的配置都是直接从环境变量中读取的。即使后续的开发过程中,修改了表格的名字,只需要修改配置即可,不用修改代码。
接下来,我们直接在 template 中定义函数、表格存储以及 API 网关:
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
views-count-demo: # service name
Type: 'Aliyun::Serverless::Service'
Properties:
Description: 'Module as a service'
Policies:
- AliyunOTSFullAccess
views-count: # function name
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: index.handler
CodeUri: './'
Description: 'views counts'
Runtime: nodejs8
EnvironmentVariables:
InstanceName: views-count-inst
TableName: viewsTablesName
Endpoint: https://views-count-inst.cn-shanghai.ots.aliyuncs.com
views-count-inst:
Type: 'Aliyun::Serverless::TableStore'
Properties:
ClusterType: HYBRID
Description: used for views_count_demo
viewsTablesName: # table name
Type: 'Aliyun::Serverless::TableStore::Table'
Properties:
PrimaryKeyList:
- Name: count_name
Type: STRING
views_count_apis:
Type: 'Aliyun::Serverless::Api'
Properties:
StageName: RELEASE
DefinitionBody:
'/':
get:
x-aliyun-apigateway-api-name: views
x-aliyun-apigateway-request-config:
requestMode: "PASSTHROUGH"
requestProtocol: "http"
x-aliyun-apigateway-fc:
arn: acs:fc:::services/${views-count-demo.Arn}/functions/${views-count.Arn}/
执行 fun deploy,即可创建、配置并部署好相关服务:
$ fun deploy
Waiting for service views-count-demo to be deployed...
Waiting for function views-count to be deployed...
function views-count deploy success
service views-count-demo deploy success
Waiting for table store views-count-inst to be deployed...
Waiting for table store viewsTablesName to be created...
create table store viewsTablesName successfully
table store views-count-inst deploy success
Waiting for api gateway views_count_apis to be deployed...
URL: GET http://81f67bf5fce5478fa5dc18864a1fda18-cn-shanghai.alicloudapi.com/
stage: RELEASE, deployed, version: 20180702222529788
stage: PRE, undeployed
stage: TEST, undeployed
api gateway views_count_apis deploy success
用浏览器打开提示的链接 http://81f67bf5fce5478fa5dc18864a1fda18-cn-shanghai.alicloudapi.com/,即可预览效果。
未来展望
Fun 2.0 对我们来说,只是前进了一小步。Fun 工具还有很多地方需要改进,接下来我们会在以下三个方向上作出努力:
- 功能更完善:提供对更多资源的支持,比如 ots trigger、oss 及其触发器等。提供对 ROS 的支持,将 ROS 的能力整合进来。
- 开发更连贯:提供提供初始化工程的功能,并且支持线上配置导出为模板文件,即使手边没有模板文件,也能将线上配置一键导出为模板文件,助您持续地开发,高效地迭代。
- 调试更容易:提供本地运行调试、依赖安装打包等功能的支持,本地运行结果即为线上预期结果。
大家有什么新的需求或者使用过程中有任何问题,请随时联系:@倚贤 @小默 @朴灵