我们看到很多关于在浏览器里使用js-ipfs的问题。这篇文章展示了用js-ipfs搭建最小化的聊天应用的例子,这个应用可以在浏览器中运行。它使用WebRTC去实现浏览器对浏览器的连接(在可用时),如不可用则使用回路中继(Circuit Relay)去连接浏览器节点。消息的传递是通过libp2p的pubsub功能实现。
获取代码
你可以在这里(https://ipfs.io/ipfs/bafybeia5f2yk6td7ciroeped2uwfivo333b524t3zmoderfhl3xn7wi7aa/)查看演示。如果你想要一个可以自行编辑的本地拷贝,可以使用IPFS下载整个目录:
然后,只要在浏览器中打开index.html,就可以立即自动连接到节点并寻找连接资源。
你也可以在GitHub上分叉 heDiscordian/browser-ipfs-chat(https://github.com/TheDiscordian/browser-ipfs-chat) 项目,就可以立刻开始测试了!如果你想部署自己的版本,只要编辑index.html并遵循以下的设置信息:
在这个例子中使用的库是js-ipfs(https://github.com/ipfs/js-ipfs/blob/master/docs/BROWSERS.md) 和 Bootstrap (只包含了最小化的css样式文件)。如果你想要一个新版本的js-ipfs,可以下载这个(https://cdn.jsdelivr.net/npm/ipfs/dist/index.min.js) 以获得最新的可用版本 .
让我们看一下这个过程的工作原理。
目录表
* 节点发现和连接(#-peer-discovery-and-connectivity)
* Docker容器 (可选)(#-docker-optional)
* 创建一个存储卷(#create-a-volume)
* 配置域名(#configure-a-domain)
* 运行容器(#running-the-container)
* WebRTC-Star(#-webrtc-star)
* 使用(#usage)
* 设置(#setup)
* ⚡ p2p-circuit(#⚡-p2p-circuit)
* 使用(#usage-2)
* 设置(#setup-2)
* 公告(#advertising)
* SSL证书 (Nginx)(#-ssl-nginx)
* 通讯(#-communication)
* PubSub(#-pubsub)
* ⚠️ 可能存在的浏览器问题(#⚠️-possible-browser-pitfalls)
* 保持与节点的连接(#staying-connected-to-peers)
* 保持与回路中继的连接(#staying-connected-to-the-circuit-relay)
* 总结(#-conclusion)
节点发现和连接
在浏览器中,发现和连接到节点可能是有难度的,因为我们无法监听新节点,也没法访问分布式哈希表(DHT)。为了实现在浏览器中运行的最佳体验,理解如何寻找节点和保持与其的连接是很重要的。
聊天应用的例子通过两种方式实现此目标。使用WebRTC-Star,我们实现了直接的浏览器对浏览器通讯,并配置了两者之间的回路中继。这个聊天应用也在左上方配置了一个状态指示器,让你知道自己的连接种类。绿色表示你连接到了中继(即便是通过另一个节点来连接);黄色表示你只看到直接连接的节点;红色表示你没有连接到节点(至少在聊天应用中没有连接)。
网络图表展示路径中的节点可以彼此间发现和通讯(https://ipfs.io/ipfs/QmX2og5BKJCMVaebEm9ZGsACEYExoGqxhJjePKNc2mZ2pE "Browser IPFS network graph")
上图展示了一个有3名用户的网络是什么样子的。值得注意的是浏览器节点也可以与 go-ipfs节点通讯。因此,浏览器C并不需要是一个浏览器,也可以是一个go-ipfs节点。
Docker容器 (可选)
如果你不想使用Docker容器,可以直接跳到WebRTC-Star(#-webrtc-star) 的章节。
在这个章节后,我们会涵盖WebRTC-Star和回路中继的作用,以及相关的设置方法。不过,如果你想通过Docker快速上手,我已经准备了一个可用的镜像。它可能不是最佳的长期解决方案,不过如果你只是想快速上手和进行实验的话,就是很好的方式了。
创建一个存储卷(volume)
首先,创建一个存储卷去存储密钥和节点数据这样的长期数据。
配置一个域名
你需要一个域名和SSl证书以在浏览器节点里使用这个套件。下面有两个选项:第一个会运行certbot证书机器人程序并自动获取域名证书。另一个选项不会处理SSl证书,你需要将9091端口反向代理到9090端口(SSL),且4011端口反向代理到4430端口(SSL)。
你可以选择其中一种方式,然后你的IPFS节点会初始化并提供像 `PeerID(节点ID)` 和回路中继地址这样的信息。记住,你想将这个信息编辑到聊天客户端里,这样可以使用自己的节点 (参考 WebRTC-Star 使用(#usage) and p2p-circuit 使用(#usage-2) 以获得示例,或者编辑 index.html文件并将我的节点的多个地址设定(multiaddresses)换成你自己的。
使用certbot证书机器人
确保80端口没有被占用,然后对比下面的检查清单,接着运行下面的命令:
不使用certbot证书机器人 (禁用SSL证书)
如果你使用这个选项,容器不会处理SSL证书,你需要将9091端口反向代理到9090端口(SSL),且4011端口反向代理到4430端口(SSL)。
检查清单
运行容器
在配置好后,运行容器是很简单的。最起码要确保4430端口和9090端口被转发。
现在你应该将此机器作用WebRTC-Star节点或p2p-circuit节点。
WebRTC-Star
我们可以使用 WebRTC-Star(https://github.com/libp2p/js-libp2p-webrtc-star)节点来帮助发现其他可以直接通过浏览器对浏览器连接的节点。我觉得可以将此看成跟STUN(https://en.wikipedia.org/wiki/STUN)类似,如果你已经熟悉了这个概念的话。实际上,每一个连接节点将会被赋予一个WebRTC-Star multiaddress(https://docs.libp2p.io/concepts/addressing/) 地址,这样其他节点可以直接发现和连接到你的浏览器。这意味着如果你与其他star节点连接上了,当star节点下线时,你依然保持连接。
使用
连接到一个star节点是很简单的:
设置
请注意这个例子使用了我自己的star节点。不过,这些节点并不一定可以在任何时候都连接上。当前重要的事情是要么找一个可靠的star节点,要么搭建自己的。你可以很容易地根据这里(https://github.com/libp2p/js-libp2p-webrtc-star#rendezvous-server-aka-signaling-server)的指示来以原生的方式搭建自己的节点,也可以根据这里(https://github.com/libp2p/js-libp2p-webrtc-star/blob/master/DEPLOYMENT.md)的信息来使用Docker容器(包含为SSL功能配置的Nginx)。如果你选择原生的方式,我们会在这篇文章的后面介绍Nginx反向代理过程和SSL证书取回的方法。
这是一个简洁\高效的P2P通讯方式。不过有时候NAT网络会带来障碍。我们使用p2p-circuit(https://docs.libp2p.io/concepts/circuit-relay/) 来绕过它。
⚡ p2p-circuit
使用 p2p-circuit对在NAT网络(或VPN等)后面的节点是很有用的。我发现p2p-circuit的中继与TURN(https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT)是很相似的,如果你对那概念熟悉的话,应该就很容易理解了。
使用
当 p2p-circuit的所有服务就绪后,可以用几种方式连接到节点。首先,在启动时 _只_ 连接到我们的节点:
或者可以之后添加我们自己的节点,然后手动初始化连接:
如果你想不复制例子并实现自己的客户端,那确保你与公告频道(announce channel)也在进行通讯,这在公告(#advertising) 这里描述了。在聊天演示应用中,相关的代码简化如下:
设置
就如star节点,你要认识到这篇文章里列出的节点是在任何时候都可能下线的,所以架设自己的节点是很重要的。
为了实践这个例子,你还需要在架设自己的go-ipfs(https://github.com/ipfs/go-ipfs) 节点的服务器上做一些事情。你还需要一个可用的Nginx安装配置,它将会被SSl证书使用,这个证书是浏览器所需要的。
首先配置Go节点,启用WebSocket(https://en.wikipedia.org/wiki/WebSocket) 支持,然后通过编辑~/.ipfs/config并添加以下的设置来将其指定为一个中继,这样就可以从浏览器里与其通讯了:
以自己习惯的方式重启go-ipfs节点 (可能是systemctl --user restart ipfs命令),这样就差不多就绪了。我们已经启用了支持中继的常规WebSockets接口,不过还需要安全的WebSockets接口配置(在下面的SSL章节有介绍),否则浏览器就无法与我们连接。
公告
使用p2p-circuit可能会有点麻烦。当我们从浏览器连接到中继时,我们并不会向网络公告自己将会通过中继接受连接。为实现这个目的,我创建了go-ipfs一起使用的Python脚本,它可以通过p2p-circuit multiaddress(https://docs.libp2p.io/concepts/addressing/)以PubSub(https://docs.libp2p.io/concepts/publish-subscribe/)来公告其发现的浏览器`js-ipfs`节点。
你可以在这里(https://gist.github.com/TheDiscordian/51962fea72f8d5a5c3bba79dd7009e1c) 找到该Python脚本,运行方式可以是python ipfs_peeradvertiser.py命令。不过,确保你先以自己的节点信息编辑CIRCUIT,否则就无法正确地公告这些节点,这些节点也无法知道如何使用你的中继连接到其他节点。
你可以简单地获取自己的信息。在你的go-ipfs节点上运行ipfs id命令获得你的PeerID标识,然后以下面的方式构造回路URL地址:
OMAIN.COM这样的配置更改成你实际想用在服务上的完整域名(包含子域名)。
可以看到,这里只要填入你拥有SSL证书的域名地址,以及自己节点的PeerID标识。在脚本里,前面的斜杠和后面的斜杠都是需要填入的。
⚠️ 注意 ⚠️
根据你的地址类型(IPv4或IPv6),确保你指定了对应的DNS6或DNS4域名解释服务。使用DNS解析服务是很重要的,否则浏览器节点很可能无法连接。同样要关注4430端口,如果你使用了另一个端口,就需要进行指定。
SSL证书 (Nginx服务)
现在我们在没有SSL证书的情况下设置了WebRTC-Star和 p2p-circuit(除非你使用了 WebRTC-Star的docker容器方案)。如果你想在互联网上通过浏览器使用节点,就需要支持SSL证书。如果你使用了当前的默认配置,那么WebRTC-Star应该是在9090端口(非SSL)上运行,而p2p-circuit应该会在4011端口(非SSL)上运行。我们将会把这些端口各自指向给9091端口(SSL)和4430端口(SSL)。
首先确保Nginx服务安装好了,然后获取并安装Certbot证书机器人(https://certbot.eff.org/docs/install.html)。
我们将从下面的模板创建两个文件。确保你将类似YOURDOMAIN.COM这样的配置更改成你实际想用在服务上的完整域名(包含子域名)。
在这个例子中,你可以看到我们在4430端口上接受SSL连接,这就是我们的 "wss端口" (安全的WebSocket端口) ,然后转发到本地的4011非安全端口(即我们的ws端口)。因此如果我们想通过浏览器连接到这个节点,就使用4430端口。
然后,运行以下命令:
现在Nginx服务已作为反向代理运行,为你提供安全的WebSockets端口了。
通讯
哇!你已经有这么多进展了,可能会想通讯是什么样子的?幸运的是,相比于节点发现,通讯是非常简单的,只是可能会有一点小坑。我们将简单介绍如何在聊天的例子中使用PubSub(https://docs.libp2p.io/concepts/publish-subscribe/) 并在此过程中发现的一些坑。
PubSub
使用PubSub,我们可以订阅主题并取回这些主题下发布的信息。在js-ipfs中,我们可以设置一个回调函数,这样在收到信息时就可以得到通知了:
发布也是很简单的:
这就是这个聊天演示应用的功能。它订阅了一个名为"discochat-global"的全局主题,并简单地将人们输入的信息通过PubSub进行中继。
⚠️ 可能存在的浏览器问题
假设你的操作都正确完成了,就可以使用WebRTC-Star和 p2p-circuit来寻找节点。太棒了!不过,你可能会发现连接超时,而且无法恢复。我并不确定这种行为发生的原因(可能是某些浏览器策略);不过我们还是可以努力尝试应对这些问题的!
与节点保持连接
我们有几种方式与节点保持连接。第一种是更直接的:每隔4秒通过discochat-keepalive订阅和发送"keepalive"信息:
这应该能帮助确保我们为有意向聊天的节点提供更高的优先度。此外,我们每隔15秒通过announce-circuit进行汇报,以确保维持与回路中继的连接,这样就可以连接到NAT网络后的节点。可通过以下的方式实现:
在p2p-circuit#使用(#usage)可以找到简化版本的processAnnounce。
回路中继上的Python脚本会每隔4秒汇报一个保持活跃连接(keepalive)信息。你可能已经注意到我们汇报的是 "peer-alive" 而不是"keep-alive"信息;这是为了将节点请求与中继请求区分开来,让我们更容易知道缺乏可用中继的情况。
与回路中继保持连接
在processAnnounce的简化版本之外,在真实的版本中有几个变量用于追踪keep-alive和peer-alive信息。它们各自是lastAlive和lastPeer。我们甚至可以通过lastBootstrap来追踪最近一次的初始化启动(bootstrap)时间。通过这些,我们可以在只连接到节点(通过lastPeer追踪)时展示黄色的状态,而在35秒内没有看到keep-alive信息时(且没有在60秒内尝试初始化启动时)可以尝试重新连接到初始化启动中继(并展示红色状态)。可通过以下的方式实现:
上述方法应该与processAnnounce的完整版本一起使用,因为它依赖于简化版本中没有包含的lastAlive和 lastPeer功能。
总结
我希望这些信息足够带你上手了。如果你成功遵循这个指引,你就有能力部署可完全在浏览器中运行的功能强大的IPFS应用,并在任何地方利用去中心化的P2P网络。我选择了一些有用的资源并分享在下面以供进一步阅读: