Mysql增量写入Hdfs(二) --Storm+hdfs的流式处理
一. 概述
上一篇我们介绍了如何将数据从mysql抛到kafka,这次我们就专注于利用storm将数据写入到hdfs的过程,由于storm写入hdfs的可定制东西有些多,我们先不从kafka读取,而先自己定义一个Spout数据充当数据源,下章再进行整合。这里默认你是拥有一定的storm知识的基础,起码知道Spout和bolt是什么。
写入hdfs可以有以下的定制策略:
- 自定义写入文件的名字
 - 定义写入内容格式
 - 满足给定条件后更改写入的文件
 - 更改写入文件时触发的Action
 
本篇会先说明如何用storm写入HDFS,写入过程一些API的描述,以及最后给定一个例子:
storm每接收到10个Tuple后就会改变hdfs写入文件,新文件的名字就是第几次改变。
ps:storm版本:1.1.1。Hadoop版本:2.7.4。
接下来我们首先看看Storm如何写入HDFS。
二.Storm写入HDFS
Storm官方有提供了相应的API让我们可以使用。可以通过创建HdfsBolt以及定义相应的规则,即可写入HDFS 。
首先通过maven配置依赖以及插件。
    <properties>
        <storm.version>1.1.1</storm.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.storm</groupId>
            <artifactId>storm-core</artifactId>
            <version>${storm.version}</version>
            <!--<scope>provided</scope>-->
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>log4j-over-slf4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>15.0</version>
        </dependency>
        <!--hadoop模块-->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.7.4</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>2.7.4</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.storm/storm-hdfs -->
        <dependency>
            <groupId>org.apache.storm</groupId>
            <artifactId>storm-hdfs</artifactId>
            <version>1.1.1</version>
            <!--<scope>test</scope>-->
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <executable>java</executable>
                    <includeProjectDependencies>true</includeProjectDependencies>
                    <includePluginDependencies>false</includePluginDependencies>
                    <classpathScope>compile</classpathScope>
                    <mainClass>com.learningstorm.kafka.KafkaTopology</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>1.7</version>
                <configuration>
                    <createDependencyReducedPom>true</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass></mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
这里要提一下,如果要打包部署到集群上的话,打包的插件需要使用maven-shade-plugin这个插件,然后使用maven Lifecycle中的package打包。而不是用Maven-assembly-plugin插件进行打包。
因为使用Maven-assembly-plugin的时候,会将所有依赖的包unpack,然后在pack,这样就会出现,同样的文件被覆盖的情况。发布到集群上的时候就会报No FileSystem for scheme: hdfs的错。
然后是使用HdfsBolt写入Hdfs。这里来看看官方文档中的例子吧。
// 使用 "|" 来替代 ",",来进行字符分割
RecordFormat format = new DelimitedRecordFormat()
        .withFieldDelimiter("|");
// 每输入 1k 后将内容同步到 Hdfs 中
SyncPolicy syncPolicy = new CountSyncPolicy(1000);
// 当文件大小达到 5MB ,转换写入文件,即写入到一个新的文件中
FileRotationPolicy rotationPolicy = new FileSizeRotationPolicy(5.0f, Units.MB);
//当转换写入文件时,生成新文件的名字并使用
FileNameFormat fileNameFormat = new DefaultFileNameFormat()
        .withPath("/foo/");
HdfsBolt bolt = new HdfsBolt()
        .withFsUrl("hdfs://localhost:9000")
        .withFileNameFormat(fileNameFormat)
        .withRecordFormat(format)
        .withRotationPolicy(rotationPolicy)
        .withSyncPolicy(syncPolicy);
//生成该 bolt
topologyBuilder.setBolt("hdfsBolt", bolt, 5).globalGrouping("randomStrSpout");
到这里就结束了。可以将HdfsBolt当作一个Storm中特殊一些的bolt即可。这个bolt的作用即使根据接收信息写入Hdfs。
而在新建HdfsBolt中,Storm为我们提供了相当强的灵活性,我们可以定义一些策略,比如当达成某个条件的时候转换写入文件,新写入文件的名字,写入时候的分隔符等等。
如果选择使用的话,Storm有提供部分接口供我们使用,但如果我们觉得不够丰富也可以自定义相应的类。下面我们看看如何控制这些策略吧。
RecordFormat
这是一个接口,允许你自由定义接收到内容的格式。
public interface RecordFormat extends Serializable {
    byte[] format(Tuple tuple);
}
Storm提供了DelimitedRecordFormat,使用方法在上面已经有了。这个类默认的分割符是逗号",",而你可以通过withFieldDelimiter方法改变分隔符。
如果你的初始分隔符不是逗号的话,那么也可以重写写一个类实现RecordFormat接口即可。
FileNameFormat
同样是一个接口。
public interface FileNameFormat extends Serializable {
    void prepare(Map conf, TopologyContext topologyContext);
    String getName(long rotation, long timeStamp);
    String getPath();
}
Storm所提供的默认的是org.apache.storm.hdfs.format.DefaultFileNameFormat。默认人使用的转换文件名有点长,格式是这样的:
{prefix}{componentId}-{taskId}-{rotationNum}-{timestamp}{extension}
例如:
MyBolt-5-7-1390579837830.txt
默认情况下,前缀是空的,扩展标识是".txt"。
SyncPolicy
同步策略允许你将buffered data缓冲到Hdfs文件中(从而client可以读取数据),通过实现org.apache.storm.hdfs.sync.SyncPolicy接口:
public interface SyncPolicy extends Serializable {
    boolean mark(Tuple tuple, long offset);
    void reset();
}
FileRotationPolicy
这个接口允许你控制什么情况下转换写入文件。
public interface FileRotationPolicy extends Serializable {
    boolean mark(Tuple tuple, long offset);
    void reset();
}
Storm有提供三个实现该接口的类:
最简单的就是不进行转换的org.apache.storm.hdfs.bolt.rotation.NoRotationPolicy,就是什么也不干。
通过文件大小触发转换的org.apache.storm.hdfs.bolt.rotation.FileSizeRotationPolicy。
通过时间条件来触发转换的org.apache.storm.hdfs.bolt.rotation.TimedRotationPolicy。
如果有更加复杂的需求也可以自己定义。
RotationAction
这个主要是提供一个或多个hook,可加可不加。主要是在触发写入文件转换的时候会启动。
public interface RotationAction extends Serializable {
    void execute(FileSystem fileSystem, Path filePath) throws IOException;
}
三.实现一个例子
了解了上面的情况后,我们会实现一个例子,根据写入记录的多少来控制写入转换(改变写入的文件),并且转换后文件的名字表示当前是第几次转换。
首先来看看HdfsBolt的内容:
        RecordFormat format = new DelimitedRecordFormat().withFieldDelimiter(" ");
        // sync the filesystem after every 1k tuples
        SyncPolicy syncPolicy = new CountSyncPolicy(1000);
//        FileRotationPolicy rotationPolicy = new FileSizeRotationPolicy(1.0f, FileSizeRotationPolicy.Units.KB);
        /** rotate file with Date,every month create a new file
         * format:yyyymm.txt
         */
        FileRotationPolicy rotationPolicy = new CountStrRotationPolicy();
        FileNameFormat fileNameFormat = new TimesFileNameFormat().withPath("/test/");
        RotationAction action = new NewFileAction();
        HdfsBolt bolt = new HdfsBolt()
                .withFsUrl("hdfs://127.0.0.1:9000")
                .withFileNameFormat(fileNameFormat)
                .withRecordFormat(format)
                .withRotationPolicy(rotationPolicy)
                .withSyncPolicy(syncPolicy)
                .addRotationAction(action);
然后分别来看各个策略的类。
FileRotationPolicy
import org.apache.storm.hdfs.bolt.rotation.FileRotationPolicy;
import org.apache.storm.tuple.Tuple;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * 计数以改变Hdfs写入文件的位置,当写入10次的时候,则更改写入文件,更改名字取决于 “TimesFileNameFormat”
 * 这个类是线程安全
 */
public class CountStrRotationPolicy implements FileRotationPolicy {
    private SimpleDateFormat df = new SimpleDateFormat("yyyyMM");
    private String date =  null;
    private int count = 0;
    public CountStrRotationPolicy(){
        this.date =  df.format(new Date());
//        this.date = df.format(new Date());
    }
    /**
     * Called for every tuple the HdfsBolt executes.
     *
     * @param tuple  The tuple executed.
     * @param offset current offset of file being written
     * @return true if a file rotation should be performed
     */
    @Override
    public boolean mark(Tuple tuple, long offset) {
        count ++;
        if(count == 10) {
            System.out.print("num :" +count + "   ");
            count = 0;
            return true;
        }
        else {
            return false;
        }
    }
    /**
     * Called after the HdfsBolt rotates a file.
     */
    @Override
    public void reset() {
    }
    @Override
    public FileRotationPolicy copy() {
        return new CountStrRotationPolicy();
    }
}
FileNameFormat
import org.apache.storm.hdfs.bolt.format.FileNameFormat;
import org.apache.storm.task.TopologyContext;
import java.util.Map;
/**
 * 决定重新写入文件时候的名字
 * 这里会返回是第几次转换写入文件,将这个第几次做为文件名
 */
public class TimesFileNameFormat implements FileNameFormat {
    //默认路径
    private String path = "/storm";
    //默认后缀
    private String extension = ".txt";
    private Long times = new Long(0);
    public TimesFileNameFormat withPath(String path){
        this.path = path;
        return this;
    }
    @Override
    public void prepare(Map conf, TopologyContext topologyContext) {
    }
    @Override
    public String getName(long rotation, long timeStamp) {
        times ++ ;
        //返回文件名,文件名为更换写入文件次数
        return times.toString() + this.extension;
    }
    public String getPath(){
        return this.path;
    }
}
RotationAction
import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.storm.hdfs.common.rotation.RotationAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URI;
/**
    当转换写入文件时候调用的 hook ,这里仅写入日志。
 */
public class NewFileAction implements RotationAction {
    private static final Logger LOG = LoggerFactory.getLogger(NewFileAction.class);
    @Override
    public void execute(FileSystem fileSystem, Path filePath) throws IOException {
        LOG.info("Hdfs change the written file!!");
        return;
    }
}
OK,这样就大功告成了。通过上面的代码,每接收到10个Tuple后就会转换写入文件,新文件的名字就是第几次转换。
完整代码包括一个随机生成字符串的Spout,可以到我的github上查看。
StormHdfsDemo:https://github.com/shezhiming/StormHdfsDemo
推荐阅读:
Mysql 流增量写入 Hdfs(一) --从 mysql 到 kafka
Spark SQL,如何将 DataFrame 转为 json 格式
Mysql增量写入Hdfs(二) --Storm+hdfs的流式处理的更多相关文章
- Mysql增量写入Hdfs(一) --将Mysql数据写入Kafka Topic
		
一. 概述 在大数据的静态数据处理中,目前普遍采用的是用Spark+Hdfs(Hive/Hbase)的技术架构来对数据进行处理. 但有时候有其他的需求,需要从其他不同数据源不间断得采集数据,然后存储到 ...
 - Storm:分布式流式计算框架
		
Storm是一个分布式的.高容错的实时计算系统.Storm适用的场景: Storm可以用来用来处理源源不断的消息,并将处理之后的结果保存到持久化介质中. 由于Storm的处理组件都是分布式的,而且处理 ...
 - Storm简介——实时流式计算介绍
		
概念 实时流式计算: 大数据环境下,流式数据将作为一种新型的数据类型,这种数据具有连续性.无限性和瞬时性.是实时数据处理所面向的数据类型,对这种流式数据的实时计算就是实时流式计算. 特征 实时流式计算 ...
 - flume-ng+Kafka+Storm+HDFS 实时系统搭建
		
转自:http://www.tuicool.com/articles/mMrQnu7 一 直以来都想接触Storm实时计算这块的东西,最近在群里看到上海一哥们罗宝写的Flume+Kafka+Storm ...
 - 大数据架构:flume-ng+Kafka+Storm+HDFS 实时系统组合
		
