初级会员
  • 第 45680 位会员
  • zzustu
  • WangZY
  • 2019-11-27 16:07:19
  • Offline
  • 1195 19 78

最近发布的文章

    暂无

最近分享的资源

    暂无

最近发布的项目

    暂无

最近的评论

  • ![code.png](https://static.golangjob.cn/220923/fad00eee64aaea2a05b0a107d29b4508.png)
  • 凭感觉写的,未经优化未经测试,仅供参考 ```go package main import ( "io" "net" "time" ) func main() { backends := []string{"172.16.5.9:18060", "172.16.5.9:18061", "172.16.5.9:18062"} lis, err := net.Listen("tcp", ":8080") if err != nil { panic(err) } for { conn, err := lis.Accept() if err != nil { continue } go distribute(conn, backends) } } func distribute(conn net.Conn, backends []string) { defer conn.Close() backs := make([]net.Conn, 0, len(backends)) for _, backend := range backends { back, err := net.DialTimeout("tcp", backend, 3*time.Second) if err != nil { continue } backs = append(backs, back) } if len(backs) == 0 { return } pipe := &pipeline{conn: conn, backs: backs} pipe.serve() } type pipeline struct { conn net.Conn backs []net.Conn } func (p *pipeline) serve() { defer p.close() // 第一个连接为主连接,需要将数据响应给客户端 master := p.backs[0] go p.readFrom(master) // 虽然不关心剩余的后端连接响应数据,但是应该将数据读出并丢弃 discards := p.backs[1:] if len(discards) != 0 { go p.discard(discards) } buf := make([]byte, 1024) for { n, err := p.conn.Read(buf) if err != nil { break } // 读出客户端的数据,并分发给所有后端连接 if hasErr := p.writeAll(buf[:n]); hasErr && len(p.backs) == 0 { break } } } func (p *pipeline) readFrom(r io.Reader) { _, _ = io.Copy(p.conn, r) _ = p.conn.Close() } func (*pipeline) discard(rds []net.Conn) { for _, rd := range rds { go io.Copy(io.Discard, rd) } } func (p *pipeline) writeAll(data []byte) bool { var hasErr bool for i, back := range p.backs { if _, err := back.Write(data); err == nil { continue } // 写入数据出现错误,说明后端连接异常,将这个连接剔除列表 p.backs = append(p.backs[:i], p.backs[i+1:]...) hasErr = true _ = back.Close() // 保险起见,剔除后再主动执行 Close() 方法 } return hasErr } func (p *pipeline) close() { _ = p.conn.Close() for _, back := range p.backs { _ = back.Close() } } ```
  • 楼主这个场景有点像流量镜像,就是把流量拷贝N多份,一份照常给业务服务器。其他的发送给流量审计服务器或者测试环境服务器。 需要有一些注意: 1. 如果只读取第一个连接的数据回复,其他的不管,`有可能`会造其他连接的消息积压。 虽然你并不关心其他连接回复的消息,但是应该把回复的消息读出来,比如读出来丢弃掉:`io.Copy(io.Discard, otherConn)` 2. 最好不要用 `io.MultiWriter()`,因为有一个写入出错就会导致 `io.MultiWriter()` 报错, 因为后端某个连接可以能会异常或断开,这样一个异常,会影响所有的 Writer 写入
  • make slice 时写错了 ```go // 错误 i := make([]io.Writer, 3) // 正确 i := make([]io.Writer, 0, 3) ```
  • 将流量拷贝分发给多个后端可以理解,可以简单的这样写: ```go mw := io.MultiWriter(dest1, dest2, dest3) io.Copy(mw, src) ``` 但是,后端的返回数据都往一个conn里面写入,这个conn收到消息该怎么解析。