GitChat 作者:伍正云
原文: 从0到1:饿了么风控计数服务是如何炼成的
关注公众号:GitChat 技术杂谈,一本正经的讲技术

引言

2017年4月份从饿了么正式进入多活领域开始,也预示着饿了么业务开始迈入下半场,此时风控团队面临着严峻的挑战,风控需要在事前、事中、事后进行全方位的防御.

而计数器的业务几乎贯穿了整个风控的需求,规则根据计数器拦截用户风险动作,运营系统需要根据计数器分析出商家、用户的刷单行为。首先,各个系统充斥着大量重复相同的计数器代码,其次开发团队对于这样重复劳动除了感觉疲惫,还有点缺乏技术含量,最后这样的开发成本与模式,并不能快速满足风控业务的需求,此时一个通用的计数器服务迫在眉睫。

案例

首先回顾一下风控使用计数器的一个场景,让大家了解计数器在风控的作用。

限制用户下单数

假设每天用户在饿了么最多下10单,那么用户在下第11单的时候将会被风控拒掉,此场景的校验流程如下:

在这个场景中,涉及到计数器的部分包括如下几部分:

  1. 获取计数器的逻辑

  2. 设置计数器的逻辑

  3. 计数器入库

方案:硬编码

由于历史原因,风控老计数器采用了硬编码的方式,伪代码如下:

    if (getUserOrderCount(userId)>10) {return "Reject";}setUserOrderCount(userId);saveUserOrderCount(userId);return “Accept”;

优点:

当计数器种类较少,改动不频繁的时候,开发效率高。

缺点:

  • 计数器改动成本高:例如改动计数器的存活周期,都需要走一遍发布流程 。

  • 当计数器种类较多时,维护性差,大量重复劳动。

思考计数器新设计

在思考新设计之前,我们先来总结一下老计数器的几大缺点:

第一:重复劳动

之前计数器的相关逻辑,各个系统都进行了相应的开发,这段逻辑大部分是相同的,是属于重复劳动的部分。

第二:key的生成规则需要暴露给其他系统

如果A系统创建了计数器counter1,此时B系统和C系统需要使用计数器counter1,必须得知道A系统创建counter1时候 key的生成规则。

第三:计数器不可配置

之前计数器是硬编码在系统中,这就意味着每次变更,例如更改计数器的生命周期和统计方式,都需要重新上线,而每次上线都需要经过alpha到生产一系列过程,耗时比较长,灵活性不够高。

计数器模型

计数器的本质是对某一对象进行分组,对某一字段进行函数计算的过程。

select sum(字段) from table group by 对象。

如果将对象抽象成主体,字段抽象成客体,sum抽象成函数,那么计数器模型组成如下:

计数器模型 = 主体+客体+函数

那么计数器模型的设计如图所示:

主要由三个部分组成:

计数器模型参数:计数器构成的三个要素分别是主体,客体,函数。

计数器逻辑执行器:主要用来执行计数器模型中函数部分,例如count、sum、max等。

结果:计数器逻辑执行的结果。

faraday系统设计

鉴于上面所说的计数器模型,我们开发了faraday服务,新计数器主要分为计数器视图中心和faraday soa 服务,具体如下图所示:

视图配置中心:

主要提供给调用方人员配置计数器模型参数。

例如:计数器类型,是否持久化,存活周期等等。

配置完参数,调用方无须关注计数器的具体实现细节,内部的操作。

流程对于调用方来说是个黑匣子。

faraday 服务:

主要有三大模块组成,分别是:

  1. 计数器模型配置器

    主要解析调用方的模型参数,并从配置视图中心获取计数器模型配置信息。

  2. 计数器逻辑执行器

    负责各种计数器的逻辑操作,例如:count、sum、max、min等。

  3. 异步引擎

    作用有2个方面,第一是异步化入库,防止操作数据库,导致接口性能降低,第二是化并行为串行,降低高并发带来的数据不一致问题。

