(一)理论基础
更多理论以后再补充,或者参考书籍
1、trident是什么?
Trident is a high-level abstraction for doing realtime computing on top of Storm. It allows you to seamlessly intermix high throughput (millions of messages per second), stateful stream processing with low latency distributed querying. If you're familiar with high level batch processing tools like Pig or Cascading, the concepts of Trident will be very familiar – Trident has joins, aggregations, grouping, functions, and filters. In addition to these, Trident adds primitives for doing stateful, incremental processing on top of any database or persistence store. Trident has consistent, exactly-once semantics, so it is easy to reason about Trident topologies.

简单的说,trident是storm的更高层次抽象,相对storm,它主要提供了2个方面的好处:

(1)提供了更高层次的抽象,将常用的count,sum等封装成了方法,可以直接调用,不需要自己实现。

(2)提供了一次原语,如groupby等。

(3)提供了事务支持,可以保证数据均处理且只处理了一次。

2、trident每次处理消息均为batch为单位,即一次处理多个元组。

3、事务类型

关于事务类型,有2个比较容易混淆的概念:spout的事务类型以及事务状态。

它们都有3种类型,分别为:事务型、非事务型和透明事务型。

(1)spout

spout的类型指定了由于下游出现问题导致元组需要重放时,应该怎么发送元组。

事务型spout:重放时能保证同一个批次发送同一批元组。可以保证每一个元组都被发送且只发送一个,且同一个批次所发送的元组是一样的。

非事务型spout:没有任何保障,发完就算。

透明事务型spout:同一个批次发送的元组有可能不同的,它可以保证每一个元组都被发送且只发送一次,但不能保证重放时同一个批次的数据是一样的。这对于部分失效的情况尤其有用,假如以kafka作为spout,当一个topic的某个分区失效时,可以用其它分区的数据先形成一个批次发送出去,如果是事务型spout,则必须等待那个分区恢复后才能继续发送。

这三种类型可以分别通过实现ITransactionalSpout、ITridentSpout、IOpaquePartitionedTridentSpout接口来定义。

 

(2)state

state的类型指定了如果将storm的中间输出或者最终输出持久化到某个地方(如内存),当某个批次的数据重放时应该如果更新状态。state对于下游出现错误的情况尤其有用。

事务型状态:同一批次tuple提供的结果是相同的。

非事务型状态:没有回滚能力,更新操作是永久的。

透明事务型状态:更新操作基于先前的值,这样由于这批数据发生变化,对应的结果也会发生变化。透明事务型状态除了保存当前数据外,还要保存上一批数据,当数据重放时,可以基于上一批数据作更新。

(二)看官方提供的示例

package org.ljh.tridentdemo;

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(); //这个流程用于统计单词数据,结果将被保存在wordCounts中
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.submitTopologyWithProgressBar(args[0], conf, buildTopology(null));
}
}
}

实例实现了最基本的wordcount功能,然后将结果输出。关键步骤如下:

1、定义了输入流

        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);

(1)使用FixedBatchSpout创建一个输入spout,spout的输出字段为sentence,每3个元组作为一个batch。
(2)数据不断的重复发送。

2、统计单词数量

        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);

这个流程用于统计单词数据,结果将被保存在wordCounts中。6行代码的含义分别为:

(1)首先从spout中读取消息,spout1定义了zookeeper中用于保存这个拓扑的节点名称。

(2)并行度设置为16,即16个线程同时从spout中读取消息。

(3)each中的三个参数分别为:输入字段名称,处理函数,输出字段名称。即从字段名称叫sentence的数据流中读取数据,然后经过new Split()处理后,以word作为字段名发送出去。其中new Split()后面介绍,它的功能就是将输入的内容以空格为界作了切分。

(4)将字段名称为word的数据流作分组,即相同值的放在一组。

(5)将已经分好组的数据作统计,结果放到MemoryMapState,然后以count作为字段名称将结果发送出去。这步骤会同时存储数据及状态,并将返回TridentState对象。

(6)并行度设置。

3、输出统计结果

        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"));

这个流程从上述的wordCounts对象中读取结果,并返回。6行代码的含义分别为:

(1)等待一个drpc调用,从drpc服务器中接受words的调用来提供消息。调用代码如下:

drpc.execute("words", "cat the dog jumped")
(2)输入为上述调用中提供的参数,经过Split()后,以word作为字段名称发送出去。

(3)以word的值作分组。

(4)从wordCounts对象中查询结果。4个参数分别代表:数据来源,输入数据,内置方法(用于从map中根据key来查找value),输出名称。

(5)过滤掉空的查询结果,如本例中,cat和dog都没有结果。

(6)将结果作统计,并以sum作为字段名称发送出去,这也是DRPC调用所返回的结果。如果没有这一行,最后的输出结果

DRPC RESULT: [["cat the dog jumped","the",2310],["cat the dog jumped","jumped",462]]
加上这一行后,结果为:
DRPC RESULT: [[180]]

