摘要:這篇文章主要介紹用js-ipfs搭建最小化的聊天應用的例子,這個應用可以在瀏覽器中運行。我們看到很多關於在瀏覽器裡使用js-ipfs的問題。這篇文章展示了用js-ipfs搭建最小化的聊天應用的例子,這個應用可以在瀏覽器中運行。它使用WebRTC去實現瀏覽器對瀏覽器的連接(在可用時),如不可用則使用…
這篇文章主要介紹用js-ipfs搭建最小化的聊天應用的例子,這個應用可以在瀏覽器中運行。
我們看到很多關於在瀏覽器裡使用js-ipfs的問題。這篇文章展示了用js-ipfs搭建最小化的聊天應用的例子,這個應用可以在瀏覽器中運行。它使用WebRTC去實現瀏覽器對瀏覽器的連接(在可用時),如不可用則使用迴路中繼(Circuit Relay)去連接瀏覽器節點。消息的傳遞是通過libp2p的pubsub功能實現。
獲取代碼
你可以在這裡(https://ipfs.io/ipfs/bafybeia5f2yk6td7ciroeped2uwfivo333b524t3zmoderfhl3xn7wi7aa/)查看演示。如果你想要一個可以自行編輯的本地拷貝,可以使用IPFS下載整個目錄:
ipfs 得到 bafybeia5f2yk6td7ciroeped2uwfivo333b524t3zmoderfhl3xn7wi7aa
然後,只要在瀏覽器中打開index.html,就可以立即自動連接到節點並尋找連接資源。
你也可以在GitHub上分叉heDiscordian/browser-ipfs-chat(https://github.com/TheDiscordian/browser-ipfs-chat) 項目,就可以立刻開始測試了!如果你想部署自己的版本,只要編輯index.html並遵循以下的設置信息:
在這個例子中使用的庫是js-ipfs 和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-電路(#p2p-電路)
* 使用(#usage-2)
* 設置(#setup-2)
* 公告(#advertising)
SSL證書(Nginx)(#ssl-nginx)
* 通訊(#communication)
*發布訂閱(#發布訂閱)
*可能存在的瀏覽器問題(#possible-browser-pitfalls)
* 保持與節點的連接(#staying-connected-to-peers)
* 保持與迴路中繼的連接(#staying-connected-to-the-circuit-relay)
總結(#conclusion)
節點發現和連接
在瀏覽器中,發現和連接到節點可能是有難度的,因為我們無法監聽新節點,也沒法訪問分佈式哈希表(DHT)。為了實現在瀏覽器中運行的最佳體驗,理解如何尋找節點和保持與其的連接是很重要的。
聊天應用的例子通過兩種方式實現此目標。使用WebRTC-Star,我們實現了直接的瀏覽器對瀏覽器通訊,並配置了兩者之間的迴路中繼。
這個聊天應用也在左上方配置了一個狀態指示器,讓你知道自己的連接種類。綠色表示你連接到了中繼(即便是通過另一個節點來連接);黃色表示你只看到直接連接的節點;紅色表示你沒有連接到節點(至少在聊天應用中沒有連接)。
上圖展示了一個有3名用戶的網絡是什麼樣子的。值得注意的是瀏覽器節點也可以與go-ipfs節點通訊。因此,瀏覽器C並不需要是一個瀏覽器,也可以是一個go-ipfs節點。
Docker容器(可選)
如果你不想使用Docker容器,可以直接跳到WebRTC-Star(#webrtc-star) 的章節。
在這個章節後,我們會涵蓋WebRTC-Star和迴路中繼的作用,以及相關的設置方法。不過,如果你想通過Docker快速上手,我已經準備了一個可用的鏡像。它可能不是最佳的長期解決方案,不過如果你只是想快速上手和進行實驗的話,就是很好的方式了。
創建一個存儲卷(volume)
首先,創建一個存儲卷去存儲密鑰和節點數據這樣的長期數據。
docker 卷創建 ipfs_bundle
配置一個域名
你需要一個域名和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端口沒有被佔用,然後對比下面的檢查清單,接著運行下面的命令:
docker run –mount source=ipfs_bundle,destination=/root -p 9091:9091 -p 4011:4011 -p 9090:9090 -p 4430:4430 -p 80:80 -it trdiscordian/ipfsbundle certbot COMAIN。
不使用certbot證書機器人(禁用SSL證書)
如果你使用這個選項,容器不會處理SSL證書,你需要將9091端口反向代理到9090端口(SSL),且4011端口反向代理到4430端口(SSL)。
docker run –mount source=ipfs_bundle,destination=/root -p 9091:9091 -p 4011:4011 -it trdiscordian/ipfsbundle DOMAIN.COM
檢查清單
-
將DOMAIN.COM替換成你的域名
-
確保域名被正確指向到容器運行的機器上(子域名也能正常工作)
運行容器
在配置好後,運行容器是很簡單的。最起碼要確保4430端口和9090端口被轉發。
docker run –mount source=ipfs_bundle,destination=/root -p 9091:9091 -p 4011:4011 -p 9090:9090 -p 4430:4430 -it trdiscordian/ipfsbundle
現在你應該將此機器作用WebRTC-Star節點或p2p-circuit節點。
WebRTC-Star
我們可以使用WebRTC-Star (https://github.com/libp2p/js-libp2p-webrtc-star)節點來幫助發現其他可以直接通過瀏覽器對瀏覽器連接的節點。
如果你已經熟悉了這個概念的話,我覺得可以將此看成跟STUN類似。實際上,每一個連接節點將會被賦予一個WebRTC-Star multiaddress 地址,這樣其他節點可以直接發現和連接到你的瀏覽器。這意味著如果你與其他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 來繞過它。
p2p電路
使用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節點的服務器上做一些事情。你還需要一個可用的Nginx安裝配置,它將會被SSl證書使用,這個證書是瀏覽器所需要的。
首先配置Go節點,啟用WebSocket支持,然後通過編輯~/.ipfs/config並添加以下的設置來將其指定為一個中繼,這樣就可以從瀏覽器裡與其通訊了:
以自己習慣的方式重啟go-ipfs節點(可能是systemctl –user restart ipfs命令),這樣就差不多就緒了。我們已經啟用了支持中繼的常規WebSockets接口,不過還需要安全的WebSockets接口配置(在下面的SSL章節有介紹),否則瀏覽器就無法與我們連接。
公告
使用p2p-circuit可能會有點麻煩。當我們從瀏覽器連接到中繼時,我們並不會向網絡公告自己將會通過中繼接受連接。為實現這個目的,我創建了go-ipfs一起使用的Python腳本,它可以通過p2p-circuit multiaddress以及PubSub來公告其發現的瀏覽器js-ipfs節點。
你可以在這裡(https://gist.github.com/TheDiscordian/51962fea72f8d5a5c3bba79dd7009e1c) 找到該Python腳本,運行方式可以是python ipfs_peeradvertiser.py 命令。
不過,確保你先以自己的節點信息編輯CIRCUIT,否則就無法正確地公告這些節點,這些節點也無法知道如何使用你的中繼連接到其他節點。
你可以簡單地獲取自己的信息。在你的go-ipfs節點上運行ipfs id命令獲得你的PeerID標識,然後以下面的方式構造迴路URL地址:
/dns6/ipfs.YOURDOMAIN.COM/tcp/4430/p2p/YOUR_PEERID/p2p-circuit/p2p/
可以看到,這裡只要填入你擁有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,我們可以訂閱主題並取回這些主題下發布的信息。在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網絡。我選擇了一些有用的資源並分享在下面以供進一步閱讀:
-
js-ipfs/docs/BROWSERS.md(https://github.com/ipfs/js-ipfs/blob/master/docs/BROWSERS.md)
-
js-ipfs/docs/CONFIG.md(https://github.com/ipfs/js-ipfs/blob/master/docs/CONFIG.md)
-
js-ipfs/docs/core-api(https://github.com/ipfs/js-ipfs/tree/master/docs/core-api)
-
js-ipfs/examples/circuit-relaying(https://github.com/ipfs/js-ipfs/tree/master/examples/circuit-relaying)
-
js-libp2p-webrtc-star(https://github.com/libp2p/js-libp2p-webrtc-star)