前言

只有光头才能变强。

文本已收录至我的GitHub精选文章,欢迎Starhttps://github.com/ZhongFuCheng3y/3y

听说过大数据的同学应该都听说过Storm吧?其实我现在负责的系统用的就是Storm,在最开始接手系统的时候,我是完全不了解Storm的(现在其实也是一知半解而已)

由于最近在整理系统,所以顺便花了点时间入门了一下Storm(前几天花了点时间改了一下,上线以后一堆Bug,于是就果断回滚了。)

这篇文章来讲讲简单Storm的简单使用,没有复杂的东西。看完这篇文章,等到接手Storm的代码的时候你们**『大概』『应该』**能看懂Storm的代码。

什么是Storm

我们首先进官方看一下Storm的介绍:

Apache Storm is a free and open source distributed realtime computation system

Storm是一个分布式的实时计算系统

分布式:我在之前已经写过挺多的分布式的系统了,比如Kafka/HDFS/Elasticsearch等等。现在看到分布式这个词,三歪第一反应就是「它的存储或者计算交由多台服务器上完成,最后汇总起来达到最终的效果」。

实时:处理速度是毫秒级或者秒级的

计算:可以简单理解为对数据进行处理,比如清洗数据(对数据进行规整,取出有用的数据)。

我们使用Storm做了什么?

我现在做的消息管理平台是可以推送各类的消息的(IM/PUSH/短信/微信消息等等),消息下发后,我们是肯定要知道这条消息的下发情况的(是否发送成功,如果用户没收到是由于什么原因导致用户没收到,消息是否被点击了等等)。

消息是否成功下发到用户上,这是运营和客服经常关心的问题。

消息下发的效果,这是运营非常关心的问题

基于上面问题,我们用了Storm做了一套自己的埋点方案,帮助我们快速确认消息是否成功下发到用户上以及统计消息下发的效果

听起来好像很牛逼,下面我来讲讲背景,看完你就会发现一点儿都不难。

需求背景

消息管理平台虽然看起来只是发消息的,但是系统设计还是有点东西的。我们以「微服务」的思想去看这个系统,会将不同的功能模块抽取到不同的系统的。

其中PUSH(推送)的链路是最长的,一条消息下发经过的后端系统就有7个,如图下:

这7个系统都有可能「干掉」了这条消息,导致用户没收到。如果我们每去查一个问题,都要逐一排查每个系统,那实在是太慢了。

很多时候客服反馈过来的问题都是当天的,甚至是前几分钟的,我们需要有一个及时的反馈给客服来帮助用户找到为什么收不到消息的原因。

于是我们要做两个功能:

  1. 能够查询用户当天所有的消息下发情况。(能够快速定位是哪个系统什么原因导致用户收不到消息)
  2. 查询某条消息的实时整体下发情况。(能够快速查看该消息的整体下发情况,包括下发量,中途过滤的量以及点击量)

如果是单纯查问题,我们将各个系统的日志收集到Kafka,然后写到Elasticsearch这个是完全没问题的(现在我们也是这么干的)

涉及到统计相关的,我们就有自己的一套埋点方案,这个是便于对数据的统计,也能完成部分排查的功能

需求实现

前面提到了「埋点」,实际上就是打日志。其实就是在关键的地方上打上日志做记录,方便排查问题。

比如,现在我们有7个系统,每个系统在执行消息的时候都会可能导致这条消息发不出去(可能是消息去重了,可能是用户的手机号不正确,可能是用户太久没有登录了等等都有可能)。我们在这些『关键位置』都打上日志,方便我们去排查。

这些「关键位置」我们都给它用简单的数字来命个名。比如说:我们用「11」来代表这个用户没有绑定手机号,用「12」来代表这个用户10分钟前收到了一条一模一样的消息,用「13」来代表这个用户屏蔽了消息.....

「11」「12」「13」「14」「15」「16」这些就叫做「点位」,把这些点位在关键的位置中打上日志,这个就叫做「埋点

有了埋点,我们要做的就是将这些点位收集起来,然后统一处理成我们的格式,输出到数据源中。

