??xml version="1.0" encoding="utf-8" standalone="yes"?>精品亚洲欧美一区,一区二区美女,成人性生交大片免费看96http://www.aygfsteel.com/xuhongxing016/category/50624.htmlzh-cnThu, 26 Jan 2012 13:22:05 GMTThu, 26 Jan 2012 13:22:05 GMT60Clojure DSLhttp://www.aygfsteel.com/xuhongxing016/articles/368750.html徐红?徐红?Thu, 19 Jan 2012 07:38:00 GMThttp://www.aygfsteel.com/xuhongxing016/articles/368750.htmlhttp://www.aygfsteel.com/xuhongxing016/comments/368750.htmlhttp://www.aygfsteel.com/xuhongxing016/articles/368750.html#Feedback0http://www.aygfsteel.com/xuhongxing016/comments/commentRss/368750.htmlhttp://www.aygfsteel.com/xuhongxing016/services/trackbacks/368750.html     Storm 用Clojure DSL 来定?spouts, bolts, and topologies?Clojure的DSL讉K的Q何公开的Java APIQ如果你是一个Clojure的用P你编写直接用Clojure ~写Storm 的TopologiesQ而不用接触Java?Clojure的DSL定义在backtype.storm.clojure命名I间里?br />       本页面概qCClojure的DSL的所有细节,包括Q?br />        1.定义TopologyQ拓扑结构)
        2.定义bolt
        3.定义spout
        4.在本地模式下或在集群模式下运行Topology
        5.试Topology
      

定义 topologies

       要定义TopologyQ?需要用Topology函数。Topology需要两个参敎ͼ一个关?#8220;Spou Specs”的映和一个关?#8220;Bolt Spec”的映。每个Spout和Bolt指定lg到Topology上,如输入和q行拓扑l构的代码?br />      让我们来看看在Storm启动目的例子的拓扑定义
(topology
 {"1" (spout-spec sentence-spout)
  "2" (spout-spec (sentence-spout-parameterized
                   ["the cat jumped over the door"
                    "greetings from a faraway land"])
                   :p 2)}
 {"3" (bolt-spec {"1" :shuffle "2" :shuffle}
                 split-sentence
                 :p 5)
  "4" (bolt-spec {"3" ["word"]}
                 word-count
                 :p 6)})
映射 Spout 和Bolt Spces 都是从组件ID到Correponding Spec的映。组件ID必须在映间唯一。就像在Java中定义Topology一P在一个Topology里,在申明bolts的输入时Q组件ID用到?br />

spout-spec

spout-spec 作ؓSpout实现的参数和可选的关键字参C??目前唯一的可选参数是QP, q个用来定义Spout的ƈ行度。如果你忽略 :p, spout会作ؓ单一d执行?/p>

bolt-spec

bolt-spec作ؓbolt的输入声明参数和可选的关键字参C?code> 。输入声明是数据ID到数据流l的一个映。数据流ID可以用以下两UŞ式中的一U:

 

  1. [==component id== ==stream id==]: 在组件上订阅指定?br />
  2. ==component id==: 在组件上订阅默认?br />

数据组可以是以下中的一?/p>

  1. :shuffle: 订阅shufflel?br />
  2. 字段名称的向? like ["id" "name"]: 订阅指定字段上的字段l?/li>
  3. :global: 订阅一?global grouping
  4. :all: subscribes with an all grouping
  5. :direct: subscribes with a direct grouping

可以参?Concepts 获得更多关于组的信? q里有一个示例来展示不同的方法来声明输入:

{["2" "1"] :shuffle  "3" ["field1" "field2"]  ["4" "2"] :global} 
输入声明d订阅三种。他在组?#8220;2”上定义流“1”Q是Shuffle分组方式。在lg"3"上订阅默认的,是Fileds分组方式Q分l标准是"Field1"?Field2"。在lg4上定义流“2”Q是Global分组方式Q?br />跟Spout-Spec 方式cMQbolt-spec目前唯一支持的关键参数是:p,q个用来定义bolt的ƈ行度?br />

shell-bolt-spec

shell-bolt-spec是用在non-JVM语言环境下来实现bolts。他作ؓ参数输入Q命令行E序去跑。the name of the file implementing the bolt, an output specification, and then the same keyword arguments that bolt-spec accepts.

以下?shell-bolt-spec的一个示?/code>:

(shell-bolt-spec {"1" :shuffle "2" ["id"]}  "python"  "mybolt.py"  ["outfield1" "outfield2"]  :p 25) 

 

输出声明的语法是在下面的defbolt部分详细描述。有如何在Storm上用multilang的更多细节,请参阅用非JVM语言

 

defbolt

defbolt is used for defining bolts in Clojure. Bolts have the constraint that they must be serializable, and this is why you can't just reify IRichBolt to implement a bolt (closures aren't serializable). defbolt works around this restriction and provides a nicer syntax for defining bolts than just implementing a Java interface.

At its fullest expressiveness, defbolt supports parameterized bolts and maintaining state in a closure around the bolt implementation. It also provides shortcuts for defining bolts that don't need this extra functionality. The signature for defbolt looks like the following:

(defbolt name output-declaration *option-map & impl)

Omitting the option map is equivalent to having an option map of {:prepare false}.

Simple bolts

Let's start with the simplest form of defbolt. Here's an example bolt that splits a tuple containing a sentence into a tuple for each word:

(defbolt split-sentence ["word"] [tuple collector]  (let [words (.split (.getString tuple 0) " ")]  (doseq [w words]  (emit-bolt! collector [w] :anchor tuple))  (ack! collector tuple)  )) 

Since the option map is omitted, this is a non-prepared bolt. The DSL simply expects an implementation for the execute method of IRichBolt. The implementation takes two parameters, the tuple and the OutputCollector, and is followed by the body of the execute function. The DSL automatically type-hints the parameters for you so you don't need to worry about reflection if you use Java interop.

This implementation binds split-sentence to an actual IRichBolt object that you can use in topologies, like so:

(bolt-spec {"1" :shuffle}  split-sentence  :p 5) 

Parameterized bolts

Many times you want to parameterize your bolts with other arguments. For example, let's say you wanted to have a bolt that appends a suffix to every input string it receives, and you want that suffix to be set at runtime. You do this with defbolt by including a :params option in the option map, like so:

(defbolt suffix-appender ["word"] {:params [suffix]}  [tuple collector]  (emit-bolt! collector [(str (.getString tuple 0) suffix)] :anchor tuple)  ) 

Unlike the previous example, suffix-appender will be bound to a function that returns an IRichBolt rather than be an IRichBolt object directly. This is caused by specifying :params in its option map. So to use suffix-appender in a topology, you would do something like:

(bolt-spec {"1" :shuffle}  (suffix-appender "-suffix")  :p 10) 

Prepared bolts

To do more complex bolts, such as ones that do joins and streaming aggregations, the bolt needs to store state. You can do this by creating a prepared bolt which is specified by including {:prepare true} in the option map. Consider, for example, this bolt that implements word counting:

(defbolt word-count ["word" "count"] {:prepare true}  [conf context collector]  (let [counts (atom {})]  (bolt  (execute [tuple]  (let [word (.getString tuple 0)]  (swap! counts (partial merge-with +) {word 1})  (emit-bolt! collector [word (@counts word)] :anchor tuple)  (ack! collector tuple)  ))))) 

The implementation for a prepared bolt is a function that takes as input the topology config, TopologyContext, and OutputCollector, and returns an implementation of the IBolt interface. This design allows you to have a closure around the implementation of execute and cleanup.

In this example, the word counts are stored in the closure in a map called counts. The bolt macro is used to create the IBolt implementation. The bolt macro is a more concise way to implement the interface than reifying, and it automatically type-hints all of the method parameters. This bolt implements the execute method which updates the count in the map and emits the new word count.

Note that the execute method in prepared bolts only takes as input the tuple since the OutputCollector is already in the closure of the function (for simple bolts the collector is a second parameter to the execute function).

Prepared bolts can be parameterized just like simple bolts.

Output declarations

The Clojure DSL has a concise syntax for declaring the outputs of a bolt. The most general way to declare the outputs is as a map from stream id a stream spec. For example:

{"1" ["field1" "field2"]  "2" (direct-stream ["f1" "f2" "f3"])  "3" ["f1"]} 

The stream id is a string, while the stream spec is either a vector of fields or a vector of fields wrapped by direct-stream. direct stream marks the stream as a direct stream (See Concepts and Direct groupings for more details on direct streams).

If the bolt only has one output stream, you can define the default stream of the bolt by using a vector instead of a map for the output declaration. For example:

["word" "count"] 
This declares the output of the bolt as the fields ["word" "count"] on the default stream id.

Emitting, acking, and failing

Rather than use the Java methods on OutputCollector directly, the DSL provides a nicer set of functions for using OutputCollector: emit-bolt!, emit-direct-bolt!, ack!, and fail!.

  1. emit-bolt!: takes as parameters the OutputCollector, the values to emit (a Clojure sequence), and keyword arguments for :anchor and :stream. :anchor can be a single tuple or a list of tuples, and :stream is the id of the stream to emit to. Omitting the keyword arguments emits an unanchored tuple to the default stream.
  2. emit-direct-bolt!: takes as parameters the OutputCollector, the task id to send the tuple to, the values to emit, and keyword arguments for :anchor and :stream. This function can only emit to streams declared as direct streams.
  3. ack!: takes as parameters the OutputCollector and the tuple to ack.
  4. fail!: takes as parameters the OutputCollector and the tuple to fail.

See Guaranteeing message processing for more info on acking and anchoring.

defspout

defspout is used for defining spouts in Clojure. Like bolts, spouts must be serializable so you can't just reify IRichSpout to do spout implementations in Clojure. defspout works around this restriction and provides a nicer syntax for defining spouts than just implementing a Java interface.

The signature for defspout looks like the following:

(defspout name output-declaration *option-map & impl)

If you leave out the option map, it defaults to {:prepare true}. The output declaration for defspout has the same syntax as defbolt.

Here's an example defspout implementation from storm-starter:

(defspout sentence-spout ["sentence"]  [conf context collector]  (let [sentences ["a little brown dog"  "the man petted the dog"  "four score and seven years ago"  "an apple a day keeps the doctor away"]]  (spout  (nextTuple []  (Thread/sleep 100)  (emit-spout! collector [(rand-nth sentences)])   )  (ack [id]  ;; You only need to define this method for reliable spouts  ;; (such as one that reads off of a queue like Kestrel)  ;; This is an unreliable spout, so it does nothing here  )))) 

The implementation takes in as input the topology config, TopologyContext, and SpoutOutputCollector. The implementation returns an ISpout object. Here, the nextTuple function emits a random sentence from sentences.

This spout isn't reliable, so the ack and fail methods will never be called. A reliable spout will add a message id when emitting tuples, and then ack or fail will be called when the tuple is completed or failed respectively. See Guaranteeing message processing for more info on how reliability works within Storm.

emit-spout! takes in as parameters the SpoutOutputCollector and the new tuple to be emitted, and accepts as keyword arguments :stream and :id. :stream specifies the stream to emit to, and :id specifies a message id for the tuple (used in the ack and fail callbacks). Omitting these arguments emits an unanchored tuple to the default output stream.

There is also a emit-direct-spout! function that emits a tuple to a direct stream and takes an additional argument as the second parameter of the task id to send the tuple to.

Spouts can be parameterized just like bolts, in which case the symbol is bound to a function returning IRichSpout instead of the IRichSpout itself. You can also declare an unprepared spout which only defines the nextTuple method. Here is an example of an unprepared spout that emits random sentences parameterized at runtime:

(defspout sentence-spout-parameterized ["word"] {:params [sentences] :prepare false}  [collector]  (Thread/sleep 500)  (emit-spout! collector [(rand-nth sentences)])) 

The following example illustrates how to use this spout in a spout-spec:

(spout-spec (sentence-spout-parameterized  ["the cat jumped over the door"  "greetings from a faraway land"])  :p 2) 

Running topologies in local mode or on a cluster

That's all there is to the Clojure DSL. To submit topologies in remote mode or local mode, just use the StormSubmitter or LocalCluster classes just like you would from Java.

To create topology configs, it's easiest to use the backtype.storm.config namespace which defines constants for all of the possible configs. The constants are the same as the static constants in the Config class, except with dashes instead of underscores. For example, here's a topology config that sets the number of workers to 15 and configures the topology in debug mode:

{TOPOLOGY-DEBUG true  TOPOLOGY-WORKERS 15} 

Testing topologies

This blog post and its follow-up give a good overview of Storm's powerful built-in facilities for testing topologies in Clojure.




徐红? 2012-01-19 15:38 发表评论
]]>
Storm 序列?/title><link>http://www.aygfsteel.com/xuhongxing016/articles/368731.html</link><dc:creator>徐红?</dc:creator><author>徐红?</author><pubDate>Thu, 19 Jan 2012 06:29:00 GMT</pubDate><guid>http://www.aygfsteel.com/xuhongxing016/articles/368731.html</guid><wfw:comment>http://www.aygfsteel.com/xuhongxing016/comments/368731.html</wfw:comment><comments>http://www.aygfsteel.com/xuhongxing016/articles/368731.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/xuhongxing016/comments/commentRss/368731.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/xuhongxing016/services/trackbacks/368731.html</trackback:ping><description><![CDATA[<div>  本文译至Storm官方Wiki, Ƣ迎转蝲Q{载请注明出处Q?div><a id="Editor_Edit_hlEntryLink" title="view: Storm 序列? href="../articles/368731.html" target="_blank">   http://www.aygfsteel.com/xuhongxing016/articles/368731.html</a></div>   初次译Q英文好的同学,可以查看英文文档Q?https://github.com/nathanmarz/storm/wiki/Serialization<br /><div>    本文是介l关于Storm 0.6.0及以上版本的序列化系l,Storm 在之前版本用了另外一套序列化pȝ?br /><div><span id="result_box"><span title="Tuples can be comprised of objects of any types.">    Tuple可以包含Mcd的对象?/span><span title="Since Storm is a distributed system, it needs to know how to serialize and deserialize objects when they're passed between tasks.">׃Storm是一个分布式pȝQ它需要知道Q务之间传递对象时Q怎样序列化和反序列化对象</span>?br /><div><span id="result_box"><span title="Storm uses Kryo for serialization.">    Storm使用Kryo来进行序列化?</span><span title="Kryo is a flexible and fast serialization library that produces small serializations.">Kryo是一个灵zd快速的的序列化库,序列化对象较?/span><span title="By default, Storm can serialize primitive types, strings, byte arrays, ArrayList, HashMap, HashSet, and the Clojure collection types.">默认情况下,Storm可以序列化的原始cdQ字W串Q字节数l的 ArrayListQHashMapQHashSet和Clojure的集合类型?/span><span title="If you want to use another type in your tuples, you'll need to register a custom serializer.">如果你想在你的元l用另一U类型,你需要注册一个自定义序列化?br /><br /></span><span title="Dynamic typing"><strong>动态类?/strong></span><span title="Dynamic typing"><br /></span><span title="There are no type declarations for fields in a Tuple.">Tuple中的字段没有q行cd声明?/span><span title="You put objects in fields and Storm figures out the serialization dynamically.">你把对象攑ֈFields里,Storm<div><span id="result_box"><span title="You put objects in fields and Storm figures out the serialization dynamically.">动?/span>序列化对象?br /><span title="Before we get to the interface for serialization, let's spend a moment understanding why Storm's tuples are dynamically typed.">在我们获得序列化接口之前Q让我们q旉理解Storm 的Tuple Z么是动态类型?br /><div><span id="result_box"><span title="Adding static typing to tuple fields would add large amount of complexity to Storm's API.">d静态类型的到Tuple 的FiledsQ将会给Storm的API增加大量的复杂性?/span><span title="Hadoop, for example, statically types its keys and values but requires a huge amount of annotations on the part of the user.">例如QHadoopQ静态类型的键和|在用户用时Q需要的很多注释?</span><span title="Hadoop's API is a burden to use and the "type safety" isn't worth it.">Hadoop的API使用h比较ȝQ?#8220;cd安全”是不值得的?/span><span title="Dynamic typing is simply easier to use.">动态类型是单容易用?br />很难用一U合理的静态方式来l计Storm的Tuples。假设一个Bolt订阅了多个流Q这些流的Tuple可能在Fields上有不同的类型,当一个Bolt在执行阶D|受TupleQ这些Tuple可能来自于Q何流Q?/span><span title="When a Bolt receives a Tuple in execute, that tuple could have come from any stream and so could have any combination of types.">q有可能使Q何类型的l合?/span><span title="There might be some reflection magic you can do to declare a different method for every tuple stream a bolt subscribes to, but Storm opts for the simpler, straightforward approach of dynamic typing.">可能有一些反魔法,你可以ؓ每一个被Bolt<div><span id="result_box"><span title="You put objects in fields and Storm figures out the serialization dynamically."><span title="Before we get to the interface for serialization, let's spend a moment understanding why Storm's tuples are dynamically typed."><span id="result_box"><span title="There might be some reflection magic you can do to declare a different method for every tuple stream a bolt subscribes to, but Storm opts for the simpler, straightforward approach of dynamic typing.">订阅的Tuple定义不同的ҎQ但?/span></span></span></span></span></div>Q但单,直接的方法是动态类型?br /></span><span title="Finally, another reason for using dynamic typing is so Storm can be used in a straightforward manner from dynamically typed languages like Clojure and JRuby.">最后,使用动态类型的另一个原因是Storm可以以一U简单直接的方式来用类gClojure和JRuby的动态类型语a<br /></span><span title="Custom serialization">自定义序列化<br /></span><span title="As mentioned, Storm uses Kryo for serialization.">如前所qͼStorm使用Kryo<div><span id="result_box"><span title="You put objects in fields and Storm figures out the serialization dynamically."><span title="Before we get to the interface for serialization, let's spend a moment understanding why Storm's tuples are dynamically typed."><span id="result_box"><span title="As mentioned, Storm uses Kryo for serialization.">来进行序列化</span></span></span></span></span></div>?/span><span title="To implement custom serializers, you need to register new serializers with Kryo.">要实现自定义的序列化Q您需要注册新的序列化与Kryo?/span><span title="It's highly recommended that you read over Kryo's home page to understand how it handles custom serialization.">强烈您通过Kryo的主阅ȝ解它是如何处理自定义序列?br /><br /></span><span title="Adding custom serializers is done through the "topology.kryo.register" property in your topology config.">d自定义的序列化是在Topology的配|属?#8220;topology.kryo.register”完成的?/span><span title="It takes a list of registrations, where each registration can take one of two forms:">它需要一个注册的清单Q其中每个登记可以采取以下两UŞ式之一Q?br /><br /></span><span title="1.The name of a class to register.">1?需要注册登记的cȝ名称?/span><span title="In this case, Storm will use Kryo's FieldsSerializer to serialize the class.">在这U情况下QStorm用Kryo的FieldsSerializer来序列化cR?/span><span title="This may or may not be optimal for the class -- see the Kryo docs for more details.">q可能是最佳的c,也可能不?- 更多l节见Kryo文档?br /></span><span title="2.A map from the name of a class to register to an implementation of com.esotericsoftware.kryo.Serializer.">2?映射下类的名字来注册登记com.esotericsoftware.kryo.Serializer实施?br /></span><span title="Let's look at an example.">让我们来看看一个例子?/span></span></div><div> <div><pre>topology.kryo.register: <br /> - com.mycompany.CustomType1 <br /> - com.mycompany.CustomType2: com.mycompany.serializer.CustomType2Serializer <br /> - com.mycompany.CustomType3 <br /><div><code>com.mycompany.CustomType1</code> ? <code>com.mycompany.CustomType3</code> 使用 <code>FieldsSerializer</code> 来进行序列化<br /><code>com.mycompany.CustomType2</code> 使用 <code>com.mycompany.serializer.CustomType2Serializer</code> 来进行序列化</div><div><div><span id="result_box"><div><span id="result_box"><span title="You put objects in fields and Storm figures out the serialization dynamically."><span title="Before we get to the interface for serialization, let's spend a moment understanding why Storm's tuples are dynamically typed."><pre><span id="result_box">注册序列化器?/span></pre></span></span></span></div>的帮助。配|类有一个名为registerSerialization一个方法,在注册时d到配|里<br /><div><span id="result_box">有更高阶的配|称为Config.TOPOLOGY_SKIP_MISSING_KRYO_REGISTRATIONS。如果设|ؓtrueQStorm忽略Q何已注册的,但在classpath?div><span id="result_box"><span title="You put objects in fields and Storm figures out the serialization dynamically."><span title="Before we get to the interface for serialization, let's spend a moment understanding why Storm's tuples are dynamically typed."><pre><span id="result_box"><span id="result_box">没有自己的代?/span></span></pre></span></span></span></div>可用的序列化。否则,Storm会引发错误时Q它无法扑ֈ一个序列化。如果你q行多个集上,每个人都有不同的序列化的拓扑l构Q这是有用的?br />但要声明所有在storm.yaml文g拓扑中所有的序列化?br /><div><span id="result_box">Java序列?br />Storm如果遇到一个类型,它没有一个序列化注册Q它有可能用Java序列化。如果对象不能被Java序列化程序序列,Storm会抛Z个错误?br /><br />Java序列化是极其昂贵的,无论是在CPU成本以及序列化的对象的大。强烈徏议您在Topology上生产环境之前注册定制序列?Java序列化在处理Topology原型的时候就是这L?br /><br />可以通过配置文g来关闭Java序列化功能,只需Config.TOPOLOGY_FALL_BACK_ON_JAVA_SERIALIZATION配置讄为false</span></div><br /></span></div><br /></span></div></div><br /></pre></div></div><br /></span></span></div></span></span></div></span></div></div></div><img src ="http://www.aygfsteel.com/xuhongxing016/aggbug/368731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/xuhongxing016/" target="_blank">徐红?</a> 2012-01-19 14:29 <a href="http://www.aygfsteel.com/xuhongxing016/articles/368731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Twitter Storm 分布式RPChttp://www.aygfsteel.com/xuhongxing016/articles/368748.html徐红?徐红?Thu, 19 Jan 2012 06:17:00 GMThttp://www.aygfsteel.com/xuhongxing016/articles/368748.htmlhttp://www.aygfsteel.com/xuhongxing016/comments/368748.htmlhttp://www.aygfsteel.com/xuhongxing016/articles/368748.html#Feedback0http://www.aygfsteel.com/xuhongxing016/comments/commentRss/368748.htmlhttp://www.aygfsteel.com/xuhongxing016/services/trackbacks/368748.html

本文转蝲?div>

http://chenlx.blog.51cto.com/4096635/748348
本来准备自己译一下,后来Google一下,发现|上已有Q遂转蝲?br />

分布式RPC

分布式RPCQDRPCQ的真正目的是用storm实时q行计算极端功能。Storm拓扑需要一个输入流作ؓ函数参数Q以一个输出流的Ş式发每个函数调用的l果?/div>
 
DRPC没有多少stormҎ,因ؓ它是从storm的原始流QspoutsQboltsQ拓扑来表达一个模式。DRPC没有单独打包Q但它如此有用,以至于和storm捆绑在一赗?/div>
 
概述
分布式RPC通过“DRPC server”协调。DRPC服务器协调接收一个RPChQ发送请求到storm拓扑Q从storm拓扑接收l果Q发送结果回{待的客L。从一个客L的角度来看,一个分布式RPC调用像是一个常规的RPC调用。例如,一个客L如何为带“http://twitter.com”参数?#8220;reach”功能计算l果?/div>
  1. DRPCClient client = new DRPCClient("drpc-host", 3772); 
  2. String result = client.execute("reach", "http://twitter.com"); 
分布式RPC工作程如下Q?/div>

客户端发送功能名U及功能所需参数?/span>DRPC服务器去执行?/span>图中的拓扑实C此功能,它用DRPCSpout从DRPC服务器接收功能调用流。每个功能调用通过DRPC服务器用唯一ID标记Q随后拓扑计结果,在拓扑的最后,一个称之ؓ“ReturnResults”的boltq接到DRPC服务器,把结果交l这个功能调用(Ҏ功能调用IDQ,DRPC服务器根据ID扑ֈ{待中的客户端,为等待中的客L消除dQƈ发送结果给客户端?/p>

LinearDRPCTopologyBuilder
Storm有一个称之ؓLinearDRPCTopologyBuilder的拓扑Builder几乎自动完成DRPC所需的所有相x骤。包括:
1.讄spout
2.q回l果lDRPC服务?/div>
3.为bolt提供对一l元l的有限聚合功能
让我们看一个简单的例子。这是一个DRPC拓扑的实玎ͼ在输入参数的Nq加“Q?#8221;q返回:
  1. public static class ExclaimBolt implements IBasicBolt { 
  2.     public void prepare(Map conf, TopologyContext context) { 
  3.     } 
  4.  
  5.     public void execute(Tuple tuple, BasicOutputCollector collector) { 
  6.         String input = tuple.getString(1); 
  7.         collector.emit(new Values(tuple.getValue(0), input + "!")); 
  8.     } 
  9.  
  10.     public void cleanup() { 
  11.     } 
  12.  
  13.     public void declareOutputFields(OutputFieldsDeclarer declarer) { 
  14.         declarer.declare(new Fields("id", "result")); 
  15.     } 
  16.  
  17.  
  18. public static void main(String[] args) throws Exception { 
  19.     LinearDRPCTopologyBuilder builder = new LinearDRPCTopologyBuilder("exclamation"); 
  20.     builder.addBolt(new ExclaimBolt(), 3); 
  21.     // ... 

如你所见,代码非常。当创徏LinearDRPCTopologyBuilderӞ你把q个拓扑的DRPC功能名称告诉storm。一个DRPC服务器可以协调许多功能,功能名称用于区别不同的功能,首先声明的bolt接收一个输入的2-tuplesQ第一个字D|hIDQ第二个字段是请求参数。LinearDRPCTopologyBuilder认ؓ最后的bolt会发一个输出流Q该输出包含[id, result]格式?-tuples。最后,所有拓扑中间过E生的元组QtupleQ都包含hid作ؓ其第一个字Dc?/p>

在这个例子中QExclaimBolt只是单地在元l的W二个字D尾部追?#8220;Q?#8221;字符。LinearDRPCTopologyBuilder处理其余的协调工作,包括q接DRPC服务器,发送最l结果?/div>
 
本地模式DRPC
DRPC可以q行在本地模式。这是如何在本地模式q行上述例子Q?/div>
  1. LocalDRPC drpc = new LocalDRPC(); 
  2. LocalCluster cluster = new LocalCluster(); 
  3.  
  4. cluster.submitTopology("drpc-demo", conf, builder.createLocalTopology(drpc)); 
  5.  
  6. System.out.println("Results for 'hello':" + drpc.execute("exclamation", "hello")); 
  7.  
  8. cluster.shutdown(); 
  9. drpc.shutdown(); 
首先你创Z个LocalDRPC对象。这个对象在q程内模拟一个DRPC服务器,像在进E内模拟一个storm集群一栗然后你创徏本地集群Q在本地模式q行q个拓扑。创建本地拓扑和q程拓扑QLinearDRPCTopologyBuilder有不同的Ҏ。在本地模式QLocalDRPC未绑定Q何端口,拓扑也需要知道与哪个对象通讯Q这是ؓ什么createLocaclTopologyҎ需要接受LocalDRPC对象作ؓ输入参数的原因?/div>
 
载入拓扑后,你可以用LocalDRPC的executeҎ执行DRPC调用?/div>
 
q程模式DRPC
在实际的集群使用DRPC也很单。有三个步骤Q?/div>
1.      启动DRPC服务?/div>
2.      配置DRPC服务器位|?/div>
3.      提交DRPC拓扑到storm集群
使用storm脚本启动DRPC服务器,和启动nimbus和ui一P
  1. bin/storm drpc 
接下来,配置你的storm集群Q让集群知道DRPC服务器的位置Q这样DRPCSpoutq道从哪里d功能调用。可以通过修改storm.yaml配置文g或拓扑配|完成配|DRPC服务器位|。修改storm.yaml配置文g如下所C:
  1. drpc.servers: 
  2.   - "drpc1.foo.com" 
  3.   - "drpc2.foo.com" 
最后,使用StormSubmitter启动DRPC拓扑Q就像启动其它拓扑一栗在q程模式q行上述例子Q代码如下所C:
  1. StormSubmitter.submitTopology("exclamation-drpc", conf, builder.createRemoteTopology()); 
createRemoteTopologyҎ用于在storm集群创徏拓扑?/div>
 
一个更完整的例?/strong>
q个exclaimation DRPC例子只是一个用来说明DRPC概念的玩兗让我们看一个更完整的例子,该例子是一个真正需要storm集群的ƈ行计的DRPC功能。我们将要看的例子是对twitter|站上的一个URL的接触用戯行统计?/div>
一个URL的接触用h是在twitter|站上接触一个URL的用hQ你需要以?步:
1. 获取tweeted the URL的全部用?/div>
2. 获取q些用户的全部追随?/div>
3. 使追随者集合中的用户唯一
4. l计唯一的用h
一个单独的reach计算在计期间涉及到数千数据库访问和数千万追随者记录。它是一个真正的耗时计算。正如你要看到的,在storm上实现这个功能非常简单。在一台机器上Qreach计算p数分钟,在storm集群Q最难计reach的URL也只需数秒?/div>
Storm-starter目
q个拓扑?个步骤的形式执行Q?/div>

1. GetTweeters获取tweeted the URL的用戗它转换一个[id, url]形式的输入流到[id, tweeter]形式的输出流。每个url元组映到多个tweeter元组?/p>

2. GetFollowers获取q些tweeter的追随者。它转换一个[id, tweeter]形式的输入流到[id, follower]形式的输出流。跨所有Q务,当某随多个tweeterQ这些tweeter又tweeted相同的URLӞq可能会得到重复的追随者?/p>

3. PartialUniquer按追随者ID对追随者数据流q行分组。同一的追随者去到同一的Q务,因此每个PartialUniquerd都接收到独立的相互独立的q随者集合。PartialUniquer一旦收到请求ID用于它的所有追随者元l,它就发射q随者子集的唯一L?/div>
4. 最后,CountAggregator从每个PartialUniquerd接收计数q对它们求和?
让我们来看看PartialUniquerQ?/div>
  1. public static class PartialUniquer implements IRichBolt, FinishedCallback { 
  2.     OutputCollector _collector; 
  3.     Map<Object, Set<String>> _sets = new HashMap<Object, Set<String>>(); 
  4.      
  5.     public void prepare(Map conf, TopologyContext context, OutputCollector collector) { 
  6.         _collector = collector; 
  7.     } 
  8.  
  9.     public void execute(Tuple tuple) { 
  10.         Object id = tuple.getValue(0); 
  11.         Set<String> curr = _sets.get(id); 
  12.         if(curr==null) { 
  13.             curr = new HashSet<String>(); 
  14.             _sets.put(id, curr); 
  15.         } 
  16.         curr.add(tuple.getString(1)); 
  17.         _collector.ack(tuple); 
  18.     } 
  19.  
  20.     public void cleanup() { 
  21.     } 
  22.  
  23.     public void finishedId(Object id) { 
  24.         Set<String> curr = _sets.remove(id); 
  25.         int count; 
  26.         if(curr!=null) { 
  27.             count = curr.size(); 
  28.         } else { 
  29.             count = 0; 
  30.         } 
  31.         _collector.emit(new Values(id, count)); 
  32.     } 
  33.  
  34.     public void declareOutputFields(OutputFieldsDeclarer declarer) { 
  35.         declarer.declare(new Fields("id", "partial-count")); 
  36.     } 
当PartialUniquer在exectueҎ中接收一个follower元组Ӟ它用一个内部HashMapd它到与请求ID对应的集合?/div>
PartialUniquer也实CFinishedCallback接口Q它告诉LinearDRPCTopologyBuilderQ对于Q意给定的hIDQ当它已收到所有指向它的元l时Q请通知它。这个回调是finishedIdҎ。在q个回调中,PartialUniquer发射单一的元l,元组包含它的q随者子集的唯一L?nbsp;

在底层,CoordinatedBolt用于一个bolt何时收到该请求ID的所有元l。CoordinatedBolt使用direct stream理协调?/p>

其它的拓扑应该是不言自明。如你所见,reach计算的每一单步都是q行执行的,而且定义一个DRPC拓扑也非常简单?/div>
Non-Linear DRPC拓扑
LinearDRPCTopologyBuilder仅处?#8220;U性的”DRPC拓扑Q计以一q串步骤的Ş式表达(像reachQ。不难想象某些功能将需要更复杂的拓扑结构,q些拓扑带有带分支和合ƈbolt。目前,要做到这一点,你需要直接用CoordinateBolt。务必在邮g列表中谈谈你的非U性DRPC拓扑用例Q写下DRPC拓扑更普遍的抽象l构?/div>
LinearDRPCTopologyBuilder如何工作Q?/strong>

DRPCSpout发射[args, return-info]Qreturn-info是DRPC服务器的L和端口,q有DRPC服务器生成的ID?/p>

拓扑l成部分Q?/div>
  • DRPCSpout
  • PrepareRequestQ生成一个请求IDQ创Z个返回信息流Q一个参数流Q?/li>
  • CoordinatedBolt包装器和直接分组
  • JoinResultQ同q回信息一赯接结果)
  • ReturnResultQ连接DRPC服务器ƈq回l果Q?/li>
  • LinearDRPCTopologyBuilder是一个构建在Storm原语之上的高层次抽象的好例子?/li>
  • 同时~排处理多个LKeyedFairBolt
  • 如何直接使用CoordinateBolt


徐红? 2012-01-19 14:17 发表评论
]]>Storm?/title><link>http://www.aygfsteel.com/xuhongxing016/articles/368503.html</link><dc:creator>徐红?</dc:creator><author>徐红?</author><pubDate>Sat, 14 Jan 2012 09:08:00 GMT</pubDate><guid>http://www.aygfsteel.com/xuhongxing016/articles/368503.html</guid><wfw:comment>http://www.aygfsteel.com/xuhongxing016/comments/368503.html</wfw:comment><comments>http://www.aygfsteel.com/xuhongxing016/articles/368503.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/xuhongxing016/comments/commentRss/368503.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/xuhongxing016/services/trackbacks/368503.html</trackback:ping><description><![CDATA[<span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt"><strong>Storm?br /></strong>Storm是一个分布式的、容错的实时计算pȝQ可以方便地在一个计机集群中编写与扩展复杂的实时计。在量领域里,Storm用于实时数据的处理,Hadoop用于Ҏ据的处理Q两者可以说是绝代双雄!Storm保证每个消息都会得到处理Q而且它很?#8212;—在一个小集群中,每秒可以处理C百万计的消息?br />Storm的优?br />1. 单的~程模型。类gMapReduce降低了ƈ行批处理复杂性,Storm降低了进行实时处理的复杂性?br />2. 服务?一个服务框?支持热部|?x上线或下UApp.<br />3. 可以使用各种~程语言。你可以在Storm之上使用各种~程语言。默认支持Clojure、Java、Ruby和Python。要增加对其他语a的支持,只需实现一个简单的Storm通信协议卛_?br />4. 定w性。Storm会管理工作进E和节点的故障?br />5. 水^扩展。计是在多个线E、进E和服务器之间ƈ行进行的?br />6. 可靠的消息处理。Storm保证每个消息臛_能得Cơ完整处理。Q务失败时Q它会负责从消息源重试消息?br />7. 快速。系l的设计保证了消息能得到快速的处理Q用ZeroMQ作ؓ其底层消息队列?br />8. 本地模式。Storm有一?#8220;本地模式”Q可以在处理q程中完全模拟Storm集群。这让你可以快速进行开发和单元试?br />有优点,必定有缺炏V不q相Ҏ_我觉得这?span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face=""><font face="宋体">问题都不?br /></font>1.</font></span> <span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">目前的开源版本中只是单节?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">NimbusQ我们可以在生环境</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">实现一个双</span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">nimbus</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">的布局Q?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><br /><font face="">2. Clojure</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">是一个在</span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">JVM</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">q_q行的动态函数式~程语言</span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">,</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">优势在于程计算Q?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">Storm</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">的部分核心内容由</span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">Clojure</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">~写Q虽然性能上提高不但同时也提升了l护成本--学学Clojure也很不错Q只要你融入里面?br /><br /><strong><span style="font-family: Arial, sans-serif; font-size: 9pt" xml:lang="EN-US" lang="EN-US"><font face="">Storm</font></span></strong><strong><span style="font-family: 宋体; font-size: 9pt">架构<br /><br /></span></strong> <div style="border-bottom: #919699 1pt solid; padding-bottom: 2pt; border-right-style: none; padding-left: 0cm; padding-right: 0cm; border-top-style: none;background: white; border-left-style: none; padding-top: 0cm; mso-element: para-border-div; mso-border-bottom-alt: solid #919699 .75pt"><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">Storm</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">集群׃个主节点和多个工作节点组成,分布式的架构大多如此Q没什么好说的。主节点q行了一个名?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">“Nimbus”</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">的守护进E,用于分配代码、布|Q务及故障。每个工作节炚wq行了一个名?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">“Supervisor”</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">的守护进E,用于监听工作Q开始ƈl止工作q程?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">Nimbus</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">Supervisor</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">都能快速失败,而且是无状态的Q这样十分健壮,两者的协调当然是由</span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">Zookeeper</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">来完成的Q?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">ZooKeeper</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">用于理集群中的不同lgQ?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">ZeroMQ</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">是内部消息系l,</span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">JZMQ</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">ZeroMQMQ</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">?/span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体" xml:lang="EN-US" lang="EN-US"><font face="">Java Binding</font></span><span style="font-family: 宋体; color: #333333; font-size: 9pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial; mso-font-kerning: 0pt">?br /></span><span style="font-family: 'Arial', 'sans-serif'; color: #333333; font-size: 9pt; mso-font-kerning: 0pt; mso-fareast-font-family: 宋体; mso-no-proof: yes" xml:lang="EN-US" lang="EN-US"></span></div></span></span><img src ="http://www.aygfsteel.com/xuhongxing016/aggbug/368503.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/xuhongxing016/" target="_blank">徐红?</a> 2012-01-14 17:08 <a href="http://www.aygfsteel.com/xuhongxing016/articles/368503.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <a href="http://www.aygfsteel.com/" title="狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频">狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频</a> </div> </footer> վ֩ģ壺 <a href="http://" target="_blank">Ʊ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank">տ</a>| <a href="http://" target="_blank">ƺ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">˫</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">®</a>| <a href="http://" target="_blank">ƽ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ˮ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">Ϫ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">潭</a>| <a href="http://" target="_blank">Զ</a>| <a href="http://" target="_blank">ʲ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank">˱</a>| <a href="http://" target="_blank">ȫ</a>| <a href="http://" target="_blank">Ϫ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ƽ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">³</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">³ľ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ʯׯ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>