跳到主要内容

9 篇博文 含有标签「architecture」

查看所有标签

Lyft 的营销自动化平台 Symphony

· 阅读需 4 分钟

获客效率问题:广告投放如何花更少的钱用更少的人得到更高回报?

具体来讲,Lyft 的广告投放要服务如下特点

  1. 管理基于地域的 campaign
  2. 数据驱动的增长:增长必须是规模化的、可测量的、可预测的
  3. 支撑起 Lyft 独特的增长模型,如图:

lyft growth model

主要的挑战是:难以规模化管理跨地域营销中的各个环节,广告竞标、预算、素材、激励、选择受众、测试等等。下图是营销者的一天:

营销者的一天

我们可以发现“执行”占去了大部分的时间,而更少的时间花在了更重要的“分析和决策”上。规模化意味着减少繁复的操作,让营销人员专注于分析与决策。

解决方案:自动化

为了降低成本,提高做实验的效率,需要

  1. 预测新用户是否对产品感兴趣
  2. 多渠道优化,有效评估和分配预算
  3. 方便地管理上千个 campaigns

数据由 Lyft 的 Amundsen 系统做增强学习。

自动化的部分包括:

  1. 更新 bid 的关键词
  2. 关掉效果不好的素材
  3. 根据市场改变 referrals values
  4. 找到高价值的用户 segment
  5. 在多个 campaign 中共享策略

构架

Lyft Symphony Architecture

技术栈:Apache Hive, Presto, ML platform, Airflow, 3rd-party APIs, UI.

具体的组成模块

LTV 预测模块

用户的终身价值是衡量渠道的重要标准,预算由 LTV 和我们愿意为该地区的获客付出的价格共同决定。

我们对新用户的认知有限,随着交互的增多,所提供的历史记录会更准确地预测。

一开始的特征值:

特征值

随着历史上的交互记录的积累,做出的判断就会越准确:

根据历史记录判断 LTV

预算分配模块

搞定了 LTV,接下来是根据价格定预算。拟合出 LTV = a * (spend)^b 形式的曲线以及周围的区间里类似参数的曲线。为了找到全局最优,需要付出一些随机性的代价。

预算计算

投放模块

分为两部分,一部分是调参者,一部分是执行者。调参者根据定价,设定基于渠道的具体的参数;执行者把这些参数执行到具体的渠道上。

有很多流行的投放策略,在各色的渠道中,是共通的:

投放策略

总结

要注意人的经验在系统中的重要性,否则会 garbage in, garbage out. 当人从繁琐的投放任务解放出来,专注于理解用户、理解渠道、理解自身要传达给受众的信息之后,就能够获得更好的投放效果——花更少的时间达到更高的 ROI。

构架入门

· 阅读需 5 分钟

什么是构架

构架是软件系统的形状。拿建筑物来举例子:

  • 范式 paradigm 是砖块
  • 设计原则是房间
  • 组件是建筑

他们共同服务于一个特定的目的,就像医院治疗病人,学校教育学生一样。

我们为什么需要架构?

行为 vs. 结构

每一个软件系统提供两个不同的价值给利益相关者:行为与结构。软件开发者必须确保这两项价值都要高

==由于其工作的需要,软件架构师更多地聚焦于系统的结构而不是特性和功能。==

终极目标——==减少每加一个新特性所需要耗费的人力成本==

架构服务于软件系统的整个生命周期,使其易于理解,开发,测试,部署和操作。 其目标是最小化每个业务用例的人力资源成本。

O’Reilly 出版的《软件架构》一书很好地介绍了这样五种基本的构架。

1.分层架构

分层架构是被广泛采用,也是被开发者所熟知的一种架构。因此,它也是应用层面上事实上的标准。如果你不知道应该使用什么架构,用分层架构就是不错的选择。

示例

  • TCP/IP模式:应用层 > 运输层 > 网际层 > 网络接口层
  • Facebook TAO网络层 > 缓存层(follower + leader) > 数据库层

优缺点:

  • 优点
    • 易于使用
    • 职责划分
    • 可测试性
  • 缺点
    • 庞大而僵化
      • 想要对架构进行调整、扩展或者更新就必须要改变所有层,十分棘手

