深入理解 IPFS - 分布式代理

发布于 2025-01-04 15:23:12 字数 5030 浏览 6 评论 0

今天我们利用 libp2p 创建一个简单的分布式 http 代理,具体 demo 参见官方 example:proxy.go

代理,同学们都很熟悉,不管是写爬虫,还是开发服务器,都或多或少的接触过。
本质上就是通过一个中间节点转发流量到目标地址。libp2p 也是一样,不过对他来说,每个节点都是平等的,理论上都可以作为客户端或者代理。另外他底层的 stream 模块也让开发变的更加容易。

                                                                                                   XX  XXXXXX
X XX
XXXXXXX XX XX XXXXXXXXXX
+----------------+ +-----------------+ XXX XXX XXX XXX
HTTP Request | | | | XX XX
+-----------------> | libp2p stream | | HTTP X X
| Local peer <----------------> Remote peer <-------------> HTTP SERVER - THE INTERNET XX
<-----------------+ | | | Req & Resp XX X
HTTP Response | libp2p host | | libp2p host | XXXX XXXX XXXXXXXXXXXXXXXXXXXX XXXX
+----------------+ +-----------------+ XXXXX

为了能够代理一个请求,我们需要先创建一个本地节点监听 localhost:9900 。HTTP requests 通过 libp2p stream 模块打到远程节点。远程节点发起 http 请求,并将 response 返回给本地节点,本地节点再中继返回给客户端。

远程节点

首先我们需要建立一个 libp2p 远程节点

func makeRandomHost(port int) host.Host {
host, err := libp2p.New(context.Background(), libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port)))
if err != nil {
log.Fatalln(err)
}
return host
}

接下来需要为节点监听端口绑定 handler

// Protocol 自己约定,为了能和 handler 匹配上
host.SetStreamHandler(Protocol, streamHandler)

func streamHandler(stream network.Stream) {
defer stream.Close()

// 接收本地结果过来的请求
// 从 stream 中读取 request 的 data
buf := bufio.NewReader(stream)
// Read the HTTP request from the buffer
req, err := http.ReadRequest(buf)
if err != nil {
stream.Reset()
return
}
defer req.Body.Close()


// 重置一些 http 字段
req.URL.Scheme = "http"
hp := strings.Split(req.Host, ":")
if len(hp) > 1 && hp[1] == "443" {
req.URL.Scheme = "https"
} else {
req.URL.Scheme = "http"
}
req.URL.Host = req.Host

outreq := new(http.Request)
*outreq = *req

// 发起请求到目标地址
resp, err := http.DefaultTransport.RoundTrip(outreq)
if err != nil {
stream.Reset()
log.Println(err)
return
}

// 将 resp 写回到 stream
resp.Write(stream)
}

本地节点

本地节点需要能处理 http 请求,然后将请求转发给远程节点

// ServeHTTP 实现了 http.Handler 的接口
func (p *ProxyService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Printf("proxying request for %s to peer %s\n", r.URL, p.dest.Pretty())
// 将 request 请求发送到远程节点
stream, _ := p.host.NewStream(context.Background(), p.dest, Protocol)

defer stream.Close()

// r.Write() 将 http 请求数据写入到 stream 里面
_ = r.Write(stream)

// 将 remote response 写回到 http response 里面
buf := bufio.NewReader(stream)
resp, _ := http.ReadResponse(buf, r)

// 写 headers
for k, v := range resp.Header {
for _, s := range v {
w.Header().Add(k, s)
}
}

// 写 StatusCode
w.WriteHeader(resp.StatusCode)

// 最后 copy body
io.Copy(w, resp.Body)
resp.Body.Close()
}

具体代码见: proxy.go

运行

build

> make deps
> cd http-proxy/
> go build

run

  1. 首先我们运行 remote 节点
    > ./http-proxy
    Proxy server is ready
    libp2p-peer addresses:
    /ip4/127.0.0.1/tcp/12000/p2p/QmddTrQXhA9AkCpXPTkcY7e22NK73TwkUms3a44DhTKJTD
  2. 然后运行本地节点 -d 参数后面就是上面运行的 remote 节点的地址
    > ./http-proxy -d /ip4/127.0.0.1/tcp/12000/p2p/QmddTrQXhA9AkCpXPTkcY7e22NK73TwkUms3a44DhTKJTD
    Proxy server is ready
    libp2p-peer addresses:
    /ip4/127.0.0.1/tcp/12001/p2p/Qmaa2AYTha1UqcFVX97p9R1UP7vbzDLY7bqWsZw1135QvN
    proxy listening on 127.0.0.1:9900
  3. 本地代理监听 127.0.0.1:9900 , 我们用 curl 命令请求下看看效果
> curl -x "127.0.0.1:9900" "http://ipfs.io/p2p/QmfUX75pGRBRDnjeoMkQzuQczuCup2aYbeLxz5NzeSu9G6"
it works!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

蛮可爱

暂无简介

文章
评论
27 人气
更多

推荐作者

七七

文章 0 评论 0

囍笑

文章 0 评论 0

盛夏尉蓝

文章 0 评论 0

ゞ花落谁相伴

文章 0 评论 0

Sherlocked

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文