http://www.aboutyun.com/thread-6855-1-1.html 个人观点:大数据我们都知道hadoop,但并不都是hadoop.我们该如何构建大数据库项目.对于离线处理,ha ...
 - [转]flume-ng+Kafka+Storm+HDFS 实时系统搭建
		
http://blog.csdn.net/weijonathan/article/details/18301321 一直以来都想接触Storm实时计算这块的东西,最近在群里看到上海一哥们罗宝写的Flu ...
 - 转:大数据架构:flume-ng+Kafka+Storm+HDFS 实时系统组合
		
虽然比较久,但是这套架构已经很成熟了,记录一下 一般数据流向,从“数据采集--数据接入--流失计算--数据输出/存储”<ignore_js_op> 1).数据采集 负责从各节点上实时采集数 ...
 - 浅谈Storm流式处理框架(转)
		
Hadoop的高吞吐,海量数据处理的能力使得人们可以方便地处理海量数据.但是,Hadoop的缺点也和它的优点同样鲜明——延迟大,响应缓慢,运维复杂. 有需求也就有创造,在Hadoop基本奠定了大数据霸 ...
 - 浅谈Storm流式处理框架
		
Hadoop的高吞吐,海量数据处理的能力使得人们可以方便地处理海量数据.但是,Hadoop的缺点也和它的优点同样鲜明——延迟大,响应缓慢,运维复杂. 有需求也就有创造,在Hadoop基本奠定了大数据霸 ...
 
随机推荐
- [Swift]LeetCode866. 回文素数 | Prime Palindrome
			
Find the smallest prime palindrome greater than or equal to N. Recall that a number is prime if it's ...
 - WebWorker与WebSocket实现前端消息总线
			
Web Worker让JS有了多线程的能力,可以将复杂耗时的操作都交付给Worker线程处理.WebSocket让web端与服务端维持一个有效的长连接,实现服务端主动推送数据.将二者一结合,业务系统信 ...
 - Maven项目执行java入口main方法
			
在Maven项目中配置pom.xml文件加载maven-surefire-plugin插件来执行testng.xml,相信大家对此种用法已经非常熟悉了.但是有些场景可能需要我们去加载执行java的ma ...
 - Kubernetes系列之监控Metres-server实战篇
			
本次系列使用的所需部署包版本都使用的目前最新的或最新稳定版,安装包地址请到公众号内回复[K8s实战]获取 介绍 从 Kubernetes 1.8 开始,Kubernetes 通过 Metrics AP ...
 - 6.jQuery(实例)
			
1.开关灯效果 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
 - 基础才是重中之重~delegate里的Invoke和BeginInvoke
			
回到目录 Invoke和BeginInvoke都是调用委托实体的方法,前者是同步调用,即它运行在主线程上,当Invode处理时间长时,会出现阻塞的情况,而BeginInvod是异步操作,它会从新开启一 ...
 - 前端笔记之HTML5&CSS3(中)选择器&伪类伪元素&CSS3效果&渐变背景&过渡
			
一.CSS3选择器 CSS3是CSS的第三代版本,新增了很多功能,例如:强大的选择器.盒模型.圆角.渐变.动画.2D/3D转换.文字特效等. CSS3和HTML5没有任何关系!HTML5骨架中,可以用 ...
 - 数据结构之哈希(hash)表
			
最近看PHP数组底层结构,用到了哈希表,所以还是老老实实回去看结构,在这里去总结一下. 1.哈希表的定义 这里先说一下哈希(hash)表的定义:哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过 ...
 - 开源库支付库Magicodes.Pay发布
			
Magicodes.Pay,是心莱科技团队提供的统一支付库,相关库均使用.NET标准库编写,支持.NET Framework以及.NET Core.目前支持以下支付方式和功能: 支付宝APP支付 支付 ...
 - kubernetes学习14—Dashboard搭建和认证
			
本文收录在容器技术学习系列文章总目录 一.介绍 Kubernetes Dashboard是Kubernetes集群的基于Web的通用UI.它允许用户管理在群集中运行的应用程序并对其进行故障排除,以及管 ...