首页 文章 精选 留言 我的

精选列表

搜索[智能推荐],共10000篇文章
优秀的个人博客,低调大师

我推荐你试试它

前言 今天给大家介绍的是FastDFS,一个开源的分布式文件系统,也是入职之后接触到的一个技术,由于公司项目业务需求,服务器里存了上亿量级的文件,所以使用了这么一项技术来存储这些文件,我也就随之开始了解这项技术,并且在这里和大家一起从0到1地开始了解它。 FastDFS介绍 FastDFS是一个以C语言开发的开源轻量级分布式文件系统,由阿里巴巴开发并开源。它对文件进行管理,功能包括:文件存储、文件同步、文件访问(上传、下载)等。解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。 FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。 从0,自己的一些疑问:FastDFS过时了吗? 相信这也是很多同学想要问的一些问题,我还没有了解这个技术的时候,也同样有这样的疑问。 首先,现在有很多文件存储都会选择像七牛云、阿里云OSS等云服务,为什么要自己搭一套文件服务器增加维护成本呢? 其次,这并不是面试的热点,甚至在入职之前自己都没有接触过,甚至甚至,没有听说过,反倒是那些热门的技术,就算自己不主动去了解,当遇到的时候,大致也能知道是用来干嘛的。 那么我来说说我的理解,首先这个技术一定是还没有过时的,因为有些特殊的文件,因为信息安全顾虑等原因,不会选择公有云服务器,还有基于成本考虑,还有很多的中型互联网公司仍然是基于FastDFS来做自己的文件服务器的。另外,FastDFS作为一个分布式服务器,对轻量级、横向扩展、容灾备份、高可用、高性能、负载均衡都有着充分的考虑,依旧是一个文件服务器的不二之选。 那么为什么这样一项技术在如今却很少有人了解呢? 第一,我认为是需求所致,与其它业务相比,需要存储大量文件的业务相对之下还是比较少。如果文件存储量不大,按照传统的文件存储方式也不会有什么大的问题。 第二,现在有七牛云、阿里云OSS等公司提供对象存储,加之国内对于”上云“的追捧,就很少有人愿意自己搭服务器来做文件存储服务了。 当然对于一个技术人来说,各种各样的技术都得去学习,去适应,所以这篇文章希望可以帮助到感兴趣的同学,或是在工作中遇到高量级文件存储的同学,FastDFS是不错的选择。 传统文件存储方式 这是传统文件存储的方式,服务器上甚至不需要装任何的应用,只需要有SFTP服务,我们就可以写对应的代码,完成文件的CRUD。 这样的好处就是很方便,只需要一台机器,几行代码就能搞定文件的存储,但是这种方式的瓶颈和缺陷是很明显的。 首先,对于单体服务器来说,不考虑宕机的情况,单体文件服务器的带宽、磁盘容量是有上限的,那么当文件的体积占满了整个磁盘,我们只能选择扩展,但是这种单体服务器的方式,对于扩容就不太友好了,我们可以想想,难道我们要将原有的硬盘数据拷到一个更大的硬盘里,然后更换硬盘吗? 除了扩展以外,我们还需要面临一个问题,就是文件的查找,如果我们将所有文件都存放到一起,文件的数量如果到了一定的数量,就会面临磁盘IO速度瓶颈,不知道大家有没有遇到过下图中这种场景: 磁盘查询慢 如果我们需要在一个磁盘中找到某个文件,如果没有一个路径,或者路径下有很多文件,那么系统就会扫描磁盘,我们都知道,计算机体系结构中,关于速度,CPU>内存>硬盘,如果在生产环境下,真的需要存储大量的文件,假设存储的是用户的头像,那么用户每次打开APP,就需要等待个十几秒,自己的头像才会显示,那这个app估计没有人会使用吧。 有同学可能会说,那我们可以使用缓存啊,Redis的String类型是可以存储二进制数据的,而且Redis的String类型是一个Key对应一个值,查询效率会很高。的确这样在查询效率上可以达到,但是我们按照一张图片1M来计算,缓存又能存多少张图片呢?很显然这是一个十分昂贵的方式。 刚才我们考虑的都是服务器不宕机的状态,那么假设服务器宕机,那么我们就无法再提供数据存储服务;如果硬盘损坏,那么所有的数据都将丢失。 分布式文件系统 上文中说了传统文件存储方式的一些缺陷和弊端,这其实也是所有“单点“的弊端,无论是单点数据库、或者单点缓存、又或者是单点网关、单点注册中心,都在往分布式集群的方向发展。 总结一下,单点文件系统大致有这些弱点: 1. 磁盘容量存在瓶颈 2. IO速度存在瓶颈 3. 宕机、硬盘损坏数据丢失的风险 那么对于文件系统,我们如何使用分布式的方式来解决上述的缺陷呢? 解决磁盘容量瓶颈 上文中提到,当服务器文件系统存在磁盘容量瓶颈的原因是磁盘无法很方便的进行扩容,我们通常需要从硬件的层面来考虑扩容硬盘,如:更换大容量硬盘。 这种方式显然是不现实的,因为更换硬盘意味着我们需要关服务器,生产环境服务器关停三十秒都是很严重的生产事故,所以我们只能使用服务器横向扩展的方式,如果不能更换硬盘,那么我们就加服务器。 多服务器 这样我们就可以使用多台服务器共同来构成我们的文件系统了,每个文件服务器都是一个独立的节点,来存储不同的文件,根据特定的逻辑(这里需要自己写),来决定文件需要存储到哪个文件服务器中。这样即使服务器容量满了,我们也还可以继续横向扩展,理论上这样我们的文件系统是没有容量上限的。 解决IO速度瓶颈 刚才我们解决了单点文件服务器容量瓶颈,但是如果某台或者某几台服务器上的文件数量过多(查询效率降低),或者有大量的用户访问某一台服务器,还是会造成IO速度瓶颈。那么要如何解决这个问题。 我们可以想一想类似的案例——MySQL数据库。 众所周知,MySQL的数据也是存在硬盘中的,而我们在写SQL语句的时候,为了保证查询效率,通常会避免全表扫描,而是通过索引让其找到我们对应的那条数据。 所以我们也可以通过这种方式,避免全盘扫描或者大范围扫描,而我们都知道,操作系统对于文件是有一个天然的索引的,就是我们的多级目录。FastDFS也正是利用了这个来增加我们文件IO的效率的,这个暂且不提,下文中会展示。 解决宕机、硬盘损坏数据丢失的风险 能否解决这个问题才是分布式文件系统和单机文件系统最根本的区别,因为无论是单机磁盘容量瓶颈还是IO速度瓶颈,我们都可以通过增加硬件配置来解决,只不过不太方便且成本太高罢了。而单机模式是绝对无法解决宕机造成的文件服务失效,或者硬盘损坏造成的数据丢失的,因为数据只存在一份。 那么我们思考一下分布式文件系统是如何来解决这些问题的。 首先我们需要解决宕机的问题,如上图,我们有多个文件服务器节点,但是如果我们自己写逻辑来决定某个文件应该存哪个服务器上,假设那个服务器此时宕机了,文件依旧是无法存入的,当然我们可以继续写逻辑来决定如果宕机了之后应该怎么办,但是FastDFS中已经替我们实现了,Tracker节点可以帮助我们选择文件应该上传到哪个服务器上,并且还可以在某个节点宕机的时候选择其从节点(备份节点)进行文件上传,防止因为宕机造成的无法操作文件。 那么根据上面这幅图,第二个问题也就很好理解了,假设某台服务器硬盘损坏了,那么数据依旧会存在备份,即使备份服务器也损坏了,数据也只会丢失一部分,而不会全部丢失。 FastDFS 上面说了这么多的关于分布式文件系统可以解决的一些实际问题,那么就直接切入今天的主题——FastDFS。 整体架构 FastDFS文件系统由两大部分组成,客户端和服务端。 客户端通常指我们写的程序(当然FastDFS也提供了客户端测试程序),例如我们使用Java去连接FastDFS、操作文件,那么我们的Java程序就是一个客户端,FastDFS提供专有API访问,目前提供了C、Java和PHP等编程语言的API,用来访问FastDFS文件系统。 服务端由两个部分组成,分别是跟踪器(Tracker)和存储节点(Storage)。 跟踪器(Tracker):主要做调度工作,类似微服务注册中心,在内存中记录集群存储节点的storage的状态信息,是客户端和服务端存储节点storage的枢纽,因为相关信息全部在内存中,每个Storage在启动后会连接Tracker,告知自己所属的group等信息,并周期性发送心跳,TrackerServer的性能非常高,假设我们有上百个Storage节点,我们只需要3台左右的Tracker就足够了。 存储节点(Storage)用于存储文件,包括文件和文件属性(metaData)都保存到服务器磁盘上,完成文件管理的所有功能:文件存储、文件同步和文件访问等。Storage以group为组织单位,一个group内可以包含多台Storage机器,数据互为备份,总存储空间以group内容量最小的storage为准(木桶),所以建议一个group内的机器存储空间大小应该尽量相同,以免造成空间的浪费。Storage在第一次启动时,会在每一个存储目录里创建二级目录,共计256 * 256个目录,我们上传的文件会以Hash的方式被路由到其中某个子目录下。 FastDFS整体架构 工作流程 上传 FastDFSUpload 下载 FastDFSDownload 客户端发送下载请求,Tracker根据文件信息,返回Storage地址和端口(客户端也可以通过自己存储的文件位置直接访问Storage)。 客户端访问Storage,Storage根据file_id(组名、虚拟磁盘、二级目录、文件名)查找到文件,返回文件数据。 当客户端发起上传请求时,会先访问Tracker,由于Storage定期向Tracker发送状态信息,所以Tracker中存有所有Storage Group的信息。 Tracker根据本地的Storage Group信息,为客户端上传的文件分配Storage Group,并返回给客户端。 客户端拿到Storage Group地址和端口后,上传文件到指定的Storage Group中。 Storage返回文件的路径信息和文件名。 Client将文件信息存储到本地。 单机安装 安装前准备 安装 安装FastDFS需要两个源码包,分别是libfastcommon-1.0.43.tar.gz和fastdfs-6.06.tar.gz。 这里附上作者的github地址:fastdfs,libfastcommon,大家可以到这里下载对应的包。 下载完成后,将其上传到我们的linux服务器中 分别运行tar -zxvf fastdfs-6.06.tar.gz和tar -zxvf libfastcommon-1.0.43.tar.gz对其进行解压,解压后进入libfastcommon-1.0.43目录中运行sh make.sh,运行完毕后运行sh make.sh install,然后进入fastdfs-6.06目录中执行相同的操作,即可完成安装。 安装成功后进入/usr/bin目录下,如果存在很多fdfs开头的命令,则证明安装成功。 而/etfc/fdfs目录中存放了所有的FastDFS的配置文件: 然后进入/etc/fdfs,这里存放了所有fastDFS的配置文件 最后一步,我们需要进入FastDFS安装包解压后的conf目录下,找到http.conf和mime.types将其复制到/etc/fdfs目录下。 这样我们就完成了FastDFS的安装。 配置文件详解 安装完成后需要先把配置文件配置好才能够正常启动,这里会贴上tracker.conf、storage.conf、client.conf的所有配置项,就当作一个配置模板吧,配置的时候可以直接copy。 首先是tracker.conf: storage.conf: 需要配置tracker.conf和storage.conf 启动 我们需要进行一些最小配置,来支持FastDFS的启动。 首先是tracker.conf 然后是storage.conf 配置好并且检查配置文件中的目录都存在之后,将配置文件拷贝到/etc/fdfs目录下,然后启动tracker和storage即可。 FastDFS的文件存储方式 启动FastDFS后,可以去到我们刚才在storage.conf中配置的storage_path目录下,可以看到FastDFS在这个目录下创建了一个data目录,在data目录中创建了256*256个文件夹,用于分散存储数据,这样可以提高查找文件的效率。这个就是上文中所说的,FastDFS解决IO效率的一种手段,将文件分布到每个目录下,类似于Java中的HashMap,通过文件的HashCode,迅速判断文件的位置从而找到文件。 至此我们的FastDFS已经成功启动。 检查Linux上是否安装了GCC、libevent、libevent-devel 如果没有安装,则需进行安装 yuminstallgcclibeventlibevent-devel-y 功能测试 上文中我们已经将FastDFS成功启动,并且可以看到启动后数据目录的变化。现在我们就可以使用客户端来测试一下FastDFS的功能了。 首先我们需要配置一下客户端,在/etc/fdfs目录下找到client.conf配置文件,进行如下的最小配置: 配置完毕后,需要在任意目录下,创建一个用于测试的文件。 创建好文件并写入内容后,就可以对已部署的fdfs进行各种方式的测试了。 首先是文件上传,FastDFS自带的客户端通过fdfs_test 配置文件 upload 需要上传的文件路径进行文件的上传,示例如下: 当执行完这条命令之后,我们可以看到文件上传到的storage server的一些信息,例如group_name,ip,端口等,还可以看到我们的文件上传到那个group,存在这个group的哪个路径下。 本次我们上传的文件通过返回信息,可以看到上传到group1下(因为我们只有一个storage并且只设置了一个group),上传到的路径为M00/ED/49/wKiJA19kwRqAI_g6AAAAEbcXlKw7921454,那么就可以到这个目录下查看一下这个文件是否已经上传成功了。 文件上传成功,但是这个目录下面不止存在我们刚才上传的文件,还存在其它的一些文件,这里做一下说明: filename:为文件本体。 filename-m:为文件元数据,例如文件的类型、大小、如果是图片还有长度宽度等。 filename_big:为备份文件,如果存在主备服务器,那么该文件会存放到备份服务器上。 filename_big-m:文件元数据的备份,如果存在主备服务器,那么该文件会存放到备份服务器上。 文件下载,使用自带的FastDFS测试客户端,文件下载的方式与上传类似,使用fdfs_test 配置文件路径 download 组名 远程文件名称,即可下载该文件。 示例如下: 文件下载成功。 文件删除测试,使用fdfs_test 配置文件路径 delete 组名 远程文件名称示例如下: 发现仅存在文件备份,而文件本体已删除成功。 FastDFS的HTTP访问 我们只使用了FastDFS自带的客户端测试工具来测试文件的上传、下载和删除,但是在实际状况下我们并不是这么操作文件的,而是通过程序发送请求来操作文件,那么就涉及到要通过HTTP访问文件,这里单纯依靠FastDFS就无法做到了,我们需要Nginx的配合。 Nginx的安装这里就不再赘述了,这里就默认大家都安装好了Nginx。这里直接进行nginx的配置。 配置之前我们首先需要一个nginx的扩展模块包——fastdfs-nginx-module,这里同样提供一个下载地址:fastdfs-nginx-module。 下载完成之后,将其上传到服务器并解压: 然后将mod_fastdfs.conf文件复制到/etc/fdfs目录下,并且修改它,修改项如下: 配置完成后我们需要将这个扩展模块新增到原来的nginx中: 修改nginx.conf文件,新增一个server: 然后重启nginx: 如果你的nginx和fastdfs都是启动状态,那么此时已经可以访问成功了。 访问成功 fastdfs-nginx-module的执行原理 完成了文件访问之后,我们复盘一下刚才我们做了什么,首先我们安装了nginx,然后将nginx的fastdfs扩展模块添加到nginx中,之后进行了一下扩展模块和nginx的配置,但在配置nginx的代理的时候,我们并没有像以前一样直接将代理地址写入配置中,而是将原来写代理地址的位置直接写了fastdfs扩展模块,那么这个模块究竟是如何运行起来的呢? 按照传统的nginx反向代理的配置方式,我们应该在拦截到请求之后,直接通过配置地址的方式,代理到目标服务器上,但是这里直接指向fastdfs模块,很显然,这个模块帮我们干了这件事。 还记得我们在扩展模块的配置文件中,配置了Tracker Server的地址吗? 当我们请求任意一个Group时,都会被nginx拦截下来然后发送给扩展模块,然后扩展模块通过我们配置的这个Tracker Server的地址,将请求转发给Tracker,Tracker会根据自己本地的group映射表,返回一个ip:port,例如我们刚才访问的是group1,那么扩展模块会将group1发送给Tracker, Tracker返回192.168.137.3:23000给nginx,然后扩展模块再通过这个地址,去访问storage,获取文件流,返回给浏览器。 扩展模块执行流程 FastDFS分布式集群搭建 单点FastDFS跑通之后,有同学可能就会有疑惑,那这和我们之前的文件系统也没有很大差别啊,前面说的横向扩展、容灾备份我们也完全都没有看到啊。 不急不急,这就来了。 刚才我们在一台机器上部署了FastDFS,并且测试了上传下载删除等功能,最后整合nginx完成了使用浏览器对文件的访问,并且了解了一下扩展模块的运行原理。这些是为了让大家更好的了解FastDFS,但是本篇文章主要介绍分布式文件系统,分布式文件系统最大的特点也就在于容灾备份、可扩展、高可用。那么接下来就是重头戏,来讲讲FastDFS分布式集群的搭建。 架构图 我们需要准备7台Linux虚拟机,来完成本次集群的搭建,包括1台Nginx,2台Tracker Server,4台Storage分为2个group,每个group中一主一备。 我这里准备的linux服务器信息如下: 其中Group1中的两台Storage相互备份,Group2中的两台Storage相互备份。 搭建 对这六台服务器,按照上文中的安装过程,依次安装Nginx和FastDFS。(步骤如上) 建议在安装之前执行yum命令先一次性将依赖包安装完成: yum -y install gcc perl openssl openssl-devel pcre pcre-devel zlib zlib-devel libevent libevent-devel wget net-tools 配置集群 集群的配置和上面单体的配置有些许不同,由于我们是将Tracker和Storage拆开,所以在装Tracker的服务器上并不需要进行Storage的配置,同理在装Storage的机器上也不需要进行Tracker的配置。 Tracker(101和102服务器)需要配置的点: Storage(103 104 105 106服务器)需要配置的点: storage配置 集群启动 使用fdfs_trackered 配置文件路径来启动trakcer: Tracker启动 使用fdfs_stroaged 配置文件路径来启动storage: 我们可以在任意一台storage服务器中,使用fdfs_monitor /etc/fdfs/storage.conf命令来查看整个集群的状态: 可以看到集群已经搭建成功了,并且我们可以看到每个storage的状态信息,例如每个节点的所属组、IP、存储空间大小、HTTP端口、是否启动、连接的tracker server等等。 集群测试 在六台机器中随意找一台配置client.conf文件,配置项如下: 然后创建一个用于测试上传功能的文件,创建完毕后,使用fdfs_upload_file进行上传,由于我们设置的上传模式是轮询,所以记住要多上传几遍,才能看出效果。 集群上传文件 上传效果,可以看到group1的两台机器互为备份,而group2的两台机器互为备份。 负载均衡策略 刚才我们设置的上传策略是轮询,所以我们可以看到,每次在上传的时候,都会切换到与上一次不同的group中。FastDFS可以设置三种不同的负载均衡策略,分别是:轮询、指定一个group上传、选择一个剩余空间最多的group进行上传。 由于篇幅有限,这里就不一一测试了,感兴趣的同学可以在线下进行测试。 访问集群中的文件 做一个简单的回顾,上文中在配置单体的FastDFS时,我们是如何通过HTTP访问文件的? 我们使用了nginx,安装了fastdfs的扩展模块,然后在nginx中做了一个反向代理指向扩展模块,扩展模块去请求我们的tracker server获取到group对应的storage服务器的ip端口号等信息,然后扩展模块拿到这个信息之后,就去storage server中获取文件流,返回给浏览器。 所以FastDFS集群也一样,我们也需要通过nginx来访问文件,但是这里的配置略微有些不同。 我们得分这么几种情况来配置nginx:Tracker、Storage、入口服务器。 Tracker Server的nginx配置: 启动nginx,如果nginx的work process没有正常启动,需要将mod_fastdfs.conf、fastdfs解压包目录中的mime.types和http.conf复制到/etc/fdfs目录下。 Storage Server的nginx配置: 首先需要配置mod_fastdfs.conf nginx配置: 然后启动Storage的nginx。 测试一下访问: 测试访问 集群访问流程 实际我们刚才不论是访问哪台服务器,都是可以正常访问到这个文件的。 我们可以来推一下刚才的访问流程,我们刚才在tracker中配置了stroage的负载均衡,而在stroage的反向代理中配置了fastdfs的扩展模块。 假设我们访问的是tracker,那么tracker服务器我们配置了负载均衡,负载均衡会自动路由到任意一台storage上,storage上配置了扩展模块,会带上我们访问的group去请求tracker,tracker返回这个group的storage的ip和端口号。 那么假设我们访问的是storage,那么storage里的扩展模块就直接携带我们的url中的group去访问tracker,一样也能找到storage的ip和端口。 所以只要group是正确的,无论访问哪台机器,都可以访问到文件。 配置统一入口 还记得我们搭集群之前说过,我们需要7台机器吗,但是现在我们只用了6台,第7台机器就是用在这里。 因为刚才我们只是把集群搭起来了,但是这样我们需要记住6个ip地址,再来回顾一下最初的架构图: 我们需要提供一个nginx,负载均衡到两个tracker中,然后我们之后的程序就只需要访问这个入口,就可以正常使用整个集群的服务了。 nginx配置如下: 测试: 通过入口访问成功 集群搭建完毕。 结语 分布式文件系统对于传统文件系统的一些优势,具体在于容灾备份、横向扩展,和解决传统文件系统文中介绍的具体的技术——FastDFS整合nginx,作为分布式文件系统的解决方案之一,可以很好的解决一些生产环境下的巨量文件存储问题。 另外FastDFS也可以很方便的通过Java程序来对文件进行诸如上传下载之类的操作,但由于篇幅原因,本文中没有介绍到,当然如果大家感兴趣的话我会在下一篇博客中来具体说说在真实项目是如何使用FastDFS的。

