时序异常数据检测从理论到落地

最近在解决一个挺有意思的问题,该问题可以抽象简化为:如何检测外部依赖接口发生异常。如果是在中小公司,那么这个问题其实是不需要解决的,从研发层面规范接口的格式,并规定依赖接口必须达到要求的可用性,只要调用成功率低于规范就进行告警。不幸的是,这个方法在当前的BU中是推不动的,是为背景。

简单的对接口调用失败量对齐时间轴看了一下,这是一个典型的时序数据异常检测问题。

从分类看,当前发展阶段的时序异常检测算法和模型可以分为一下几类:

  • 统计模型:优点是复杂度低,计算速度快,泛化能力强悍。因为没有训练过程,即使没有前期的数据积累,也可以快速的投入生产使用。缺点是准确率一般。但是这个其实是看场景的,并且也有简单的方法来提高业务层面的准确率。这个后面会提到。
  • 机器学习模型:鲁棒性较好,准确率较高。需要训练模型,泛化能力一般。
  • 深度学习模型:普遍需要喂大量的数据,计算复杂度高。整体看,准确性高,尤其是近段时间,强化学习的引入,进一步巩固其准确性方面的领先优势。

而我们希望在9月份就能够上线运行,并且没有历史数据,更不要提打标数据了。因此,只能选择统计模型作为一期落地的方案。而在统计模型中,twitter 在2015年发布的 AnomalyDetection 自然是翘楚。如果你正好使用 R 语言,那么直接上手就可以用。如果你需要 pure python 版本,推荐使用 Twitter’s Anomaly Detection in Pure Python.

S-H-ESD 原理

twitter 公开的异常检测算法的核心是使用了S-H-ESD异常检测算法。这种算法的思想是将时序数据使用 STL 分解,然后将分解的余项使用 Grubbs’ Test 进行异常点的检测(实际使用的算法考虑了极值异常点对整体的影响,实际使用的是的Grubbs’ Test变形)。关于算法的细节可以参看 twitter 发布的论文 Automatic Anomaly Detection in the Cloud Via Statistical Learning. 显然,这个算法之所以有效的两个关键就是 STL 和 Grubbs’ Test。

STL 将时序数据分解为 趋势 + 周期 + 余项:

直观上,可以将趋势项理解为时序数据的骨骼;周期数据是数据的振幅;余项是则是消除趋势和周期数据后,相对平滑稳定的“皮毛”。而这种皮毛数据是符合 Grubbs’ Test 假设中正常数据正态分布的。反之,则被 Grubbs’ Test 认为是异常数据。

因此,S-H-ESD 只适用于周期性数据。对于无周期性或数据变化特别剧烈的时序数据,S-H-ESD都不是好的选择。

S-H-ESD 用于生产环境

S-H-ESD 原理简单,理论效果也非常不错,基本起手能达到 40% ~ 60% 的准确率。但是实际应用中经常会遇到以下典型的误报情况:

而这种误报其实很难从算法本身消除,即使消除了其实也没有泛化性。一种简单的思路是引入更多数据和规则:

  • 上图中我们只是用的接口的报错量作为时序数据,单村在报错量上提高准确性在统计模型这个大前提下边际成本已经很高了。因此,可以考虑引入接口调用量和成功率来综合判断该点是否真正异常。
  • 结合实际业务,设定一些简单的规则减少误报量。对于我们的场景,可以设定的规则有报错量的基础阈值、报错点的持续时长等。

S-H-ESD 不是银弹,结合多维数据和业务规则以后,准确率基本达到了我们的预期。S-H-ESD 也不是终点,确切说是我们顺便解决当前问题,同时收集异常数据的手段。未来,我们会尝试结合深度学习模型提高异常检测点上的准确性,同时融合多维数据,将点上的异常检测逐步整合为线和面上的检测能力。

扩展阅读

GopherChina 2019 keynote 点评

今年的 GopherChina 大会如期而至,没能亲临现场,但是 keynote 绝不会错过。一如往常,谢大第一时间放出了今年的keynote。今年的 keynote 中有不少老面孔,不知道以后大会是否会把固定若干老面孔作为惯例。如果你错过了去年的 keynote, 可以参见鄙人拙文《GopherChina 2018 keynote 点评》

整体上,今年的演讲主题跟往年所涉及的领域和覆盖的范围区别不大,无论你是关注架构、微服务、语言细节,还是数据库、存储、业务及应用系统构建,都能从中找到自己感兴趣的内容。

1.1 大型微服务框架设计实践 – 杜欢

如果你曾经想用比较hack的方式获取goroutine id, 那么你有很大可能性使用过杜欢的goroutine. 也因为写Golang 获取 goroutine id 完全指南的缘故,跟杜欢结识。看到这个keynote,心里还是有种从未谋面,但是久违的熟悉感。在大概3年前,我其实也做过类似的框架设计和开发。很多理念和原则的确是 cant't agree more. 其中,“框架和业务正交”的原则也是充分发挥了golang自带的正交特性。

在框架中,隔离层的思想很朴素,但是很实用。我曾经因为在设计之初没有引入隔离层,自己手动修改了多个数据库驱动库,以满足框架某个特性的引入。如今想想,真的是血与泪的教训。

1.2 用Go打造Grab的路径规划和ETA引擎

不得不感慨,Grab 的业务才是真的大型生活类服务。从形态上看,已经约等于国内滴滴+美团+顺丰组合了。演讲内容偏向算法。对地图路径规划(无论是游戏地图还是现实地图)感兴趣的同学,可以看看算法到实际工程落地之间的gap如何弥补和解决。

1.3 Go practices in TiDB – 姚维

印象中,PingCAP 出来的speaker分享质量一直都挺高。姚维老师的这次分享也保持了PingCAP一如既往的高水准,深入浅出,以小见大。一直比较好奇TiDB这种对软件质量要求极高且分布式的领域是如何做测试的,看了其Schrodingergofail 的介绍,无论从主观体感还是技术信赖都TiDB加分不少。failpoint 在实现层面是基于golang AST 做的,编译时被转换为一个 IF 语句,整体设计简单直接有效,是我喜欢的风格。

另外一个比较有意思的点是使用 chunk 来优化内存使用。以前只知道使用整块连续的内存分配策略比碎片化的内存分配更有效率,但是不知道连续内存带来的矢量化执行优势。如果你是做高性能数据库的,这个点一定不能不知道。

1.4 Testing; how, what, why – Dave

Golang官方人员 Dave 大胡子老师出品,必属精品。关于golang如何做测试的资料,看这一个就够了。

1.5 Go 业务开发中 Error & Context – 毛剑

在 golang 1.x 中,错误处理一直是一个不太舒服点。因此才有去年 Rethinking Errors for Go 2 对golang 2.x 错误处理的预览和展望。但是,golang 2 是没有具体时间表的,当前阶段,如果你在实际业务系统中对错误处理有疑惑,可以看看毛剑的处理方式。

Context 其实算是一个老生常谈的话题了,但是毛剑总结了很多实际使用中的最佳实践,分享内容还是诚意满满的。

1.6 Go并发编程实践 – 晁岳攀

从源码级别探究Go在并发层面的基础库实现。跟去年的深入CGO编程一个风格,内容非常全面和丰富,有细节有深度。如果想深入golang源码,一定不可以错过。

1.7 百度APP Go 语言实践 – 陈肖楠

从ppt内容看,算是一个大厂在小场景的golang实践。涉及的问题,以鄙人浅见:使用golang落地1年以内的创业公司都会遇到。给出的解决方案和踩过的坑已经远看不到国内巨头的风范了。如果百度再被扣上技术不行的标签,那就是哪都不行了……

1.8 Golang to build a real-time interactive SaaS Cloud – 董海冰

golang 在 WebRTC 场景下的工程实践。以前对 WebRTC 比较模糊,细致看了分享内容以后,才发现这块的内容和涉及的技术如此广博。前端时间,提供视频会议解决方案的 zoom 上市了,日后我们应该有很大概率看到更多 golang 和 WebRTC 的落地方案。

2.1 基于MINIO的对象存储方案在探探的实践 – 于乐