4、split的字义

    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));
}
}
}

注意它最后会发送数据。

5、创建并启动拓扑

    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.submitTopologyWithProgressBar(args[0], conf, buildTopology(null));
}
}

(1)当无参数运行时,启动一个本地的集群,及自已创建一个drpc对象来输入。
(2)当有参数运行时,设置worker数量为3,然后提交拓扑到集群,并等待远程的drpc调用。

 

(三)使用kafka作为数据源的一个例子

 

 

package com.netease.sytopology;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import storm.kafka.BrokerHosts;
import storm.kafka.StringScheme;
import storm.kafka.ZkHosts;
import storm.kafka.trident.OpaqueTridentKafkaSpout;
import storm.kafka.trident.TridentKafkaConfig;
import storm.trident.TridentTopology;
import storm.trident.operation.BaseFunction;
import storm.trident.operation.TridentCollector;
import storm.trident.operation.builtin.Count;
import storm.trident.testing.MemoryMapState;
import storm.trident.tuple.TridentTuple;
import backtype.storm.Config;
import backtype.storm.StormSubmitter;
import backtype.storm.generated.AlreadyAliveException;
import backtype.storm.generated.InvalidTopologyException;
import backtype.storm.generated.StormTopology;
import backtype.storm.spout.SchemeAsMultiScheme;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values; /*
* 本类完成以下内容
*/
public class SyTopology { public static final Logger LOG = LoggerFactory.getLogger(SyTopology.class); private final BrokerHosts brokerHosts; public SyTopology(String kafkaZookeeper) {
brokerHosts = new ZkHosts(kafkaZookeeper);
} public StormTopology buildTopology() {
TridentKafkaConfig kafkaConfig = new TridentKafkaConfig(brokerHosts, "ma30", "storm");
kafkaConfig.scheme = new SchemeAsMultiScheme(new StringScheme());
// TransactionalTridentKafkaSpout kafkaSpout = new
// TransactionalTridentKafkaSpout(kafkaConfig);
OpaqueTridentKafkaSpout kafkaSpout = new OpaqueTridentKafkaSpout(kafkaConfig);
TridentTopology topology = new TridentTopology(); // TridentState wordCounts =
topology.newStream("kafka4", kafkaSpout).
each(new Fields("str"), new Split(),
new Fields("word")).groupBy(new Fields("word"))
.persistentAggregate(new MemoryMapState.Factory(), new Count(),
new Fields("count")).parallelismHint(16);
// .persistentAggregate(new HazelCastStateFactory(), new Count(),
// new Fields("aggregates_words")).parallelismHint(2); return topology.build();
} public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException {
String kafkaZk = args[0];
SyTopology topology = new SyTopology(kafkaZk);
Config config = new Config();
config.put(Config.TOPOLOGY_TRIDENT_BATCH_EMIT_INTERVAL_MILLIS, 2000); String name = args[1];
String dockerIp = args[2];
config.setNumWorkers(9);
config.setMaxTaskParallelism(5);
config.put(Config.NIMBUS_HOST, dockerIp);
config.put(Config.NIMBUS_THRIFT_PORT, 6627);
config.put(Config.STORM_ZOOKEEPER_PORT, 2181);
config.put(Config.STORM_ZOOKEEPER_SERVERS, Arrays.asList(dockerIp));
StormSubmitter.submitTopology(name, config, topology.buildTopology()); } static class Split extends BaseFunction {
public void execute(TridentTuple tuple, TridentCollector collector) {
String sentence = tuple.getString(0);
for (String word : sentence.split(",")) {
try {
FileWriter fw = new FileWriter(new File("/home/data/test/ma30/ma30.txt"),true);
fw.write(word);
fw.flush();
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
collector.emit(new Values(word)); }
}
}
}

 

本例将从kafka中读取消息,然后对消息根据“,”作拆分,并写入一个本地文件。
1、定义kafka想着配置
        TridentKafkaConfig kafkaConfig = new TridentKafkaConfig(brokerHosts, "ma30", "storm");
        kafkaConfig.scheme = new SchemeAsMultiScheme(new StringScheme());

        OpaqueTridentKafkaSpout kafkaSpout = new OpaqueTridentKafkaSpout(kafkaConfig);

其中ma30是订阅的topic名称。

2、从kafka中读取消息并处理

        topology.newStream("kafka4", kafkaSpout).
        each(new Fields("str"), new Split(),new Fields("word")).
        groupBy(new Fields("word"))
        .persistentAggregate(new MemoryMapState.Factory(), new Count(),
                        new Fields("count")).parallelismHint(16);

(1)指定了数据来源,并指定zookeeper中用于保存数据的位置,即保存在/transactional/kafka4。
(2)指定处理方法及发射的字段。
(3)根据word作分组。

(4)计数后将状态写入MemoryMapState

 

提交拓扑:

storm jar target/sytopology2-0.0.1-SNAPSHOT.jar com.netease.sytopology.SyTopology 192.168.172.98:2181/kafka test3 192.168.172.98

此时可以在/home/data/test/ma30/ma30.txt看到split的结果

 

trident教程的更多相关文章

  1. Storm入门(十三)Storm Trident 教程

    转自:http://blog.csdn.net/derekjiang/article/details/9126185 英文原址:https://github.com/nathanmarz/storm/ ...

  2. Apache Storm 1.1.0 中文文档 | ApacheCN

    前言 Apache Storm 是一个免费的,开源的,分布式的实时计算系统. 官方文档: http://storm.apache.org 中文文档: http://storm.apachecn.org ...

  3. fiddler使用教程

    转载地址:写得很不错的fildder教程   http://kb.cnblogs.com/page/130367/ Fiddler的基本介绍 Fiddler的官方网站:  www.fiddler2.c ...

  4. fidder 使用教程

    fidder 使用教程 1. Fiddler 是什么? Fiddler是用C#编写的一个免费的HTTP/HTTPS网络调试器.英语中Fiddler是小提琴的意思,Fiddler Web Debugge ...

  5. [C#HttpHelper]类1.4正式版教程与升级报告

       [C#HttpHelper]类1.4正式版教程与升级报告 导读 1.升级报告 2.HttpHelper1.4正式版下载 3.HttpHelper类使用方法, 4.最简单的Post与Get的写法 ...

  6. 【教程】模拟登陆百度之Java代码版

    [背景] 之前已经写了教程,分析模拟登陆百度的逻辑: [教程]手把手教你如何利用工具(IE9的F12)去分析模拟登陆网站(百度首页)的内部逻辑过程 然后又去用不同的语言: Python的: [教程]模 ...

  7. storm入门教程 第一章 前言[转]

    1.1   实时流计算 互联网从诞生的第一时间起,对世界的最大的改变就是让信息能够实时交互,从而大大加速了各个环节的效率.正因为大家对信息实时响应.实时交互的需求,软件行业除了个人操作系统之外,数据库 ...

  8. Python3.x爬虫教程:爬网页、爬图片、自己主动登录

    林炳文Evankaka原创作品. 转载请注明出处http://blog.csdn.net/evankaka 摘要:本文将使用Python3.4爬网页.爬图片.自己主动登录.并对HTTP协议做了一个简单 ...

  9. 【ASP.NET Web API教程】5.2 发送HTML表单数据:URL编码的表单数据

    原文:[ASP.NET Web API教程]5.2 发送HTML表单数据:URL编码的表单数据 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面的内 ...

随机推荐

  1. form 转json最佳示例

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. latch:library cache

    一:硬解析造成的shared pool latch 争用: 每一个sql被执行之前,先要到library cache中根据hash_value查找parent cursor,这就需要先获得librar ...

  3. 十大免费移动程序测试框架(Android/iOS)

    十大免费移动程序测试框架(Android/iOS) 概述:本文将介绍10款免费移动程序测试框架,帮助开发人员简化测试流程,一起来看看吧. Bug是移动开发者最头痛的一大问题.不同于Web应用程序开发, ...

  4. 【转】android去掉EditView的默认焦点问题

    原文网址:http://www.111cn.net/sj/android/54680.htm 做一个输入框时发现android中EditView的默认焦点了,这种问题如果是在输入框还好,但在搜索页面或 ...

  5. 向Int数组插入随机1到100

    这是一个经典的面试题,考察了几个知识点 下边的代码是传统经典的做法 ]; ArrayList myList=new ArrayList(); Random rnd=new Random(); ) { ...

  6. AOJ 0121 广度优先搜索

    题意:7数码问题.在2×4的棋盘上,摆有7个棋子,每个棋子上标有1至7的某一数字,不同棋子上标的数字不相同.棋盘上还有一个空格(用0表示),与空格相邻(上下左右)的棋子可以移到空格中,该棋子原先位置成 ...

  7. Matchers and assertthat

    assertThat的一般句型为: assertThat([value],[matcher statement]); 这种断言的优点有: 1.更具有可读性:该语法允许我们以“主谓宾”的方式来思考(as ...

  8. Hdu 1856(离散化+并查集)More is better

    题意:一些人遵循朋友的朋友也是朋友原则,现在找出最大的朋友圈, 因为人的编号比较大,但是输入的数据最多是10w行,所以可得出最多也就20w人,所以可以进行一下离散化处理,这样数据就会毫无压力 //// ...

  9. SQL中Merge的用法

    SQL中Merge的用法 Merge的用法 Merge可以完成以下功能: 1.  两个表之间数据的更新 2.  进行进销存更新库存 3.  进行表之间数据的复制 语法说明: 1.  在语句结束后一定要 ...

  10. Struts2学习笔记(一):Struts2开发环境的配置

    一.Struts2应用所需的jar文件. 开发struts2应用需要依赖的jar文件在解压目录下的lib文件夹里面.开发struts2程序最少需要的jar文件为:struts2-core-2.xx.j ...