优秀的个人博客,低调大师

好物推荐|下载超过 23w 次的 IDE 插件,让效率飞速提升

日常开发运维的过程中,我们总是会遇到各样重复性劳动而这些工作占据了我们大量时间。 我们一定都经历过这样的场景: 每一次产品迭代更新,都要一遍遍「打包-上传-登录服务器-替换-部署-重启」重复的类似工作; 在本地 IDE、云产品控制台、Git/SVN、Maven/Gradle、镜像仓库、ECS、容器等多个产品间来回切换; 研发过程中,想要找到一些提升效率的代码管理、自动化测试等等工具,需要自己在各种插件市场挨个踩雷,也不知道好不好用,耗时耗力; 日常遇到研发运维问题时,找不到相关社区/社群去帮助解决技术问题,可能一个微不足道的小问题就困扰了我们很久。 在结合对超过 1000 位开发者的需求调研后,我们为了帮助大家打造了这样的 IDE 插件工具-- Cloud Toolkit 去解决上述问题。Cloud Toolkit作为阿里云发布的免费本地IDE插件,希望帮助开发者更高效地开发、测试、诊断并部署应用。而到现在 Cloud Toolkit 的下载量已超过 23w 人次,成为了众多开发者的共同选择,到底是什么让 Cloud Toolkit 深受开发者喜爱? 一键式部署,有效提升部署速度 8 倍以上 支持标准SSH协议,无需在一系列运维工具之间切换,只需在图形界面上选择目标服务器,即可实现应用快速部署。一键部署至任意服务器及云端(ECS、EDAS、ACK、ACR、小程序云等),支持多种服务器及主流云端服务,有效降低开发者重复性工作量。我们需要操心的只是如何提高产品的代码质量,无需再为部署操心。不仅仅有IntelliJ IDEA主流版本,还有Eclipse、Pycharm、Maven等其他版本。 全面覆盖开发部署流程所需功能与服务,大幅提高研发效率 内置alibaba 代码规范、Arthas诊断、Dubbo工具、Terminal终端、文件上传、函数计算 和MySQL执行器等工具。从代码管理到持续集成、自动化测试、自动部署,全面覆盖开发部署工作场景,无需对接第三方工具,不用再到处找插件或者使用测评了,大幅提升研发效率。 产品免费用,不间断「社群支持」确保开发者使用顺畅 产品免费使用,「持续更新」确保开发者使用流畅,功能不断增加,使用效率不断提高。社群支持让问题及时响应,不仅是阿里云的技术专家支持,更有相似使用者的经验分享,让日常工作所遇问题迎刃而解。 Cloud Toolkit 上线以来,一直遵循着开发者第一的原则进行产品迭代与升级。近期,Cloud Toolkit 近期发布了新版本,增加了新特性以及功能优化与增强。进一步加强了对与开发者支持,帮助开发者提升工作效率。 【新特性】 IDEA版新增对Kotlin语言项目的部署支持 VSCode版本增加Deploy to Windows的支持 【优化&增强】 VSCode版支持SSH代理(跳板机) Deploy to Windows增加对freesshd的支持 IDEA版本修复ROS模块未知异常的问题 IDEA版本针对自建Kubernetes,客户端提供创建Controller的流程 修复VSCode版本Deploy to ECS在没有Before Command情况下部署卡住的问题 修复IDEA Deploy to ECS/Host打开Dialog后CPU飙高的问题 下个版本预告: 多项目同步部署&远程服务器文件下载 下载:https://www.aliyun.com/product/cloudtoolkit 离线包:http://toolkit.aliyun.com/idea/toolkit-intellij-2020.7.1.zip 使用指南:https://developer.aliyun.com/article/665049 “阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