2.事件驱动架构

任何一个状态的改变都会向系统发出一个事件。系统组件之间的通信都是经由事件完成的。

一个简化的架构包含中介(mdiator),事件队列(event queue)和通道(channel)。下图所示即为简化的事件驱动架构:

示例

  • QT:信号(signals)和槽(slots)
  • 支付基础设施:由于银行网关通常有较高的延迟,因此银行的架构中采用了异步技术

3.微核架构(aka Plug-in Architecture)

软件的功能被分散到一个核心和多个插件中。核心仅仅含有最基本的功能。各个插件之间互相独立并实现共享借口以实现不同的目标。

示例

  • Visual Studio Code 和 Eclipse
  • MINIX 操作系统

4.微服务架构

大型系统被解离成众多微服务,每一个都是单独部署的单位,他们之间通过RPCs进行通信。

uber architecture

示例

5.基于空间的架构

“基于空间的架构”这一名称来源于“元组空间”,“元组空间“有”分布式共享空间“的含义。基于空间的架构中没有数据库或同步数据库访问,因此该架构没有数据库的瓶颈问题。所有处理单元共享内存中应用数据副本。这些处理单元都可以很弹性地启动和关闭。

示例:详见 Wikipedia

  • 主要被使用Java的架构所采用:例如:JavaSpaces

流处理和批处理框架

· 阅读需 3 分钟

为什么有这种框架?

  • 为了在更短的时间内处理更多的数据。
  • 统一处理分布式系统中的容错问题。
  • 将任务简化抽象以应对多变的业务要求。
  • 分别适用于有界数据集(批处理)和无界数据集(流处理)。

批处理与流处理的发展史简介

  1. Hadoop 与 MapReduce。谷歌让批处理在一个分布式系统中像 MapRduceresult = pairs.map((pair) => (morePairs)).reduce(somePairs => lessPairs)一样简单。
  2. Apache Storm 与有向图拓扑结构。MapReduce 不能很好地表示迭代算法。因此,内森·马兹(Nathan Marz)将流处理抽象成一个由 spouts 和 bolts 组件构成的图结构。
  3. Spark 内存计算。辛湜(Reynold Xin)指出 Spark 在处理相同数据的时候比 Hadoop 少使用十倍机器的同时速度却快三倍
  4. 基于 Millwheel 和 FlumeJava 的谷歌数据流(Google Dataflow)。谷歌使用窗口化API同时支持批处理与流处理。
  1. Flink 快速采纳了 ==Google Dataflow== 以及 Apache Beam 的编程模式。
  2. Flink 对 Chandy-Lamport checkpointing 算法的高效实现。

这些框架

架构选择

若要用商业机器来满足以上的需求,有这些热门的分布式系统架构……

  • master-slave(中心化的):Apache Storm + zookeeper, Apache Samza + YARN
  • P2P(去中心化的):Apache s4

功能

  1. DAG Topology 用来迭代处理 -例如Spark 中的 GraphX, Apache Storm 中的 topologies, Flink 中的 DataStream API。
  2. 交付保证 (Delivery Guarantees)。如何确保节点与节点之间数据交付的可靠性?至少一次 / 至多一次 / 一次。
  3. 容错性。使用cold/warm/hot standby, checkpointing 或者 active-active 来实现容错。
  4. 无界数据集的窗口化API。例如 Apache 的流式窗口。Spark 的Window函数。Apache Beam 的窗口化。

不同架构的区别对照表

架构StormStorm-tridentSparkFlink
模型原生微批量微批量原生
Guarentees至少一次一次一次一次
容错性记录Ack记录Ack检查点检查点
最大容错
延迟非常低
吞吐量

使用半监督学习进行欺诈检测

· 阅读需 6 分钟

明确需求

实时计算风险概率分数,并结合规则引擎做出决策,以防止账户接管 (ATO) 和僵尸网络攻击。

通过在线和离线管道训练聚类特征

  1. 来源于网站日志、认证日志、用户行为、交易、监控列表中的高风险账户

  2. 在 Kafka 主题中跟踪事件数据

  3. 处理事件并准备聚类特征

