佛山外包网站建设,上海团购网站建设,织梦网站怎么建设,济阳网站建设公司只写一下如何使用#xff0c;不对实现进行大量描述#xff0c;两个库的代码都比较精炼#xff0c;花一会看一下就行。 cmux 对端口进行复用#xff0c;单端口可以建立不同协议的连接#xff08;本质都是 TCP#xff09;#xff0c;如 TCP/TLS/HTTP/gRPC 或自定义协议 s…只写一下如何使用不对实现进行大量描述两个库的代码都比较精炼花一会看一下就行。 cmux 对端口进行复用单端口可以建立不同协议的连接本质都是 TCP如 TCP/TLS/HTTP/gRPC 或自定义协议 smux 对TCP连接复用单TCP连接承载多条 smux stream 适用使用场景 cmux 一些对外只提供单端口的服务比如一个端口同时提供 HTTP/HTTPS 功能其实还能够更多 smux一些性能敏感的地方比如大量TLS的短连接请求对于频繁的握手非常消耗CPU见性能压测反向连接将 TCP 客户端抽象为服务端方便如 HTTP/gRPC 的服务开发 cmux 的使用 借用一些官方示例使用还是相对简单的23456 端口同时提供了 gRPC/HTTP/tRPC 复用。 // Create the main listener.
l, err : net.Listen(tcp, :23456)
if err ! nil {log.Fatal(err)
}// Create a cmux.
m : cmux.New(l)// Match connections in order:
// First grpc, then HTTP, and otherwise Go RPC/TCP.
grpcL : m.Match(cmux.HTTP2HeaderField(content-type, application/grpc))
httpL : m.Match(cmux.HTTP1Fast())
trpcL : m.Match(cmux.Any()) // Any means anything that is not yet matched.// Create your protocol servers.
grpcS : grpc.NewServer()
grpchello.RegisterGreeterServer(grpcS, server{})httpS : http.Server{ Handler: helloHTTP1Handler{} }trpcS : rpc.NewServer()
trpcS.Register(ExampleRPCRcvr{})// Use the muxed listeners for your servers.
go grpcS.Serve(grpcL)
go httpS.Serve(httpL)
go trpcS.Accept(trpcL)// Start serving!
m.Serve() 自定义 Matcher cmux 的实现上是对 payload 进行匹配cmux.HTTP1Fast 是一个匹配函数为内置的集中匹配函数中的一种这类匹配函数可以同时设置多个。内部将 net.Conn 的数据读入至 buffer 内依次调用各个匹配函数对这个 buffer 进行分析如果匹配成功则 httpL 被返回httpS 服务收到请求。
所以我们可以自定义一些 Matcher比如一些携带 Magic/字符串 头的数据 const (PacketMagic 0x00114514PacketToken xyz_token
)func PacketMagicMatcher(r io.Reader) bool {buf : make([]byte, 4)n, err : io.ReadFull(r, buf)if err ! nil {return false}return binary.BigEndian.Uint32(buf[:n]) PacketMagic
}func PacketTokenMatcher(r io.Reader) bool {buf : make([]byte, len(PacketToken))n, err : io.ReadFull(r, buf)if err ! nil {return false}return string(buf[:n]) PacketToken
} 使用上和 cmux.HTTP1Fast 相同需要注意的是net.Conn 头部的 Magic/Token 是和连接相关的和业务数据无关在使用这些数据之前需要先将其读取出来 tcpMux : cmux.New(lis)
magicLis : tcpMux.Match(PacketMagicMatcher)go func() {conn, _ : magicLis.Accept()buf : make([]byte, 4)io.ReadAtLeast(conn, buf, len(buf)) // Read header magic length// Handle data ...
}()tcpMux.Serve() 多 mux 场景 需求只开放一个端口 12345需要支持 HTTP 协议的包下载 基于 TLS 的 gRPC 服务 基于 TLS 的自定义服务 分析对于 HTTP 和 TLS 需要使用一个 mux 进行区分TLS 中的 gRPC 和 自定义服务需要再通过一个 mux 区分 参考的实现截取了一部分业务代码 // TCP 分流 http/tls
tcpMux : cmux.New(lis)
installerL : tcpMux.Match(cmux.HTTP1Fast())
anyL : tcpMux.Match(cmux.Any())// tls.NewListener(anyL, ...)
mtlsL, err : mTLSListener(anyL, tlsEnable, tlsCertPath, tlsKeyPath, tlsCAPath)
if err ! nil {return err
}
tlsMux : cmux.New(mtlsL)
grpcL : tlsMux.Match(cmux.HTTP2())
gwL : tlsMux.Match(gw.Matcher) smux 的使用 还是放一些官方的简单示例 func client() {// Get a TCP connectionconn, err : net.Dial(...)if err ! nil {panic(err)}// Setup client side of smuxsession, err : smux.Client(conn, nil)if err ! nil {panic(err)}// Open a new streamstream, err : session.OpenStream()if err ! nil {panic(err)}// Stream implements io.ReadWriteCloserstream.Write([]byte(ping))stream.Close()session.Close()
}func server() {// Accept a TCP connectionconn, err : listener.Accept()if err ! nil {panic(err)}// Setup server side of smuxsession, err : smux.Server(conn, nil)if err ! nil {panic(err)}// Accept a streamstream, err : session.AcceptStream()if err ! nil {panic(err)}// Listen for a messagebuf : make([]byte, 4)stream.Read(buf)stream.Close()session.Close()
} smux.Session 和 net.Conn 对应smux.Stream 实现了 net.Conn 接口所以使用起来和普通的连接无异。smux.Session 是双向的Client/Server 的区分仅仅是内部的 Id 区别这就为反向连接打下了基础 基于 smux 的反向连接 对于一个普通的 TCP 服务而言A(client) - B(server)。在 B 上 建立 gRPC/HTTP 服务是一件非常自然的事情。 在某些场景下比如 A 在公网B 在内网不做公网映射的话只能够 B(client) - A(server)。但是这个情况下B 上面的 gRPC/HTTP 的服务就不能直接建立了。 上面说过 smux.Session 再使用上时没有方向的并且提供了和 net.Listener 相近的接口如果将 smux.Session 封装实现 net.Listener加上 smux.Stream 是 net.Conn那么 B 连接 A 继续在 B 上建立 gRPC/HTTP 服务是可以的内部感知不到具体的实现细节。 对 smux.Session 的封装如下 type SmuxSession struct{ *smux.Session }func (s *SmuxSession) Addr() net.Addr { return s.Session.LocalAddr() }
func (s *SmuxSession) Accept() (net.Conn, error) { return s.Session.AcceptStream() }
func (s *SmuxSession) Close() error { return s.Session.Close() } 将 B(tcp:client) - A(tcp:server) 的场景改为 A(gRPC:client) - B(gRPC:Server)关键实现如下 // 忽略错误处理
// 在 A 上的实现如下cc 为后续使用的 gRPC client
func handleConn(conn net.Conn) {sess, _ : smux.Client(conn, nil)cc, _ : grpc.Dial(,grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) { return sess.OpenStream() }),grpc.WithTransportCredentials(insecure.NewCredentials()),)// do something.
}// 在 B 的实现如下
func dialAndServe() {conn, _ : net.Dial(...)sess, _ : smux.Server(conn, nil)return g.server.Serve(SmuxSession{Session: sess})
} cmux/smux 结合使用 其实 cmux/smux 是两个不同的维度单端口/单连接所以只要保证做好 net.Listener/net.Conn 的抽象使用起来是感知不到的。 比如上面的反向连接中handleConn 在上面的 gwL : tlsMux.Match(gw.Matcher) 中驱动的 // A 的实现
func handleConn(conn net.Conn) {// 增加的代码读取 Headerio.CopyN(io.Discard, conn, int64(len(gw.MatcherToken)))sess, _ : smux.Client(conn, nil)cc, _ : grpc.Dial(,grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) { return sess.OpenStream() }),grpc.WithTransportCredentials(insecure.NewCredentials()),)
}// B 的实现
func dialAndServe() {conn, _ : net.Dial(...)conn.Write([]byte(gw.MatcherToken)) // 增加的代码发送一个头sess, _ : smux.Server(conn, nil)return g.server.Serve(SmuxSession{Session: sess})
} 性能压测
性能压测代码见此.
长连接的读写
测试的连接的 case TCP 连接作为一个参考基准 TLS SmuxTCP底层协议为 TCP 的情况TLS底层协议为 TLS 的情况 CmuxTCP底层协议为 TCP 的多个 MatcherTLS底层协议为 TLS 的单个 Matcher复合 mux $ go test -v -benchtime10s -benchmem -run^$ -bench ^BenchmarkConn .
goos: linux
goarch: amd64
pkg: benchmark/connection
cpu: 12th Gen Intel(R) Core(TM) i7-12700
BenchmarkConnCmux
BenchmarkConnCmux/MagicMatcher
BenchmarkConnCmux/MagicMatcher-20 997550 11862 ns/op 11049.73 MB/s 0 B/op 0 allocs/op
BenchmarkConnCmux/TokenMatcher
BenchmarkConnCmux/TokenMatcher-20 958461 11714 ns/op 11188.94 MB/s 0 B/op 0 allocs/op
BenchmarkConnCmux/TLSMatcher
BenchmarkConnCmux/TLSMatcher/TLS
BenchmarkConnCmux/TLSMatcher/TLS-20 295111 40471 ns/op 3238.68 MB/s 192 B/op 7 allocs/op
BenchmarkConnCmux/TLSMatcher/MagicMatcher
BenchmarkConnCmux/TLSMatcher/MagicMatcher-20 296203 39566 ns/op 3312.75 MB/s 192 B/op 7 allocs/op
BenchmarkConnCmux/AnyMatcher
BenchmarkConnCmux/AnyMatcher-20 932871 11870 ns/op 11041.90 MB/s 0 B/op 0 allocs/op
BenchmarkConnSmux
BenchmarkConnSmux/OverTCP
BenchmarkConnSmux/OverTCP-20 438889 24703 ns/op 5305.97 MB/s 1380 B/op 26 allocs/op
BenchmarkConnSmux/OverTLS
BenchmarkConnSmux/OverTLS-20 210336 57345 ns/op 2285.69 MB/s 1596 B/op 36 allocs/op
BenchmarkConnTCP
BenchmarkConnTCP-20 917894 12120 ns/op 10814.60 MB/s 0 B/op 0 allocs/op
BenchmarkConnTLS
BenchmarkConnTLS-20 292843 40310 ns/op 3251.57 MB/s 192 B/op 7 allocs/op
PASS
ok benchmark/connection 106.287s 短连接的读写 较长连接的 case 变化减少 Cmux 为一个Matcher额外引入了 net.HTTP 和 fasthttp 参与 PK。
短连接的测试包含了连接的建立和关闭的场景。 $ go test -v -benchtime10s -benchmem -run^$ -bench ^BenchmarkEcho .
goos: linux
goarch: amd64
pkg: benchmark/connection
cpu: 12th Gen Intel(R) Core(TM) i7-12700
BenchmarkEchoCmux
BenchmarkEchoCmux-20 83162 164356 ns/op 797.49 MB/s 34005 B/op 26 allocs/op
BenchmarkEchoFastHTTP
BenchmarkEchoFastHTTP-20 144302 95231 ns/op 1376.36 MB/s 12941 B/op 41 allocs/op
BenchmarkEchoNetHTTP
BenchmarkEchoNetHTTP-20 65124 239187 ns/op 547.99 MB/s 370816 B/op 59 allocs/op
BenchmarkEchoSmux
BenchmarkEchoSmux/OverTCP
BenchmarkEchoSmux/OverTCP-20 153706 70494 ns/op 1859.34 MB/s 79824 B/op 85 allocs/op
BenchmarkEchoSmux/OverTLS
BenchmarkEchoSmux/OverTLS-20 106585 112120 ns/op 1169.04 MB/s 81776 B/op 102 allocs/op
BenchmarkEchoTCP
BenchmarkEchoTCP-20 308125 39266 ns/op 3338.05 MB/s 1078 B/op 23 allocs/op
BenchmarkEchoTLS
BenchmarkEchoTLS-20 10000 1988704 ns/op 65.91 MB/s 241188 B/op 1112 allocs/op
PASS
ok benchmark/connection 112.673s 性能压测的结论 对照 TCP 为基准 cmu长连接下对性能的影响很小接近 TCP测试有的时候还会比 TCP 高一些短链接下性能比较低应该是在 Accept 返回 cmux.MuxConn 之前慢的多了一次内存拷贝函数匹配chan 传递 smux底层协议为 TCP性能相对 TCP 50% 左右长连接和短连接表现差不多底层协议为 TLS性能相对 TCP 25-30% 左右长连接和短连接表现也接近这个比例 TLS 正常的 Read/Write 性能大概在 50% 左右在短连接的情况下性能非常差TLS 握手攻击原理 fasthttp 速度非常快 从性能的角度看smux 适用于频繁建立 TLS 短连接的场景将短连接变成了一般的 TLS 长连接参考 BenchmarkConnSmux/OverTLS-20 和 BenchmarkConnTLS-20 性能只下降了 30% 左右还是比较能接受的。 文章转载自小胖西瓜 原文链接https://www.cnblogs.com/shuqin/p/18027908 体验地址引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构