优秀的个人博客,低调大师

为什么阿里会推荐所有的局部变量使用基本数据类型?

云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! Java中共有三种变量,分别是类变量、成员变量和局部变量。他们分别存放在JVM的方法区、堆内存和栈内存中。 上面定义的三个变量中: 变量a就是类变量,存放在方法区,线程共享。 变量b就是成员变量,随着对象一起存放在堆内存,线程共享。 变量c和d是局部变量,存放在栈内存,线程独享。 也就是说,变量a和b是共享变量,变量c和d是非共享变量。 即局部变量是非共享变量,作用域只在方法内部,并不会逃逸到方法外。 那么,既然是一个作用域只在方法内的变量,不需要考虑那么多,怎么性能高怎么定义,怎么方便使用怎么定义。 我们都知道在Java语言中,new一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象;所以,对象本身来说是比较消耗资源的。 对于经常用到的类型,如int等,如果我们每次使用这种变量的时候都需要new一个Java对象的话,就会比较笨重。 所以,和C++一样,Java提供了基本数据类型,这种数据的变量不需要使用new创建,他们不会在堆上创建,而是直接在栈内存中存储,因此会更加高效。 【云栖号在线课堂】每天都有产品技术专家分享!课程地址:https://yqh.aliyun.com/live 立即加入社群,与专家面对面,及时了解课程最新动态!【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK 原文发布时间:2020-06-03本文作者:HollisChuang本文来自:“掘金”,了解相关信息可以关注“掘金”

优秀的个人博客,低调大师

阿里云 Elasticsearch 向量检索,轻松玩转人脸识别、搜索推荐等29个业务场景

本文作者:清豆 — 阿里巴巴高级开发工程师 本文字数:2979阅读时间:3~6分钟 您将了解:1、开源向量检索库存在的问题2、源自阿里Proxima向量检索库的优势3、阿里云向量检索的技术原理4、阿里云向量检索的应用场景5、如何使用阿里云向量检索 【全链路云上Elastic Stack 全景图】100%兼容开源,9大独有能力 ----> 直播回顾 | 请点击观看 :阿里云Elasticsearch向量检索介绍 寻根问源 (图一:开源向量检索库面临的问题) 我们知道,市面上有不少开源向量检索库供大家选择使用,例如 Facebook 推出的 Faiss 以及 Nswlib,虽然选择较多,但当业务上需要用到向量检索时,依旧要面对四大共性问题。 1、离线方案,增量不可查 市面上大多向量检索库都是对增量数据不友好,有的需要做一次性全量数据来构建索引,有

优秀的个人博客,低调大师

艾伟:推荐三个关于Azure的Session(pdc2008大会)

到目前为止,介绍关于Azure平台的底层架构的文章一直都不多,可以说少的跟“国宝大熊猫”似的。所幸在PDC2008上有一些相关的信息(主要是一些SESSION和相关的PPTX)。今天就先整理一下,希望有哪位在微软从事相关工作的兄台出手(在不违反保密协议的前提下)将更多的信息发出来,让大家一起学习进步。 费话不多说了,开始今天的正文。 Session 1:Windows Azure: Architecting & Managing Cloud Services PPTX下载链接:http://mschnlnine.vo.llnwd.net/d1/pdc08/PPTX/ES02.pptx 在该SESSION中最让人印象深刻的是对Fabric Controller(FC)的讲解,这里暂且称之为“组织控制器”吧。其所要做的事件就是对发布的服务进行资源分派,管理服务生命周期,维护系统正常运转。如下图所示: 图中左上角的“What” is needed,按我的理解应该“你想要得到的东西”,换句话说是你的需求是什么。因为微软在之前的宣传中提出过一些信息,即: Windows Azure管理服务并不仅仅是服务器,它是“告之它你想要的,然后它就会自己实现相关细节”的这样一个平台。 而Fabric Controller就是在对这些需求进行分析整理并加以布署实现,在该图的下方我们看到有两块内容,一个是load-balancers(负载均衡),另一个是switches. 除了Fabric Controller(FC)之外,该Session还讲了关于服务(托管代码)的隔离和安全性问题,如下图: 然后就是在布署服务时,平台是采用负载均衡方式来路由那些有效的活动节点,同时会跨越那些失效的域。 FC提供了状态检查点(state check-pointed),其实现的功能包括: 用于回滚到前一个检查点。(按我的理解:服务的执行可能会出现中断或失败,当出现这种情况时,可以回滚到最近的成功执行的检查点继续执行)。 同时预防中断或FC状态的丢失。 跨失效域(faultdomain)的存储(注:失效域应该是指在Azure平台上那些服务器或主机出现硬软故障或进行升级服务时节点) 最后该SESSION还发布了在2009年度要做的工作,包括: 暴露更多的基础服务模型 更加丰富的服务生命周期管理 Session 2:Under the Hood: Inside the Windows Azure Hosting Environment PPTX下载链接:http://mschnlnine.vo.llnwd.net/d1/pdc08/PPTX/ES19.pptx 看了上面的Session1如果感觉还不过瘾的话,可以看看这个课程,这个Session可以看过是对之前课程的高级版讲解。里面对FC的讲解要更加透彻(我目前还在看,所以就先不多说了) Session 3:Windows Azure: Essential Cloud Storage Services PPTX下载链接:http://mschnlnine.vo.llnwd.net/d1/pdc08/PPTX/ES04.pptx 该Session讲解的是关于云存储服务相关的内容,因为在之前我看过Azure tookit开发包中的一些例子,然而却不能完全搞懂Azure平台上是如何存储结构化和非结构化数据的,所以才专门看了这个课程,一看之下,还真发现了不少门道,下面简要归纳一下: 在Azure平台上要存储数据要使用帐号进行存储。当用户创建相应帐户后,会得到一个256位的加密串(开发时使用)。在Azure平台上有三个基本的数据(层)抽象,分别是: Blobs:提供一个简单接口来用户存储非结构化数据文件(图片,普通文件等)和相关的元数据(我想可能会用于数据文件同步的需要) Tables:提供结构化存储。该Table是一组entities,该entities包括一系列的属性 Queues:提供可靠的数据存储并对相应的应用(服务)发送消息(messages) 关于Blobs (非结构化文件存储),其提出了下面的概念: 即:一个存储帐号可以有许多的Blob Containers(容器),而一个Container即是一组blob的集合。所谓的共享策略需被施加于容器(container)级别之上。 同时该Session也解释了Azure平台上进行服务请求时的URL串的结构,如下图: 当然该Session的精彩之处还不在于此,其所提出的“使用Blocks机制上传大文件”是一个不错的亮点。如下图: 1.原理分析: 2.使用PutBlockList 的例子: 3.然后是上传之后的图形讲解: 通过这些可以看出,blob结构由一个Blocks列表所组成的。每个block是通过blockid进行标识的,其ID发布为64字节长。其次每个BLOCK之间是彼此关联,互相影响的。且每个block的最大长度是4M,但每个BLOCK的大小可以不同。在BLOB讲解最后就是解释了如何使用RESTAPI来操作管理BLOB(更新,添加,拷贝等)。 而对于Tables存储(结构化数据)的讲解,因为之前我一直不太赞同将核心数据移入到云平台之后,所以就没过多关注,但从其介绍的内容中还是可以看出一些很好的地方的,比如数据起码被拷贝三次(冗余的需要),可以随时访问您的数据,提供以TB为单位的存储空间等等,如下: 对于Queues应该是Azure平台的一个重点,因为消息这种数据结构在ServiceBus中是核心数据,可以说服务总结本身就是消息驱动的。当然在 Azure平台上所提供的异步的消息发送机制,并确保每个消息至少被处理一次,参见下图: 该图演示当consumer1当机后,消息msg1在30秒之内还是可见(即要求被其它consumer处理) 用户可以使用当前帐户创建多个Queues,每个Queues由一系列消息组成,每个消息长度小于8K. 在该SESSION最后,作者用一张图来总结blobs,tables,queues三种数据抽象所存储结构信息类型。我想只要记住这张图,这节SESSION应用就没白听了。 好了,今天的内容就先到这里了。 更多关于Azure平台的Session可以访问:http://channel9.msdn.com/tags/pdc2008.azure/

优秀的个人博客,低调大师

推荐一个有趣的Chrome扩展程序-查看任意网站的开发技术栈

对于前端开发人员来说,目前的前端框架层出不穷,最受欢迎的莫过于所谓的前端框架三驾马车:Angular, React和Vue。在学习的过程中,肯定好奇现在的互联网公司的网站用的何种前端框架来开发的。 Chrome扩展程序商店里就提供了这样一款应用:wappalyzer: 安装后,打开某个你想研究的网站,点击Chrome浏览器工具栏这个小按钮,即可召唤出Wappalyzer的页面。 下面是用这个介绍的Chrome扩展应用Wappalyzer查看到的一些互联网公司的网站的技术栈。 百度:用了SWFObject和jQuery。 京东:用了webpack,jQuery。京东的jQuery版本比百度低不少啊。 github: 服务器用的Tengine,JavaScript库用的Zepto。 Github:编程语言用的Ruby,有自己的CDN:Github pages。Web框架:Ruby on Rails。还用了Google用于报表处理的服务:Google Analytics。 SAP的官网:编程语言为Java,前端也用的jQuery。因为和Facebook做了集成,所以用了Facebook Widget。 国内程序员写博客的博客园:jQuery 2.2.0, XRegExp 1.5.0. 也用了Google的报表服务Google Analytics。程序员写博客,少不了贴代码,需要语法高亮,所以用了SyntaxHighlighter。 腾讯云社区: 服务器用的Nginx, 前端框架用的React,此外还有Prism。 国内另一个IT博客网站ITEye:用了Prototype。 今日头条:服务器用的Nginx,前端框架用的jQuery。也用了Google Analytics。 阿里云社区:用的jQuery,webpack,服务器用的Tengine,也用了Google Analytics。 简书:难得地用了Vue,版本2.4.2. 编程语言用的Ruby,也用了Moment.js和jQuery。Web框架还用了Ruby on Rails和Bootstrap。 Twitter:Google Analytics。 Google:用的Google自己的服务器Google Web Server。 要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:

优秀的个人博客,低调大师

推荐一款功能强大的Tomcat 管理监控工具,可替代Tomcat Manager

我们在本地启动Tomcat服务器后,用localhost:<默认端口>访问: 再点Manager App,即可进入Tomcat自带的Manager这个应用,此处可以单独部署/卸载每一个应用。可以看到在Manager应用里提供的功能是很有限的。 最近我在github上找到一个功能强大的Tomcat 管理监控工具,可以用来替代Tomcat默认的Manager应用: https://github.com/psi-probe/psi-probe 使用非常简单,从github的链接下载psi-probe的war包,部署到您本地的Tomcat服务器即可。 这是psi-probe登录后的界面,可以看到提供的监控和管理功能非常多: 不喜欢用英文的朋友们,可以点击这个国旗图标切换到中文显示: 应用程序标签 单击每个应用,可以看到应用运行的明细:比如某一时间段的处理的请求数量,平均响应时间和处理性能指标。 应用使用到的Servlet一览: 日志 对Tomcat的日志文件提供了更友好的操作和界面。 能在这里方便的更改日志等级: 线程 对Tomcat线程池设计不熟悉的朋友们,可以在这个标签页里学习了。 点击“正在运行的线程”可以从线程池显示切换到单个线程显示: 系统信息 连接器 要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:

优秀的个人博客,低调大师

来自小姐姐的入门推荐:7个基本机器学习算法Python实现

本文来自AI新媒体量子位(QbitAI) 有位美女小姐姐,刚刚在GitHub上放出一份福利。 嗯,正经的福利。 她总结了一份基本的机器学习算法,全部以纯Python(版本3.6+)实现。其中包括线性回归等七套算法,具体地址在此: https://github.com/zotroneneis/machine_learning_basics 这7个算法,及代码实现地址: 线性回归,http://t.cn/REk46x1 逻辑回归,http://t.cn/REk4Kpp 感知器,http://t.cn/REk40Y5 K近邻,http://t.cn/REk4H9I k平均聚类,http://t.cn/REk4rqI 只有一个隐层的简单神经网络,http://t.cn/REk4kHb 多类别逻辑回归,http://t.cn/REkbxOe 所有的算法都是从0开始实

优秀的个人博客,低调大师