faraday 整体流程图如下:

计数器类型

风控主要使用的计数器类型是count、sum、max、min、top,为了应对每天的订单,风控使用redis进行计数服务,主要是看中了redis不错的单机性能。

count和sum可以使用redis的incr 就可以办到。对于top类型的计数器,可以使用redis的sorted set,利用sorted set的score进行排序。

计数器中的max和min,计算的是最大值和最小值,最大值和最小值在高并发存储的时候会有一个问题,就以max为例讲解,如图:

在并发的时候,当a和b同时读到m的值是8,此时a=9,比m大,满足修改m的条件,去修改m=9;另外b=10,也满足修改m的条件,此时b也去修改m=10;因为修改的顺序不同,有可能最终m=9,与我们的预期值10不一致。

那么有没有什么办法彻底解决这种场景呢,答案肯定是有的,就是利用mq,将消息发给mq,然后部署一台机器,去单点消费这个mq,再去比较m的值,就不会存在同时修改m值的问题。
在实际生产环境中,部署单个机器消费mq,肯定是行不通的,因为这样不满足可用性,而且单点消费还会出现服务挂掉,不消费mq,从而导致mq消息堆积等一系列问题。

在faraday中其实采用了一个折中方案,就是将消息发送给local queue,在local queue中先获取m的值作比较,然后利用redis中的getset方法,再去比较一次,这样可以大大降低高并发带来的赋值不一致问题,具体流程如图所示:

时间窗口设计

计数器设计中有一大难点是时间窗口的设计。当初想到的方案有2种:

第一种是每隔xx时间,例如:每隔1天,每隔3小时,每隔5分钟。如果计数器选择的类型是每隔1小时,就将一天划分成24个1小时;如果是每隔2小时,就将一天划分成12个2小时;这种时间划分有一个缺点,如果是每隔5小时这种的,是没有办法整除的。所以这种时间窗口方案被我们抛弃了。

第二种是最近xx时间,例如:最近1天,最近3小时,最近5分钟。我们还是以最近xx小时举例,不管我们设置的是最近3小时,还是最近5小时,我们在redis中都是以小时账的形式进行存储,获取的时候,只要将前几小时的小时账进行合并就可以了,那么同理如果是天,就是日账,月就是月账。如图所示:

对于这种时间窗口有一个弊端,如果滑动时间窗口粒度很长,计算的复杂度就会越高,例如 统计最近10小时的计数器,就需要累加10个小时账;为了应对这种粒度很长的时间窗口,我们提出了中间值的概念,将历史的时间窗口计数器计算成中间值,那么无论滑动窗口多长,只需要计算一次,即: 计数器 = 当前时间窗口值 + 中间值

最终风控采用的时间窗口是第二种方案。

计数器key的设计

faraday 中key主要由 编号+主体+客体+统计方式+时间戳 组成,由于Redis是纯内存的,所以成本也不算低。为了降低成本,我们需要缩减key的长度,首先去掉了一些不必要的前缀,这些前缀加起来是33个byte,如果以10亿个计数器计算,去掉这些前缀,可以节省近31G的内存空间。另外对于时间戳,我们是采用yyyyMMdd 这种字符串形式存储,第一比较容易阅读,第二相比timestamp存储的字节更少。

faraday的接入步骤

视图配置

计数器的配置主要是由计数器的使用者自助完成。计数器在后台配置如图所示:

调用faraday服务

调用方只需要配置几行代码就可以完成计数器服务调用,伪代码如下:

    Counter counter = Client.getClient(Counter.class);Map<String,String> counterParam = new HashMap<>();counterParam.put(“mainBody”,”shop”);counterParam.put(“subBody”,”amount”);counterParam.put(“type”,”sum”);counter.counter(counterParam);

新计数器的优势

最后新计数器的优势也很明显,主要体现在以下几点。

第一:避免重复劳动

