JStorm:概念与编程模型

本文介绍了JStorm的集群架构和Topology编程模型。集群架构方面,Nimbus是调度器,Supervisor是任务执行者,Zookeeper是协调者。Topology编程模型包含spout、bolt和Tuple,spout是数据源,bolt处理数据,Tuple是消息传递单元。后续将深入探讨实践、运维和原理等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、集群架构

JStorm从设计的角度,就是一个典型的调度系统,简单集群的架构如下图所示,其中Nimbus可增加一个备节点,多个Supervisor节点组成任务执行集群。


简单集群示意图
1.1、Nimbus

Nimbus是作为整个集群的调度器角色,负责分发topology代码、分配任务,监控集群运行状态等,其主要通过ZK与supervisor交互。可以和Supervisor运行在同一物理机上,JStorm中Nimbus可采用主从备份,支持热切。

1.2、Supervisor

Supervisor 是集群中任务的执行者,负责运行具体任务以及关闭任务。其从ZK中监听nimbus的指令,然后接收分发代码和任务并执行、监控反馈任务执行情况。

1.3 、Zookeeper

ZK是整个系统中的协调者,Nimbus的任务调度通过ZK下发至Supervisor来执行。

2、Topology编程模型

Topology是一个可以在JStorm中运行的任务的抽象表达,在JStorm的topology中,有两种组件:spout和bolt。下面是一张比较经典的Topology结构图。每一个topology,既可以有多个spout,代表同时从多个数据源接收消息,也可以多个bolt,来执行不同的业务逻辑。一个topology会一直运行直到你手动kill掉,JStorm自动重新分配执行失败的任务。
在JStorm中有对于流stream的抽象,流是一个不间断的无界的连续tuple,注意JStorm在建模事件流时,把流中的事件抽象为tuple即元组。
我们可以认为spout就是一个一个的水龙头,并且每个水龙头里流出的水是不同的tuple,我们想拿到哪种水tuple就拧开哪个水龙头,然后使用管道将水龙头的水tuple导向到一个水处理器(bolt),水处理器bolt处理后再使用管道导向另一个处理器或者存入容器中。

Topology示意图

JStorm将上图抽象为Topology即拓扑,拓扑结构是有向无环的,拓扑是Jstorm中最高层次的一个抽象概念,它可以被提交到Jstorm集群执行,一个拓扑就是一个数据流转换图,图中每个节点是一个spout或者bolt,图中的边表示bolt订阅了哪些流,当spout或者bolt发送元组到流时,它就发送元组到每个订阅了该流的bolt。

2.1、spout

JStorm认为每个stream都有一个stream源,也就是原始元组的源头,所以它将这个源头抽象为spout,spout可能是连接消息中间件(如MetaQ, Kafka, TBNotify等),并不断发出消息,也可能是从某个队列中不断读取队列元素并装配为tuple发射。
JStorm框架对spout组件定义了一个主要方法:nextTuple,顾名思义,就是获取下一条消息。执行时,可以理解成JStorm框架会不停地调这个接口,以从数据源拉取数据并往bolt发送数据。
Tuple是一次消息传递的基本单元,tuple里的每个字段一个名字,并且不同tuple的对应字段的类型必须一样。tuple的字段类型可以是: integer, long, short, byte, string, double, float, boolean和byte array。还可以自定义类型,只要实现对应的序列化器。
JStorm中与spout相关的接口主要是ISpout和IRichSpout、IBatchSpout,后两接口实现了对ISpout接口的上层封装。

ISpout接口主要方法:
open:在worker中初始化该ISpout时调用,一般用来设置一些属性:比如从spring容器中获取对应的Bean。
close:和open相对应(在要关闭的时候调用)。
activate:从非活动状态变为活动状态时调用。
deactivate:和activate相对应(从活动状态变为非活动状态时调用)。
nextTuple:JStorm希望在每次调用该方法的时候,它会通过collector.emit发射一个tuple。
ack:jstorm发现msgId对应的tuple被成功地完整消费会调用该方法。
fail:和ack相对应(jstorm发现某个tuple在某个环节失败了)。和ack一起保证tuple一定被处理。

2.2、bolt

JStorm将tuple的中间处理过程抽象为Bolt,bolt可以消费任意数量的输入流,只要将流方向导向该bolt,同时它也可以发送新的流给其他bolt使用,这样一来,只要打开特定的spout(管口)再将spout中流出的tuple导向特定的bolt,然后bolt对导入的流做处理后再导向其他bolt或者目的地。
bolt代表处理逻辑,bolt收到消息之后,对消息做处理(即执行用户的业务逻辑),处理完以后,既可以将处理后的消息继续发送到下游的bolt,这样会形成一个处理流水线(不过更复杂的情况应该是个有向图);也可以直接结束。
bolt组件主要方法:execute,这个接口就是用户用来处理业务逻辑的地方。
通常一个流水线的最后一个bolt,会做一些数据的存储工作,比如将实时计算出来的数据写入DB、HBase等,以供前台业务进行查询和展现。Bolts可以发射多条消息流, 使用OutputFieldsDeclarer.declareStream定义stream,使用OutputCollector.emit来选择要发射的stream。
在保证不丢消息的场景中,在bolts必须要为它处理的每一个tuple调用OutputCollector的ack方法,以通知JStorm这个tuple被处理完成了,从而通知这个tuple的发射者spouts。 一般的流程是: bolts处理一个输入tuple, 发射0个或者多个tuple, 然后调用ack通知JStorm自己已经处理过这个tuple了。JStorm提供了一个IBasicBolt会自动调用ack。
JStorm中与Bolt相关的接口主要是IBolt,IRichBolt,IBasicBolt和IBatchBolt,后面接口实现了对IBolt接口的上层封装。

