Commit 83cd16fc authored by 神楽坂玲奈's avatar 神楽坂玲奈

init

parent 9286e781
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
{
"printWidth": 140,
"singleQuote": true
}
dgram = require 'dgram'
assert = require 'assert'
http = require 'http'
Quality = require './quality'
server_port = 495
timeout = 10
regions = [
{id: 0, name: "default"},
{id: 1, name: "ap"},
{id: 2, name: "cn"}
]
gateways = {
2: {
2: {server_id: 2, region_id: 2, delay: 0, jitter: 0, reliability: 1}
},
3: {
0: {server_id: 3, region_id: 0, delay: 0.1, jitter: 0, reliability: 1},
1: {server_id: 3, region_id: 1, delay: 0, jitter: 0, reliability: 1}
},
4: {
0: {server_id: 4, region_id: 0, delay: 0, jitter: 0, reliability: 1}
},
6: {
2: {server_id: 6, region_id: 2, delay: 0, jitter: 0, reliability: 1}
},
7: {
0: {server_id: 7, region_id: 0, delay: 0.1, jitter: 0, reliability: 1},
1: {server_id: 7, region_id: 1, delay: 0, jitter: 0, reliability: 1}
}
}
servers = {
2: {id: 2, inbound_cost: 0, outbound_cost: 0.8},
3: {id: 3, inbound_cost: 0, outbound_cost: 0.05},
4: {id: 4, inbound_cost: 0, outbound_cost: 0.03},
6: {id: 6, inbound_cost: 0, outbound_cost: 0.8},
7: {id: 7, inbound_cost: 0, outbound_cost: 1},
}
sequence = 0
updating = null
timeout_timer = null
socket = dgram.createSocket('udp4')
route_quality = (from, to, next_hop = servers[from].next_hop[to])->
assert next_hop?
assert from != next_hop
assert from != to
result = new Quality()
current = from
next = next_hop
route = [current, next]
while true
quality = servers[next].quality[current]
return Quality.unreachable if !quality or quality.reliability <= 0 # 网络不通,视为黑洞
result.concat(quality.delay, quality.jitter, quality.reliability, servers[current].outbound_cost + servers[next].inbound_cost)
return result if next == to # 到达
# 寻找下一跳
current = next
next = servers[current].next_hop[to]
assert next #to server_id 型路由,由于 server 两两相连,下一跳一定是存在的,最坏情况也是直达
if next in route # 环路
return Quality.unreachable
else
route.push next
route_metric = (from, to, next_hop)->
route_quality(from, to, next_hop).metric()
gateway_metric = (from, to, gateway)->
quality = gateways[gateway][to]
assert quality
result = new Quality(quality.delay, quality.jitter, quality.reliability, servers[gateway].outbound_cost + servers[gateway].inbound_cost)
if from != gateway
result.concat route_quality(from, gateway)
result.concat route_quality(gateway, from)
result.metric()
update_route = (server, to)->
# 计算当前和最优路线
if to.name? # is region
type = 'gateway'
current_route = server[type][to.id]
if current_route
current_metric = gateway_metric(server.id, to.id, current_route)
else
current_metric = Number.POSITIVE_INFINITY
best_route = current_route
best_metric = current_metric
for index, route_server of servers when route_server.id != current_route and gateways[route_server.id]? and gateways[route_server.id][to.id]
metric = gateway_metric(server.id, to.id, route_server.id)
if metric < best_metric
best_route = route_server.id
best_metric = metric
else
assert(to.inbound_cost?) # is server
type = 'next_hop'
current_route = server[type][to.id]
assert(current_route) # 对于 to server 型路由,下一跳一定是存在的
current_metric = route_metric(server.id, to.id, current_route)
best_route = current_route
best_metric = current_metric
for index, route_server of servers when route_server.id != current_route and route_server.id != server.id # 对于 to server型路由,下一跳是自己是没有意义的
metric = route_metric(server.id, to.id, route_server.id)
if metric < best_metric
best_route = route_server.id
best_metric = metric
# 决定是否变更
now = new Date().getTime() / 1000
if current_route != best_route and (current_metric is Number.POSITIVE_INFINITY or (current_metric - best_metric - 0.01) * (now - server[type+'_updated_at'][to.id]) > 10)
console.log server[type+'_updated_at'][to.id],server[type+'_updated_at'], to.id
console.log "update ##{sequence}: #{type} server #{server.id} to #{to.id}: #{current_route}(#{current_metric}) -> #{best_route}(#{best_metric}) age #{now - server[type+'_updated_at'][to.id]}"
server[type][to.id] = best_route
server[type+'_updated_at'][to.id] = now
updating = setInterval ->
send_route server, to.id, best_route, type
, 1000
send_route server, to.id, best_route, type
timeout_timer = setTimeout ->
delete server.address
delete server.port
clearInterval(updating)
updating = null
console.log "server#{server.id} lost connection."
, timeout * 1000
return true
false
send_route = (server, to, route, type)->
message = {sequence: sequence, to: to}
message[type] = route
message = JSON.stringify message
socket.send message, 0, message.length, server.port, server.address
reset_route = (server)->
now = new Date().getTime() / 1000
server.quality = {}
server.next_hop = {}
server.next_hop_updated_at = {}
server.gateway = {}
server.gateway_updated_at = {}
for i,s of servers
server.next_hop[s.id] = s.id
server.next_hop_updated_at[s.id] = now
for region_id, region of regions when gateways[server.id]? and gateways[server.id][region_id]?
server.gateway[region_id] = server.id
server.gateway_updated_at[region_id] = now
socket.on 'message', (message, rinfo) ->
message = JSON.parse(message)
server = servers[message.server_id]
return unless server?
if server.address != rinfo.address or server.port != rinfo.port # 重置 server 状态
server.address = rinfo.address
server.port = rinfo.port
console.log "server #{server.id} connected from #{server.address}:#{server.port}"
reset_route(server)
server.quality = message.quality
#console.log updating
if updating? and message.acknowledgement == sequence
#console.log "ack ##{sequence}: server#{server.id}"
clearInterval(updating)
clearTimeout(timeout_timer)
updating = null
sequence += 1
if !updating?
for i, server of servers when server.address?
for j, to_server of servers when server.id != to_server.id
return if update_route(server, to_server)
for j, to_region of regions
return if update_route(server, to_region)
for server_id, server of servers
reset_route(server)
socket.bind server_port
http.createServer (req, res)->
res.writeHead 200, 'Content-Type': 'application/json'
res.end JSON.stringify(servers)
.listen server_port
\ No newline at end of file
// Generated by CoffeeScript 1.9.3
(function() {
var Quality, assert, dgram, gateway_metric, gateways, http, regions, reset_route, route_metric, route_quality, send_route, sequence, server, server_id, server_port, servers, socket, timeout, timeout_timer, update_route, updating,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
dgram = require('dgram');
assert = require('assert');
http = require('http');
Quality = require('./quality');
server_port = 495;
timeout = 10;
regions = [
{
id: 0,
name: "default"
}, {
id: 1,
name: "ap"
}, {
id: 2,
name: "cn"
}
];
gateways = {
2: {
2: {
server_id: 2,
region_id: 2,
delay: 0,
jitter: 0,
reliability: 1
}
},
3: {
0: {
server_id: 3,
region_id: 0,
delay: 0.1,
jitter: 0,
reliability: 1
},
1: {
server_id: 3,
region_id: 1,
delay: 0,
jitter: 0,
reliability: 1
}
},
4: {
0: {
server_id: 4,
region_id: 0,
delay: 0,
jitter: 0,
reliability: 1
}
},
6: {
2: {
server_id: 6,
region_id: 2,
delay: 0,
jitter: 0,
reliability: 1
}
},
7: {
0: {
server_id: 7,
region_id: 0,
delay: 0.1,
jitter: 0,
reliability: 1
},
1: {
server_id: 7,
region_id: 1,
delay: 0,
jitter: 0,
reliability: 1
}
}
};
servers = {
2: {
id: 2,
inbound_cost: 0,
outbound_cost: 0.8
},
3: {
id: 3,
inbound_cost: 0,
outbound_cost: 0.05
},
4: {
id: 4,
inbound_cost: 0,
outbound_cost: 0.03
},
6: {
id: 6,
inbound_cost: 0,
outbound_cost: 0.8
},
7: {
id: 7,
inbound_cost: 0,
outbound_cost: 1
}
};
sequence = 0;
updating = null;
timeout_timer = null;
socket = dgram.createSocket('udp4');
route_quality = function(from, to, next_hop) {
var current, next, quality, result, route;
if (next_hop == null) {
next_hop = servers[from].next_hop[to];
}
assert(next_hop != null);
assert(from !== next_hop);
assert(from !== to);
result = new Quality();
current = from;
next = next_hop;
route = [current, next];
while (true) {
quality = servers[next].quality[current];
if (!quality || quality.reliability <= 0) {
return Quality.unreachable;
}
result.concat(quality.delay, quality.jitter, quality.reliability, servers[current].outbound_cost + servers[next].inbound_cost);
if (next === to) {
return result;
}
current = next;
next = servers[current].next_hop[to];
assert(next);
if (indexOf.call(route, next) >= 0) {
return Quality.unreachable;
} else {
route.push(next);
}
}
};
route_metric = function(from, to, next_hop) {
return route_quality(from, to, next_hop).metric();
};
gateway_metric = function(from, to, gateway) {
var quality, result;
quality = gateways[gateway][to];
assert(quality);
result = new Quality(quality.delay, quality.jitter, quality.reliability, servers[gateway].outbound_cost + servers[gateway].inbound_cost);
if (from !== gateway) {
result.concat(route_quality(from, gateway));
result.concat(route_quality(gateway, from));
}
return result.metric();
};
update_route = function(server, to) {
var best_metric, best_route, current_metric, current_route, index, metric, now, route_server, type;
if (to.name != null) {
type = 'gateway';
current_route = server[type][to.id];
if (current_route) {
current_metric = gateway_metric(server.id, to.id, current_route);
} else {
current_metric = Number.POSITIVE_INFINITY;
}
best_route = current_route;
best_metric = current_metric;
for (index in servers) {
route_server = servers[index];
if (!(route_server.id !== current_route && (gateways[route_server.id] != null) && gateways[route_server.id][to.id])) {
continue;
}
metric = gateway_metric(server.id, to.id, route_server.id);
if (metric < best_metric) {
best_route = route_server.id;
best_metric = metric;
}
}
} else {
assert(to.inbound_cost != null);
type = 'next_hop';
current_route = server[type][to.id];
assert(current_route);
current_metric = route_metric(server.id, to.id, current_route);
best_route = current_route;
best_metric = current_metric;
for (index in servers) {
route_server = servers[index];
if (!(route_server.id !== current_route && route_server.id !== server.id)) {
continue;
}
metric = route_metric(server.id, to.id, route_server.id);
if (metric < best_metric) {
best_route = route_server.id;
best_metric = metric;
}
}
}
now = new Date().getTime() / 1000;
if (current_route !== best_route && (current_metric === Number.POSITIVE_INFINITY || (current_metric - best_metric - 0.01) * (now - server[type + '_updated_at'][to.id]) > 10)) {
console.log(server[type + '_updated_at'][to.id], server[type + '_updated_at'], to.id);
console.log("update #" + sequence + ": " + type + " server " + server.id + " to " + to.id + ": " + current_route + "(" + current_metric + ") -> " + best_route + "(" + best_metric + ") age " + (now - server[type + '_updated_at'][to.id]));
server[type][to.id] = best_route;
server[type + '_updated_at'][to.id] = now;
updating = setInterval(function() {
return send_route(server, to.id, best_route, type);
}, 1000);
send_route(server, to.id, best_route, type);
timeout_timer = setTimeout(function() {
delete server.address;
delete server.port;
clearInterval(updating);
updating = null;
return console.log("server" + server.id + " lost connection.");
}, timeout * 1000);
return true;
}
return false;
};
send_route = function(server, to, route, type) {
var message;
message = {
sequence: sequence,
to: to
};
message[type] = route;
message = JSON.stringify(message);
return socket.send(message, 0, message.length, server.port, server.address);
};
reset_route = function(server) {
var i, now, region, region_id, results, s;
now = new Date().getTime() / 1000;
server.quality = {};
server.next_hop = {};
server.next_hop_updated_at = {};
server.gateway = {};
server.gateway_updated_at = {};
for (i in servers) {
s = servers[i];
server.next_hop[s.id] = s.id;
server.next_hop_updated_at[s.id] = now;
}
results = [];
for (region_id in regions) {
region = regions[region_id];
if (!((gateways[server.id] != null) && (gateways[server.id][region_id] != null))) {
continue;
}
server.gateway[region_id] = server.id;
results.push(server.gateway_updated_at[region_id] = now);
}
return results;
};
socket.on('message', function(message, rinfo) {
var i, j, server, to_region, to_server;
message = JSON.parse(message);
server = servers[message.server_id];
if (server == null) {
return;
}
if (server.address !== rinfo.address || server.port !== rinfo.port) {
server.address = rinfo.address;
server.port = rinfo.port;
console.log("server " + server.id + " connected from " + server.address + ":" + server.port);
reset_route(server);
}
server.quality = message.quality;
if ((updating != null) && message.acknowledgement === sequence) {
clearInterval(updating);
clearTimeout(timeout_timer);
updating = null;
sequence += 1;
}
if (updating == null) {
for (i in servers) {
server = servers[i];
if (!(server.address != null)) {
continue;
}
for (j in servers) {
to_server = servers[j];
if (server.id !== to_server.id) {
if (update_route(server, to_server)) {
return;
}
}
}
for (j in regions) {
to_region = regions[j];
if (update_route(server, to_region)) {
return;
}
}
}
}
});
for (server_id in servers) {
server = servers[server_id];
reset_route(server);
}
socket.bind(server_port);
http.createServer(function(req, res) {
res.writeHead(200, {
'Content-Type': 'application/json'
});
return res.end(JSON.stringify(servers));
}).listen(server_port);
}).call(this);
//# sourceMappingURL=main.js.map
{
"version": 3,
"file": "main.js",
"sourceRoot": "",
"sources": [
"main.coffee"
],
"names": [],
"mappings": "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"
}
\ No newline at end of file
{
"name": "application-name",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "application-name",
"version": "0.0.1",
"dependencies": {
"@types/lodash": "^4.14.172",
"lodash": "^4.17.21",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
},
"devDependencies": {
"@types/node": "^14.14.19"
}
},
"node_modules/@types/lodash": {
"version": "4.14.172",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.172.tgz",
"integrity": "sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw=="
},
"node_modules/@types/node": {
"version": "14.14.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz",
"integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==",
"dev": true
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
},
"node_modules/buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/ts-node": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
"dependencies": {
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"typescript": ">=2.7"
}
},
"node_modules/typescript": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"engines": {
"node": ">=6"
}
}
},
"dependencies": {
"@types/lodash": {
"version": "4.14.172",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.172.tgz",
"integrity": "sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw=="
},
"@types/node": {
"version": "14.14.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz",
"integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==",
"dev": true
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"ts-node": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
"requires": {
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
}
},
"typescript": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg=="
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
}
}
}
{ {
"name": "application-name", "name": "application-name",
"version": "0.0.1" "version": "0.0.1",
"devDependencies": {
"@types/node": "^14.14.19"
},
"dependencies": {
"@types/lodash": "^4.14.172",
"lodash": "^4.17.21",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
}
} }
assert = require 'assert'
module.exports = class Quality
constructor: (@delay=0, @jitter=0, @reliability=1, @cost=0)->
@unreachable: new Quality(0,0,0,0)
concat: (delay, jitter, reliability, cost)->
if delay instanceof Quality
@concat(delay.delay, delay.jitter, delay.reliability, delay.cost)
else
@delay += delay
@jitter += jitter
@reliability *= reliability
@cost += cost
this
# 若 reliability = 0,metric 应为 +∞
# 对于两条路线,同时连接任何一个相同的(reliability > 0的)线路,大小关系不变
metric: ()->
assert(@jitter >= 0)
assert(0 <= @reliability <= 1)
assert(@cost >= 0)
if @reliability == 0
Number.POSITIVE_INFINITY
else
@delay + (1 - @reliability) * 6 + @cost * 0.1
// Generated by CoffeeScript 1.9.3
(function() {
var Quality, assert;
assert = require('assert');
module.exports = Quality = (function() {
function Quality(delay1, jitter1, reliability1, cost1) {
this.delay = delay1 != null ? delay1 : 0;
this.jitter = jitter1 != null ? jitter1 : 0;
this.reliability = reliability1 != null ? reliability1 : 1;
this.cost = cost1 != null ? cost1 : 0;
}
Quality.unreachable = new Quality(0, 0, 0, 0);
Quality.prototype.concat = function(delay, jitter, reliability, cost) {
if (delay instanceof Quality) {
this.concat(delay.delay, delay.jitter, delay.reliability, delay.cost);
} else {
this.delay += delay;
this.jitter += jitter;
this.reliability *= reliability;
this.cost += cost;
}
return this;
};
Quality.prototype.metric = function() {
var ref;
assert(this.jitter >= 0);
assert((0 <= (ref = this.reliability) && ref <= 1));
assert(this.cost >= 0);
if (this.reliability === 0) {
return Number.POSITIVE_INFINITY;
} else {
return this.delay + (1 - this.reliability) * 6 + this.cost * 0.1;
}
};
return Quality;
})();
}).call(this);
//# sourceMappingURL=quality.js.map
{
"version": 3,
"file": "quality.js",
"sourceRoot": "",
"sources": [
"quality.coffee"
],
"names": [],
"mappings": ";AAAA;AAAA,MAAA;;EAAA,MAAA,GAAS,OAAA,CAAQ,QAAR;;EAET,MAAM,CAAC,OAAP,GAAuB;IACR,iBAAC,MAAD,EAAW,OAAX,EAAsB,YAAtB,EAAsC,KAAtC;MAAC,IAAC,CAAA,yBAAD,SAAO;MAAG,IAAC,CAAA,2BAAD,UAAQ;MAAG,IAAC,CAAA,qCAAD,eAAa;MAAG,IAAC,CAAA,uBAAD,QAAM;IAA5C;;IAEb,OAAC,CAAA,WAAD,GAAkB,IAAA,OAAA,CAAQ,CAAR,EAAU,CAAV,EAAY,CAAZ,EAAc,CAAd;;sBAGlB,MAAA,GAAQ,SAAC,KAAD,EAAQ,MAAR,EAAgB,WAAhB,EAA6B,IAA7B;MACN,IAAG,KAAA,YAAiB,OAApB;QACE,IAAC,CAAA,MAAD,CAAQ,KAAK,CAAC,KAAd,EAAqB,KAAK,CAAC,MAA3B,EAAmC,KAAK,CAAC,WAAzC,EAAsD,KAAK,CAAC,IAA5D,EADF;OAAA,MAAA;QAGE,IAAC,CAAA,KAAD,IAAU;QACV,IAAC,CAAA,MAAD,IAAW;QACX,IAAC,CAAA,WAAD,IAAgB;QAChB,IAAC,CAAA,IAAD,IAAS,KANX;;aAOA;IARM;;sBAYR,MAAA,GAAQ,SAAA;AACN,UAAA;MAAA,MAAA,CAAO,IAAC,CAAA,MAAD,IAAW,CAAlB;MACA,MAAA,CAAO,CAAA,CAAA,WAAK,IAAC,CAAA,YAAN,OAAA,IAAqB,CAArB,CAAP;MACA,MAAA,CAAO,IAAC,CAAA,IAAD,IAAS,CAAhB;MACA,IAAG,IAAC,CAAA,WAAD,KAAgB,CAAnB;eACE,MAAM,CAAC,kBADT;OAAA,MAAA;eAGE,IAAC,CAAA,KAAD,GAAS,CAAC,CAAA,GAAI,IAAC,CAAA,WAAN,CAAA,GAAqB,CAA9B,GAAkC,IAAC,CAAA,IAAD,GAAQ,IAH5C;;IAJM;;;;;AApBV"
}
\ No newline at end of file
[
{ "id": 2, "inbound_cost": 0, "outbound_cost": 0.8 },
{ "id": 3, "inbound_cost": 0, "outbound_cost": 0.05 },
{ "id": 4, "inbound_cost": 0, "outbound_cost": 0.03 },
{ "id": 6, "inbound_cost": 0, "outbound_cost": 0.8 },
{ "id": 7, "inbound_cost": 0, "outbound_cost": 1 }
]
import assert from 'assert';
export class Quality {
static unreachable = new Quality(0, 0, 0, 0);
constructor(public delay = 0, public jitter = 0, public reliability = 1, public cost = 0) {}
concat(delay: number, jitter: number, reliability: number, cost: number) {
this.delay += delay;
this.jitter += jitter;
this.reliability *= reliability;
this.cost += cost;
return this;
}
// 若 reliability = 0,metric 应为 +∞
// 对于两条路线,同时连接任何一个相同的(reliability > 0的)线路,大小关系不变
metric() {
assert(this.jitter >= 0);
assert(0 <= this.reliability && this.reliability <= 1);
assert(this.cost >= 0);
if (this.reliability === 0) {
return Infinity;
} else {
// 公式
return this.delay + (1 - this.reliability) * 6 + this.cost * 0.1;
}
}
}
import { RemoteInfo } from 'dgram';
import { PeerQuality } from './protocol';
import servers from '../servers.json';
import assert from 'assert';
import { Quality } from './Quality';
export class Router {
static all: Router[] = servers.map(s => Object.assign(new Router(), s));
// config
id!: number;
inbound_cost!: number;
outbound_cost!: number;
//report
peers: Record<number, PeerQuality> = {};
// routing
next_hop: Map<Router, Router> = new Map();
rinfo?: RemoteInfo;
reset() {
this.peers = {};
for (const router of Router.all) {
this.next_hop.set(router, router);
}
}
lost() {
this.rinfo = undefined;
return console.log(`router ${this.id} lost connection.`);
}
route_quality(to: Router, next_hop = this.next_hop.get(to)!) {
assert(next_hop != null);
assert(this !== next_hop);
assert(this !== to);
const result = new Quality();
let current: Router = this;
let next = next_hop;
const route = [current, next];
while (true) {
const quality = next.peers[current.id];
if (!quality || quality.reliability <= 0) return Quality.unreachable; // 不通
result.concat(quality.delay, quality.jitter, quality.reliability, current.outbound_cost + current.inbound_cost);
if (next === to) return result; // 到达
// 寻找下一跳
current = next;
next = current.next_hop.get(to)!;
assert(next); //to server_id 型路由,由于 server 两两相连,下一跳一定是存在的,至少能直达
if (route.includes(next)) {
// 环路
return Quality.unreachable;
} else {
route.push(next);
}
}
}
}
for (const router of Router.all) {
router.reset();
}
import dgram from 'dgram';
import http from 'http';
import peers from '../servers.json';
import { ServerMessage } from './protocol';
import assert from 'assert';
import { Router } from './Router';
import _ from 'lodash';
const server_port = 495;
const timeout = 10;
let sequence = 0;
let updating: any = null;
let timeout_timer: any = null;
export function send_route(router: Router, to: Router, next_hop: Router) {
const message = { sequence, to: to.id, next_hop: next_hop.id };
return socket.send(JSON.stringify(message), router.rinfo!.port, router.rinfo!.address);
}
const socket = dgram
.createSocket('udp4')
.on('message', function(message, rinfo) {
try {
const hello: ServerMessage = JSON.parse(message.toString());
assert(hello.id);
const router: Router = Router.all.find(r => r.id === hello.id)!;
assert(router);
if (_.isEqual(rinfo, router.rinfo)) {
router.rinfo = rinfo;
router.reset();
}
router.peers = hello.peers;
if (updating) {
if (hello.ack === sequence) {
clearInterval(updating);
clearTimeout(timeout_timer);
updating = null;
sequence++;
}
return;
}
} catch (e) {
console.warn(e);
}
for (const from of Router.all) {
for (const to of Router.all.filter(r => r.id !== from.id)) {
// 计算最优下一跳
const best_route = _.minBy(Router.all.filter(router => router.id !== from.id), (router) => from.route_quality(to, router).metric())!;
// 变更
if (from.next_hop.get(to) !== best_route) {
from.next_hop.set(to, best_route);
updating = setInterval(() => send_route(from, to, best_route), 1000);
send_route(from, to, best_route);
timeout_timer = setTimeout(function() {
from.lost();
clearInterval(updating);
updating = null;
}, timeout * 1000);
return;
}
}
}
});
socket.bind(server_port);
//web management api
http
.createServer(function(req, res) {
res.writeHead(200, { 'Content-Type': 'application/json' });
return res.end(JSON.stringify(peers));
})
.listen(server_port);
// setInterval(refresh(socket, routers), 1000)
export interface PeerQuality {
delay: number;
jitter: number;
reliability: number;
}
export interface ServerMessage {
id: number;
ack: number;
peers: PeerQuality[];
}
{
"compilerOptions": {
"module": "commonjs",
"target": "esnext",
"sourceMap": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"strict": true
},
"exclude": ["node_modules"]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment