Fun 发布 2.0 新版本啦

本文涉及的产品
简介: 不久前,我们发布了 Fun 的 2.0 版本。欢迎大家使用! Fun 是什么 Fun 是 have Fun with Serverless 的缩写,是一款 Serverless 应用开发的工具,可以帮助用户定义函数计算、API 网关、日志服务等资源。

不久前,我们发布了 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/,即可预览效果。

demo 完整代码

未来展望

Fun 2.0 对我们来说,只是前进了一小步。Fun 工具还有很多地方需要改进,接下来我们会在以下三个方向上作出努力:

  • 功能更完善:提供对更多资源的支持,比如 ots trigger、oss 及其触发器等。提供对 ROS 的支持,将 ROS 的能力整合进来。
  • 开发更连贯:提供提供初始化工程的功能,并且支持线上配置导出为模板文件,即使手边没有模板文件,也能将线上配置一键导出为模板文件,助您持续地开发,高效地迭代。
  • 调试更容易:提供本地运行调试、依赖安装打包等功能的支持,本地运行结果即为线上预期结果。

大家有什么新的需求或者使用过程中有任何问题,请随时联系:@倚贤 @小默 @朴灵

Fun 相关文档

使用入门:中文英文

Serverless Application Model 规范文档:中文英文

github demos

目录
相关文章
|
5月前
|
Windows
window升级版本
window升级版本
30 0
|
4月前
|
资源调度 大数据 流计算
CloudEon V1.3.0版本发布!
CloudEon V1.3.0版本发布!2023 年最后一个版本了,感谢朋友们的支持,祝大家新年快乐🎉🎉,咱们明年再见!
|
7月前
|
Kubernetes 大数据 Java
CloudEon V1.2.0版本发布!
CloudEon V1.2.0版本发布!实现用k8s job进行初始化服务任务,替换原来用ssh执行命令的方式,提高兼容性和更好监听状态和获取日志等
|
定位技术 ice
OSGeoLive 15.0 版本发布
OSGeoLive 15.0 版本发布
92 0
|
JavaScript 前端开发 API
VS Code v1.23发布,这个版本有点意思
Welcome to the April 2018 release of Visual Studio Code. This milestone the VS Code team has been focused on API work to support extension authors, but there are still plenty of updates in this version that we hope you will like. Some of the key highlights include:
333 0
|
应用服务中间件
2.55.0 版本发布
信息摘要: 1. Dubbo 服务治理 公网体验 专业版试用 跨账号访问 EDAS 日志功能优化适用客户: EDAS 全体客户版本/规格功能: 1. Dubbo 服务治理。支持动态配置、权重调整、负载均衡、全局配置服务治理功能。
540 0
|
Web App开发 JavaScript API
|
Web App开发 JavaScript 前端开发