当 Serverless 与低代码这两个不同的技术共同相交于同一个业务时会有怎样的价值展现?本文以 “盲盒抽奖” 这个 Serverless Devs 做过的创意营销活动为例,为大家讲述 Serverless 和低代码是如何搭配来满足一个业务诉求的。
01前言
线上 H5 创意动画结合线下实体奖励是互联网营销活动的一种常见手段。为了抓住关键时间节点,活动从策划到落地的周期一般都比较短,短时间内落地线上服务对于做技术开发的同学有着不小的挑战。
尤其是当有更多需求,比如增加后台管理以及关键前端访问数据埋点等需求时,挑战难度往往会加倍。对于开发而言除了完成业务核心诉求,往往还需要关注非业务诉求以外的其他状况,比如系统访问安全,高并发流量应对,系统运行指标可观测等。
在以前这类需求往往需要跟多的角色参与,如产品,前端,后端,设计,测试,运营,运维等,使得投入产出比变得比较低,活动持续性比较差。今天使用 Serverless + 低代码的技术我们可以大幅缩减做此类活动的成本,让它变成持续性的活动,从而大大提高运营效果。
实际上 Serverless Devs 此次的盲盒抽奖活动仅仅投了 3.5 个人,便完成了活动策划,产品设计, 前后端实现,系统部署运维等工作。并且借助于 Serverless 的服务能力,轻松应对了系统访问安全,高并发流量,系统可观测等这些非业务挑战。功能性的补齐 + 效率提升,让本次运营取得了非常好的收益,活动服务的模板化沉淀又为后续同类活动奠定好了良好的基础。
“盲盒抽奖” 活动的整体流程如下:
- 需求设计(含业务逻辑和交互逻辑)
- 设计稿
- 低代码实现前端
- 编码实现 Serverless 服务
- 联调测试
- 部署上线
- 活动中问题修复
- 活动后复盘
应用效果预览:
“1 分钟 Serverless 极速部署盲盒” 抽奖活动目前已经结束,但是感兴趣的同学仍可以体验一下:
https://developer.aliyun.com/adc/series/serverless2
架构预览:
本次的部署架构没有使用阿里云 API 网关,而是直接用函数计算Custom Runtime 作为托管形态,这样做是因为本次需求的特殊性,我们是 “自己部署自己抽”,实际上意味着端侧访问是非中心化的,端侧访问的服务做的也比较薄。只是数据处理接口调用和提供静态渲染服务,后经过一个中心化的逻辑后台处理中奖概率,管理奖品查询数据库等。
如果你是自己做中心化的活动后台,建议参考下面的架构模式,采用 API 网关作为流量入口,方便做更多的安全限制以及更灵活的扩展。
02实现解析
前端交互低代码实现
本次前端实现使用低代码工作 hype4,hype4 的具体使用这里不做详细介绍,有兴趣的同学可以自行搜索。
设计稿除了之后将需要的图切出来,然后跟处理 Flash 一样将动画效果实现,最后就是添加 js 代码实现接口访问,场景切换等能力。整个流程会比全编码要快很多,尤其是动效实现上比纯手写效率高 2-3 倍。
数据层 Serverless 服务
如架构所示,这里的数据层实际上就是我们理解的 SSF ,这层仅做数据转发和静态的渲染。代码实现比较简单,采用 express 框架,然后以函数计算 Custom runtime 形式部署。
目的是为了这个服务既可以托管静态内容也可以做动态的数据转发。值得注意的是,用户的信息获取也是在这层实现的。
这里我们获取的是阿里云用户的 accountId,方便对齐中奖信息发放奖品。对于普通开发者而言这个参数没有太大意义。
我们会提前将摇奖后台部署完毕,接下来就是,每个用户自己部署完这层服务后,访问自己部署好的服务,然后向摇奖后台传递 uid 等基本信息,发起摇奖。最终返回中奖结果透传给前端展示。作为管理员则可以通过后台操作设置奖品和概率等。
后台抽奖逻辑实现
后端服务采用 Python Web 框架:Django 进行实现,主要方法有:
1.获取用户的 uid 信息,并对uid信息进行校验,以确保:
- uid 信息的准确性
- 该客户端服务是通过 Serverless Devs 开发者工具进行部署;
2.当日奖品池的构建;3.用户中奖信息的初步确定;4.用户中奖信息的复核;5.返回最终的结果给客户端;
基本流程
1.用户在本地,将盲盒抽奖的客户端服务,通过 Serverless Devs 开发者工具进行部署,部署到用户自己的账号下;
在部署期间,需要给用户下发一个临时域名 (这个临时域名需要用到用户的 uid),Serverless Devs 在进行临时域名下发的过程中,会生成部分的客户端token,并记录到 Serverless Devs 后端服务中;这个token实际上就是鉴定用户身份的重要标记;
(如果用户在 Yaml 中声明了不使用系统自动下发的域名信息,可能就没办法顺利参加本次活动)。
2.用户部署完成之后,会返回一个Serverless Devs下发的临时域名,供用户学习和测试使用。
3.接下来用户通过浏览器,打开该临时域名,便可以看到抽奖的相关页面,用户可以点击进行抽奖操作。
4.用户点击抽奖操作之后,会发起请求,请求用户账号下的 Serverless 服务,该服务会根据用户的 uid 信息进行相关的处理,并发起真正的抽奖请求到本次活动的后端 Serverless 服务上;
5.本次活动的后端 Serverless 服务接收到用户的抽奖请求时,会:
- 获取用户账号下的 Serverless 服务发起抽奖请求时所传递 uid 信息;
- 使用获得到的 uid 信息在临时域名下发系统中进行数据匹配,确定用户本次使用了临时域名下发系统,并下发了对应的域名;
- 实现抽奖操作;
抽奖核心实现
在抽奖操作的过程中,也是对当前系统进行了初步的评估,设定了一个简单的,易于实现的,可以针对小规模抽奖活动的抽奖功能:
关于这一部分,Django 项目的实现方法:
@csrf_exempt def prize(request): uid = request.POST.get("uid", None) if not uid: return JsonResponse({"Error": "Uid is required."}) temp_url = "<获取uid的合法性和有效性,重要判断依据>?uid=" + str(uid) if json.loads(urllib.request.urlopen(temp_url).read().decode("utf-8"))["Response"] == '0': return JsonResponse({"Error": "Uid is required."}) token = randomStr(10) # 获取当日奖品 prizes = {} for eve_prize in PrizeModel.objects.filter(date=time.strftime("%Y-%m-%d", time.localtime())): prizes[eve_prize.name] = { "count": eve_prize.count, "rate": eve_prize.rate } # 构建抽奖池 prize_list = [] for evePrize, eveInfo in prizes.items(): temp_prize_list = [evePrize, ] * int((100 * eveInfo['rate'])) prize_list = prize_list + temp_prize_list none_list = [None, ] * (100 - len(prize_list)) prize_list = prize_list + none_list pre_prize = random.choice(prize_list) # 数据入库 try: UserModel.objects.create(uid=uid, token=token, pre_prize=pre_prize, result=False) except: try: if not UserModel.objects.get(uid=uid).result: return JsonResponse({"Result": "0"}) except: pass return JsonResponse({"Error": "Everyone can only participate once."}) if not pre_prize: return JsonResponse({"Result": "0"}) user_id = UserModel.objects.get(uid=uid, token=token).id users_count = UserModel.objects.filter(pre_prize=pre_prize, id__lt=user_id, date=time.strftime("%Y-%m-%d", time.localtime())).count() # 是否获奖的最终判断 if users_count >= prizes.get(pre_prize, {}).get("count", 0): return JsonResponse({"Result": "0"}) UserModel.objects.filter(uid=uid, token=token).update(result=True) return JsonResponse({"Result": { "token": token, "prize": pre_prize }})