17

关于触发器产品的一些思考

 3 years ago
source link: https://canmeng.net/c/995
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

不知不觉,从8月份敲下第一行需求描述到现在,已经三个月过去了。这三个月对我来讲很痛苦,对研发和合作团队来讲也很痛苦。这是一项浩大的工程,从后台到控制流再到前端,跨团队合作,几个新瓜蛋子愣是把整个触发器合作流程趟了个遍。真的很不容易。也成功完成了最重要的触发器对接。

好在,大差不差的产品成功上线了,这对整个后续的产品协同和产品接入规划其实有着很深刻的意义。对产品如是,对研发也如是。

其实这些都是题外话了,这次我想分享一些关于触发器产品的思考和总结。也顺带给其他做B端的一些同学警醒和建议。

首先在做一个功能前,需要解决的问题是这个东西有没有用户用。其次才是怎么让用户用的爽。这其实是一个需求的原动力,推合作方如此,推客户更是如此。所以在做需求前,为了不犯错,其实我在私下也会做很多分析&调研。能做是一方面,做出来有价值其实又是另一方面。

3aMFjyM.png!mobile

上图是艾瑞咨询发的基础云服务行业发展概览图。其实本身云函数其实就是基础云设施,只是它的粒度足够小,足够便捷,也足够即用即走。它可能短时间无法替代主流的虚拟机,数据库,存储,但是它有一个最大的优势 ———— 无孔不入。做触发器其实就是渗入到各个云产品,让各个云产品所产生的事件都能被函数消费,处理。

更准确一点,就是把上图的核心基础服务全部接入函数计算。这带来的价值是不可估量的,这里就以我们已经接入并发布的CLS触发器场景来讲吧,链接在 这里 。这个其实只是前期调研的冰山一角,我们还探索了很多类似这样的细分场景。它小吗?小,小到根本不值一提,但是它确实解决了用户在日志场景下处理数据的诉求。而且在这些场景下,云函数是不可替代的,其他云服务都干不了这事儿。当然除非另外产品化能力,但与之而来的是高昂的成本。

以这个例子阐述,就可以明白触发器对整个FaaS生态的重要了吧,简单点就是接一个触发器可能会有10个左右的细分场景,接十个就是100个细分场景,在配合函数间调用,或者工作流伴随着用户理解函数计算的概念提升,这里会越来越具价值和战略意义。

那么问题来了,这么多云产品,从哪块入手呢?这个问题其实很简单,使用排除法+简单分析就完全能搞定:

  1. 函数本身提供的就是基础计算能力,所以 VM虚拟机,容器 等一系列计算产品本身就搭不上边
  2. 以艾瑞咨询发布的概览图为例子,我们能搞的只有 对象存储/CDN/LB/数据库?对象存储其实很早就已经有了,CDN 其实主要场景还是刷新,LB也算但对函数来讲不太刚需,数据库本身就有事务的概念所以这里对函数来讲入局的希望不大。

那我们换个思维,除了这些开发者最关注的是啥?那首当其冲的就是日志!日志永远是所有业务最核心而且开发者最常用的。简单梳理了下,便画出了这样的业务流,通过 CLS+SCF 构建整个云上业务的数据流处理枢纽,一个负责数据采集,一个负责数据计算,简直不要太搭:

reaaauJ.png!mobile

大方向有了,客户场景也有了,那接下来就是细节设计了。我们想到 AWS Lambda 一定会惊叹于它海量的数据源支持,它其实是Faas乃至整个云服务的祖师爷。过庙留香,何况带我入行的师傅说过一句让我至今记忆犹新的话“如果能把产品策略抄到AWS 90%,其实就能干掉 99% 的做云服务的产品经理”。所以这尊佛还是要拜拜的。

触发器的核心其实就是 event,没有什么能比定一个靠谱的 event 对触发器更重要了,这里调研了三个方案:

祖师爷 AWS:

AWS的做法十分精巧,直接通过gzip压缩+base64来把日志流的日志投递到函数 event,按照条数依次投递到用户函数,具体做法如下:

这个是函数收到的event:

