主题架构案例研究
以下案例研究展示了如何应用主题架构的最佳实践:
- 案例研究1:事件驱动的微服务、编排和事件溯源
- 案例研究2:与ASAPIO的事件驱动集成
- 案例研究3:物联网设备—命令、控制和分析
- 案例研究4:数据摄取和分发
案例研究1:事件驱动的微服务、编排和事件溯源
实现微服务将单体应用程序(服务)拆分为更小、更易于管理的组件(微服务)。使微服务事件驱动使它们更高效、性能更好、可扩展和健壮。
事件驱动微服务的两个关键属性是:
- 编排,微服务对其环境的变化做出反应,与依赖微服务之间紧密联系或中央编排器的编排方法形成对比,尽管它推动了微服务的交互,但也是阻碍扩展的主要瓶颈。
- 事件溯源允许微服务在不需要原子事务处理大量工作的情况下运行和恢复,通过在事件中捕获系统状态而不是将其存储在数据库中。您可以通过重放特定事件而不是管理紧密耦合服务的状态来实现系统的一致性。
要了解更多关于使用事件驱动微服务的信息,请参见:
- 事件驱动微服务
- 架构师指南:事件驱动微服务
让我们看看“Sol-Beer”示例,了解如何为事件驱动微服务设计主题层次结构,该示例在事件驱动微服务的博客文章中的消息模式中有所展示。
正如博客所解释的,移除点对点服务链已经创建了一个非常类似于数据摄取和分发案例研究中的中间办公室示例的场景。不仅所有应用程序或服务都对同一事件做出反应,而且它们也朝着最终一致性发展。不同之处在于,可能由服务生成的后续事件可能会影响或以其他方式影响其他服务。例如,计费过程可能需要在派遣送货车辆之前完成。下图显示了不同主题域之间的相互关系,其中一些事件可以独立处理,但其他事件被阻塞。在我们的示例中,尽管您不能在计费清除之前派遣送货车辆,但您可以通知送货车辆在他们的区域有一个订单正在处理。
让我们看看一个定义明确的主题架构如何使事件流经这个系统,并提供扩展数据集的灵活性。最初,系统通过汽车交付啤酒,但随着业务的增长,公司可能会增加其他产品,如苏格兰威士忌,或增加新的交付方式,如无人机交付,因此他们需要为业务增长做计划。
构建主题根锚
在这个系统中,由Sol-Beer WebApp注入客户生成的事件到事件网格。客户下订单,然后希望了解他们订单状态的更新,但他们不需要有关物流或库存水平的详细信息。
我们可以从事件主题模板开始,Domain/ObjectType/Verb/Version/Properties
。首先,关注事件主题根,我们的应用领域是Sol-Beer的在线商店,所以store
是Domain
的适当选择。商店的一个主要功能是接受订单,所以order
可能是ObjectType
。在订单中,有几个可能的动作,包括:created
、updated
和canceled
。因为这是商店的第一个版本,所以版本是v1
。基于这些元素,我们有以下根主题:
store/order/created/v1
store/order/updated/v1
store/order/canceled/v1
添加主题属性锚
在构建主题根之后,您可以为每个单独的对象/动作组合考虑各种属性。在本应用中可能有用的潜在属性包括:
属性 | 描述 |
---|---|
{productGroup} | 基于可能的产品组类型的枚举值(啤酒、苏格兰威士忌等)。这个属性对于特定产品组上采取的行动可能很有用,例如对某个产品组的营销行动。 |
{area} | 库存经理负责的一般区域。这个属性对于服务多个区域可能很有用,因为送货车辆可能只对它们区域中的订单感兴趣。 |
{productID} | 订购的具体项目(黑啤酒、淡啤酒、十年陈苏格兰威士忌等)。这个属性对于特定产品上采取的行动可能很有用,例如对某个特定产品的促销。 |
{customerID} | 客户标识符。这个属性对于访问控制很有用,因为客户可能只能为他们的自己的ID下订单。 |
{orderID} | 唯一的订单标识符。这个属性允许客户接收特定订单的更新,可能很有用。 |
每个新属性在有路由、过滤或访问控制用例时都包括在内。每个属性应该按基数大致按顺序排列。在这个示例中,客户比产品多,可能的订单ID比客户多得多。这导致以下完整的主题:
store/order/created/v1/{productGroup}/{area}/{productID}/{customerID}/{orderID}
您可以为更新和取消动作采取类似的方法。
这个主题分类导致了许多潜在的订阅用例。例如,库存管理系统可能被设置为仅处理特定区域。负责渥太华地区分销的库存经理将订阅以下主题:
store/order/created/v1/*/ottawa/>
请注意,前一个示例中使用的通配符表示订阅者希望接收该属性的所有可能值的消息。在这里,库存经理对每个产品类别(productCategory
)的订单都感兴趣,因此他们在订阅中使用通配符(*
)以包括所有值。
扩展事件目录锚
除了处理订单,Sol-Beer商店还处理付款并通过当地交付履行订单。这些事件可以添加到目录中,与之前的示例主题具有类似的主题根。例如:
store/billing/paid/v1
store/billing/nofunds/v1
store/delivery/enroute/v1
store/delivery/delivered/v1
如果订单、交付和计费过程中的所有事件都带有customerID
和orderID
,则该过程允许追踪和消息重放单个订单或客户。这个功能对于事件溯源至关重要。例如,当计费事件包含customerID
和orderID
时,您可以将计费事件追溯回订单事件。结果的计费主题在事件目录中将是:
store/billing/paid/v1/{productGroup}/{area}/{deliveryMode}/{productID}/{customerID}/{orderID}
store/billing/nofunds/v1/{productGroup}/{area}/{deliveryMode}/{productID}/{customerID}/{orderID}
其中属性可能如下所示:
beer/ottawa/drone/product456/cust4321/order12345
这个丰富的主题分类允许许多不同的用例。例如,需要知道计费已经清除的库存经理和送货车辆将订阅:
store/billing/enroute/v1/beer/ottawa/drone/product456/customer4321/order12345>
在啤酒购买获得授权并且分配了司机或无人机之后,客户可以使用交付事件实时更新来跟踪他们的交付。主题层次结构中可能有助于包括的一些属性:
属性 | 描述 |
---|---|
{vehicleType} | 来自车辆类型枚举的值,如汽车、无人机等。 |
{latitude} | 送货车辆位置的纬度。 |
{longitude} | 送货车辆位置的经度。 |
{vehicleID} | 送货车辆标识符。 |
结 果的事件目录条目将是:
store/delivery/enroute/v1/{vehicleType}/{vehicleID}/{customerID}/{orderID}/{latitude}/{longitude}
store/delivery/dispatched/v1/{vehicleType}/{vehicleID}/{customerID}/{orderID}
事件主题属性可能如下所示:
store/delivery/enroute/v1/car/vehicle4321/order12345/cust4321/45.421532/-75.697189
一些可能的订阅事件包括:
- 客户跟踪他们所有订单的交付可以订阅:
store/delivery/enroute/v1/*/*/cust4321/>
- 不管类型或当前订单如何,在地理区域内跟踪所有车辆的车队经理可能订阅:
store/delivery/enroute/v1/*/*/*/*/45*/-75*
灵活的订阅允许事件在必要时以新的方式被重用。例如,如果新法规要求商业无人机被跟踪并报告给城市,报告代理可以订阅它负责的地理区域:
store/delivery/enroute/v1/drone/*/*/*/45*/-75*
案例研究2:与ASAPIO的事件驱动集成
在这个示例中,我们将使用ASAPIO为SAP事件赋能。这是一个事件驱动集成的例子,其中iPaaS的数据转换能力与事件代理和事件网格的实时动态编排相结合。如下图所示,当SAP系统中创建新的采购订单时,ASAPIO可以将事件通知推送到PubSub+的事件网格中,该网格将该通知流式传输到多个感兴趣的应用程序(本地和云中),如用于计费、制造和支持的应用程序。这个用例的价值在于,不是每个下游应用程序轮询记录系统的变更,而是记录系统每个变更发出一个事件,所有订阅的下游应用程序都有机会消费该事件。这种架构减少了记录系统的工作负载。 有关这个特定SAP记录系统用例的更多信息,请参见Solace与ASAPIO。
有了ASAPIO,可以对同一事件有不同的编码(JSON、XML等)。由于每个事件只能有一个事件模式,我们需要将每种编码视为单独的事件。在这种情况下,我们在事件根中包括一个HandlingInstruction
属性。我们还将HandlingInstruction
属性移到版本属性之前,以便如果仅更改单个编码,我们可以仅对一个事件进行版本控制。结果如下事件主题模板:
Domain/ObjectType/Verb/HandlingInstruction/Version/Locality/SourceId/ObjectId
让我们看看一个定义明确的主题架构如何帮助数据流经这个复杂的系统。
在这个用例中,每次SAP系统中的某些业务对象(BO)发生变化时,都会发出事件。
从SAP流向下游应用程序的事件具有三种不同的对象类型或_方面_,每种都包含越来越多的信息,这通过ACL进行控制。这些方面在以下表格中描述:
方面 | 描述 |
---|---|
sap-bo-event | 通知特定业务对象已创建或更新。主题和负载包含足够的信息,以便应用程序或用户根据他们的SAP访问权限检索业务对象。 |
sap-bo-metadata | 包含sap-bo-event 的信息加上已更新字段的ID。这允许应用程序在检索业务对象之前根据他们感兴趣的字段进一步过滤。 |
sap-bo-data | 包含sap-bo-event 的信息加上实际字段和更改的值。这允许授权应用程序立即获取更改及其值。 |
对于这些SAP事件,主题模板将是:
sap/{resourceType}/{action}/{representation}/{version}/{aspect}/{siteID}/{orgID}/{systemID}/{resourceID}
项目 | 描述 |
---|---|
{sap} | 指定这些是SAP起源事件的字面值。 |
{resourceType} | 通知适用的业务对象类型。例如,BUS2032 是销售订单。 |
{action} | 来自动作枚举的值,例如创建或更新。 |
{representation} | 事件的编码(例如XML、JSON)。 |
{version} | 事件的版本。 |
{aspect} | 来自对象类型枚举的值sap-bo-event 、sap-bo-metadata 或sap-bo-data 。 |
{siteID} | siteID 、plantID 或locationID ,其中这个BO所属,其中GLOBAL表示全球实体,EMPTY表示通知与siteID无关。 |
{orgID} | 拥有业务对象的组织单位,其中GLOBAL表示全球实体,EMPTY表示通知与orgID无关 |
{systemID} | SAP实例或系统ID。 |
{resourceID} | 业务对象ID。 |
从事件主题根派生的事件目录条目将如下所示:
sap/BUS2032/create/JSON/v1
sap/BUS2032/update/JSON/v1
要看看这个事件主题根是如何使用的,请考虑以下事件:
sap/BUS2032/update/JSON/v1/sap-bo-data/empty/1000/ERD100/0000096852
当我们阅读主题层次结构中的字段时,我们可以看到以下信息:
- SAP事件
- 业务对象:BUS2032(销售订单)
- 业务对象的更新
- 编码为JSON
- API版本:1
- 方面:sap-bo-data(包含实际数据)
- 站点ID:无
- 组织ID:1000
- SAP实例:ERD100
- 业务对象ID:96852
想要接收来自组织1000的所有销售订单更新及其数据的下游应用程序可以订阅:
sap/BUS2032/update/JSON/v1/sap-bo-data/*/1000/>
请注意,此应用程序需要适当的访问权限(通过管理员应用的ACL)才能允许它订阅来自组织1000的sap-bo-data
事件。
想要接收来自任何SAP实例、任何组织、任何站点的所有销售订单更新(仅事件)的下游应用程序可以订阅:
sap/BUS2032/*/JSON/v1/sap-bo-event/>
同样,此应用程序需要适当的访问权限才能允许它接收新销售订单的通知事件。
案例研究3:物联网设备—命令、控制和分析
物联网(IoT)用例传统上涉及仅从现场传感器到后端状态捕获或分析系统的数据移动。最初目标是创建可以水平扩展以处理数百万个设备的简单系统。随着设备数量和复杂性的演变,IoT用例已经发展。现在必须安全,并且控制系统中的最终设备在这些系统中更为常见。来自IoT传感器的事件现在不仅仅用于简单统计。它们现在还驱动上游系统的变更。一些关键传感器事件可能需要人为交互,重要的是事件准确有效。例如,考虑移动车辆的气囊部署事件及其如果这些事件被伪造或忽略的后果。现在也有很多用例,其中IoT传感器从后端系统接收数据,例如,连接车辆接收天气和交通更新。
现代工业物联网用例将大量组件集成到统一的系统中。要有效地组织和路由跨这些系统的数据,需要统一命名空间(UNS)。UNS是企业实时状态的单一真相来源。它允许企业内的所有组件以标准方式双向通信。事件网格提供此功能,定义明确的主题层次结构是标准化通信的关键,特别是通过使数据在系统中的所有组件中可识别。例如,从IoT设备发布的事件可能被许多后端系统使用,从数据湖到分析引擎。事件主题必须构造良好,以便在这些后端系统中有用,并防止数据湖变成数据沼泽。特别是,应在主题属性中包括设备ID,以便所有特定设备的事件都可以通过事件网格实时监控,并在后端系统中历史查看。在主题中包含设备ID还允许您通过ACL替换表达式保护数据。
要了解更多关于连接汽车用例的信息,请参见Solace物联网事件网格文章。
让我们看看一个定义明确的主题架构如何帮助授权和路由从远程设备到后端系统的事件,以及后端系统如何控制远程设备。
设备到后端服务锚
边缘IoT设备发出的事件主要由传感器数据组成。
从Domain/ObjectType/Verb/Version/Properties
的主题事件模板开始,我们有一个主题模板:
fromVehicle/{vehicleComponent}/{action}/{version}/{severity}/{username}/{sensorID}
使用fromVehicle
作为域表示主题中的信息来自传感器。如果同一事件网格上托管多个车辆世代,域可能需要包含额外的信息,例如myCar/myGeneration/fromVehicle
。
属性 | 描述 |
---|---|
fromVehicle | 指定这些是来自车辆的事件的字面值。 |
{vehicleComponent} | 车辆内控制器或应用程序的名称(例如TCU和MCU)。 |
{action} | 来自传感器动作枚举的值(例如重置、更新和故障)。不同的车辆组件可能有不同的动作。 |
{version} | 此版本fromVehicle 的版本号。 |
{username} | 来自严重程度枚举的值(例如错误、警告和信息)。 |
{sensorID} | 产生事件的唯一传感器。 |
从事件主题根派生的事件目录条目将如下所示:
fromVehicle/tcu/reset/v1
fromVehicle/tcu/update/v1
fromVehicle/tcu/failure/v1
fromVehicle/mcu/reset/v1
fromVehicle/mcu/update/v1
主题的属性为路由和过滤提供了更多元数据。例如,车辆的位置可能影响事件路由,事件的严重程度可能影响对事件感兴趣的应用程序数量。每个主题还应包括传感器ID,因为这对于访问控制限制至关重要,以确保其他传感器的数据不能被伪造。
假设一辆车辆发生事故并且气囊部署。车辆可能会发出主题为:
fromVehicle/tcu/update/v1/critical/vin4321/airbag
可能有一个应用程序监听所有严重程度的事件:
fromVehicle/*/update/v1/critical/>
在许多情况下,IoT设备不在安全位置,可能受到物理或软件黑客攻击。因此,设备必须在每次连接时进行身份验证,并且必须在每个消息中包含并验证经过身份验证的ID,以确保设备只发送授权事件。这就是为什么MQTT用户名字段在这种环境中如此重 要——它防止了一个IoT设备伪装成另一个IoT设备。
例如,验证用户名将防止被黑客攻击的车辆用户名vin4321
发送事件作为vin1234
。以下来自用户名vin4321
的更新将被允许:
fromVehicle/tcu/update/v1/critical/vin4321/airbag
然而,以下来自用户名vin4321
的更新将不被允许:
fromVehicle/tcu/update/v1/critical/vin1234/airbag
后端服务到设备锚
从后端应用程序发送到车辆的数据可能包括向多辆车的多播消息或向特定车辆的单播消息。多播消息可能基于位置、车载应用程序、车辆类型或其他标准。对于单播消息,用户名确保只有目标车辆接收更新。
考虑一个主题模板:
toVehicle/{vehicleComponent}/{action}/{version}/{location}/{clientID}/{sensorID}
字段 | 描述 |
---|---|
toVehicle | 指定这些是发送到车辆的事件的字面值。 |
{vehicleComponent} | 控制器或应用程序的值,例如TCU、MCU等。MCU可以作为下游子组件的多路复用器。 |
{action} | 来自动作枚举的值(可能取决于车辆组件)。 |
{version} | 此版本订单的版本号。 |
{location} | 多播区域、组或空值。 |
{username} | 用于验证发送者是否有权生产事件的用户名或设备标识符。 |
{endpointID} | 应该接收控制消息的特定子组件或应用程序的标识符。 |
从事件主题根派生的事件目录条目将如下所示:
-
toVehicle/tcu/update/v1
-
toVehicle/mcu/update/v1
为了区分可供所有车辆使用的多播事件,可以使用allVehicles
应用领域:
-
allVehicles/weather/update/v1
-
allVehicles/traffic/update/v1
主题的属性为路由、过滤和访问控制提供了更多信息。
车辆将订阅所有专门针对它的事件:
toVehicle/*/*/v1/*/vin4321/>
车辆还可以订阅其地区的天气更新:
allVehicles/weather/update/v1/ottawa/weatherPlan4321/>
它还可以订阅当前道路和方向的交通更新:
allVehicles/traffic/update/v1/417east/trafficPlan4321/>
如前所述,用户名用于访问策略和过滤。例如,车辆可能只允许订阅包含其VIN的主题中的toVehicle
域更新,防止其访问其他车辆的信息,而它可能被允许消费任何allVehicle
更新。有关IoT应用程序中访问控制的详细讨论,请参见使用访问控制列表替换变量提高IoT安全性。
案例研究4:数据摄取和分发
在这个示例中,我们查看企业内的数据源,它们将数据写入分布式数据存储。这些数据源可能是数据库发布的事务日志、在线购买、市场数据订单等。
下图说明了市场数据订单和交易后数据的一个例子,其中交易本身发生在前台办公室,而交易后活动发生在中台办公室。在这里我们可以看到,客户网关为前台办公室生产事件,交易平台是流向中台系统事件的生产者。我们还可以看到,中台办公室不是一个单一用途的消费者应用程序,而是一系列每个都有特定任务和数据需求的应用程序。
有关这个用例的更多详细信息,请参见投资银行和中台办公室减震器。
如果没有一个设计得当的事件主题架构允许事件重用,您可能需要一个非常昂贵的交易平台多次以多种格式发布相同的事件,以满足每个中台办公室的需求。
我们从标准的主题事件模板Domain/ObjectType/Verb/Version/Properties
开始。
让我们看看一个定义明确的主题架构如何帮助数据流经这个复杂的系统。
前台办公室锚
此交互阶段的消息交换模式包括一个简单的命令消息。如上图所示,前台系统的数据处理如下:
- 客户在客户网关注册购买或销售产品的意图。
- 客户网关将此请求转换为客户订单并发送到交易平台系统。
- 交易平台系统确定要使用的适当交易所,并放置市场订单。
- 交易所履行市场订单并向交易平台系统发送确认。
- 在收到交易所的成交事件后,交易平台通知客户(通过客户网关)其订单已成交。
对于前 台办公室,客户订单的主题模板将是:
orders/{productGroup}/{action}/{version}/{market}/{source}/{product}
项目 | 描述 |
---|---|
orders | 指定主题描述所有订单事件的字面值。 |
{productGroup} | 来自可能的产品组枚举值。 |
{action} | 来自订单动作枚举值(例如new 、update 和cancel )。 |
{version} | 此版本orders 的版本号。 |
{market} | 交易平台负责的一般区域或市场组。 |
{source} | 客户标识符。 |
{product} | 被订购的具体项目。 |
从事件主题根派生的事件目录条目将如下所示:
orders/equity/new/v1
orders/equity/update/v1
orders/equity/cancel/v1
orders/bond/new/v1
orders/bond/update/v1
orders/bond/cancel/v1
事件主题属性可能如下所示:
orders/bond/cancel/v1/NA/pri1/customer1/appl
监控应用程序可以使用以下订阅跟踪单个客户:
orders/equity/*/v1/NA/custID4321/>
可以监控像取消这样的动作,以标记不寻常的活动,使用以下订阅:
orders/equity/cancel/v1/NA/>
水平扩展锚
单个应用程序可能无法跟上大量数据。消费者水平扩展可以通过多种方式实现,例如非独占队列或分区队列(参见使用分区队列的消息分发) 。然而,如果负载对于单个事件代理来说太多,可能需要使用主题分区在事件网格中的多个事件代理之间进行数据分片。
在这个用例中,主题的属性为路由和过滤提供了更多信息。智能订单路由器(SOR)可以根据这些属性进行分片,每个处理产品子集。这个结构允许在事件网格中的多个事件代理之间进行水平扩展。
在这个例子中,SOR首先根据productGroup
拆分流量,然后在较低级别处理碎片或产品空间。
第一个SOR实例可以有以下订阅:
orders/equity/*/v1/NA/*/a*/>
...
orders/equity/*/v1/NA/*/m*/>
第二个实例可以有以下订阅:
orders/equity/*/v1/NA/*/n*/>
...
orders/equity/*/v1/NA/*/z*/>
使用订阅异常进行水平扩展锚
考虑一个预期的高容量事件,例如ACME Rideshare(ACME)的IPO,预计在有限的时间内会有大量针对一个特定符号的交易。为了处理这种情况,您可以部署一个专用的事件代理来处理只有ACME Rideshare交易事件,有一个SOR实例订阅orders/equity/*/v1/NA/*/acme/>
。
为确保没有ACME Rideshare事件传播到其他SOR实例,处理以“a”开头的股票的SOR实例可以添加一个负订阅!/equity/*/v1/NA/*/acme/>
,允许其他SOR实例专门处理ACME订单。
中台办公室锚
交易平台系统完成后,它会将事件消息发送到中台办公室系统。中台办公室系统包括几个必须对发生的交易有一致视图的系统,但每个系统需要特定数据来执行其任务。因为这是一个单独的领域,SOR将发布到交易领域下的主题。事件包括具体的交易所,例如:
transaction/equity/new/v1/NYSE/traderID1234/Order4321
中台办公室交易所网关将订阅它们服务的交易所和池,例如:
transaction/equity/new/v1/NYSE/>
中台办公室系统的完整主题分类将是:
transaction/{productGroup}/{action}/{version}/{venue}/{source}/{orderID}
项目 | 描述 |
---|---|
transaction | 指定主题描述所有订单事件的字面值。 |
{productGroup} | 来自可能的产品组类型的枚举值。 |
{action} | 来自交易动作枚举值,如new 、updated 、filled 、canceled 。 |
{version} | 此版本transaction 的版本号。 |
{venue} | 交易可以发生的流动性池或交易所的枚举值。 |
{source} | 客户标识符。 |
{orderID} | 唯一产品订单的标识符。 |
在这个例子中,您可以想象像结算和风险系统这样的应用程序只对成功完成的交易感兴趣,而报告系统可能需要访问包括取消的在内的每一笔交易,交易数据捕获系统可能消费每个订单事件和每个交易事件,无论是整个数据集还是再次按产品类型、地区、场所或产品分片。
例如,对于事件:
transaction/equity/new/v1/NYSE/traderID1234/Order4321
结算系统可以订阅:
transaction/equity/filled/v1/>
或:
transaction/equity/filled/v1/NYSE/>
而风险系统可能会订阅以下主题来监控特定交易者的位置:
transaction/equity/*/v1/*/traderID1234/>