Storm概念学习系列之storm-starter项目(完整版)(博主推荐)

这是书籍《从零开始学Storm》赵必厦 2014年出版的配套代码! storm-starter项目包含使用storm的各种各样的例子。项目托管在GitHub上面,其网址为:http://github.com/nathanmarz/storm-starter 或者 storm-starter项目的包结构: storm-starter项目的拓扑结构: 新建maven项目的方式 以“新建Maven项目的方式”导入storm-starter项目的步骤如下: 1、新建一个Maven项目,项目名称可以随意,如storm-starter。 2、把storm-starter项目根目录的src\jvm目录中的全部文件复制到Maven项目的src/main/java目录下。 storm-starter-master\src\jvm\storm\starter下的BasicDRPCTopology.java package storm.starter; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.LocalDRPC; import backtype.storm.StormSubmitter; import backtype.storm.drpc.LinearDRPCTopologyBuilder; import backtype.storm.topology.BasicOutputCollector; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseBasicBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; /** * This topology is a basic example of doing distributed RPC on top of Storm. It implements a function that appends a * "!" to any string you send the DRPC function. * <p/> * See https://github.com/nathanmarz/storm/wiki/Distributed-RPC for more information on doing distributed RPC on top of * Storm. */ public class BasicDRPCTopology { public static class ExclaimBolt extends BaseBasicBolt { @Override public void execute(Tuple tuple, BasicOutputCollector collector) { String input = tuple.getString(1); collector.emit(new Values(tuple.getValue(0), input + "!")); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "result")); } } public static void main(String[] args) throws Exception { LinearDRPCTopologyBuilder builder = new LinearDRPCTopologyBuilder("exclamation"); builder.addBolt(new ExclaimBolt(), 3); Config conf = new Config(); if (args == null || args.length == 0) { LocalDRPC drpc = new LocalDRPC(); LocalCluster cluster = new LocalCluster(); cluster.submitTopology("drpc-demo", conf, builder.createLocalTopology(drpc)); for (String word : new String[]{ "hello", "goodbye" }) { System.out.println("Result for \"" + word + "\": " + drpc.execute("exclamation", word)); } cluster.shutdown(); drpc.shutdown(); } else { conf.setNumWorkers(3); StormSubmitter.submitTopology(args[0], conf, builder.createRemoteTopology()); } } } storm-starter-master\src\jvm\storm\starter下的的ExclamationTopology.java package storm.starter; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.StormSubmitter; import backtype.storm.task.OutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.testing.TestWordSpout; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.TopologyBuilder; import backtype.storm.topology.base.BaseRichBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import backtype.storm.utils.Utils; import java.util.Map; /** * This is a basic example of a Storm topology. */ public class ExclamationTopology { public static class ExclamationBolt extends BaseRichBolt { OutputCollector _collector; @Override public void prepare(Map conf, TopologyContext context, OutputCollector collector) { _collector = collector; } @Override public void execute(Tuple tuple) { _collector.emit(tuple, new Values(tuple.getString(0) + "!!!")); _collector.ack(tuple); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word")); } } public static void main(String[] args) throws Exception { TopologyBuilder builder = new TopologyBuilder(); builder.setSpout("word", new TestWordSpout(), 10); builder.setBolt("exclaim1", new ExclamationBolt(), 3).shuffleGrouping("word"); builder.setBolt("exclaim2", new ExclamationBolt(), 2).shuffleGrouping("exclaim1"); Config conf = new Config(); conf.setDebug(true); if (args != null && args.length > 0) { conf.setNumWorkers(3); StormSubmitter.submitTopology(args[0], conf, builder.createTopology()); } else { LocalCluster cluster = new LocalCluster(); cluster.submitTopology("test", conf, builder.createTopology()); Utils.sleep(10000); cluster.killTopology("test"); cluster.shutdown(); } } } storm-starter-master\src\jvm\storm\starter下的ManualDRPC.java package storm.starter; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.LocalDRPC; import backtype.storm.drpc.DRPCSpout; import backtype.storm.drpc.ReturnResults; import backtype.storm.topology.BasicOutputCollector; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.TopologyBuilder; import backtype.storm.topology.base.BaseBasicBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; public class ManualDRPC { public static class ExclamationBolt extends BaseBasicBolt { @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("result", "return-info")); } @Override public void execute(Tuple tuple, BasicOutputCollector collector) { String arg = tuple.getString(0); Object retInfo = tuple.getValue(1); collector.emit(new Values(arg + "!!!", retInfo)); } } public static void main(String[] args) { TopologyBuilder builder = new TopologyBuilder(); LocalDRPC drpc = new LocalDRPC(); DRPCSpout spout = new DRPCSpout("exclamation", drpc); builder.setSpout("drpc", spout); builder.setBolt("exclaim", new ExclamationBolt(), 3).shuffleGrouping("drpc"); builder.setBolt("return", new ReturnResults(), 3).shuffleGrouping("exclaim"); LocalCluster cluster = new LocalCluster(); Config conf = new Config(); cluster.submitTopology("exclaim", conf, builder.createTopology()); System.out.println(drpc.execute("exclamation", "aaa")); System.out.println(drpc.execute("exclamation", "bbb")); } } storm-starter-master\src\jvm\storm\starter下的PrintSampleStream.java /* // to use this example, uncomment the twitter4j dependency information in the project.clj, // uncomment storm.starter.spout.TwitterSampleSpout, and uncomment this class package storm.starter; import storm.starter.spout.TwitterSampleSpout; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.topology.TopologyBuilder; import backtype.storm.utils.Utils; import storm.starter.bolt.PrinterBolt; public class PrintSampleStream { public static void main(String[] args) { String username = args[0]; String pwd = args[1]; TopologyBuilder builder = new TopologyBuilder(); builder.setSpout("spout", new TwitterSampleSpout(username, pwd)); builder.setBolt("print", new PrinterBolt()) .shuffleGrouping("spout"); Config conf = new Config(); LocalCluster cluster = new LocalCluster(); cluster.submitTopology("test", conf, builder.createTopology()); Utils.sleep(10000); cluster.shutdown(); } } */ storm-starter-master\src\jvm\storm\starter下的ReachTopology.java package storm.starter; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.LocalDRPC; import backtype.storm.StormSubmitter; import backtype.storm.coordination.BatchOutputCollector; import backtype.storm.drpc.LinearDRPCTopologyBuilder; import backtype.storm.task.TopologyContext; import backtype.storm.topology.BasicOutputCollector; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseBasicBolt; import backtype.storm.topology.base.BaseBatchBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import java.util.*; /** * This is a good example of doing complex Distributed RPC on top of Storm. This program creates a topology that can * compute the reach for any URL on Twitter in realtime by parallelizing the whole computation. * <p/> * Reach is the number of unique people exposed to a URL on Twitter. To compute reach, you have to get all the people * who tweeted the URL, get all the followers of all those people, unique that set of followers, and then count the * unique set. It's an intense computation that can involve thousands of database calls and tens of millions of follower * records. * <p/> * This Storm topology does every piece of that computation in parallel, turning what would be a computation that takes * minutes on a single machine into one that takes just a couple seconds. * <p/> * For the purposes of demonstration, this topology replaces the use of actual DBs with in-memory hashmaps. * <p/> * See https://github.com/nathanmarz/storm/wiki/Distributed-RPC for more information on Distributed RPC. */ public class ReachTopology { public static Map<String, List<String>> TWEETERS_DB = new HashMap<String, List<String>>() {{ put("foo.com/blog/1", Arrays.asList("sally", "bob", "tim", "george", "nathan")); put("engineering.twitter.com/blog/5", Arrays.asList("adam", "david", "sally", "nathan")); put("tech.backtype.com/blog/123", Arrays.asList("tim", "mike", "john")); }}; public static Map<String, List<String>> FOLLOWERS_DB = new HashMap<String, List<String>>() {{ put("sally", Arrays.asList("bob", "tim", "alice", "adam", "jim", "chris", "jai")); put("bob", Arrays.asList("sally", "nathan", "jim", "mary", "david", "vivian")); put("tim", Arrays.asList("alex")); put("nathan", Arrays.asList("sally", "bob", "adam", "harry", "chris", "vivian", "emily", "jordan")); put("adam", Arrays.asList("david", "carissa")); put("mike", Arrays.asList("john", "bob")); put("john", Arrays.asList("alice", "nathan", "jim", "mike", "bob")); }}; public static class GetTweeters extends BaseBasicBolt { @Override public void execute(Tuple tuple, BasicOutputCollector collector) { Object id = tuple.getValue(0); String url = tuple.getString(1); List<String> tweeters = TWEETERS_DB.get(url); if (tweeters != null) { for (String tweeter : tweeters) { collector.emit(new Values(id, tweeter)); } } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "tweeter")); } } public static class GetFollowers extends BaseBasicBolt { @Override public void execute(Tuple tuple, BasicOutputCollector collector) { Object id = tuple.getValue(0); String tweeter = tuple.getString(1); List<String> followers = FOLLOWERS_DB.get(tweeter); if (followers != null) { for (String follower : followers) { collector.emit(new Values(id, follower)); } } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "follower")); } } public static class PartialUniquer extends BaseBatchBolt { BatchOutputCollector _collector; Object _id; Set<String> _followers = new HashSet<String>(); @Override public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, Object id) { _collector = collector; _id = id; } @Override public void execute(Tuple tuple) { _followers.add(tuple.getString(1)); } @Override public void finishBatch() { _collector.emit(new Values(_id, _followers.size())); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "partial-count")); } } public static class CountAggregator extends BaseBatchBolt { BatchOutputCollector _collector; Object _id; int _count = 0; @Override public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, Object id) { _collector = collector; _id = id; } @Override public void execute(Tuple tuple) { _count += tuple.getInteger(1); } @Override public void finishBatch() { _collector.emit(new Values(_id, _count)); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "reach")); } } public static LinearDRPCTopologyBuilder construct() { LinearDRPCTopologyBuilder builder = new LinearDRPCTopologyBuilder("reach"); builder.addBolt(new GetTweeters(), 4); builder.addBolt(new GetFollowers(), 12).shuffleGrouping(); builder.addBolt(new PartialUniquer(), 6).fieldsGrouping(new Fields("id", "follower")); builder.addBolt(new CountAggregator(), 3).fieldsGrouping(new Fields("id")); return builder; } public static void main(String[] args) throws Exception { LinearDRPCTopologyBuilder builder = construct(); Config conf = new Config(); if (args == null || args.length == 0) { conf.setMaxTaskParallelism(3); LocalDRPC drpc = new LocalDRPC(); LocalCluster cluster = new LocalCluster(); cluster.submitTopology("reach-drpc", conf, builder.createLocalTopology(drpc)); String[] urlsToTry = new String[]{ "foo.com/blog/1", "engineering.twitter.com/blog/5", "notaurl.com" }; for (String url : urlsToTry) { System.out.println("Reach of " + url + ": " + drpc.execute("reach", url)); } cluster.shutdown(); drpc.shutdown(); } else { conf.setNumWorkers(6); StormSubmitter.submitTopology(args[0], conf, builder.createRemoteTopology()); } } } storm-starter-master\src\jvm\storm\starter下的RollingTopWords.java package storm.starter; import backtype.storm.Config; import backtype.storm.testing.TestWordSpout; import backtype.storm.topology.TopologyBuilder; import backtype.storm.tuple.Fields; import storm.starter.bolt.IntermediateRankingsBolt; import storm.starter.bolt.RollingCountBolt; import storm.starter.bolt.TotalRankingsBolt; import storm.starter.util.StormRunner; /** * This topology does a continuous computation of the top N words that the topology has seen in terms of cardinality. * The top N computation is done in a completely scalable way, and a similar approach could be used to compute things * like trending topics or trending images on Twitter. */ public class RollingTopWords { private static final int DEFAULT_RUNTIME_IN_SECONDS = 60; private static final int TOP_N = 5; private final TopologyBuilder builder; private final String topologyName; private final Config topologyConfig; private final int runtimeInSeconds; public RollingTopWords() throws InterruptedException { builder = new TopologyBuilder(); topologyName = "slidingWindowCounts"; topologyConfig = createTopologyConfiguration(); runtimeInSeconds = DEFAULT_RUNTIME_IN_SECONDS; wireTopology(); } private static Config createTopologyConfiguration() { Config conf = new Config(); conf.setDebug(true); return conf; } private void wireTopology() throws InterruptedException { String spoutId = "wordGenerator"; String counterId = "counter"; String intermediateRankerId = "intermediateRanker"; String totalRankerId = "finalRanker"; builder.setSpout(spoutId, new TestWordSpout(), 5); builder.setBolt(counterId, new RollingCountBolt(9, 3), 4).fieldsGrouping(spoutId, new Fields("word")); builder.setBolt(intermediateRankerId, new IntermediateRankingsBolt(TOP_N), 4).fieldsGrouping(counterId, new Fields( "obj")); builder.setBolt(totalRankerId, new TotalRankingsBolt(TOP_N)).globalGrouping(intermediateRankerId); } public void run() throws InterruptedException { StormRunner.runTopologyLocally(builder.createTopology(), topologyName, topologyConfig, runtimeInSeconds); } public static void main(String[] args) throws Exception { new RollingTopWords().run(); } } storm-starter-master\src\jvm\storm\starter下的SingleJoinExample.java package storm.starter; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.testing.FeederSpout; import backtype.storm.topology.TopologyBuilder; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Values; import backtype.storm.utils.Utils; import storm.starter.bolt.SingleJoinBolt; public class SingleJoinExample { public static void main(String[] args) { FeederSpout genderSpout = new FeederSpout(new Fields("id", "gender")); FeederSpout ageSpout = new FeederSpout(new Fields("id", "age")); TopologyBuilder builder = new TopologyBuilder(); builder.setSpout("gender", genderSpout); builder.setSpout("age", ageSpout); builder.setBolt("join", new SingleJoinBolt(new Fields("gender", "age"))).fieldsGrouping("gender", new Fields("id")) .fieldsGrouping("age", new Fields("id")); Config conf = new Config(); conf.setDebug(true); LocalCluster cluster = new LocalCluster(); cluster.submitTopology("join-example", conf, builder.createTopology()); for (int i = 0; i < 10; i++) { String gender; if (i % 2 == 0) { gender = "male"; } else { gender = "female"; } genderSpout.feed(new Values(i, gender)); } for (int i = 9; i >= 0; i--) { ageSpout.feed(new Values(i, i + 20)); } Utils.sleep(2000); cluster.shutdown(); } } storm-starter-master\src\jvm\storm\starter下的TransactionalGlobalCount.java package storm.starter; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.coordination.BatchOutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.testing.MemoryTransactionalSpout; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseBatchBolt; import backtype.storm.topology.base.BaseTransactionalBolt; import backtype.storm.transactional.ICommitter; import backtype.storm.transactional.TransactionAttempt; import backtype.storm.transactional.TransactionalTopologyBuilder; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This is a basic example of a transactional topology. It keeps a count of the number of tuples seen so far in a * database. The source of data and the databases are mocked out as in memory maps for demonstration purposes. This * class is defined in depth on the wiki at https://github.com/nathanmarz/storm/wiki/Transactional-topologies */ public class TransactionalGlobalCount { public static final int PARTITION_TAKE_PER_BATCH = 3; public static final Map<Integer, List<List<Object>>> DATA = new HashMap<Integer, List<List<Object>>>() {{ put(0, new ArrayList<List<Object>>() {{ add(new Values("cat")); add(new Values("dog")); add(new Values("chicken")); add(new Values("cat")); add(new Values("dog")); add(new Values("apple")); }}); put(1, new ArrayList<List<Object>>() {{ add(new Values("cat")); add(new Values("dog")); add(new Values("apple")); add(new Values("banana")); }}); put(2, new ArrayList<List<Object>>() {{ add(new Values("cat")); add(new Values("cat")); add(new Values("cat")); add(new Values("cat")); add(new Values("cat")); add(new Values("dog")); add(new Values("dog")); add(new Values("dog")); add(new Values("dog")); }}); }}; public static class Value { int count = 0; BigInteger txid; } public static Map<String, Value> DATABASE = new HashMap<String, Value>(); public static final String GLOBAL_COUNT_KEY = "GLOBAL-COUNT"; public static class BatchCount extends BaseBatchBolt { Object _id; BatchOutputCollector _collector; int _count = 0; @Override public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, Object id) { _collector = collector; _id = id; } @Override public void execute(Tuple tuple) { _count++; } @Override public void finishBatch() { _collector.emit(new Values(_id, _count)); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "count")); } } public static class UpdateGlobalCount extends BaseTransactionalBolt implements ICommitter { TransactionAttempt _attempt; BatchOutputCollector _collector; int _sum = 0; @Override public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, TransactionAttempt attempt) { _collector = collector; _attempt = attempt; } @Override public void execute(Tuple tuple) { _sum += tuple.getInteger(1); } @Override public void finishBatch() { Value val = DATABASE.get(GLOBAL_COUNT_KEY); Value newval; if (val == null || !val.txid.equals(_attempt.getTransactionId())) { newval = new Value(); newval.txid = _attempt.getTransactionId(); if (val == null) { newval.count = _sum; } else { newval.count = _sum + val.count; } DATABASE.put(GLOBAL_COUNT_KEY, newval); } else { newval = val; } _collector.emit(new Values(_attempt, newval.count)); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "sum")); } } public static void main(String[] args) throws Exception { MemoryTransactionalSpout spout = new MemoryTransactionalSpout(DATA, new Fields("word"), PARTITION_TAKE_PER_BATCH); TransactionalTopologyBuilder builder = new TransactionalTopologyBuilder("global-count", "spout", spout, 3); builder.setBolt("partial-count", new BatchCount(), 5).noneGrouping("spout"); builder.setBolt("sum", new UpdateGlobalCount()).globalGrouping("partial-count"); LocalCluster cluster = new LocalCluster(); Config config = new Config(); config.setDebug(true); config.setMaxSpoutPending(3); cluster.submitTopology("global-count-topology", config, builder.buildTopology()); Thread.sleep(3000); cluster.shutdown(); } } storm-starter-master\src\jvm\storm\starter下的TransactionalWords.java package storm.starter; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.coordination.BatchOutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.testing.MemoryTransactionalSpout; import backtype.storm.topology.BasicOutputCollector; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseBasicBolt; import backtype.storm.topology.base.BaseTransactionalBolt; import backtype.storm.transactional.ICommitter; import backtype.storm.transactional.TransactionAttempt; import backtype.storm.transactional.TransactionalTopologyBuilder; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This class defines a more involved transactional topology then TransactionalGlobalCount. This topology processes a * stream of words and produces two outputs: * <p/> * 1. A count for each word (stored in a database) 2. The number of words for every bucket of 10 counts. So it stores in * the database how many words have appeared 0-9 times, how many have appeared 10-19 times, and so on. * <p/> * A batch of words can cause the bucket counts to decrement for some buckets and increment for others as words move * between buckets as their counts accumulate. */ public class TransactionalWords { public static class CountValue { Integer prev_count = null; int count = 0; BigInteger txid = null; } public static class BucketValue { int count = 0; BigInteger txid; } public static final int BUCKET_SIZE = 10; public static Map<String, CountValue> COUNT_DATABASE = new HashMap<String, CountValue>(); public static Map<Integer, BucketValue> BUCKET_DATABASE = new HashMap<Integer, BucketValue>(); public static final int PARTITION_TAKE_PER_BATCH = 3; public static final Map<Integer, List<List<Object>>> DATA = new HashMap<Integer, List<List<Object>>>() {{ put(0, new ArrayList<List<Object>>() {{ add(new Values("cat")); add(new Values("dog")); add(new Values("chicken")); add(new Values("cat")); add(new Values("dog")); add(new Values("apple")); }}); put(1, new ArrayList<List<Object>>() {{ add(new Values("cat")); add(new Values("dog")); add(new Values("apple")); add(new Values("banana")); }}); put(2, new ArrayList<List<Object>>() {{ add(new Values("cat")); add(new Values("cat")); add(new Values("cat")); add(new Values("cat")); add(new Values("cat")); add(new Values("dog")); add(new Values("dog")); add(new Values("dog")); add(new Values("dog")); }}); }}; public static class KeyedCountUpdater extends BaseTransactionalBolt implements ICommitter { Map<String, Integer> _counts = new HashMap<String, Integer>(); BatchOutputCollector _collector; TransactionAttempt _id; int _count = 0; @Override public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, TransactionAttempt id) { _collector = collector; _id = id; } @Override public void execute(Tuple tuple) { String key = tuple.getString(1); Integer curr = _counts.get(key); if (curr == null) curr = 0; _counts.put(key, curr + 1); } @Override public void finishBatch() { for (String key : _counts.keySet()) { CountValue val = COUNT_DATABASE.get(key); CountValue newVal; if (val == null || !val.txid.equals(_id)) { newVal = new CountValue(); newVal.txid = _id.getTransactionId(); if (val != null) { newVal.prev_count = val.count; newVal.count = val.count; } newVal.count = newVal.count + _counts.get(key); COUNT_DATABASE.put(key, newVal); } else { newVal = val; } _collector.emit(new Values(_id, key, newVal.count, newVal.prev_count)); } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "key", "count", "prev-count")); } } public static class Bucketize extends BaseBasicBolt { @Override public void execute(Tuple tuple, BasicOutputCollector collector) { TransactionAttempt attempt = (TransactionAttempt) tuple.getValue(0); int curr = tuple.getInteger(2); Integer prev = tuple.getInteger(3); int currBucket = curr / BUCKET_SIZE; Integer prevBucket = null; if (prev != null) { prevBucket = prev / BUCKET_SIZE; } if (prevBucket == null) { collector.emit(new Values(attempt, currBucket, 1)); } else if (currBucket != prevBucket) { collector.emit(new Values(attempt, currBucket, 1)); collector.emit(new Values(attempt, prevBucket, -1)); } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("attempt", "bucket", "delta")); } } public static class BucketCountUpdater extends BaseTransactionalBolt { Map<Integer, Integer> _accum = new HashMap<Integer, Integer>(); BatchOutputCollector _collector; TransactionAttempt _attempt; int _count = 0; @Override public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, TransactionAttempt attempt) { _collector = collector; _attempt = attempt; } @Override public void execute(Tuple tuple) { Integer bucket = tuple.getInteger(1); Integer delta = tuple.getInteger(2); Integer curr = _accum.get(bucket); if (curr == null) curr = 0; _accum.put(bucket, curr + delta); } @Override public void finishBatch() { for (Integer bucket : _accum.keySet()) { BucketValue currVal = BUCKET_DATABASE.get(bucket); BucketValue newVal; if (currVal == null || !currVal.txid.equals(_attempt.getTransactionId())) { newVal = new BucketValue(); newVal.txid = _attempt.getTransactionId(); newVal.count = _accum.get(bucket); if (currVal != null) newVal.count += currVal.count; BUCKET_DATABASE.put(bucket, newVal); } else { newVal = currVal; } _collector.emit(new Values(_attempt, bucket, newVal.count)); } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("id", "bucket", "count")); } } public static void main(String[] args) throws Exception { MemoryTransactionalSpout spout = new MemoryTransactionalSpout(DATA, new Fields("word"), PARTITION_TAKE_PER_BATCH); TransactionalTopologyBuilder builder = new TransactionalTopologyBuilder("top-n-words", "spout", spout, 2); builder.setBolt("count", new KeyedCountUpdater(), 5).fieldsGrouping("spout", new Fields("word")); builder.setBolt("bucketize", new Bucketize()).noneGrouping("count"); builder.setBolt("buckets", new BucketCountUpdater(), 5).fieldsGrouping("bucketize", new Fields("bucket")); LocalCluster cluster = new LocalCluster(); Config config = new Config(); config.setDebug(true); config.setMaxSpoutPending(3); cluster.submitTopology("top-n-topology", config, builder.buildTopology()); Thread.sleep(3000); cluster.shutdown(); } } storm-starter-master\src\jvm\storm\starter下的WordCountTopology.java package storm.starter; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.StormSubmitter; import backtype.storm.task.ShellBolt; import backtype.storm.topology.BasicOutputCollector; import backtype.storm.topology.IRichBolt; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.TopologyBuilder; import backtype.storm.topology.base.BaseBasicBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import storm.starter.spout.RandomSentenceSpout; import java.util.HashMap; import java.util.Map; /** * This topology demonstrates Storm's stream groupings and multilang capabilities. */ public class WordCountTopology { public static class SplitSentence extends ShellBolt implements IRichBolt { public SplitSentence() { super("python", "splitsentence.py"); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word")); } @Override public Map<String, Object> getComponentConfiguration() { return null; } } public static class WordCount extends BaseBasicBolt { Map<String, Integer> counts = new HashMap<String, Integer>(); @Override public void execute(Tuple tuple, BasicOutputCollector collector) { String word = tuple.getString(0); Integer count = counts.get(word); if (count == null) count = 0; count++; counts.put(word, count); collector.emit(new Values(word, count)); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word", "count")); } } public static void main(String[] args) throws Exception { TopologyBuilder builder = new TopologyBuilder(); builder.setSpout("spout", new RandomSentenceSpout(), 5); builder.setBolt("split", new SplitSentence(), 8).shuffleGrouping("spout"); builder.setBolt("count", new WordCount(), 12).fieldsGrouping("split", new Fields("word")); Config conf = new Config(); conf.setDebug(true); if (args != null && args.length > 0) { conf.setNumWorkers(3); StormSubmitter.submitTopology(args[0], conf, builder.createTopology()); } else { conf.setMaxTaskParallelism(3); LocalCluster cluster = new LocalCluster(); cluster.submitTopology("word-count", conf, builder.createTopology()); Thread.sleep(10000); cluster.shutdown(); } } } storm-starter-master\src\jvm\storm\starter\spout的RandomSentenceSpout.java package storm.starter.spout; import backtype.storm.spout.SpoutOutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseRichSpout; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Values; import backtype.storm.utils.Utils; import java.util.Map; import java.util.Random; public class RandomSentenceSpout extends BaseRichSpout { SpoutOutputCollector _collector; Random _rand; @Override public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) { _collector = collector; _rand = new Random(); } @Override public void nextTuple() { Utils.sleep(100); String[] sentences = new String[]{ "the cow jumped over the moon", "an apple a day keeps the doctor away", "four score and seven years ago", "snow white and the seven dwarfs", "i am at two with nature" }; String sentence = sentences[_rand.nextInt(sentences.length)]; _collector.emit(new Values(sentence)); } @Override public void ack(Object id) { } @Override public void fail(Object id) { } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word")); } } storm-starter-master\src\jvm\storm\starter\spout的TwitterSampleSpout.java /* package storm.starter.spout; import backtype.storm.Config; import twitter4j.conf.ConfigurationBuilder; import twitter4j.TwitterStream; import twitter4j.TwitterStreamFactory; import backtype.storm.spout.SpoutOutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseRichSpout; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Values; import backtype.storm.utils.Utils; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import twitter4j.Status; import twitter4j.StatusDeletionNotice; import twitter4j.StatusListener; public class TwitterSampleSpout extends BaseRichSpout { SpoutOutputCollector _collector; LinkedBlockingQueue<Status> queue = null; TwitterStream _twitterStream; String _username; String _pwd; public TwitterSampleSpout(String username, String pwd) { _username = username; _pwd = pwd; } @Override public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) { queue = new LinkedBlockingQueue<Status>(1000); _collector = collector; StatusListener listener = new StatusListener() { @Override public void onStatus(Status status) { queue.offer(status); } @Override public void onDeletionNotice(StatusDeletionNotice sdn) { } @Override public void onTrackLimitationNotice(int i) { } @Override public void onScrubGeo(long l, long l1) { } @Override public void onException(Exception e) { } }; TwitterStreamFactory fact = new TwitterStreamFactory(new ConfigurationBuilder().setUser(_username).setPassword(_pwd).build()); _twitterStream = fact.getInstance(); _twitterStream.addListener(listener); _twitterStream.sample(); } @Override public void nextTuple() { Status ret = queue.poll(); if(ret==null) { Utils.sleep(50); } else { _collector.emit(new Values(ret)); } } @Override public void close() { _twitterStream.shutdown(); } @Override public Map<String, Object> getComponentConfiguration() { Config ret = new Config(); ret.setMaxTaskParallelism(1); return ret; } @Override public void ack(Object id) { } @Override public void fail(Object id) { } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("tweet")); } } */ storm-starter-master\src\jvm\storm\starter\bolt的AbstractRankerBolt.java package storm.starter.bolt; import backtype.storm.Config; import backtype.storm.topology.BasicOutputCollector; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseBasicBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import org.apache.log4j.Logger; import storm.starter.tools.Rankings; import storm.starter.util.TupleHelpers; import java.util.HashMap; import java.util.Map; /** * This abstract bolt provides the basic behavior of bolts that rank objects according to their count. * <p/> * It uses a template method design pattern for {@link AbstractRankerBolt#execute(Tuple, BasicOutputCollector)} to allow * actual bolt implementations to specify how incoming tuples are processed, i.e. how the objects embedded within those * tuples are retrieved and counted. */ public abstract class AbstractRankerBolt extends BaseBasicBolt { private static final long serialVersionUID = 4931640198501530202L; private static final int DEFAULT_EMIT_FREQUENCY_IN_SECONDS = 2; private static final int DEFAULT_COUNT = 10; private final int emitFrequencyInSeconds; private final int count; private final Rankings rankings; public AbstractRankerBolt() { this(DEFAULT_COUNT, DEFAULT_EMIT_FREQUENCY_IN_SECONDS); } public AbstractRankerBolt(int topN) { this(topN, DEFAULT_EMIT_FREQUENCY_IN_SECONDS); } public AbstractRankerBolt(int topN, int emitFrequencyInSeconds) { if (topN < 1) { throw new IllegalArgumentException("topN must be >= 1 (you requested " + topN + ")"); } if (emitFrequencyInSeconds < 1) { throw new IllegalArgumentException( "The emit frequency must be >= 1 seconds (you requested " + emitFrequencyInSeconds + " seconds)"); } count = topN; this.emitFrequencyInSeconds = emitFrequencyInSeconds; rankings = new Rankings(count); } protected Rankings getRankings() { return rankings; } /** * This method functions as a template method (design pattern). */ @Override public final void execute(Tuple tuple, BasicOutputCollector collector) { if (TupleHelpers.isTickTuple(tuple)) { getLogger().debug("Received tick tuple, triggering emit of current rankings"); emitRankings(collector); } else { updateRankingsWithTuple(tuple); } } abstract void updateRankingsWithTuple(Tuple tuple); private void emitRankings(BasicOutputCollector collector) { collector.emit(new Values(rankings.copy())); getLogger().debug("Rankings: " + rankings); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("rankings")); } @Override public Map<String, Object> getComponentConfiguration() { Map<String, Object> conf = new HashMap<String, Object>(); conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, emitFrequencyInSeconds); return conf; } abstract Logger getLogger(); } storm-starter-master\src\jvm\storm\starter\bolt的IntermediateRankingsBolt.java package storm.starter.bolt; import backtype.storm.tuple.Tuple; import org.apache.log4j.Logger; import storm.starter.tools.Rankable; import storm.starter.tools.RankableObjectWithFields; /** * This bolt ranks incoming objects by their count. * <p/> * It assumes the input tuples to adhere to the following format: (object, object_count, additionalField1, * additionalField2, ..., additionalFieldN). */ public final class IntermediateRankingsBolt extends AbstractRankerBolt { private static final long serialVersionUID = -1369800530256637409L; private static final Logger LOG = Logger.getLogger(IntermediateRankingsBolt.class); public IntermediateRankingsBolt() { super(); } public IntermediateRankingsBolt(int topN) { super(topN); } public IntermediateRankingsBolt(int topN, int emitFrequencyInSeconds) { super(topN, emitFrequencyInSeconds); } @Override void updateRankingsWithTuple(Tuple tuple) { Rankable rankable = RankableObjectWithFields.from(tuple); super.getRankings().updateWith(rankable); } @Override Logger getLogger() { return LOG; } } storm-starter-master\src\jvm\storm\starter\bolt的PrinterBolt.java package storm.starter.bolt; import backtype.storm.topology.BasicOutputCollector; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseBasicBolt; import backtype.storm.tuple.Tuple; public class PrinterBolt extends BaseBasicBolt { @Override public void execute(Tuple tuple, BasicOutputCollector collector) { System.out.println(tuple); } @Override public void declareOutputFields(OutputFieldsDeclarer ofd) { } } storm-starter-master\src\jvm\storm\starter\bolt的RollingCountBolt.java package storm.starter.bolt; import backtype.storm.Config; import backtype.storm.task.OutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseRichBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import org.apache.log4j.Logger; import storm.starter.tools.NthLastModifiedTimeTracker; import storm.starter.tools.SlidingWindowCounter; import storm.starter.util.TupleHelpers; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; /** * This bolt performs rolling counts of incoming objects, i.e. sliding window based counting. * <p/> * The bolt is configured by two parameters, the length of the sliding window in seconds (which influences the output * data of the bolt, i.e. how it will count objects) and the emit frequency in seconds (which influences how often the * bolt will output the latest window counts). For instance, if the window length is set to an equivalent of five * minutes and the emit frequency to one minute, then the bolt will output the latest five-minute sliding window every * minute. * <p/> * The bolt emits a rolling count tuple per object, consisting of the object itself, its latest rolling count, and the * actual duration of the sliding window. The latter is included in case the expected sliding window length (as * configured by the user) is different from the actual length, e.g. due to high system load. Note that the actual * window length is tracked and calculated for the window, and not individually for each object within a window. * <p/> * Note: During the startup phase you will usually observe that the bolt warns you about the actual sliding window * length being smaller than the expected length. This behavior is expected and is caused by the way the sliding window * counts are initially "loaded up". You can safely ignore this warning during startup (e.g. you will see this warning * during the first ~ five minutes of startup time if the window length is set to five minutes). */ public class RollingCountBolt extends BaseRichBolt { private static final long serialVersionUID = 5537727428628598519L; private static final Logger LOG = Logger.getLogger(RollingCountBolt.class); private static final int NUM_WINDOW_CHUNKS = 5; private static final int DEFAULT_SLIDING_WINDOW_IN_SECONDS = NUM_WINDOW_CHUNKS * 60; private static final int DEFAULT_EMIT_FREQUENCY_IN_SECONDS = DEFAULT_SLIDING_WINDOW_IN_SECONDS / NUM_WINDOW_CHUNKS; private static final String WINDOW_LENGTH_WARNING_TEMPLATE = "Actual window length is %d seconds when it should be %d seconds" + " (you can safely ignore this warning during the startup phase)"; private final SlidingWindowCounter<Object> counter; private final int windowLengthInSeconds; private final int emitFrequencyInSeconds; private OutputCollector collector; private NthLastModifiedTimeTracker lastModifiedTracker; public RollingCountBolt() { this(DEFAULT_SLIDING_WINDOW_IN_SECONDS, DEFAULT_EMIT_FREQUENCY_IN_SECONDS); } public RollingCountBolt(int windowLengthInSeconds, int emitFrequencyInSeconds) { this.windowLengthInSeconds = windowLengthInSeconds; this.emitFrequencyInSeconds = emitFrequencyInSeconds; counter = new SlidingWindowCounter<Object>(deriveNumWindowChunksFrom(this.windowLengthInSeconds, this.emitFrequencyInSeconds)); } private int deriveNumWindowChunksFrom(int windowLengthInSeconds, int windowUpdateFrequencyInSeconds) { return windowLengthInSeconds / windowUpdateFrequencyInSeconds; } @SuppressWarnings("rawtypes") @Override public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { this.collector = collector; lastModifiedTracker = new NthLastModifiedTimeTracker(deriveNumWindowChunksFrom(this.windowLengthInSeconds, this.emitFrequencyInSeconds)); } @Override public void execute(Tuple tuple) { if (TupleHelpers.isTickTuple(tuple)) { LOG.debug("Received tick tuple, triggering emit of current window counts"); emitCurrentWindowCounts(); } else { countObjAndAck(tuple); } } private void emitCurrentWindowCounts() { Map<Object, Long> counts = counter.getCountsThenAdvanceWindow(); int actualWindowLengthInSeconds = lastModifiedTracker.secondsSinceOldestModification(); lastModifiedTracker.markAsModified(); if (actualWindowLengthInSeconds != windowLengthInSeconds) { LOG.warn(String.format(WINDOW_LENGTH_WARNING_TEMPLATE, actualWindowLengthInSeconds, windowLengthInSeconds)); } emit(counts, actualWindowLengthInSeconds); } private void emit(Map<Object, Long> counts, int actualWindowLengthInSeconds) { for (Entry<Object, Long> entry : counts.entrySet()) { Object obj = entry.getKey(); Long count = entry.getValue(); collector.emit(new Values(obj, count, actualWindowLengthInSeconds)); } } private void countObjAndAck(Tuple tuple) { Object obj = tuple.getValue(0); counter.incrementCount(obj); collector.ack(tuple); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("obj", "count", "actualWindowLengthInSeconds")); } @Override public Map<String, Object> getComponentConfiguration() { Map<String, Object> conf = new HashMap<String, Object>(); conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, emitFrequencyInSeconds); return conf; } } storm-starter-master\src\jvm\storm\starter\bolt的SingleJoinBolt.java package storm.starter.bolt; import backtype.storm.Config; import backtype.storm.generated.GlobalStreamId; import backtype.storm.task.OutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.OutputFieldsDeclarer; import backtype.storm.topology.base.BaseRichBolt; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.utils.TimeCacheMap; import java.util.*; public class SingleJoinBolt extends BaseRichBolt { OutputCollector _collector; Fields _idFields; Fields _outFields; int _numSources; TimeCacheMap<List<Object>, Map<GlobalStreamId, Tuple>> _pending; Map<String, GlobalStreamId> _fieldLocations; public SingleJoinBolt(Fields outFields) { _outFields = outFields; } @Override public void prepare(Map conf, TopologyContext context, OutputCollector collector) { _fieldLocations = new HashMap<String, GlobalStreamId>(); _collector = collector; int timeout = ((Number) conf.get(Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS)).intValue(); _pending = new TimeCacheMap<List<Object>, Map<GlobalStreamId, Tuple>>(timeout, new ExpireCallback()); _numSources = context.getThisSources().size(); Set<String> idFields = null; for (GlobalStreamId source : context.getThisSources().keySet()) { Fields fields = context.getComponentOutputFields(source.get_componentId(), source.get_streamId()); Set<String> setFields = new HashSet<String>(fields.toList()); if (idFields == null) idFields = setFields; else idFields.retainAll(setFields); for (String outfield : _outFields) { for (String sourcefield : fields) { if (outfield.equals(sourcefield)) { _fieldLocations.put(outfield, source); } } } } _idFields = new Fields(new ArrayList<String>(idFields)); if (_fieldLocations.size() != _outFields.size()) { throw new RuntimeException("Cannot find all outfields among sources"); } } @Override public void execute(Tuple tuple) { List<Object> id = tuple.select(_idFields); GlobalStreamId streamId = new GlobalStreamId(tuple.getSourceComponent(), tuple.getSourceStreamId()); if (!_pending.containsKey(id)) { _pending.put(id, new HashMap<GlobalStreamId, Tuple>()); } Map<GlobalStreamId, Tuple> parts = _pending.get(id); if (parts.containsKey(streamId)) throw new RuntimeException("Received same side of single join twice"); parts.put(streamId, tuple); if (parts.size() == _numSources) { _pending.remove(id); List<Object> joinResult = new ArrayList<Object>(); for (String outField : _outFields) { GlobalStreamId loc = _fieldLocations.get(outField); joinResult.add(parts.get(loc).getValueByField(outField)); } _collector.emit(new ArrayList<Tuple>(parts.values()), joinResult); for (Tuple part : parts.values()) { _collector.ack(part); } } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(_outFields); } private class ExpireCallback implements TimeCacheMap.ExpiredCallback<List<Object>, Map<GlobalStreamId, Tuple>> { @Override public void expire(List<Object> id, Map<GlobalStreamId, Tuple> tuples) { for (Tuple tuple : tuples.values()) { _collector.fail(tuple); } } } } storm-starter-master\src\jvm\storm\starter\bolt的TotalRankingsBolt.java package storm.starter.bolt; import backtype.storm.tuple.Tuple; import org.apache.log4j.Logger; import storm.starter.tools.Rankings; /** * This bolt merges incoming {@link Rankings}. * <p/> * It can be used to merge intermediate rankings generated by {@link IntermediateRankingsBolt} into a final, * consolidated ranking. To do so, configure this bolt with a globalGrouping on {@link IntermediateRankingsBolt}. */ public final class TotalRankingsBolt extends AbstractRankerBolt { private static final long serialVersionUID = -8447525895532302198L; private static final Logger LOG = Logger.getLogger(TotalRankingsBolt.class); public TotalRankingsBolt() { super(); } public TotalRankingsBolt(int topN) { super(topN); } public TotalRankingsBolt(int topN, int emitFrequencyInSeconds) { super(topN, emitFrequencyInSeconds); } @Override void updateRankingsWithTuple(Tuple tuple) { Rankings rankingsToBeMerged = (Rankings) tuple.getValue(0); super.getRankings().updateWith(rankingsToBeMerged); super.getRankings().pruneZeroCounts(); } @Override Logger getLogger() { return LOG; } } storm-starter-master\src\jvm\storm\starter\tools的NthLastModifiedTimeTracker.java package storm.starter.tools; import backtype.storm.utils.Time; import org.apache.commons.collections.buffer.CircularFifoBuffer; /** * This class tracks the time-since-last-modify of a "thing" in a rolling fashion. * <p/> * For example, create a 5-slot tracker to track the five most recent time-since-last-modify. * <p/> * You must manually "mark" that the "something" that you want to track -- in terms of modification times -- has just * been modified. */ public class NthLastModifiedTimeTracker { private static final int MILLIS_IN_SEC = 1000; private final CircularFifoBuffer lastModifiedTimesMillis; public NthLastModifiedTimeTracker(int numTimesToTrack) { if (numTimesToTrack < 1) { throw new IllegalArgumentException( "numTimesToTrack must be greater than zero (you requested " + numTimesToTrack + ")"); } lastModifiedTimesMillis = new CircularFifoBuffer(numTimesToTrack); initLastModifiedTimesMillis(); } private void initLastModifiedTimesMillis() { long nowCached = now(); for (int i = 0; i < lastModifiedTimesMillis.maxSize(); i++) { lastModifiedTimesMillis.add(Long.valueOf(nowCached)); } } private long now() { return Time.currentTimeMillis(); } public int secondsSinceOldestModification() { long modifiedTimeMillis = ((Long) lastModifiedTimesMillis.get()).longValue(); return (int) ((now() - modifiedTimeMillis) / MILLIS_IN_SEC); } public void markAsModified() { updateLastModifiedTime(); } private void updateLastModifiedTime() { lastModifiedTimesMillis.add(now()); } } storm-starter-master\src\jvm\storm\starter\tools的Rankable.java package storm.starter.tools; public interface Rankable extends Comparable<Rankable> { Object getObject(); long getCount(); /** * Note: We do not defensively copy the object wrapped by the Rankable. It is passed as is. * * @return a defensive copy */ Rankable copy(); } storm-starter-master\src\jvm\storm\starter\tools的RankableObjectWithFields.java package storm.starter.tools; import backtype.storm.tuple.Tuple; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.io.Serializable; import java.util.List; /** * This class wraps an objects and its associated count, including any additional data fields. * <p/> * This class can be used, for instance, to track the number of occurrences of an object in a Storm topology. */ public class RankableObjectWithFields implements Rankable, Serializable { private static final long serialVersionUID = -9102878650001058090L; private static final String toStringSeparator = "|"; private final Object obj; private final long count; private final ImmutableList<Object> fields; public RankableObjectWithFields(Object obj, long count, Object... otherFields) { if (obj == null) { throw new IllegalArgumentException("The object must not be null"); } if (count < 0) { throw new IllegalArgumentException("The count must be >= 0"); } this.obj = obj; this.count = count; fields = ImmutableList.copyOf(otherFields); } /** * Construct a new instance based on the provided {@link Tuple}. * <p/> * This method expects the object to be ranked in the first field (index 0) of the provided tuple, and the number of * occurrences of the object (its count) in the second field (index 1). Any further fields in the tuple will be * extracted and tracked, too. These fields can be accessed via {@link RankableObjectWithFields#getFields()}. * * @param tuple * * @return new instance based on the provided tuple */ public static RankableObjectWithFields from(Tuple tuple) { List<Object> otherFields = Lists.newArrayList(tuple.getValues()); Object obj = otherFields.remove(0); Long count = (Long) otherFields.remove(0); return new RankableObjectWithFields(obj, count, otherFields.toArray()); } public Object getObject() { return obj; } public long getCount() { return count; } /** * @return an immutable list of any additional data fields of the object (may be empty but will never be null) */ public List<Object> getFields() { return fields; } @Override public int compareTo(Rankable other) { long delta = this.getCount() - other.getCount(); if (delta > 0) { return 1; } else if (delta < 0) { return -1; } else { return 0; } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof RankableObjectWithFields)) { return false; } RankableObjectWithFields other = (RankableObjectWithFields) o; return obj.equals(other.obj) && count == other.count; } @Override public int hashCode() { int result = 17; int countHash = (int) (count ^ (count >>> 32)); result = 31 * result + countHash; result = 31 * result + obj.hashCode(); return result; } public String toString() { StringBuffer buf = new StringBuffer(); buf.append("["); buf.append(obj); buf.append(toStringSeparator); buf.append(count); for (Object field : fields) { buf.append(toStringSeparator); buf.append(field); } buf.append("]"); return buf.toString(); } /** * Note: We do not defensively copy the wrapped object and any accompanying fields. We do guarantee, however, * do return a defensive (shallow) copy of the List object that is wrapping any accompanying fields. * * @return */ @Override public Rankable copy() { List<Object> shallowCopyOfFields = ImmutableList.copyOf(getFields()); return new RankableObjectWithFields(getObject(), getCount(), shallowCopyOfFields); } } storm-starter-master\src\jvm\storm\starter\tools的Rankings.java package storm.starter.tools; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.io.Serializable; import java.util.Collections; import java.util.List; public class Rankings implements Serializable { private static final long serialVersionUID = -1549827195410578903L; private static final int DEFAULT_COUNT = 10; private final int maxSize; private final List<Rankable> rankedItems = Lists.newArrayList(); public Rankings() { this(DEFAULT_COUNT); } public Rankings(int topN) { if (topN < 1) { throw new IllegalArgumentException("topN must be >= 1"); } maxSize = topN; } /** * Copy constructor. * @param other */ public Rankings(Rankings other) { this(other.maxSize()); updateWith(other); } /** * @return the maximum possible number (size) of ranked objects this instance can hold */ public int maxSize() { return maxSize; } /** * @return the number (size) of ranked objects this instance is currently holding */ public int size() { return rankedItems.size(); } /** * The returned defensive copy is only "somewhat" defensive. We do, for instance, return a defensive copy of the * enclosing List instance, and we do try to defensively copy any contained Rankable objects, too. However, the * contract of {@link storm.starter.tools.Rankable#copy()} does not guarantee that any Object's embedded within * a Rankable will be defensively copied, too. * * @return a somewhat defensive copy of ranked items */ public List<Rankable> getRankings() { List<Rankable> copy = Lists.newLinkedList(); for (Rankable r: rankedItems) { copy.add(r.copy()); } return ImmutableList.copyOf(copy); } public void updateWith(Rankings other) { for (Rankable r : other.getRankings()) { updateWith(r); } } public void updateWith(Rankable r) { synchronized(rankedItems) { addOrReplace(r); rerank(); shrinkRankingsIfNeeded(); } } private void addOrReplace(Rankable r) { Integer rank = findRankOf(r); if (rank != null) { rankedItems.set(rank, r); } else { rankedItems.add(r); } } private Integer findRankOf(Rankable r) { Object tag = r.getObject(); for (int rank = 0; rank < rankedItems.size(); rank++) { Object cur = rankedItems.get(rank).getObject(); if (cur.equals(tag)) { return rank; } } return null; } private void rerank() { Collections.sort(rankedItems); Collections.reverse(rankedItems); } private void shrinkRankingsIfNeeded() { if (rankedItems.size() > maxSize) { rankedItems.remove(maxSize); } } /** * Removes ranking entries that have a count of zero. */ public void pruneZeroCounts() { int i = 0; while (i < rankedItems.size()) { if (rankedItems.get(i).getCount() == 0) { rankedItems.remove(i); } else { i++; } } } public String toString() { return rankedItems.toString(); } /** * Creates a (defensive) copy of itself. */ public Rankings copy() { return new Rankings(this); } } storm-starter-master\src\jvm\storm\starter\tools的SlidingWindowCounter.java package storm.starter.tools; import java.io.Serializable; import java.util.Map; /** * This class counts objects in a sliding window fashion. * <p/> * It is designed 1) to give multiple "producer" threads write access to the counter, i.e. being able to increment * counts of objects, and 2) to give a single "consumer" thread (e.g. {@link PeriodicSlidingWindowCounter}) read access * to the counter. Whenever the consumer thread performs a read operation, this class will advance the head slot of the * sliding window counter. This means that the consumer thread indirectly controls where writes of the producer threads * will go to. Also, by itself this class will not advance the head slot. * <p/> * A note for analyzing data based on a sliding window count: During the initial <code>windowLengthInSlots</code> * iterations, this sliding window counter will always return object counts that are equal or greater than in the * previous iteration. This is the effect of the counter "loading up" at the very start of its existence. Conceptually, * this is the desired behavior. * <p/> * To give an example, using a counter with 5 slots which for the sake of this example represent 1 minute of time each: * <p/> * <pre> * {@code * Sliding window counts of an object X over time * * Minute (timeline): * 1 2 3 4 5 6 7 8 * * Observed counts per minute: * 1 1 1 1 0 0 0 0 * * Counts returned by counter: * 1 2 3 4 4 3 2 1 * } * </pre> * <p/> * As you can see in this example, for the first <code>windowLengthInSlots</code> (here: the first five minutes) the * counter will always return counts equal or greater than in the previous iteration (1, 2, 3, 4, 4). This initial load * effect needs to be accounted for whenever you want to perform analyses such as trending topics; otherwise your * analysis algorithm might falsely identify the object to be trending as the counter seems to observe continuously * increasing counts. Also, note that during the initial load phase <em>every object</em> will exhibit increasing * counts. * <p/> * On a high-level, the counter exhibits the following behavior: If you asked the example counter after two minutes, * "how often did you count the object during the past five minutes?", then it should reply "I have counted it 2 times * in the past five minutes", implying that it can only account for the last two of those five minutes because the * counter was not running before that time. * * @param <T> The type of those objects we want to count. */ public final class SlidingWindowCounter<T> implements Serializable { private static final long serialVersionUID = -2645063988768785810L; private SlotBasedCounter<T> objCounter; private int headSlot; private int tailSlot; private int windowLengthInSlots; public SlidingWindowCounter(int windowLengthInSlots) { if (windowLengthInSlots < 2) { throw new IllegalArgumentException( "Window length in slots must be at least two (you requested " + windowLengthInSlots + ")"); } this.windowLengthInSlots = windowLengthInSlots; this.objCounter = new SlotBasedCounter<T>(this.windowLengthInSlots); this.headSlot = 0; this.tailSlot = slotAfter(headSlot); } public void incrementCount(T obj) { objCounter.incrementCount(obj, headSlot); } /** * Return the current (total) counts of all tracked objects, then advance the window. * <p/> * Whenever this method is called, we consider the counts of the current sliding window to be available to and * successfully processed "upstream" (i.e. by the caller). Knowing this we will start counting any subsequent * objects within the next "chunk" of the sliding window. * * @return The current (total) counts of all tracked objects. */ public Map<T, Long> getCountsThenAdvanceWindow() { Map<T, Long> counts = objCounter.getCounts(); objCounter.wipeZeros(); objCounter.wipeSlot(tailSlot); advanceHead(); return counts; } private void advanceHead() { headSlot = tailSlot; tailSlot = slotAfter(tailSlot); } private int slotAfter(int slot) { return (slot + 1) % windowLengthInSlots; } } storm-starter-master\src\jvm\storm\starter\tools的SlotBasedCounter.java package storm.starter.tools; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * This class provides per-slot counts of the occurrences of objects. * <p/> * It can be used, for instance, as a building block for implementing sliding window counting of objects. * * @param <T> The type of those objects we want to count. */ public final class SlotBasedCounter<T> implements Serializable { private static final long serialVersionUID = 4858185737378394432L; private final Map<T, long[]> objToCounts = new HashMap<T, long[]>(); private final int numSlots; public SlotBasedCounter(int numSlots) { if (numSlots <= 0) { throw new IllegalArgumentException("Number of slots must be greater than zero (you requested " + numSlots + ")"); } this.numSlots = numSlots; } public void incrementCount(T obj, int slot) { long[] counts = objToCounts.get(obj); if (counts == null) { counts = new long[this.numSlots]; objToCounts.put(obj, counts); } counts[slot]++; } public long getCount(T obj, int slot) { long[] counts = objToCounts.get(obj); if (counts == null) { return 0; } else { return counts[slot]; } } public Map<T, Long> getCounts() { Map<T, Long> result = new HashMap<T, Long>(); for (T obj : objToCounts.keySet()) { result.put(obj, computeTotalCount(obj)); } return result; } private long computeTotalCount(T obj) { long[] curr = objToCounts.get(obj); long total = 0; for (long l : curr) { total += l; } return total; } /** * Reset the slot count of any tracked objects to zero for the given slot. * * @param slot */ public void wipeSlot(int slot) { for (T obj : objToCounts.keySet()) { resetSlotCountToZero(obj, slot); } } private void resetSlotCountToZero(T obj, int slot) { long[] counts = objToCounts.get(obj); counts[slot] = 0; } private boolean shouldBeRemovedFromCounter(T obj) { return computeTotalCount(obj) == 0; } /** * Remove any object from the counter whose total count is zero (to free up memory). */ public void wipeZeros() { Set<T> objToBeRemoved = new HashSet<T>(); for (T obj : objToCounts.keySet()) { if (shouldBeRemovedFromCounter(obj)) { objToBeRemoved.add(obj); } } for (T obj : objToBeRemoved) { objToCounts.remove(obj); } } } storm-starter-master\src\jvm\storm\starter\trident的TridentReach.java package storm.starter.trident; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.LocalDRPC; import backtype.storm.generated.StormTopology; import backtype.storm.task.IMetricsContext; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Values; import storm.trident.TridentState; import storm.trident.TridentTopology; import storm.trident.operation.BaseFunction; import storm.trident.operation.CombinerAggregator; import storm.trident.operation.TridentCollector; import storm.trident.operation.builtin.MapGet; import storm.trident.operation.builtin.Sum; import storm.trident.state.ReadOnlyState; import storm.trident.state.State; import storm.trident.state.StateFactory; import storm.trident.state.map.ReadOnlyMapState; import storm.trident.tuple.TridentTuple; import java.util.*; public class TridentReach { public static Map<String, List<String>> TWEETERS_DB = new HashMap<String, List<String>>() {{ put("foo.com/blog/1", Arrays.asList("sally", "bob", "tim", "george", "nathan")); put("engineering.twitter.com/blog/5", Arrays.asList("adam", "david", "sally", "nathan")); put("tech.backtype.com/blog/123", Arrays.asList("tim", "mike", "john")); }}; public static Map<String, List<String>> FOLLOWERS_DB = new HashMap<String, List<String>>() {{ put("sally", Arrays.asList("bob", "tim", "alice", "adam", "jim", "chris", "jai")); put("bob", Arrays.asList("sally", "nathan", "jim", "mary", "david", "vivian")); put("tim", Arrays.asList("alex")); put("nathan", Arrays.asList("sally", "bob", "adam", "harry", "chris", "vivian", "emily", "jordan")); put("adam", Arrays.asList("david", "carissa")); put("mike", Arrays.asList("john", "bob")); put("john", Arrays.asList("alice", "nathan", "jim", "mike", "bob")); }}; public static class StaticSingleKeyMapState extends ReadOnlyState implements ReadOnlyMapState<Object> { public static class Factory implements StateFactory { Map _map; public Factory(Map map) { _map = map; } @Override public State makeState(Map conf, IMetricsContext metrics, int partitionIndex, int numPartitions) { return new StaticSingleKeyMapState(_map); } } Map _map; public StaticSingleKeyMapState(Map map) { _map = map; } @Override public List<Object> multiGet(List<List<Object>> keys) { List<Object> ret = new ArrayList(); for (List<Object> key : keys) { Object singleKey = key.get(0); ret.add(_map.get(singleKey)); } return ret; } } public static class One implements CombinerAggregator<Integer> { @Override public Integer init(TridentTuple tuple) { return 1; } @Override public Integer combine(Integer val1, Integer val2) { return 1; } @Override public Integer zero() { return 1; } } public static class ExpandList extends BaseFunction { @Override public void execute(TridentTuple tuple, TridentCollector collector) { List l = (List) tuple.getValue(0); if (l != null) { for (Object o : l) { collector.emit(new Values(o)); } } } } public static StormTopology buildTopology(LocalDRPC drpc) { TridentTopology topology = new TridentTopology(); TridentState urlToTweeters = topology.newStaticState(new StaticSingleKeyMapState.Factory(TWEETERS_DB)); TridentState tweetersToFollowers = topology.newStaticState(new StaticSingleKeyMapState.Factory(FOLLOWERS_DB)); topology.newDRPCStream("reach", drpc).stateQuery(urlToTweeters, new Fields("args"), new MapGet(), new Fields( "tweeters")).each(new Fields("tweeters"), new ExpandList(), new Fields("tweeter")).shuffle().stateQuery( tweetersToFollowers, new Fields("tweeter"), new MapGet(), new Fields("followers")).each(new Fields("followers"), new ExpandList(), new Fields("follower")).groupBy(new Fields("follower")).aggregate(new One(), new Fields( "one")).aggregate(new Fields("one"), new Sum(), new Fields("reach")); return topology.build(); } public static void main(String[] args) throws Exception { LocalDRPC drpc = new LocalDRPC(); Config conf = new Config(); LocalCluster cluster = new LocalCluster(); cluster.submitTopology("reach", conf, buildTopology(drpc)); Thread.sleep(2000); System.out.println("REACH: " + drpc.execute("reach", "aaa")); System.out.println("REACH: " + drpc.execute("reach", "foo.com/blog/1")); System.out.println("REACH: " + drpc.execute("reach", "engineering.twitter.com/blog/5")); cluster.shutdown(); drpc.shutdown(); } } storm-starter-master\src\jvm\storm\starter\trident的TridentWordCount.java package storm.starter.trident; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.LocalDRPC; import backtype.storm.StormSubmitter; import backtype.storm.generated.StormTopology; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Values; import storm.trident.TridentState; import storm.trident.TridentTopology; import storm.trident.operation.BaseFunction; import storm.trident.operation.TridentCollector; import storm.trident.operation.builtin.Count; import storm.trident.operation.builtin.FilterNull; import storm.trident.operation.builtin.MapGet; import storm.trident.operation.builtin.Sum; import storm.trident.testing.FixedBatchSpout; import storm.trident.testing.MemoryMapState; import storm.trident.tuple.TridentTuple; public class TridentWordCount { public static class Split extends BaseFunction { @Override public void execute(TridentTuple tuple, TridentCollector collector) { String sentence = tuple.getString(0); for (String word : sentence.split(" ")) { collector.emit(new Values(word)); } } } public static StormTopology buildTopology(LocalDRPC drpc) { FixedBatchSpout spout = new FixedBatchSpout(new Fields("sentence"), 3, new Values("the cow jumped over the moon"), new Values("the man went to the store and bought some candy"), new Values("four score and seven years ago"), new Values("how many apples can you eat"), new Values("to be or not to be the person")); spout.setCycle(true); TridentTopology topology = new TridentTopology(); TridentState wordCounts = topology.newStream("spout1", spout).parallelismHint(16).each(new Fields("sentence"), new Split(), new Fields("word")).groupBy(new Fields("word")).persistentAggregate(new MemoryMapState.Factory(), new Count(), new Fields("count")).parallelismHint(16); topology.newDRPCStream("words", drpc).each(new Fields("args"), new Split(), new Fields("word")).groupBy(new Fields( "word")).stateQuery(wordCounts, new Fields("word"), new MapGet(), new Fields("count")).each(new Fields("count"), new FilterNull()).aggregate(new Fields("count"), new Sum(), new Fields("sum")); return topology.build(); } public static void main(String[] args) throws Exception { Config conf = new Config(); conf.setMaxSpoutPending(20); if (args.length == 0) { LocalDRPC drpc = new LocalDRPC(); LocalCluster cluster = new LocalCluster(); cluster.submitTopology("wordCounter", conf, buildTopology(drpc)); for (int i = 0; i < 100; i++) { System.out.println("DRPC RESULT: " + drpc.execute("words", "cat the dog jumped")); Thread.sleep(1000); } } else { conf.setNumWorkers(3); StormSubmitter.submitTopology(args[0], conf, buildTopology(null)); } } } storm-starter-master\src\jvm\storm\starter\util的StormRunner.java package storm.starter.util; import backtype.storm.Config; import backtype.storm.LocalCluster; import backtype.storm.generated.StormTopology; public final class StormRunner { private static final int MILLIS_IN_SEC = 1000; private StormRunner() { } public static void runTopologyLocally(StormTopology topology, String topologyName, Config conf, int runtimeInSeconds) throws InterruptedException { LocalCluster cluster = new LocalCluster(); cluster.submitTopology(topologyName, conf, topology); Thread.sleep((long) runtimeInSeconds * MILLIS_IN_SEC); cluster.killTopology(topologyName); cluster.shutdown(); } } storm-starter-master\src\jvm\storm\starter\util的TupleHelpers.java package storm.starter.util; import backtype.storm.Constants; import backtype.storm.tuple.Tuple; public final class TupleHelpers { private TupleHelpers() { } public static boolean isTickTuple(Tuple tuple) { return tuple.getSourceComponent().equals(Constants.SYSTEM_COMPONENT_ID) && tuple.getSourceStreamId().equals( Constants.SYSTEM_TICK_STREAM_ID); } } 本文转自大数据躺过的坑博客园博客,原文链接:http://www.cnblogs.com/zlslch/p/5989195.html,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册