本文共 4128 字,大约阅读时间需要 13 分钟。
今天我们利用 libp2p 创建一个简单的分布式 http 代理,具体 demo 参见官方 example: https://github.com/libp2p/go-libp2p-examples/blob/master/http-proxy/proxy.go
代理,同学们都很熟悉,不管是写爬虫,还是开发服务器,都或多或少的接触过。
本质上就是通过一个中间节点转发流量到目标地址。libp2p 也是一样,不过对他来说,每个节点都是平等的,理论上都可以作为客户端或者代理。另外他底层的 stream 模块也让开发变的更加容易。12345678910 | 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 远程节点
1234567 | 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
123456789101112131415161718192021222324252627282930313233343536373839404142 | // 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 请求,然后将请求转发给远程节点
1234567891011121314151617181920212223242526272829 | // 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()} |
具体代码见:https://github.com/libp2p/go-libp2p-examples/blob/master/http-proxy/proxy.go
123 | > make deps> cd http-proxy/> go build |
首先我们运行 remote 节点
1234 | > ./http-proxyProxy server is readylibp2p-peer addresses:/ip4/127.0.0.1/tcp/12000/p2p/QmddTrQXhA9AkCpXPTkcY7e22NK73TwkUms3a44DhTKJTD |
然后运行本地节点 -d 参数后面就是上面运行的 remote 节点的地址
12345 | > ./http-proxy -d /ip4/127.0.0.1/tcp/12000/p2p/QmddTrQXhA9AkCpXPTkcY7e22NK73TwkUms3a44DhTKJTDProxy server is readylibp2p-peer addresses:/ip4/127.0.0.1/tcp/12001/p2p/Qmaa2AYTha1UqcFVX97p9R1UP7vbzDLY7bqWsZw1135QvNproxy listening on 127.0.0.1:9900 |
本地代理监听 127.0.0.1:9900
, 我们用 curl 命令请求下看看效果
12 | > curl -x "127.0.0.1:9900" "http://ipfs.io/p2p/QmfUX75pGRBRDnjeoMkQzuQczuCup2aYbeLxz5NzeSu9G6"it works! |
转载地址:http://fkmii.baihongyu.com/