实时评分和基于规则的决策

  1. 综合评估在线服务的风险评分

  2. 在规则引擎中手动配置以保持灵活性

  3. 在在线服务中共享或使用洞察

从易到难检测的账户接管

  1. 来自单个 IP

  2. 来自同一设备的 IP

  3. 来自全球的 IP

  4. 来自 100k 个 IP

  5. 针对特定账户的攻击

  6. 网络钓鱼和恶意软件

挑战

  • 手动特征选择

  • 对抗环境中的特征演变

  • 可扩展性

  • 无在线 DBSCAN

高级架构

核心组件和工作流程

半监督学习 = 未标记数据 + 少量标记数据

为什么?比无监督学习具有更好的学习准确性 + 比监督学习花费更少的时间和成本

训练:在数据库中准备聚类特征

  • Spark 上的流处理管道:

    • 实时连续运行。

    • 动态执行特征归一化和类别转换。

      • 特征归一化:将数值特征(如年龄、收入)缩放到 0 和 1 之间。

      • 类别特征转换:应用独热编码或其他转换,将类别特征转换为适合机器学习模型的数值格式。

    • 使用 Spark MLlib 的 K-means 将流数据聚类成组。

      • 运行 k-means 并形成聚类后,可能会发现某些聚类中有更多的欺诈实例。

      • 一旦根据历史数据或专家知识将某个聚类标记为欺诈,就可以在推断过程中使用该聚类分配。任何分配到该欺诈聚类的新数据点都可以标记为可疑。

  • 每小时定时任务管道:

    • 每小时定期运行(批处理)。

    • 应用 阈值 来根据聚类模型的结果识别异常。

    • 调整 DBSCAN 算法 的参数以改善聚类和异常检测。

    • 使用 scikit-learn 的 DBSCAN 在批量数据中寻找聚类并检测异常值。

      • DBSCAN 可以检测异常值,可能会识别出常规交易的聚类,并将其与可能不寻常、潜在欺诈的交易分开。

      • 在噪声或异常区域(不属于任何密集聚类的点)中的交易可以标记为可疑。

      • 在识别出某个聚类为欺诈后,DBSCAN 有助于即使在不规则形状的交易分布中也能检测到欺诈模式。

服务

服务层是将机器学习模型和业务规则转化为实际欺诈预防决策的地方。其工作原理如下:

  • 欺诈检测评分服务:

    • 提取来自传入请求的实时特征

    • 应用两种聚类模型(流处理中的 K-means 和批处理中的 DBSCAN)

    • 将分数与流计数器(如每个 IP 的登录尝试次数)结合

    • 输出 0 到 1 之间的统一风险评分

  • 规则引擎:

    • 作为系统的“头脑”

    • 将 ML 分数与可配置的业务规则结合

    • 规则示例:

      • 如果风险评分 > 0.8 且用户从新 IP 访问 → 需要 2FA

      • 如果风险评分 > 0.9 且账户为高价值 → 阻止交易

    • 规则存储在数据库中,可以无需代码更改进行更新

    • 为安全团队提供调整规则的管理门户

  • 与其他服务的集成:

    • 暴露用于实时评分的 REST API

    • 将结果发布到流计数器以进行监控

    • 将决策反馈到训练管道以提高模型准确性

  • 可观察性:

    • 跟踪关键指标,如误报/漏报率

    • 监控模型漂移和特征分布变化

    • 为安全分析师提供调查模式的仪表板

    • 记录详细信息以进行事后分析

再窥iOS架构模式

· 阅读需 4 分钟

我们为什么要在架构上费心思?

答案是:为了减少在每做一个功能的时候所耗费的人力资源

移动开发人员会在以下三个层面上评估一个架构的好坏:

  1. 各个功能分区的职责分配是否均衡
  2. 是否具有易测试性
  3. 是否易于使用和维护
职责分配的均衡性易测试性易用性
紧耦合MVC
Cocoa MVC❌ V和C是耦合的✅⭐
MVP✅ 独立的视图生命周期一般:代码较多
MVVM一般:视图(View)存在对UIKit的依赖一般
VIPER✅⭐️✅⭐️