OK,就是分三步:

  1. 收集日志
  2. 清洗日志
  3. 输出到数据源

收集日志我们有logAgent帮我们收集到Kafka,实时清洗日志我们用的就是Storm,清洗完我们输出到Redis(实时)/Hive(离线)。

Storm一般是在处理(清洗)那层,Storm的上下游也很明确了(上游是消息队列,下游写到各种数据源,这种是最常见的):

Storm统一清洗出来放到Redis,我们就可以通过接口来很方便去查一条消息的整体下发情况,比如:

到这里,主要想说明我们通过Storm来实时清洗数据,下来来讲讲Storm的基本使用~

Storm入门

我们从一段最简单的Storm代码入门,先看看下面的代码:

如果完全没看过Storm代码的同学,看到上面的代码会怎么分析?我是这样的:

  • 首先有一个TopologyBuilder的东西,这个东西可能是Storm的构造器之类的
  • 然后设置了Spout和Bolt(但是我不知道这两个东西是用来干嘛的,但是我可以点进去对象里边看看做了什么)
  • 然后设置了一下Config配置(应该是设置Storm分配多少内存,多少线程之类的,反正跟配置相关)
  • 最后用StormSubmitter提交任务,把配置和TopologyBuilder的内容给提交上去。

我们简单搜一下,就可以发现它的流程大致是这样的:

Spout是数据的源头,一般我们用它去接收数据,Spout接收到数据后往Bolt上发送,Bolt处理数据(清洗)。Bolt清洗完数据可以写到一个数据源或者传递给下一个Bolt继续清洗。

Topology关联了我们在程序中定义好的Spout和Bolt。各种 Spout 和 Bolt 连接在一起之后,就成了一个 Topology,一个 Topology 就是一个 Storm 应用。

Spout往Bolt传递数据,Bolt往Bolt传递数据,这个传递的过程叫做Stream,Stream传递的是一个一个Tuple

现在问题来了,我们的Spout和Bolt之间是怎么关联起来的呢?Bolt和Bolt之间是怎么关联起来的呢?

在上面的图我们知道一个Topology会有多个Spout和多个Bolt,那我怎么知道这个Spout传递的数据是给这个Bolt,这个Bolt传递的数据是给另外一个Bolt?(说白了,就是上面图上的箭头是怎么关联的呢?)

在Storm中,有Grouping的机制,就是决定Spout的数据流向哪个Bolt,Bolt的数据流向下一个Bolt。

为了提高并发度,我们在setBolt的时候,可以指定Bolt的线程数,也就是所谓的Executor(Spout也同样可以指定线程数的,只是这次我拿Bolt来举例)。我们的结构可能会是这样的:

分组的策略有以下:

  • 1)shuffleGrouping(随机分组)
  • 2)fieldsGrouping(按照字段分组,在这里即是同一个单词只能发送给一个Bolt)
  • 3)allGrouping(广播发送,即每一个Tuple,每一个Bolt都会收到)
  • 4)globalGrouping(全局分组,将Tuple分配到task id值最低的task里面)
  • 5)noneGrouping(随机分派)
  • 6)directGrouping(直接分组,指定Tuple与Bolt的对应发送关系)
  • 7)Local or shuffle Grouping
  • 8)partialKeyGrouping(关键字分组,与按字段分组很相似,但他分配更加均衡)
  • 9)customGrouping (自定义的Grouping)

shuffleGrouping策略我们是用得最多的,比如上面的图上有两个Spout,我们会将这两个Spout的Tuple均匀分发到各个Bolt中执行。

说到这里,我们再回头看看最开始的代码,我给补充一下注释,你们应该就能看得懂了:

我还是再画一个图吧:

入门的过程复杂吗?不复杂。说白了就是Spout接收到数据,通过grouping机制将Spout的数据传到给Bolt处理,Bolt处理完看还需不需要继续往下处理,如果需要就传递给下一个Bolt,不需要就写到数据源、调接口等等。

Storm架构

