写点什么

如何把全世界的 Web 浏览器连成一个超级计算机?

2018 年 3 月 14 日

黑客 Ben 尝试了一个非常大胆的想法,通过 WebSocket 将全世界的 Web 浏览器连接在一起,组成一个“超级计算机”,并利用这个超级计算机解决分布式问题。以下内容翻译自作者的博文。

写在前面

我们将讨论一个具有争议性的话题——如何从网站访客的浏览器中“偷”走计算资源。目前有很多讨论是关于如何利用浏览器来挖掘数字货币的,但我不想加入到这些话题的讨论当中,我只是想探讨一种有效利用计算资源的方式。

Web 浏览器执行代码的能力越来越强大。JavaScript 的发展、WebAssembly 的出现、对 GPU 访问能力的提升和线程模型的演变,这些因素组合在一起,让浏览器与计算机一样具备了强大的计算能力。随着浏览器数字货币挖矿机的崛起,我也在思考这样的一个问题:如何把全世界的计算资源整合成一个单独的实体——一台由网站访客的浏览器组成的超级计算机。

就像普通的计算机集群一样,这台超级计算机的所有计算节点在协调之下共同解决一个问题。但与普通的计算机集群不同的是,这些计算节点时临时性的(随着网站访客的来来去去),而且它们之前无法彼此对话(没有跨站点的请求)。

这是我想到的一个例子:

右边是超级计算机控制服务器。左边是访问某个网站的浏览器,它是这个超级计算机中的一个节点,上面还显示了它的CPU 指标。

这个超级计算机要解决的问题是找出某个给定哈希值的原始值。从图上可以看出,总共有23 个节点参与了计算,计算并比对了380,204,032 个哈希值,其中美国的访客贡献了50% 的处理能力。

代码实现

这里主要用WebSocket 技术在服务器和计算节点之间建立持久连接。这些连接用于协调节点的行为,从而让它们成为相互协作的实体。WebSocket 可以传输代码和协作消息,让一切都成为可能。

WebSocket 的出现戏剧性地改变了 Web 客户端的行为。客户端连接到网站上,先执行预先定义好的 JavaScript,等建立起 WebSocket 连接之后,就可以执行其他任意 JavaScript 脚本。

下图右边是超级计算机控制服务器,左边是接收动态指令的 Web 客户端。

如果一款App 使用了WebView,JavaScript 就可以直接跑到App 中,也就是说,通过WebSocket 传输的代码可以跳过WebView,直接进入App 的领地。

下图右边是超级计算机控制服务器,左边是接收指令的Web App。可以看到,指令直接渗透到了App 层。

剩下的就没有什么新鲜的了。App 可以通过C&C 协议(Botnet Command and Control)获取指令,网页在初次加载之后就可以动态获得JavaScript 脚本,而WebSocket 具有真正的动态性(不像Ajax 的轮询拉取模式),可以跨多浏览器和设备运行,而且对运行环境有完全的访问权限。

所以,我们完全可以通过WebSocket 向计算节点传输指令代码,当然也可以用来传递消息,实现分布式协调。

Crackzor.js