{
  "awslogs": {
    "data": "ewogICAgIm1lc3NhZ2VUeXBlIjogIkRBVEFfTUVTU0FHRSIsCiAgICAib3duZXIiOiAiMTIzNDU2Nzg5MDEyIiwKICAgICJsb2dHcm91cCI6I..."
  }
}

这个是解压+解码后的数据结构:

{
    "messageType": "DATA_MESSAGE",
    "owner": "123456789012",
    "logGroup": "/aws/lambda/echo-nodejs",
    "logStream": "2019/03/13/[$LATEST]94fa867e5374431291a7fc14e2f56ae7",
    "subscriptionFilters": [
        "LambdaStream_cloudwatchlogs-node"
    ],
    "logEvents": [
        {
            "id": "34622316099697884706540976068822859012661220141643892546",
            "timestamp": 1552518348220,
            "message": "REPORT RequestId: 6234bffe-149a-b642-81ff-2e8e376d8aff\tDuration: 46.84 ms\tBilled Duration: 100 ms \tMemory Size: 192 MB\tMax Memory Used: 72 MB\t\n"
        }
    ]
}

这样做大大提升了传输效率并有效降低了用户使用成本。但是,AWS 这里只支持了日志流的日志,意味着EC2其实不能采集到Lambda的,这点完全不可取。

qqAFZfU.png!mobile

国内老大 阿里云:

阿里云其实做法很讨巧,知道日志服务都为数据量极大的场景,所以他们只传了数据游标,大概是这样的:

{
    "parameter": {},
    "source": {
        "endpoint": "http://cn-shanghai-intranet.log.aliyuncs.com",
        "projectName": "log-com",
        "logstoreName": "log-en",
        "shardId": 0,
        "beginCursor": "MTUyOTQ4MDIwOTY1NTk3ODQ2Mw==",
        "endCursor": "MTUyOTQ4MDIwOTY1NTk3ODQ2NA=="
    },
    "jobName": "1f7043ced683de1a4e3d8d70b5a412843d817a39",
    "taskId": "c2691505-38da-4d1b-998a-f1d4bb8c9994",
    "cursorTime": 1529486425
}

这个做法其实是牺牲了用户体验,并且变相延长了函数的运行事件,如果游标设置过大会导致函数一直超时。这个方案其实很LOW,最起码不符合触发器的定义。按照阿里的做法用户需要使用特定的SDK来获取游标信息,代码如下:

# -*- coding: utf-8 -*-
import logging
import json
from aliyun.log import LogClient
from time import time
 
def logClient(endpoint, creds):
  logger = logging.getLogger()
  logger.info('creds info')
  logger.info(creds.access_key_id)
  logger.info(creds.access_key_secret)
  logger.info(creds.security_token)
  accessKeyId = 'your accessKeyId'
  accessKey = 'your accessKeyId scr'
  client = LogClient(endpoint, accessKeyId, accessKey)
  return client
 
def handler(event, context):
  logger = logging.getLogger()
  logger.info('start deal SLS data')
  logger.info(event.decode().encode())
  info_arr = json.loads(event.decode())
  fetchdata(info_arr['source'],context)
  return 'hello world'

年轻人,讨巧可以,但是这么搞用户不会投诉吗?把自己的触发器成本完全转嫁到用户头上。

华为云:

讲真,我真的不想说。抄都不认真抄,当真是垃圾云啊。