紧耦合MVC

传统 MVC

举一个例子,在一个多页面的网页Web应用程序中,当你点击一个链接导航至其他页面的时候,该页面就会被全部重新加载。该架构的问题在于视图(View)与控制器(Controller)和模型(Model)是紧密耦合的。

Cocoa MVC

Cocoa MVC 是苹果公司建议iOS开发者使用的架构。理论上来说,该架构可以通过控制器(Controller)将模型(Model)与视图(View)剥离开。

Cocoa MVC

然而,在实际操作过程中,Cocoa MVC 鼓励大规模视图控制器的使用,最终使得视图控制器完成所有操作。

实际的 Cocoa MVC

尽管测试这样的耦合大规模视图控制器是十分困难的,然而在开发速度方面,Cocoa MVC是现有的这些选择里面表现最好的。

MVP

在MVP中,Presenter与视图控制器(view controller)的生命周期没有任何关系,视图可以很轻易地被取代。我们可以认为UIViewController实际上就是视图(View)。

MVC 的变体

还有另外一种类型的MVP:带有数据绑定的MVP。如下图所示,视图(View)与模型(Model)和控制器(Controller)是紧密耦合的。

MVP

MVVM

MVVM与MVP相似不过MVVM绑定的是视图(View)与视图模型(View Model)。

MVVM

VIPER

不同于MV(X)的三层结构,VIPER具有五层结构(VIPER View, Interactor, Presenter, Entity, 和 Routing)。这样的结构可以很好地进行职责分配但是其维护性较差。

VIPER

相较于MV(X),VIPER有下列不同点:

  1. Model 的逻辑处理转移到了 Interactor 上,这样一来,Entities 没有逻辑,只是纯粹的保存数据的结构。
  2. ==UI相关的业务逻辑分在Presenter中,数据修改功能分在Interactor中==。
  3. VIPER为实现模块间的跳转功能引入了路由模块 Router 。

iOS 架构模式再探

· 阅读需 3 分钟

为什么要关注架构?

答案:为了降低每个功能的人力资源成本

移动开发者从三个维度评估架构。

  1. 功能参与者之间责任的平衡分配。
  2. 可测试性
  3. 易用性和可维护性
责任分配可测试性易用性
紧耦合 MVC
Cocoa MVC❌ VC 耦合✅⭐
MVP✅ 分离的视图生命周期一般:代码较多
MVVM一般:由于视图依赖 UIKit一般
VIPER✅⭐️✅⭐️

紧耦合 MVC

传统 MVC

例如,在一个多页面的 web 应用中,一旦点击链接导航到其他地方,页面会完全重新加载。问题在于视图与控制器和模型紧密耦合。

Cocoa MVC

苹果的 MVC 理论上通过控制器将视图与模型解耦。

Cocoa MVC

实际上,苹果的 MVC 鼓励 ==庞大的视图控制器==。而视图控制器最终负责所有事情。

现实中的 Cocoa MVC

测试耦合的庞大视图控制器是困难的。然而,Cocoa MVC 在开发速度方面是最佳的架构模式。

MVP

在 MVP 中,呈现者与视图控制器的生命周期无关,视图可以轻松模拟。我们可以说 UIViewController 实际上就是视图。

MVC 变体

还有另一种 MVP:带有数据绑定的 MVP。正如你所看到的,视图与其他两个之间存在紧密耦合。

MVP

MVVM

它类似于 MVP,但绑定是在视图和视图模型之间。

MVVM

VIPER

与 MV(X) 相比,VIPER 有五个层次(VIPER 视图、交互器、呈现者、实体和路由)。这很好地分配了责任,但可维护性较差。

VIPER

与 MV(X) 相比,VIPER

  1. 模型逻辑转移到交互器,实体作为简单的数据结构保留。
  2. ==与 UI 相关的业务逻辑放入呈现者,而数据修改能力放入交互器==。
  3. 引入路由器负责导航。

Lambda 架构

· 阅读需 2 分钟

为什么要使用lambda架构?

为了解决大数据所带来的三个问题

  1. 准确性(好)
  2. 延迟(快)
  3. 吞吐量(多)