将分离在各个系统的计数器逻辑,抽象成一个服务,避免了重复劳动,调用方系统不需要关心计数器逻辑的实现细节,而key的生成规则对调用方系统是透明的,调用方只需要传几个参数就可以获取和设置计数器的值。

第二:快速满足风控业务需求

新计数器可以在后台系统动态配置,计数器的特性可以在线动态修改和生效,避免了每次更改都要走上线流程的繁琐步骤。

第三:对计数器有更强的把控能力

新技术器是一个服务,一个系统,让专业的系统去做专业的事情,更有利于职责分离,当计数器出现问题的时候,更有利于排查问题,而不是像之前那样去check各个系统。


实录:《伍正云:饿了么风控计数服务实战解析》


彩蛋

重磅 Chat 分享:

《高效学习,快速变现:不走弯路的五大学习策略》

分享人:
一名会在 B 站直播写代码,会玩杂耍球、弹 Ukulele、极限健身、跑步、写段子、画画、翻译、写作、演讲、培训的程序员。喜欢用编程实现自己的想法,在 Android 市场上赚过钱,有多次创业经历。擅长学习,习惯养成,时间管理。身体力行地影响他人做出积极的改变!目前就职于 ThoughtWorks,致力于传播快乐高效的编程理念。业余创立软件匠艺社区 CodingStyle.cn,组织超过30场技术活动。

Chat简介:
说到学习呀,真是头大哟:碎片化,没有较长的连续时间来学习难专注,捧起书,手机却在召唤:来呀,快活呀~ 反正有,大把时光~做不到,看了很多书,生活中却做不到然并卵,学了方法和工具,找不到使用场景效率低,学习速度跟不上知识产生的速度记不牢,学习速度赶不上遗忘速度在这个知识泛滥、跨界竞争的年代,学习能力才是核心竞争力。你想想,过去一周,有没有哪一件工作是不需要学习就能完成的?尽管如此重要,大部分人却没研究过学习这件事,以为上下班路上打开「得到」听本书,就是碎片时间终身学习者了。

我是程序员,咨询师,培训师,这几个角色都要求我必须学得又快又好。本场 Chat 将分析学习的「趋势,原则,策略」,帮你站在更高的视角看待学习,从「内容,动机,交互,收益,资源」五方面制定策略,解决学习痛点,助你成为高效学习者!

想要免费参与本场 Chat ?很简单,「GitChat技术杂谈」公众号后台回复「高效学习」