{
    "lts": {
        "data": "ewogICAgICAgICJsb2dzIjpbewogICAgICAgICAgICAgICAgIm1lc3NhZ2UiOiIyMDE4LTA2LTI2LzE4OjQwOjUzIFtJTkZdIFtjb25maWcuZ286NzJdIFN1Y2Nlc3NmdWxseSBsb2FkZWQgZ2VuZXJhbCBjb25maWd1cmF0aW9uIGZpbGVcXHJcXG4iLAogICAgICAgICAgICAgICAgInRpbWUiOjE1MzAwMDk2NTMwNTksCiAgICAgICAgICAgICAgICAiaG9zdF9uYW1lIjoiZWNzLXRlc3RhZ2VudC5ub3ZhbG9jYWwiLAogICAgICAgICAgICAgICAgImlwIjoiMTkyLjE2OC4xLjk4IiwKICAgICAgICAgICAgICAgICJwYXRoIjoidXNyL2xvY2FsL3RlbGVzY29wZS9sb2cvY29tbW9uLmxvZyIsCiAgICAgICAgICAgICAgICAibG9nX3VpZCI6IjY2M2Q2OTMwLTc5MmQtMTFlOC04YjA4LTI4NmVkNDg4Y2U3MCIsCiAgICAgICAgICAgICAgICAibGluZV9ubyI6NjE1CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJtZXNzYWdlIjoiMjAxOC0wNi0yNi8xODo0MDo1MyBbV1JOXSBbY29uZmlnLmdvOjgyXSBUaGUgcHJvamVjdElkIG9yIGluc3RhbmNlSWQgb2YgY29uZmlnLmpzb24gaXMgbm90IGNvbnNpc3RlbnQgd2l0aCBtZXRhZGF0YSwgdXNlIG1ldGFkYXRhLlxcbiIsCiAgICAgICAgICAgICAgICAidGltZSI6MTUzMDAwOTY1MzA1OSwKICAgICAgICAgICAgICAgICJob3N0X25hbWUiOiJlY3MtdGVzdGFnZW50Lm5vdmFsb2NhbCIsCiAgICAgICAgICAgICAgICAiaXAiOiIxOTIuMTY4LjEuOTgiLAogICAgICAgICAgICAgICAgInBhdGgiOiIvdXNyL2xvY2FsL3RlbGVzY29wZS9sb2cvY29tbW9uLmxvZyIsCiAgICAgICAgICAgICAgICAibG9nX3VpZCI6IjY2M2Q2OTMwLTc5MmQtMTFlOC04YjA5LTI4NmVkNDg4Y2U3MCIsCiAgICAgICAgICAgICAgICAibGluZV9ubyI6NjE2CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJtZXNzYWdlIjoiIEluIGNvbmYuanNvbiwgcHJvamVjdElkIGlzIFtdLCBpbnN0YW5jZUlkIGlzIFtdLiBNZXRhRGF0YSBpcyB7NDU0MzI5M2EtNWIyYy00NGM0LWI3YTAtZGUyMThmN2YyZmE2IDYyODBlMTcwYmQ5MzRmNjBhNGQ4NTFjZjVjYTA1MTI5ICB9XFxyXFxuIiwKICAgICAgICAgICAgICAgICJ0aW1lIjoxNTMwMDA5NjUzMDU5LAogICAgICAgICAgICAgICAgImhvc3RfbmFtZSI6ImVjcy10ZXN0YWdlbnQubm92YWxvY2FsIiwKICAgICAgICAgICAgICAgICJpcCI6IjE5Mi4xNjguMS45OCIsCiAgICAgICAgICAgICAgICAicGF0aCI6Ii91c3IvbG9jYWwvdGVsZXNjb3BlL2xvZy9jb21tb24ubG9nIiwKICAgICAgICAgICAgICAgICJsb2dfdWlkIjoiNjYzZDY5MzAtNzkyZC0xMWU4LThiMGEtMjg2ZWQ0ODhjZTcwIiwKICAgICAgICAgICAgICAgICJsaW5lX25vIjo2MTcKICAgICAgICAgICAgfQogICAgICAgICAgICBdLAogICAgICAgICJvd25lciI6ICI2MjgwZTE3MGJkOTM0ZjYwYTRkODUxY2Y1Y2EwNTEyOSIsCiAgICAgICAgImxvZ19ncm91cF9pZCI6ICI5N2E5ZDI4NC00NDQ4LTExZTgtOGZhNC0yODZlZDQ4OGNlNzAiLAogICAgICAgICJsb2dfdG9waWNfaWQiOiAiMWE5Njc1YTctNzg0ZC0xMWU4LTlmNzAtMjg2ZWQ0ODhjZTcwIgogICAgICAgIH0="
    }
}