作者用 golang 撸了一个支持多集群的分布式对象存储系统。有两个技术细节值得技术投资和持续关注:

  1. Reed-Solomon,一种低冗余,高可靠的纠删码。golang 版本的实现可以参见reedsolomon.
  2. The Linux Storage Stack Diagram. 能让你系统全面的了解 IO,并且知道 Direct IO, page cache 的本质。

2.2 从零开始用 Go 实现 Lexer & Parser – 何源

作者编译原理的底子还是在的。想当年,我们该课程的期末课程设计就是编写一个编译器。不过大部分时候,如作者所言,如果不是万不得已,不要自己写 parser. 毕竟,在不使用正则表达式的前提下,golang 提供了非常完善易用的 AST 基础库支持。

2.3 高性能高可用的微服务框架TarsGo的腾讯实践 – 陈明杰

golang和微服务经过这几年的演进发展,无论是基础框架还是周边生态,已经达到了水乳交融的程度。鹅厂的这个实践从当前时间点看,没有什么亮点,更没有什么突破。本以为会有一些 service mesh 方面的尝试,但是比较遗憾,这方面从分享内容看还走得比较靠后。

2.4 闪电网络—BTC小额支付解决方案 – 方圆

不知道这个方圆老师跟去年代表罗辑思维做分享的speaker是不是同一个人?如果是的话,真的是选错了行业风口呀。币圈有风险,跳巢需谨慎。

2.6 用Go构建高性能数据库中间件- 徐成选

一个使用golang打造中间件的实践。文末提到了一些优化方案和细节,挺受用。

2.7 花椒直播基于golang的中台技术实践 – 周洋

周洋老师也是老面孔了,第一次出现在gopher大会应该是大表360做IM长连接的分享。听那一次分享自己几乎是跪着听完的,因为在那之前自己要解决的问题和场景跟其非常类似,只是碍于当时的人手和自己的技术栈储备,我没能做出周洋那样的方案和架构,而是用了一个比较trick的方案。晃眼间,4年过去了,周洋对于中台的思考又给了自己很多启发。感谢 GopherChina 这样的平台,感谢周洋老师的分享。

2.8 知乎社区核心业务 Golang 化实践 – 杜旭

作者分享了知乎从 python 迁移到 go 的历程。巧合的是,三年前,我们也做了同样的事情,同样是从 python 迁移到 go. 不过作者有几点做得比当时的我们更好:

  1. 在接口验证环节上,我们当时希望靠尽可能覆盖全面的单元测试和QA验证来保证;知乎在额外还引入了python和go版本的接口交叉校验。test case的丰富和覆盖程度应该比我们当年更好。
  2. 引入了静态代码检查。如果用强类型语言不适用静态代码检查,那么就损失了强类型语言一般的优势。道理都知道,但是碍于当时CI/CD流程不够完善,我们这个环节一直是缺失的。

注意

以上内容只是看完keynote以后的个人观感。因为没有去现场,细节肯定有所缺失,有些观点也未必跟现场同学的反馈吻合。希望后面放出大会现场视频以后,自己能够进一步完善以上内容。

Flutter 学习资料及笔记总结

也许是程序员天生的鄙视链作祟,后端出身的自己一直认为前端/客户端就是一个“画皮”的事,因此一直不愿意投资时间系统地学习前端技术栈。一般都是实在不得不做前端的时候,在 github 找个功能需求差不多的代码库,修改一下上线。开发速度倒是很快,但是出来的效果总是被评价为“程序员审美”。

一直在寻找一个性价比高的方案,希望能够真正 Write once, run anywhere. 对于桌面客户端,尝试过一段时间 Electron, 但是发布安装包的体积大以及web级别的性能使得只能在有限场景下使用。移动端,当前最火的自然是 RN. 但个人是在受不了RN中屁大点的事情都搞得复杂难用,因此一直也是敬而远之。而去年12月Google发布的Flutter却让自己有了眼前一亮的感觉。

RN vs Flutter

Flutter比RN晚出生两年,而在很多概念上,其实也是借鉴的RN。无论是从社区还是成熟度来看,Flutter都还有很长一段时间要走:

