From ef450a8d3856a0c93b769e164efc88f669a3ee56 Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Thu, 10 May 2018 20:56:14 +0800 Subject: [PATCH] 11. --- 11.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/11.md b/11.md index 90c815b..ce2cb13 100644 --- a/11.md +++ b/11.md @@ -211,7 +211,7 @@ function request(nest, target, type, content) { } ``` -因为承诺只能解析(或拒绝)一次,所以这个是有效的。 第一次调用`resolve`或`reject`会决定`Promise`的结果,并且任何进一步的调用(例如请求结束后到达的超时,或在另一个请求结束后返回的请求)都将被忽略。 +因为`Promise`只能解析(或拒绝)一次,所以这个是有效的。 第一次调用`resolve`或`reject`会决定`Promise`的结果,并且任何进一步的调用(例如请求结束后到达的超时,或在另一个请求结束后返回的请求)都将被忽略。 为了构建异步循环,对于重试,我们需要使用递归函数 - 常规循环不允许我们停止并等待异步操作。 `attempt`函数尝试发送请求一次。 它还设置了超时,如果 250 毫秒后没有响应返回,则开始下一次尝试,或者如果这是第四次尝试,则以`Timeout`实例为理由拒绝该`Promise`。 @@ -245,7 +245,7 @@ function requestType(name, handler) { 每台鸟巢计算机在其`neighbors`属性中,都保存了传输距离内的其他鸟巢的数组。 为了检查当前哪些可以访问,您可以编写一个函数,尝试向每个鸟巢发送一个`"ping"`请求(一个简单地请求响应的请求),并查看哪些返回了。 -在处理同时运行的`Promise`集合时,`Promise.all`函数可能很有用。 它返回一个`Promise`,等待数组中的所有`Promise`解决,然后解析这些`Promise`产生的值的数组(与原始数组的顺序相同)。 如果任何`Promise`被拒绝,`Promise.all`的结果本身被拒绝。 +在处理同时运行的`Promise`集合时,`Promise.all`函数可能很有用。 它返回一个`Promise`,等待数组中的所有`Promise`解析,然后解析这些`Promise`产生的值的数组(与原始数组的顺序相同)。 如果任何`Promise`被拒绝,`Promise.all`的结果本身被拒绝。 ```js requestType("ping", () => "pong"); @@ -294,7 +294,7 @@ requestType("gossip", (nest, message, source) => { }); ``` -为了避免永远在网络上发送相同的消息,每个鸟巢都保留一组已经看到的闲话串。 为了定义这个数组,我们使用`everywhere`函数(它在每个鸟巢上运行代码)向鸟巢的状态对象添加一个属性,这是我们将保持鸟巢局部状态的地方。 +为了避免永远在网络上发送相同的消息,每个鸟巢都保留一组已经看到的闲话字符串。 为了定义这个数组,我们使用`everywhere`函数(它在每个鸟巢上运行代码)向鸟巢的状态对象添加一个属性,这是我们将保存鸟巢局部状态的地方。 当一个鸟巢收到一个重复的闲话消息,它会忽略它。每个人都盲目重新发送这些消息时,这很可能发生。 但是当它收到一条新消息时,它会兴奋地告诉它的所有邻居,除了发送消息的那个邻居。 @@ -308,3 +308,95 @@ requestType("gossip", (nest, message, source) => { sendGossip(bigOak, "Kids with airgun in the park"); ``` +## 消息路由 + +如果给定节点想要与其他单个节点通信,泛洪不是一种非常有效的方法。 特别是当网络很大时,这会导致大量无用的数据传输。 + +另一种方法是为消息设置节点到节点的传输方式,直到它们到达目的地。 这样做的困难在于,它需要网络布局的知识。 为了向远方的鸟巢发送请求,有必要知道哪个邻近的鸟巢更靠近其目的地。 以错误的方向发送它不会有太大好处。 + +由于每个鸟巢只知道它的直接邻居,因此它没有计算路线所需的信息。 我们必须以某种方式,将这些连接的信息传播给所有鸟巢。 当放弃或建造新的鸟巢时,最好是允许它随时间改变的方式。 + +我们可以再次使用泛洪,但不检查给定的消息是否已经收到,而是检查对于给定鸟巢来说,邻居的新集合,是否匹配我们拥有的当前集合。 + +```js +requestType("connections", (nest, {name, neighbors}, + source) => { + let connections = nest.state.connections; + if (JSON.stringify(connections.get(name)) == + JSON.stringify(neighbors)) return; + connections.set(name, neighbors); + broadcastConnections(nest, name, source); +}); + +function broadcastConnections(nest, name, exceptFor = null) { + for (let neighbor of nest.neighbors) { + if (neighbor == exceptFor) continue; + request(nest, neighbor, "connections", { + name, + neighbors: nest.state.connections.get(name) + }); + } +} + +everywhere(nest => { + nest.state.connections = new Map; + nest.state.connections.set(nest.name, nest.neighbors); + broadcastConnections(nest, nest.name); +}); +``` + +该比较使用`JSON.stringify`,因为对象或数组上的`==`只有在两者完全相同时才返回`true`,这不是我们这里所需的。 比较 JSON 字符串是比较其内容的一种简单而有效的方式。 + +节点立即开始广播它们的连接,它们应该立即为每个鸟巢提供当前网络图的映射,除非有一些鸟巢完全无法到达。 + +你可以用图做的事情,就是找到里面的路径,就像我们在第 7 章中看到的那样。如果我们有一条通往消息目的地的路线,我们知道将它发送到哪个方向。 + +这个`findRoute`函数非常类似于第 7 章中的`findRoute`,它搜索到达网络中给定节点的路线。 但不是返回整个路线,而是返回下一步。 下一个鸟巢将使用它的有关网络的当前信息,来决定将消息发送到哪里。 + +```js +function findRoute(from, to, connections) { + let work = [{at: from, via: null}]; + for (let i = 0; i < work.length; i++) { + let {at, via} = work[i]; + for (let next of connections.get(at) || []) { + if (next == to) return via; + if (!work.some(w => w.at == next)) { + work.push({at: next, via: via || next}); + } + } + } + return null; +} +``` + +现在我们可以建立一个可以发送长途信息的函数。 如果该消息被发送给直接邻居,它将照常发送。 如果不是,则将其封装在一个对象中,并使用`"route"`请求类型,将其发送到更接近目标的邻居,这将导致该邻居重复相同的行为。 + +```js +function routeRequest(nest, target, type, content) { + if (nest.neighbors.includes(target)) { + return request(nest, target, type, content); + } else { + let via = findRoute(nest.name, target, + nest.state.connections); + if (!via) throw new Error(`No route to ${target}`); + return request(nest, via, "route", + {target, type, content}); + } +} + +requestType("route", (nest, {target, type, content}) => { + return routeRequest(nest, target, type, content); +}); +``` + +我们现在可以将消息发送到教堂塔楼的鸟巢中,它的距离有四跳。 + +```js +routeRequest(bigOak, "Church Tower", "note", + "Incoming jackdaws!"); +``` + +我们已经在原始通信系统的基础上构建了几层功能,来使其便于使用。 这是一个(尽管是简化的)真实计算机网络工作原理的很好的模型。 + +计算机网络的一个显着特点是它们不可靠 - 建立在它们之上的抽象可以提供帮助,但是不能抽象出网络故障。所以网络编程通常关于预测和处理故障。 +