Monthly Archives: 一月 2015

NetworkWordCount失败之Spark Master URLs

这篇文章写的

Spark Streaming 和 Spark Master URLs

Spark任务在提交时,需要指定 --master 参数,表明该spark任务提交到哪个集群。

这个参数有以下几种可能的值:

  • local

    Run Spark locally with one worker thread (i.e. no parallelism at all).

  • local[K]

    Run Spark locally with K worker threads (ideally, set this to the number of cores on your machine).

  • local[*]

    Run Spark locally with as many worker threads as logical cores on your machine.

  • spark://HOST:PORT

    Connect to the given Spark standalone cluster master. The port must be whichever one your master is configured to use, which is 7077 by default.

  • mesos://HOST:PORT

    Connect to the given Mesos cluster. The port must be whichever one your is configured to use, which is 5050 by default. Or, for a Mesos cluster using ZooKeeper, use mesos://zk://….

  • yarn-client

    Connect to a YARN cluster in client mode. The cluster location will be found based on the HADOOP_CONF_DIR variable.

  • yarn-cluster

    Connect to a YARN cluster in cluster mode. The cluster location will be found based on HADOOP_CONF_DIR.

如果没有指定该参数,如:

bin/run-example org.apache.spark.examples.streaming.NetworkWordCount localhost 9999

此时默认 --masterlocal .

这看起来毫无问题,但我在一台1核1G的主机上测试该样例,却永远无法成功。 原因这位老兄已道出:

Note

I experienced exactly the same problems when using SparkContext with “local[1]” master specification, because in that case one thread is used for receiving data, the others for processing. As there is only one thread running, no processing will take place. Once you shut down the connection, the receiver thread will be used for processing.

也就是说,spark streaming在启动时会启动两个线程: receving threadprocessing data thread .

local 模式下,spark streaming只启动了与CPU核数相同个数的线程(在我的例子中, 只有一个线程),无论如何是无法测试成功的;而在其它开发主机(4核主机)则完全不会出现此问题。

这涉及到了spark的线程模型。更深入的文档我还尚未找到。如果你有更深入的理解,欢迎探讨。

参考:

 

JavaScript中循环遍历时的闭包问题探究

这篇文章写的

什么问题?

让我们来看看 JavaScript 中关于循环的一个最常见的错误:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var items = [
    {"id": "email"},
    {"id": "name"},
    {"id": "age"},
];

function setupHelp() {
    var helpText = [
            {'id': 'email', 'help': 'Your e-mail address'},
            {'id': 'name', 'help': 'Your full name'},
            {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

    for (var i = 0; i < helpText.length; i++) {
        var item = helpText[i];

        // Bind help function to this item.
        items[i].help = function() {
            console.log(item.help);
        }
    }
}

setupHelp();

for (var i = 0; i < items.length; i++) {
    items[i].help();
}

想必许多web前端的朋友们看着眼熟。
这段代码虽然冗长且毫无意义,却是一个前端代码中常见的一个场景:
遍历一些元素,并为对应元素绑定相应的方法,比如 help 方法。

但无论这段代码如何执行,你得到的结果总是:

[cipher@Rose test_js]$ node a.js
Your age (you must be over 16)
Your age (you must be over 16)
Your age (you must be over 16)

知其然,知其所以然

这段代码涉及了许多的知识,可不是三言两语能够说明白的。

但一个令我尊敬的前辈是这么说的:

“如果你需要花几天时间都不能够解释清楚,说明你还不够理解。”

所以还是让我尝试着用几句概括性语句说明吧:

  1. 这段代码使用了 closure 作为绑定的方法;
  2. closure 总是能 记住 它创建时的 environment。(这是 closure 的一大特性)

Tip

Closures are functions that refer to independent (free) variables.

In other words, the function defined in the closure ‘remembers’ the environment in which it was created.

好,到此为止,我们知道这是由于 closure 的一个特性。
但既然这条特性使人容易犯错,为什么还要设计这条特性呢?

存在即是合理 [1]

closure 使得我们可以将一些数据(环境)绑定到一个函数上。

是不是有点耳熟?

因为 OOP (Object Oriendted Programming)
同样是通过将属性/方法与对象绑定起来而实现面向对象编程的。

  1. 可以将一个 一次性 的方法绑定在一个对象上

这种场景在web前端很常见,因为 JavaScript 是基于事件的:我们先定义一个
行为( callback ),并在用户触发事件( event )后(如鼠标点击或者
输入 Enter 键等)后执行该行为。

我们在开篇看到的正是这种应用场景。

还有一种更高级的用法

  1. 使用 closure 模拟对象的 private methods

JavaScript 原生并没有提供一种创建私有方法的概念,
但通过 closure 我们可以实现“曲线救国”。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var makeCounter = function() {
    var privateCounter = 0;
    function changeBy(val) {
        privateCounter += val;
    }
    return {
        increment: function() {
            changeBy(1);
        },
        decrement: function() {
            changeBy(-1);
        },
        value: function() {
            return privateCounter;
        }
    }
};

var counter1 = makeCounter();
var counter2 = makeCounter();

alert(counter1.value()); /* Alerts 0 */

counter1.increment();
counter1.increment();
alert(counter1.value()); /* Alerts 2 */

counter1.decrement();
alert(counter1.value()); /* Alerts 1 */

alert(counter2.value()); /* Alerts 0 */

同样,这个例子可圈可点之处也非常之多:

  1. 通过一个匿名函数,这里创建了一个被三个函数: counter.increment, counter.decrementcounter.value共享的执行环境
  2. 这个 共享执行环境 包含两个私有的数据: 变量 privateCounter 和 函数 changeBy ; 这两个都不能被该除该匿名函数外的其它对象访问到; 而只能通过该匿名函数内的三个公共函数访问;
  3. 因此尽管 makeCounter 的执行已经结束,其返回的对象 counter1 还是能够访问它生成时环境的所有变量;
  4. 最后,尽管这个这是一个共享的环境, 但对于 counter1counter2 而言,它们依旧是独立的。 因此 counter1counter2 访问的 privateCounter 是完全独立的。

如何解决问题 — IIFE [2]

说完 closure ,我们再来谈谈怎么解决开篇的问题。

首先要引入一个概念,”Immediately-Invoked Function Expression”(IIFE).

要理解“什么是IIFE“,先要知道”什么不是IIFE“。

看个简单的例子:

1
function() { return "I'm statement"; };

这段代码定义了一个匿名函数。

使用 node 执行该段代码:

[cipher@Rose test_js]$ node
> var foo = function() { return "I'm statement"; };
undefined
> foo
[Function]

foo 变量指向了匿名函数。

而当使用括号包含 function 关键字时,就变成表达式了:

1
(function() { return "I'm expression"; }());

所以这段代码,不仅定义了一个匿名函数,同时它还是一个表达式。

使用 node 执行:

[cipher@Rose test_js]$ node
> var foo = (function() { return "I'm expression"; }());
undefined
> foo
'I\'m expression'

此时, foo 变量指向了一个字符串。

这便是 Ben Alman 提出的 JavaScript IIFE ,利用

  • JavaScript 中,括号( () )是不能包含语句的。

创建一个IIFE使得表达式立即执行。

根据这个思路,我们可以定义一个IIFE, 将变量 i 保存起来

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var items = [
    {"id": "email"},
    {"id": "name"},
    {"id": "age"},
];

function setupHelp() {
    var helpText = [
            {'id': 'email', 'help': 'Your e-mail address'},
            {'id': 'name', 'help': 'Your full name'},
            {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

    for (var i = 0; i < helpText.length; i++) {
        // What's different?
        (function(lockedIndex) {
            var item = helpText[lockedIndex];

            // Bind help function to this item.
            items[lockedIndex].help = function() {
                console.log(item.help);
            }
        }(i));
    }
}

setupHelp();

for (var i = 0; i < items.length; i++) {
    items[i].help();
}

通过 IIFE ,我们将变量 i “锁在”了变量 lockedIndex 中。

这样, help 指向的匿名函数中引用的 item 变量与 i 就一一对应起来了。

执行修改过的代码的结果:

[cipher@Rose test_js]$ node a.js
Your e-mail address
Your full name
Your age (you must be over 16)

IIFE有两种表达式:

// 推荐使用这一种
(function(parameters){ /* code */ }(arguments));

(function(parameters){ /* code */ })(arguments);

Tip

Argument
An expression in the comma-separated list bounded by the parentheses in a method or instance construction call expression or bounded by the square brackets in an element access expression.
It is also known as a actual argument.
Paramenter
A variable declared as part of a method, instance constructor, operator, or indexer definition, which acquires a value on entry to that function member.
It is also known as formal parameter.

总结

通过一个常见的循环闭包的例子,

我们解释了为何会出现这一类常见的错误:闭包的特性导致,

以及如何解决该类错误:利用IIFE“锁住”变量(还是 JavaScript 的特性)。

同时,还给出了相关知识点的具体解释: closureIIFE .