从0到1:饿了么风控计数服务是如何炼成的相关推荐

  1. 【饿了么】—— Vue2.0高仿饿了么核心模块移动端Web App项目爬坑(一)

    [饿了么]-- Vue2.0高仿饿了么核心模块&移动端Web App项目爬坑(一) 前言:学习Vue.js高仿饿了么课程过程中,总结了这个Web App项目从准备到开发完毕自己觉得很重要的知识 ...

  2. 饿了么风控及安全负责人王彬:互联网风控与安全

    上海,中国的经济.金融.贸易.航运中心,一直以变革.创新引领着中国企业的发展.在"互联网+"浪潮下,上海企业首当其冲,这座城市,一直用信息化手段推动着企业的高速发展.2016年10 ...

  3. Oracle 11g 的bug?: aix 上,expdp 11.2.0.1 导出,impdp 11.2.0.3 导入,Interval 分区的 【Interval】 分区属性成了【N】...

    如题: Oracle 11g 的bug?: aix 上,expdp 11.2.0.1 导出,impdp 11.2.0.3 导入,Interval 分区的 [Interval] 分区属性成了[N] 谨记 ...

  4. Notadd 4.0.0-alpha.1 基于 nest.js 的微服务架构

    新功能 AOP 面向切面编程 支持 Typescript Graphql API 支持 Grpc 的微服务架构 用户组织权限 服务(需要额外下载) Nest 采纳了 Angular 最核心的设计思想和 ...

  5. vert.x_使用vert.x 2.0,RxJava和mongoDB创建simpe RESTful服务

    vert.x 中断了将近半年后发表了一篇新文章. 在本文中,我们将快速了解如何开始使用vert.x,更有趣的是,如何使用RxJava简化异步系统的编程. 我们将涵盖以下主题: 使用Maven创建一个空 ...

  6. 使用vert.x 2.0,RxJava和mongoDB创建simpe RESTful服务

    中断了将近半年后发表了一篇新文章. 在本文中,我们将快速了解如何开始使用vert.x,更有趣的是,如何使用RxJava简化异步系统的编程. 我们将涵盖以下主题: 使用Maven创建一个空的vert.x ...

  7. 8.0魔兽服务器维护时间,《魔兽世界》8.0上线时间公布 取消PVE、PVP服务器之分...

    原标题:<魔兽世界>8.0上线时间公布 取消PVE.PVP服务器之分 <魔兽世界>8.0版本自公布以来就颇受关注,各种情报总能引起玩家热议.今天暴雪正式宣布该版本将会在8月14 ...

  8. Fabric 1.0源代码分析(30) Orderer #BroadcastServer(Broadcast服务端)

    # Fabric 1.0源代码笔记 之 Orderer #BroadcastServer(Broadcast服务端) ## 1.BroadcastServer概述 BroadcastServer相关代 ...

  9. 金融科技大数据产品推荐:蜜蜂+蜜罐报告——基于互联网大数据的风控技术服务平台

    1.产品名称 蜜蜂+蜜罐报告 2.所属分类 金融科技·风控反欺诈 3.产品介绍 蜜蜂报告通过用户授权后,对借款人基本信息通过海量的数据交叉验证,对借款人进行身份核实.验真.通过借款人授权后的电商信息, ...

最新文章

  1. 沈腾自比元宇宙DogKing,小扎变身「光头强」!Meta推出3D化身
  2. 青藤云安全张福:我们可以用“免疫系统”对抗黑客入侵吗?
  3. I00031 Look-and-say sequence
  4. 直接输出代码_C语言入门 | 优秀代码(四)A—F
  5. Sublime Text3 配置设置攻略
  6. 详解jenkins几个有用的插件如何使用(emma,findbugs)
  7. ROS中阶笔记(四):机器人仿真—Gazebo物理仿真环境搭建(重点)
  8. 综述!信息检索中的花式预训练
  9. 找出消费者在使用你的产品的时候,什么时刻觉得“值了”
  10. submit和button的区别
  11. 思科路由器上配置了rip但是没有生成动态路由表_思科路由器 RIP、OSPF、EIGRP 路由协议最简单的配置实例详解...
  12. Excel function
  13. python环境下skimage处理高通道tif图片(10通道)
  14. 解读 | 关于阿里巴巴架构大调整,有 7 个重点值得特别关注
  15. java pack unpack_解释一下pack和unpack
  16. QML编写自定义控件:手风琴图片滑动
  17. 清华大学计算机研究生课程表
  18. html中获取浏览器窗口宽度,JavaScript 获取浏览器窗口的大小
  19. 软件测试发展前景进阶路线(最全面)
  20. 比赛即实战!中国软件杯发布全新产业创新赛项,校企可联合参赛

热门文章

  1. 中文分词的原理与方法及其在运维领域中的实践
  2. 详细教你两台电脑之间传文件
  3. LatinIME中几个主要方法说明
  4. exe4j打包问题解决方案
  5. TP6基础教程-更新中
  6. 解决 Ubuntu 无法播放 MP4 格式视频的问题的办法
  7. OpenCV机器视觉-HSV颜色模型
  8. 无限级分类添加,修改,删除
  9. Android自学笔记 第四天(下)
  10. 一个程序员可以差到什么程度?真诚地感激曾经没有当面嘲笑我的每一个同事