华为云直接把格式做了一层Base64就投递给函数了!!!!

why? 产品经理该被炒了把,这层Base64有啥意义? AWS做Base64是因为人家做了gzip解压啊,用句老话真的是脱裤子放屁多此一举。不仅增加了用户成本,而且对整个触发器没有任何收益,就是单单抄了个AWS皮毛,怕不是在逗我。这种产品搞出来是开玩笑的吧。

我不相邪,专门查了他们的demo,如下:

import json
import base64
import sys
import os
import requests
 
from com.obs.client.obs_client import ObsClient
from com.obs.models.put_object_header import PutObjectHeader
from com.obs.models.get_object_header import GetObjectHeader
from com.obs.models.get_object_request import GetObjectRequest
from com.obs.models.server_side_encryption import SseKmsHeader, SseCHeader
from com.obs.log.Log import *
 
current_file_path = os.path.dirname(os.path.realpath(__file__))
# append current path to search paths, so that we can import some third party libraries.
sys.path.append(current_file_path)
.......
path_style = Truedef handler(event, context):
 # Obtains the data of lts logs.
 print ("*********the data of lts logs********")
 encodingData = event["lts"]["data"]
 data = base64.b64decode(encodingData) # Base64 decoding is required because the information of lts logs has been encoded.
 text = json.loads(data)
.......

嗯嗯,他们确实只做了base64 !!!! 好吧,从此华为云就从竞品分析的名单消失了。

一通分析下来,我发现需要一个更严谨的针对腾讯云的Event方案,如下:

首先,AWS 的 Gzip+Base64是可取的,但是又不可完全抄。因为AWS的 cloud watch logs 只支持了一些流式单条数据,但是现有的CLS其实是可以批量采集 Nginx 或 Tomcat 这种传统日志的。所以,单条触发函数一定不可取。那么这里只能借鉴其他触发器了增加 batch size 或者 batch window,和研发同学对齐后发现其实 batch window 会更符合聚合场景也更容易实现。所以 CLS 触发器大致的处理流程是这样的:

为保证单次触发传递数据的效率,数据字段的值是 Base64 编码的 ZIP 文档。

{
  "clslogs": {
    "data": "ewogICAgIm1lc3NhZ2VUeXBlIjogIkRBVEFfTUVTU0FHRSIsCiAgICAib3duZXIiOiAiMTIzNDU2Nzg5MDEyIiwKICAgICJsb2dHcm91cCI6I..."
  }
}

用户可在创建触发器时选择聚合事件,触发器组件会自动按照超时时间打包数据,并一次投递多条消息到云函数,当然这个过程是异步的。在解码和解压缩后,日志数据类似以下 JSON 体,以 CLS Logs 消息数据(已解码)为例:

{
    "topic_id": "xxxx-xx-xx-xx-yyyyyyyy",
    "topic_name": "testname",
    "records": [{
        "timestamp": "1605578090000000",
        "content": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }, {
        "timestamp": "1605578090000000",
        "content": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }]
}

这个,是我能想到业内最完美的 CLS 触发器解决方案了,中和了AWS,阿里云的优势,支持Nginx,CDN等多种产品的日志直接聚合投递。用户体验也非常爽,基本只需要三行代码:

const zlib = require('zlib');
 
exports.main_handler = async (event, context) => {
    const payload = Buffer.from(event.clslogs.data, 'base64');
    const parsed = JSON.parse(zlib.gunzipSync(payload).toString('utf8'));
    console.log('Decoded payload:', JSON.stringify(parsed));
    return `Successfully `;
};

这个体验基本可以甩阿里云,华为云20多条街。所以还是那句话,跑得快不是成功,不栽跟头想清楚然后在投入做才是。

哈哈,如上就是一些关于做这个触发器的一些思考,当然后续还有更多。会在得空的时候分享一些心得。同时也给自己备忘。

见解浅薄,如有疏漏还望见谅。最后打个广告: CLB 触发器请看这里! 使用实践请看这里!

切记,切记,切记。做B端产品千万别搞出笑话,华为云真的是。。。。。一言难尽。。。。。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK