<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="utf-8" />
<meta name="referrer" content="never">
<title>JavaScript 请求服务负载均衡,仿 nginx upstream</title>
</head>
<body>
<h4>请打开控制台或按F12</h4>
<button id="btn1">可用的源</button>
<button id="btn2">最快的源</button>
<button id="btn3">自定义超时</button>
<button id="btn4">获取天气</button>
<div id="result"></div>
</body>
</html>
#result{
color:#fff;
overflow: auto;
margin-top: 30px;
max-height: 300px;
background-color: #333;
padding: 0 15px;
}
/*
upstream: From nginx upstream
Source must support cross-domain
v1.0.1
by netnr
2021-01-15
*/
(function (window) {
var ups = function (hosts, callback, timeout) {
//全局对象、默认请求超时、默认源过期
var gk = "upstreamCache", dto = 3000, es = 30000;
if (!(gk in window)) {
try {
window[gk] = JSON.parse(localStorage.getItem(gk)) || {};
} catch (e) {
window[gk] = {}
}
}
var startTime = new Date().valueOf(),
cacheKey = hosts.join(','),
hostsCache = window[gk][cacheKey];
if (hostsCache && hostsCache.ok.length && startTime - hostsCache.date < es) {
callback(hostsCache.ok[0], hostsCache.ok, true);
} else {
var ok = [], bad = 0, i = 0, len = hosts.length;
for (; i < len;) {
var host = hosts[i++];
//自动补齐链接
host = host.trim().toLowerCase().indexOf("//") >= 0 ? host : "//" + host;
//发起fetch,添加成功的url(该url与hosts可能不一样),须支持跨域请求
fetch(host).then(function (res) {
res.ok ? ok.push(res.url) : bad++;
}).catch(() => bad++)
}
var si = setInterval(function () {
var isc = false, now = new Date().valueOf();
//当timeout为1,返回最快可用的host
if (timeout == 1 && ok.length > 0) {
isc = true;
}
//所有请求结束 或 超时,返回结果
var istimeout = now - startTime > ((timeout == 1 || !timeout) ? dto : timeout);
if (ok.length + bad == len || istimeout) {
isc = true;
}
if (isc) {
clearInterval(si);
window[gk][cacheKey] = { date: now, ok: ok };
localStorage.setItem(gk, JSON.stringify(window[gk]));
callback(ok[0], ok, false);
}
}, 1)
}
}
window.upstream = ups;
return ups;
})(window);
//示例
var hosts = ["cors.eu.org", "api.github.com"];
document.getElementById("btn1").onclick = function () {
upstream(hosts, function (fast, ok) {
console.log(fast, ok)
log(fast, ok)
});
}
document.getElementById("btn2").onclick = function () {
upstream(hosts, function (fast, ok) {
console.log(fast, ok)
log(fast, ok)
}, 1);
}
document.getElementById("btn3").onclick = function () {
upstream(hosts, function (fast, ok) {
console.log(fast, ok)
log(fast, ok)
}, 280);
}
document.getElementById("btn4").onclick = function () {
upstream(["https://cors.eu.org/", "https://netnr.zme.ink/api/v1/Proxy?url="], function (fast) {
var api = "http://wthrcdn.etouch.cn/weather_mini?citykey=101040100";
var url = fast + api;
fetch(url).then(res => res.json()).then(function (data) {
console.log(data);
log(data);
});
}, 1);
}
function log() {
var ret = document.getElementById("result");
for (var i = 0; i < arguments.length; i++) {
var msg = arguments[i];
if (typeof msg != "string") {
msg = JSON.stringify(msg);
}
var pre = document.createElement("pre");
pre.innerHTML = msg;
ret.appendChild(pre);
}
ret.appendChild(document.createElement("hr"))
ret.scrollTo(0, 9999999);
}