当我们提交任务之后,会发生什么呢?我们来看看。

  1. 任务提交后,会被上传到Nimbus节点上,它是主控节点,负责分配代码、布置任务及检测故障
  2. Nimbus会去Zookeeper上读取整个集群的信息,将任务交给Supervisor,它是工作节点,负责创建、执行任务
  3. Supervisor创建Worker进程,每个Worker对应一个Topology的子集。Worker是Task的容器,Task是真正的任务执行者。

流程大致如下:

Nimbus和Supervisor都是节点(服务器),Storm用Zookeeper去管理Supervisor节点的信息。

Supervisor节点下会创建Worker进程,创建多少个Worker进程由Conf配置文件决定。线程Executor,由进程产生,用于执行任务,Executor线程数有多少个是在setBolt、setSpout的时候决定。Task是真正的任务执行者,Task其实就是包装了Bolt/Spout实例。

关于Worker、Executor、Task之间的关系,在官网有一个例子专门说明了,我们可以看看。先放出代码:

内部的图:

解释一下:

  • 默认情况下:如果不指定Tasks数,那么一个线程会有一个Task
  • conf.setNumWorkers(2)代表会创建两个Worker进程
  • setSpout("blue-spout", new BlueSpout(), 2)蓝色Spout会有两个线程处理,因为有两个进程,所以一个进程会有一个蓝色Spout线程
  • topologyBuilder.setBolt("green-bolt", new GreenBolt(), 2).setNumTasks(4) 绿色Bolt会有两个线程处理,因为有两个进程Worker所以一个进程会有一个绿色Bolt线程。又因为设置了4个Task数,所以一个线程会分配两个绿色的Task
  • topologyBuilder.setBolt("yellow-bolt", new YellowBolt(), 6).shuffleGrouping("green-bolt")。黄色Bolt会有6个线程处理,因为创建了两个进程,所以一个进程会有3个黄色Bolt线程。没有单独设置Task书,所以一个线程默认有一个Task

从上面我们可以知道threads ≤ tasks线程数是肯定小于等于Task数的。有没有好奇宝宝会问:「Storm用了线程,那么会有线程不安全的情况吗?」(其实这是三歪刚学的疑问)

一般来说不会,因为很多情况下,一个线程是对应一个Task的(Task你可以理解为Bolt/Spout的实例),既然每个线程是处理自己的实例了,那当然不会有线程安全的问题啦。(当然了,你如果在Bolt/Spout中设置了静态成员变量,那还是会有线程安全问题)

最后

这篇文章简单地介绍了一下Storm,Storm的东西其实还有很多,包括ack机制什么的。现在进官方找文档,都在主推Trident了,有兴趣的同学可以继续往下看。

话又说回来,我司也在主推Flink了,这块后续如果有迁移计划,我也准备学学搞搞,到时候再来分享分享入门文章。

参考资料:

各类知识点总结

下面的文章都有对应的原创精美PDF,在持续更新中,可以来找我催更~

涵盖Java后端所有知识点的开源项目(已有7 K star):https://github.com/ZhongFuCheng3y/3y

如果大家想要实时关注我更新的文章以及分享的干货的话,微信搜索Java3y

PDF文档的内容均为手打,有任何的不懂都可以直接来问我(公众号有我的联系方式)。

本文使用 mdnice 排版

花了几天入门Storm,上了一版,全是Bug的更多相关文章

  1. Storm入门-Storm与Spark对比

    作为一名程序员通病就是不安分,对业界的技术总要折腾一番,哪怕在最终实际工作中应用到的就那么一点.最近自己准备入门Storm学习,关于流式大数据框架目前比较流行的有Spark和Storm等,在入门之前, ...

  2. Apache Maven 入门篇 ( 上 )

    作者:George Ma 写这个 maven 的入门篇是因为之前在一个开发者会的动手实验中发现挺多人对于 maven 不是那么了解,所以就有了这个想法. 这个入门篇分上下两篇.本文着重动手,用 mav ...

  3. [转]Apache Maven 入门篇 ( 上 )

    原文地址:Apache Maven 入门篇 ( 上 ) 作者:George Ma 写这个 maven 的入门篇是因为之前在一个开发者会的动手实验中发现挺多人对于 maven 不是那么了解,所以就有了这 ...

  4. maven入门(上)

    Apache Maven 入门篇 ( 上 ) 作者:George Ma 写这个 maven 的入门篇是因为之前在一个开发者会的动手实验中发现挺多人对于 maven 不是那么了解,所以就有了这个想法.这 ...

  5. 花点时间顺顺Git(上)

    花点时间顺顺Git(上) 为了让你们点进来贼努力的想了一个色彩斑斓大吉大利的标题,好,看正文 历史:Linus的作者创建了开源的Linux,02年以前代码管理都依赖手动合并,后来管理不了了,拒绝SVN ...

  6. ELKStack之极速入门(上)

    ELKStack之极速入门(上) 链接:https://pan.baidu.com/s/1V2aYpB86ZzxL21Hf-AF1rA 提取码:7izv 复制这段内容后打开百度网盘手机App,操作更方 ...

  7. Docker技术入门与实战第2版-高清文字版

      Docker技术入门与实战第2版-高清文字版 下载地址https://pan.baidu.com/s/1bAoRQQlvBa-PXy5lgIlxUg 扫码下面二维码关注公众号回复100011 获取 ...

  8. Java快速入门-03-小知识汇总篇(全)

    Java快速入门-03-小知识汇总篇(全) 前两篇介绍了JAVA入门的一系小知识,本篇介绍一些比较偏的,说不定什么时候会用到,有用记得 Mark 一下 快键键 常用快捷键(熟记) 快捷键 快捷键作用 ...

  9. 《Unity3D大风暴之入门篇(海量教学视频版)》

    <Unity3D大风暴之入门篇(海量教学视频版)> 基本信息 作者: 智画互动开发团队 出版社:电子工业出版社 ISBN:9787121222429 上架时间:2014-1-13 出版日期 ...

随机推荐

  1. delphi中DateTimePicker控件同时输入日期和时间

    将DateTimePicker的Format属性中加入日期格式设成 'yyyy-MM-dd HH:mm',注意大小写 , 将kind设置为dtkTime即可,可以在每次Form onShow时将Dat ...

  2. A - Engines Atcoder 4900

    题目大意:n个点,任意几个点组合后得到的点距离原点的最远距离. 题解:极角排序:https://blog.csdn.net/qq_39942341/article/details/79840394 利 ...

  3. E - Travel by Car

    连接https://atcoder.jp/contests/abc143/tasks/abc143_e 题目大意: 在一个无向图中,当前的油量为L,给出q个问题,判断从a到b需要多少加几次油,路上每个 ...

  4. vue2.x学习笔记(一)

    使用vue开发项目已经过了一段时间了,对其中的很多东西还是一知半解,于是想要系统学习一下.主要内容是参照官方中文网站https://cn.vuejs.org/v2/guide/,然后加上一些自己的理解 ...

  5. Python 【面试强化宝典】

    四大数据类型的常用方法 列表常用方法 #1. append 用于在列表末尾追加新的对象 a = [1,2,3] a.append(4) #the result : [1, 2, 3, 4] #2. c ...

  6. Springboot:整合Mybaits和Druid【监控】(十一)

    MyBatis默认提供了一个数据库连接池PooledDataSource,在此我们使用阿里提供的Druid数据库连接池 项目下载:https://files.cnblogs.com/files/app ...

  7. [WPF] 考古Expression Web:微软当年最漂亮的WPF软件

    1. 什么是Expression Web Expression Studio是微软在2007年推出的一套针对设计师的套件,其中包含专业的设计工具和新技术,可以弹性且自由地将设计方案转为实际--无论设计 ...

  8. 小程序里json字符串转json对象需注意的地方

    一.JSON字符串转换为JSON对象 要使用上面的str1,必须使用下面的方法先转化为JSON对象: //由JSON字符串转换为JSON对象 var obj = eval('(' + str + ') ...

  9. hash算法解决冲突的方案

    1, 开放定址法: 所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入 公式为:fi(key) = (f(key)+di) MOD m ...

  10. Openstack Keystone V3 利用 curl 命令获取 token

    curl -i \ -H "Content-Type: application/json" \ -d ' { "auth": { "identity& ...