func (tcp *tcpUnidlerSocket) acceptConns(ch chan<- net.Conn, svcInfo *userspace.ServiceInfo) { defer close(ch) // Block until a connection is made. for { inConn, err := tcp.Accept() if err != nil { if isTooManyFDsError(err) { panic("Accept failed: " + err.Error()) } // TODO: indicate errors here? if isClosedError(err) { return } if !svcInfo.IsAlive() { // Then the service port was just closed so the accept failure is to be expected. return } utilruntime.HandleError(fmt.Errorf("Accept failed: %v", err)) continue } ch <- inConn } }
func (tcp *tcpUnidlerSocket) ProxyLoop(service proxy.ServicePortName, svcInfo *userspace.ServiceInfo, loadBalancer userspace.LoadBalancer) { if !svcInfo.IsAlive() { // The service port was closed or replaced. return } // accept connections asynchronously inConns := make(chan net.Conn) go tcp.acceptConns(inConns, svcInfo) endpointsAvail := make(chan interface{}) var allConns *connectionList for { glog.V(4).Infof("unidling TCP proxy start/reset for service %s/%s:%s", service.Namespace, service.Name, service.Port) var cont bool if allConns, cont = tcp.awaitAwakening(service, svcInfo.ServiceRef, loadBalancer, inConns, endpointsAvail); !cont { break } } glog.V(4).Infof("unidling TCP proxy waiting for endpoints for service %s/%s:%s to become available with %v accumulated connections", service.Namespace, service.Name, service.Port, allConns.Len()) // block until we have endpoints available select { case _, ok := <-endpointsAvail: if ok { close(endpointsAvail) // this shouldn't happen (ok should always be false) } case <-time.NewTimer(needPodsWaitTimeout).C: if allConns.Len() > 0 { utilruntime.HandleError(fmt.Errorf("timed out %v TCP connections while waiting for idled service %s/%s:%s to awaken.", allConns.Len(), service.Namespace, service.Name, service.Port)) allConns.Clear() } return } glog.V(4).Infof("unidling TCP proxy got endpoints for service %s/%s:%s, connecting %v accumulated connections", service.Namespace, service.Name, service.Port, allConns.Len()) for _, inConn := range allConns.GetConns() { glog.V(3).Infof("Accepted TCP connection from %v to %v", inConn.RemoteAddr(), inConn.LocalAddr()) outConn, err := userspace.TryConnectEndpoints(service, inConn.(*net.TCPConn).RemoteAddr(), "tcp", loadBalancer) if err != nil { utilruntime.HandleError(fmt.Errorf("Failed to connect to balancer: %v", err)) inConn.Close() continue } // Spin up an async copy loop. go userspace.ProxyTCP(inConn.(*net.TCPConn), outConn.(*net.TCPConn)) } }
// readFromSock tries to read from a socket, returning true if we should continue trying // to read again, or false if no further reads should be made. func (udp *udpUnidlerSocket) readFromSock(buffer []byte, svcInfo *userspace.ServiceInfo) bool { if !svcInfo.IsAlive() { // The service port was closed or replaced. return false } // Block until data arrives. // TODO: Accumulate a histogram of n or something, to fine tune the buffer size. _, _, err := udp.ReadFrom(buffer) if err != nil { if e, ok := err.(net.Error); ok { if e.Temporary() { glog.V(1).Infof("ReadFrom had a temporary failure: %v", err) return true } } utilruntime.HandleError(fmt.Errorf("ReadFrom failed, exiting ProxyLoop: %v", err)) return false } return true }