六年前,我基于 OpenMPI 开发了一款分布式密码破解器( http://ben.akrin.com/?p=1424 ),叫作 crackzor。密码破解是一个非常典型的分布式问题,说起来很简单,就是通过排列组合字符猜出密码。我使用 JavaScript 重写了 crackzor,使用 WebSocket 替代了 OpenMPI。

不过,每一个分布式问题都是不一样的,crackzor 并不是解决所有问题的良方。crackzor 的魔力在于它的灵活性,它把一个字符排列组合空间拆分成很多个块,再把这些块分摊给计算节点。在给定了要解决的问题以及迭代的起始和结束位置之后,节点就可以开始工作,不需要再为它们提供字符排列组合,这样就不会出现网络带宽瓶颈问题。

第一个问题:如何最大程度利用节点的 CPU

JavaScript 默认使用的是单线程模型,代码通过 WebSocket 传送到客户端,默认情况下只使用了 CPU 的一个核。而现今的大部分计算机 CPU 都是多核的,所以,我们要想办法把这些 CPU 都利用起来。

于是救星出现了——Web Worker。HTML5 提供了这一特性,极大简化了多线程的实现。不过,我们还需要解决一个问题。Web Worker 文档告诉我们要从文件加载脚本文件,但我们的代码是通过 WebSocket 传输过来的,并驻存在内存中,所以我们无法直接通过指定脚本文件的方式来执行代码。

我们通过把代码包装成一个 Blob 对象来解决这个问题:

复制代码
var worker_code = 'alert( "this code is threaded on the nodes" );'
window.URL = window.URL || window.webkitURL;
var blob;
try {
    blob = new Blob([worker_code], {type: 'application/javascript'});
} catch (e) {
    window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
    blob = new BlobBuilder();
    blob.append(worker_code);
    blob = blob.getBlob();
}
workers.push( new Worker(URL.createObjectURL(blob)) ) ;

第二个问题:在节点间分配任务

WebSocket 服务器承担了后续的大部分协调工作,它需要跟踪节点的接入和退出,以及某个节点是否在执行计算任务,并在有可用节点时给它们分配任务。

服务器需要一直处于运行状态,处理来自节点的连接。不过,这台超级计算机可能每天需要解决不同的问题。为此,我写了一个函数用来读取文件,并执行文件中的代码。这个函数通过一个进程来调用。

复制代码
function eval_code_from_file() {
    if( !file_exists("/tmp/code") ) {
        console.log( "error: file /tmp/code does not exist" ) ;
    } else {
        var code = read_file( "/tmp/code" ) ;
        code = code.toString() ;
        eval( code ) ;
    }
}
 
process.on('SIGUSR1', eval_code_from_file.bind() );

有了这个函数,下一次我就可以杀掉旧进程,然后使用新进程加载新代码。这要归功于 JavaScript 的灵活性,这种灵活性让我们可以在任意时刻运行任意代码,只要对运行环境有完全的访问权限。

要给节点分发任务也很简单,只要让客户端在连接到服务器时注册一个回调函数,然后在回调函数里执行代码即可。比如:

客户端:

复制代码
var WebSocket_client=io.connect("http://WebSocket_server.domain.com"); 
WebSocket_client.on( "eval_callback",function(data){data=atob(data),eval(data)}.bind() ) ;

服务器端:

client_socket.emit( "eval_callback", new Buffer("alert('this code will run on the client');").toString("base64") ) ;到目前为止:

  1. 所有的临时节点(网站用户的 Web 浏览器)连接到 WebSocket 服务器上
  2. 通过进程信号让 WebSocket 服务器执行新的代码
  3. 新代码中包含了节点需要解决的新问题
  4. 新代码告诉 WebSocket 服务器如何协调节点
  5. 一旦某个节点解决了问题,接着处理下一个问题

现在我们知道了如何利用 Web 浏览器来构建一台超级计算机。出于多方面的考虑,比如可读性、安全性和复杂性方面的问题,我不想把我的代码全部都公开出来。不过,如果有人感兴趣,可以联系我,我很乐意分享跟多的想法。

更多小建议:

  • 在拆分任务时,任务不能太大。因为节点都是临时性的,如果任务太重,极有可能发生中断。大部分 Web 浏览器会拒绝执行或终止执行太耗资源的代码,而小任务可以在几秒钟之内就完成,不会被打断。
  • 使用 JavaScript 实现 MD5: https://gist.github.com/josedaniel/951664
  • 记录节点解决问题所使用的平均时间,把运行缓慢的节点排除在外,以免影响“超级计算机”的整体性能。

原文链接: http://ben.akrin.com/?p=5997

感谢郭蕾对本文的审校。

2018 年 3 月 14 日 17:421294

评论

发布
暂无评论
发现更多内容

架构师训练营第 1 期第 10 周作业

du tiezheng

极客大学架构师训练营

第六周-作业

jizhi7

智慧公安二维码定位报警系统开发

t13823115967

Python进阶——如何正确使用yield?

Kaito

Python

解密智联招聘的大前端架构Ada

智联大前端

Serverless 前端架构 开发工具 前端工程化 微前端

重点人员管控系统开发,智慧公安系统搭建方案

WX13823153201

重点人员管控系统开发

第六周-总结

jizhi7

极客大学架构师训练营

第 06 周学习总结

Airship

极客大学架构师训练营

源码 | 浅谈Webpack原理,以及loader和plugin实现。

梁龙先森

前端 前端工程化 webpack 前端进阶

接口测试如何在json中引用mock变量

测试人生路

json 接口测试 Mock

Linux 笔记(三): 软件安装

Leo

Linux 学习 前端进阶训练营

Canal 组件简介与 vivo 帐号实践

vivo互联网技术

数据库 分布式 数据存储

除了梦里什么都有之外,我想可以让现实生活中也可以有点什么。

叶小鍵

日本 健康 川村昌嗣 瘦身 走路 运动

shell脚本的使用该熟练起来了,你说呢?(篇一)

良知犹存

Linux shell脚本编写

第四代Express框架koa简介

程序那些事

nodejs 异步编程 koa Express 程序那些事

架构师训练营第 1 期第 10 周总结

du tiezheng

极客大学架构师训练营

第十周学习总结

饭桶

成千上万个站点,日数据过亿的大规模爬虫是怎么实现的?

今日长剑在握

Python redis 爬虫

身为程序员还记得C语言经典算法(附带答案)吗?

ShenDu_Linux

c c++ 算法 编程语言

第十周课后练习

饭桶

CAP理论

DL

CAP原理

2020双11,Dubbo3.0 在考拉的超大规模实践

阿里巴巴云原生

阿里云 开源 云原生 dubbo

第06周 CAP 原理

Airship

极客大学架构师训练营

40 张图带你搞懂 TCP 和 UDP

cxuan

计算机网络 计算机基础 计算机

如何高效的使用并行流

Silently9527

java8 java 并发

智慧公安大数据可视化分析系统搭建

t13823115967

云算力矿机系统开发,区块链挖矿平台搭建

薇電13242772558

区块链 云算力

我是如何使计算时间提速25.6倍的

Lart

Python 代码优化 Numpy 代码加速

Tensorflow 每日练习(2020.1130)

郑可夫斯基

Appium上下文和H5测试(二)

清菡

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

如何把全世界的Web浏览器连成一个超级计算机?-InfoQ