但是,Flutter的设计哲学更加简单,更符合自己的品味。举个例子,Flutter中,几乎所有的UI元素都是一个 widget, 没有那些乱七八糟故弄玄虚的概念,一切都非常直白。从上表的比较可以看出,其实技术层面,Flutter其实并没有绝对的优势,在社区和成熟度上还有明显的短板,但这些都不重要。重要的是,判断一个技术是值得投资,要看未来三五年中,这个技术有没有可能成为行业标准,尤其是在大公司引领下成为事实标准。

显然,Flutter具有这样的潜力。移动开发中,在 web/H5 无法覆盖的场景,在效率和成本的驱动下,跨平台开发会是行业的趋势。最近火热的 996.icu 也算是一种从侧面透露出以后并不需要那么多的客户端开发同学。另一方面,Flutter 是Google为下一代操作系统Fusion的重要布局,且有chromium项目加持。无论从战略还是项目层面,都是天空给的足够高,翅膀长的足够硬。从这个层面看,RN简直就是一群小学生在玩过家家了(开个玩笑,不要当真)。国内方面,无论是阿里还是腾讯,都开始在Flutter上进行布局和项目技术投入。需要特别指出的是,在Flutter之前,阿里是有自研 Weex的,具体项目上,闲鱼团队已经有在实际项目中使用 Flutter.

Flutter 学习资料及笔记

Flutter 可以简单的理解为一个使用Dart语言进行开发的跨平台UI框架。因此,入门需要学习的东西主要有两块:

  1. Dart语言;
  2. Flutter 框架;

Dart 语言设计之初是为了替代 Javascript, 因此,整体上没有那么多语言bug, 会比 JS 要更加符合后端同学的语言习惯。语言入门只推荐一个官方的 A Tour of the Dart Language 学习资料。看资料的过程中,可以结合 DartPad
写一些代码片段,辅助记忆和理解。Dart 语言学习笔记:

  • 一切皆对象,默认值都是null
  • 下划线开头的变量是私有变量,跟Golang的语言设计风格类似
  • 基础类型转换,依赖 parse 和 toString 方法
  • string是 utf-16, 有点非主流
  • 函数默认值必须是 const, 规避不可重入的问题
  • Dart的所有异常都是 unchecked exception,且可以抛出任意object作为异常
  • 构造函数可以取名称,便于阅读理解,但是这点改进对于引入的复杂度并不划算

整体看,Dart 引入的一些语言特性是 python + Java 的合体。语言上中规中矩,没有什么亮点。可以看成是 javascript 的静态类型版。但是,谁让Dart搭上了Flutter这趟潜力股,而Flutter又找了Google这个大干爹呢。老实学吧,反正也就一两个小时的事。

关于Flutter本身的学习资料,其官方网站的文档一如既往地保持了 Google 级文档的水准。但是官方文档有点大而全,可能没有足够时间通读,一时半会也不容易抓住重点。我推荐几个资料你一定要仔细阅读:

学习完以上资料,附带一些课程联系,你会花大约1~2个周末的时间。学完以后,你已经初步具备开发跨平台 app 的能力了。(我个人的情况是, 两个周末学习完上面的资料,并完成一个简单的 app 开发并提交到app store审核,相信你也能做到。)

在开发app过程中,你可能会查找组件,这里推荐阿里的flutter-go.

如果需要进一步深入,可以阅读学习其他人的代码

此外,如果你关注 Flutter 在桌面端的跨平台情况,由于官方还没有发布桌面支持的正式版本。推荐你当前先watch Desktop Embedding for Fluttergo-flutter 两个项目。

总结

在Google Fusion战略和明星项目chromium加持下,以及国内巨头积极跟入的背景下,花两个周末系统学习一下Flutter个人认为是一个潜在投资性价比很高的事情。另一方面,Flutter从去年12月才发布1.0正式版,整体成熟度还不是很高,因此在实际项目中并不建议太激进的引入使用,并且Flutter在一些原生支持上海不够完善。比如,使用Flutter接收其他app分享的内容,android平台下你可以根据How do I handle incoming intents from external applications in Flutter?来解决,但是在iOS平台,当前阶段则是彻底不支持。

四五年前iOS客户端开发炙手可热的机会已经一去不复返了,Flutter也许是下一个机会。