IBolt接口的主要方法:
prepare:在worker中初始化该IBolt时调用,一般用来设置一些属性:比如从spring容器中获取对应的Bean。
cleanup:和prepare相对应(在显示关闭topology的时候调用)
execute:处理jstorm发送过来的tuple。

2.3、Tuple

JStorm将流中数据抽象为tuple,一个tuple就是一个值列表value list,list中的每个value都有一个name,tuple可以由任意类型组合而成,因为storm是分布式的,所以它需要知道在task间如何序列化和反序列化数据的。storm使用Kryo进行序列化,Kryo是java开发中一个快速灵活序列器。默认情况下,storm可以序列化基础类型,比如字符串,字节,数组,ArrayList, HashMap, HashSet和 Clojure 集合类型,如果需要使用其他类型,需要自定义序列器。拓扑的每个节点都要说明它所发射出的元组的字段的name,其他节点只需要订阅该name就可以接收处理。
在spout和Bolt组件中,使用declareOutputFields方法定义发射出的tuple的字段名。

3、小结

本文主要讲述了JStorm中集群的架构以及Topology编程模型方面的概念知识,后续会更深入的写一些实践、运维、原理等方面的文章。




转载于:https://juejin.im/post/5d0897396fb9a07eda03199d

### 使用Transformer模型进行图像分类的方法 #### 方法概述 为了使Transformer能够应用于图像分类任务,一种有效的方式是将图像分割成固定大小的小块(patches),这些小块被线性映射为向量,并加上位置编码以保留空间信息[^2]。 #### 数据预处理 在准备输入数据的过程中,原始图片会被切分成多个不重叠的patch。假设一张尺寸为\(H \times W\)的RGB图像是要处理的对象,则可以按照设定好的宽度和高度参数来划分该图像。例如,对于分辨率为\(224\times 224\)像素的图像,如果选择每边切成16个部分的话,那么最终会得到\((224/16)^2=196\)个小方格作为单独的特征表示单元。之后,每一个这样的补丁都会通过一个简单的全连接层转换成为维度固定的嵌入向量。 ```python import torch from torchvision import transforms def preprocess_image(image_path, patch_size=16): transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), # 假设目标分辨率是224x224 transforms.ToTensor(), ]) image = Image.open(image_path).convert('RGB') tensor = transform(image) patches = [] for i in range(tensor.shape[-2] // patch_size): # 高度方向上的循环 row_patches = [] for j in range(tensor.shape[-1] // patch_size): # 宽度方向上的循环 patch = tensor[:, :, i*patch_size:(i+1)*patch_size, j*patch_size:(j+1)*patch_size].flatten() row_patches.append(patch) patches.extend(row_patches) return torch.stack(patches) ``` #### 构建Transformer架构 构建Vision Transformer (ViT),通常包括以下几个组成部分: - **Patch Embedding Layer**: 将每个图像块转化为低维向量; - **Positional Encoding Layer**: 添加绝对或相对位置信息给上述获得的向量序列; - **Multiple Layers of Self-Attention and Feed Forward Networks**: 多层自注意机制与前馈神经网络交替堆叠而成的核心模块; 最后,在顶层附加一个全局平均池化层(Global Average Pooling)以及一个多类别Softmax回归器用于预测类标签。 ```python class VisionTransformer(nn.Module): def __init__(self, num_classes=1000, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4., qkv_bias=False, drop_rate=0.): super().__init__() self.patch_embed = PatchEmbed(embed_dim=embed_dim) self.pos_embed = nn.Parameter(torch.zeros(1, self.patch_embed.num_patches + 1, embed_dim)) self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) dpr = [drop_rate for _ in range(depth)] self.blocks = nn.Sequential(*[ Block( dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=dpr[i], ) for i in range(depth)]) self.norm = nn.LayerNorm(embed_dim) self.head = nn.Linear(embed_dim, num_classes) def forward(self, x): B = x.shape[0] cls_tokens = self.cls_token.expand(B, -1, -1) x = self.patch_embed(x) x = torch.cat((cls_tokens, x), dim=1) x += self.pos_embed x = self.blocks(x) x = self.norm(x) return self.head(x[:, 0]) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值