例如:以传统方式扩展网页浏览数据记录的问题

  1. 首先使用传统的关系数据库
  2. 然后添加一个“发布/订阅”模式队列
  3. 然后通过横向分区或者分片的方式来扩展规模
  4. 容错性问题开始出现
  5. 数据损坏(data corruption)的现象开始出现

关键问题在于AKF扩展立方体中,==仅有X轴的水平分割一个维度是不够的,我们还需要引入Y轴的功能分解。而 lambda 架构可以指导我们如何为一个数据系统实现扩展==。

什么是lambda架构

如果我们将一个数据系统定义为以下形式:

Query=function(all data)

那么一个lamda架构就是

Lambda Architecture

batch view = function(all data at the batching job's execution time)
realtime view = function(realtime view, new data)

query = function(batch view. realtime view)

==lambda架构 = 读写分离(批处理层 + 服务层) + 实时处理层==

Lambda Architecture for big data systems

将软件架构视为物理建筑

· 阅读需 2 分钟

什么是架构?

架构是软件系统的形状。将其视为物理建筑的全景。

  • 范式是砖块。
  • 设计原则是房间。
  • 组件是建筑。

它们共同服务于特定的目的,比如医院是为治愈病人而设,学校是为教育学生而设。

我们为什么需要架构?

行为与结构

每个软件系统为利益相关者提供两种不同的价值:行为和结构。软件开发人员负责确保这两种价值保持高水平。

::软件架构师由于其职位描述,更加关注系统的结构,而非其特性和功能。::

最终目标 - ==降低每个特性的人工资源成本==

架构服务于软件系统的整个生命周期,使其易于理解、开发、测试、部署和操作。 目标是最小化每个业务用例的人力资源成本。

设计非常大的(JavaScript)应用程序

· 阅读需 4 分钟

非常大的 JS 应用 = 很多开发者 + 大型代码库

如何处理很多开发者?

同理心

什么是 ==高级工程师==?没有初级工程师的高级工程师团队就是工程师团队

  1. 成为高级工程师意味着我能够解决几乎所有别人可能抛给我的问题。
  2. 让初级工程师最终成为高级工程师。

高级工程师的下一步是什么?

  1. 高级: “我知道我会如何解决这个问题”,因为我知道我会如何解决它,我也可以教别人怎么做。
  2. 下一个层次: “我知道别人会如何解决这个问题”

良好的编程模型

人们如何编写软件,例如 react/redux, npm。这里有一个影响所有大型 JS 应用的模型 - 代码分割。

  1. 人们必须考虑打包什么以及何时加载
  2. ==基于路由的代码分割==
  3. 但是,如果这还不够呢?
    1. 懒加载我们网站的每一个组件
    2. Google 是怎么做的?通过渲染逻辑和应用逻辑进行分割。==简单地在服务器端渲染一个页面,然后实际渲染的内容触发下载相关的应用程序包。== Google 不做同构渲染 - 没有双重渲染

如何处理大型代码库?

==代码可移除性/可删除性==

例如,CSS 在代码可移除性方面表现不佳

  1. 一个大的 CSS 文件。里面有这个选择器。谁真的知道它是否仍然与您的应用程序中的任何内容匹配?所以,您最终只是把它留在那里。
  2. 因此,人们创建了 CSS-in-JS

==不惜一切代价避免应用程序的中央配置==

  1. 坏例子
    1. 中央路由配置
    2. 中央 webpack.config.js
  2. 好例子
    1. 去中心化的 package.json

避免中央导入问题:路由器导入组件 A、B 和 C

  1. 为了解决这个问题,做 ==“增强”而不是“导入”==
  2. 然而,开发者仍然必须决定何时增强,何时导入。由于这可能导致非常糟糕的情况,我们使“增强”成为非法,没人可以使用它——只有一个例外:生成的代码。

避免基础包的垃圾堆

  1. 例如,基础包绝不应包含 UI 代码
  2. 通过禁止依赖测试解决此问题
  3. ==最直接的方法必须是正确的方法;否则添加一个测试以确保正确的方法。==

小心抽象

我们必须变得善于找到正确的抽象:同理心和经验 -> 正确的抽象