github.com/matrixorigin/matrixone@v1.2.0/pkg/proxy/server_conn.go (about) 1 // Copyright 2021 - 2023 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package proxy 16 17 import ( 18 "context" 19 "net" 20 "sync" 21 "sync/atomic" 22 "time" 23 24 "github.com/fagongzi/goetty/v2" 25 "github.com/matrixorigin/matrixone/pkg/common/moerr" 26 "github.com/matrixorigin/matrixone/pkg/config" 27 "github.com/matrixorigin/matrixone/pkg/frontend" 28 "github.com/matrixorigin/matrixone/pkg/logutil" 29 "go.uber.org/zap" 30 ) 31 32 // serverBaseConnID is the base connection ID for server. 33 var serverBaseConnID uint32 = 1000 34 35 // ServerConn is the connection to the backend server. 36 type ServerConn interface { 37 // ConnID returns connection ID of the backend CN server. 38 ConnID() uint32 39 // RawConn return the raw connection. 40 RawConn() net.Conn 41 // HandleHandshake handles the handshake communication with CN server. 42 // handshakeResp is a auth packet received from client. 43 HandleHandshake(handshakeResp *frontend.Packet, timeout time.Duration) (*frontend.Packet, error) 44 // ExecStmt executes a simple statement, it sends a query to backend server. 45 // After it finished, server connection should be closed immediately because 46 // it is a temp connection. 47 // The first return value indicates that if the execution result is OK. 48 // NB: the stmt can only be simple stmt, which returns OK or Err only. 49 ExecStmt(stmt internalStmt, resp chan<- []byte) (bool, error) 50 // Close closes the connection to CN server. 51 Close() error 52 } 53 54 // serverConn is the connection between proxy and CN server. 55 type serverConn struct { 56 // cnServer is the backend CN server. 57 cnServer *CNServer 58 // conn is the raw TCP connection between proxy and server. 59 conn goetty.IOSession 60 // connID is the proxy connection ID, which is not useful in most case. 61 connID uint32 62 // mysqlProto is used to build handshake info. 63 mysqlProto *frontend.MysqlProtocolImpl 64 // rebalancer is used to track connections between proxy and server. 65 rebalancer *rebalancer 66 // tun is the tunnel which this server connection belongs to. 67 tun *tunnel 68 } 69 70 var _ ServerConn = (*serverConn)(nil) 71 72 // newServerConn creates a connection to CN server. 73 func newServerConn(cn *CNServer, tun *tunnel, r *rebalancer, timeout time.Duration) (ServerConn, error) { 74 var logger *zap.Logger 75 if r != nil && r.logger != nil { 76 logger = r.logger.RawLogger() 77 } 78 c, err := cn.Connect(logger, timeout) 79 if err != nil { 80 return nil, err 81 } 82 s := &serverConn{ 83 cnServer: cn, 84 conn: c, 85 connID: nextServerConnID(), 86 rebalancer: r, 87 tun: tun, 88 } 89 fp := config.FrontendParameters{} 90 fp.SetDefaultValues() 91 s.mysqlProto = frontend.NewMysqlClientProtocol(s.connID, c, 0, &fp) 92 return s, nil 93 } 94 95 // wrappedConn wraps the connection to disconnect from connection manager. 96 type wrappedConn struct { 97 net.Conn 98 sync.Once 99 closeFn func() 100 } 101 102 // Close closes the wrapped connection, which calls closeFn to disconnect 103 // from the connection manager. 104 func (w *wrappedConn) Close() error { 105 if w != nil && w.closeFn != nil { 106 w.Once.Do(w.closeFn) 107 } 108 return w.Conn.Close() 109 } 110 111 // ConnID implements the ServerConn interface. 112 func (s *serverConn) ConnID() uint32 { 113 return s.connID 114 } 115 116 // RawConn implements the ServerConn interface. 117 func (s *serverConn) RawConn() net.Conn { 118 if s != nil { 119 if s.cnServer != nil { 120 return &wrappedConn{ 121 Conn: s.conn.RawConn(), 122 closeFn: func() { 123 s.rebalancer.connManager.disconnect(s.cnServer, s.tun) 124 }, 125 } 126 } 127 return s.conn.RawConn() 128 } 129 return nil 130 } 131 132 // HandleHandshake implements the ServerConn interface. 133 func (s *serverConn) HandleHandshake( 134 handshakeResp *frontend.Packet, timeout time.Duration, 135 ) (*frontend.Packet, error) { 136 ctx, cancel := context.WithTimeout(context.Background(), timeout) 137 defer cancel() 138 139 var r *frontend.Packet 140 var err error 141 ch := make(chan struct{}) 142 go func() { 143 // Step 1, read initial handshake from CN server. 144 if err = s.readInitialHandshake(); err != nil { 145 return 146 } 147 // Step 2, write the handshake response to CN server, which is 148 // received from client earlier. 149 r, err = s.writeHandshakeResp(handshakeResp) 150 ch <- struct{}{} 151 }() 152 153 select { 154 case <-ch: 155 return r, err 156 case <-ctx.Done(): 157 logutil.Errorf("handshake to cn %s timeout %v, conn ID: %d", 158 s.cnServer.addr, timeout, s.connID) 159 // Return a retryable error. 160 return nil, newConnectErr(context.DeadlineExceeded) 161 } 162 } 163 164 // ExecStmt implements the ServerConn interface. 165 func (s *serverConn) ExecStmt(stmt internalStmt, resp chan<- []byte) (bool, error) { 166 req := make([]byte, 1, len(stmt.s)+1) 167 req[0] = byte(stmt.cmdType) 168 req = append(req, []byte(stmt.s)...) 169 s.mysqlProto.SetSequenceID(0) 170 if err := s.mysqlProto.WritePacket(req); err != nil { 171 return false, err 172 } 173 execOK := true 174 for { 175 // readPacket makes sure return value is a whole MySQL packet. 176 res, err := s.readPacket() 177 if err != nil { 178 return false, err 179 } 180 bs := packetToBytes(res) 181 if resp != nil { 182 sendResp(bs, resp) 183 } 184 if isEOFPacket(bs) || isOKPacket(bs) { 185 break 186 } 187 if isErrPacket(bs) { 188 execOK = false 189 break 190 } 191 } 192 return execOK, nil 193 } 194 195 // Close implements the ServerConn interface. 196 func (s *serverConn) Close() error { 197 // Un-track the connection. 198 s.rebalancer.connManager.disconnect(s.cnServer, s.tun) 199 return nil 200 } 201 202 // readPacket reads packet from CN server, usually used in handshake phase. 203 func (s *serverConn) readPacket() (*frontend.Packet, error) { 204 msg, err := s.conn.Read(goetty.ReadOptions{}) 205 if err != nil { 206 return nil, err 207 } 208 packet, ok := msg.(*frontend.Packet) 209 if !ok { 210 return nil, moerr.NewInternalErrorNoCtx("message is not a Packet") 211 } 212 return packet, nil 213 } 214 215 // nextServerConnID increases baseConnID by 1 and returns the result. 216 func nextServerConnID() uint32 { 217 return atomic.AddUint32(&serverBaseConnID, 1) 218 }