github.com/matrixorigin/matrixone@v1.2.0/pkg/util/address/address.go (about)

     1  // Copyright 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 address
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"strings"
    21  	"sync"
    22  	"time"
    23  )
    24  
    25  const (
    26  	defaultListenAddressHost = "0.0.0.0"
    27  	defaultReservedSlots     = 20
    28  )
    29  
    30  // Address is used to describe the address of a MO running service, divided into 2 addresses,
    31  // ListenAddress and ServiceAddress.
    32  //
    33  // ListenAddress is used to indicate the address of the service listener, used to accept external
    34  // connections.
    35  //
    36  // ServiceAddress is used to register to the HAKeeper address, other nodes can get this address to
    37  // connect to this MO's service
    38  //
    39  // TODO(fagongzi): refactor all address configurations in MO.
    40  type Address struct {
    41  	// ListenAddress listen address
    42  	ListenAddress string `toml:"listen-address"`
    43  	// ServiceAddress service address
    44  	ServiceAddress string `toml:"service-addresss"`
    45  }
    46  
    47  // Adjust adjust address according to the rules:
    48  // 1. If ListenAddress is not set, then use defaultListenAddress
    49  // 2. if ServiceAddress is not set, then use ListenAddress
    50  // 3. if machineHost is set, then replace the host of ServiceAddress with machineHost
    51  func (addr *Address) Adjust(
    52  	machineHost string,
    53  	defaultListenAddress string) {
    54  	if addr.ListenAddress == "" {
    55  		addr.ListenAddress = defaultListenAddress
    56  	}
    57  	if addr.ServiceAddress == "" {
    58  		addr.ServiceAddress = addr.ListenAddress
    59  	}
    60  	if machineHost != "" {
    61  		addr.ServiceAddress = replaceHost(addr.ServiceAddress, machineHost)
    62  	}
    63  }
    64  
    65  func replaceHost(
    66  	address string,
    67  	newHost string) string {
    68  	oldHost := address[:strings.Index(address, ":")]
    69  	if oldHost == "" {
    70  		panic("address's host not found: " + address)
    71  	}
    72  	return strings.Replace(address, oldHost, newHost, 1)
    73  }
    74  
    75  // AddressManager manages all service names and ports. It uses unified
    76  // listen address and service address. The port of each service is generated
    77  // by port base and the port slot.
    78  type AddressManager interface {
    79  	// Register registers a service by its name and port slot.
    80  	Register(portSlot int) int
    81  	// ListenAddress returns the service address of the service.
    82  	ListenAddress(slot int) string
    83  	// ServiceAddress returns the service address of the service.
    84  	ServiceAddress(slot int) string
    85  }
    86  
    87  type addressManager struct {
    88  	portBase      int
    89  	reservedSlots int
    90  	address       Address
    91  	mu            struct {
    92  		sync.Mutex
    93  		portAdvanced int
    94  		services     map[int]int // slot => port number
    95  	}
    96  }
    97  
    98  func NewAddressManager(serviceAddress string, portBase int) AddressManager {
    99  	am := &addressManager{
   100  		address: Address{
   101  			ListenAddress:  defaultListenAddressHost,
   102  			ServiceAddress: serviceAddress,
   103  		},
   104  		portBase:      portBase,
   105  		reservedSlots: defaultReservedSlots,
   106  	}
   107  	am.mu.portAdvanced = 0
   108  	am.mu.services = make(map[int]int)
   109  	return am
   110  }
   111  
   112  // Register implements the AddressManager interface.
   113  func (m *addressManager) Register(portSlot int) int {
   114  	m.mu.Lock()
   115  	defer m.mu.Unlock()
   116  	if m.portBase == 0 {
   117  		return 0
   118  	}
   119  	if _, ok := m.mu.services[portSlot]; ok {
   120  		return m.mu.services[portSlot]
   121  	}
   122  	m.mu.services[portSlot] = m.portAdvanceLocked()
   123  	return m.mu.services[portSlot]
   124  }
   125  
   126  // ListenAddress implements the AddressManager interface.
   127  func (m *addressManager) ListenAddress(slot int) string {
   128  	m.mu.Lock()
   129  	defer m.mu.Unlock()
   130  	p, ok := m.mu.services[slot]
   131  	if !ok {
   132  		panic(fmt.Sprintf("slot %d has not been registered yet", slot))
   133  	}
   134  	return fmt.Sprintf("%s:%d", m.address.ListenAddress, p)
   135  }
   136  
   137  // ServiceAddress implements the AddressManager interface.
   138  func (m *addressManager) ServiceAddress(slot int) string {
   139  	m.mu.Lock()
   140  	defer m.mu.Unlock()
   141  	p, ok := m.mu.services[slot]
   142  	if !ok {
   143  		panic(fmt.Sprintf("slot %d has not been registered yet", slot))
   144  	}
   145  	return fmt.Sprintf("%s:%d", m.address.ServiceAddress, p)
   146  }
   147  
   148  // portAdvanceLocked advances the port and return the first available one.
   149  func (m *addressManager) portAdvanceLocked() int {
   150  	var port int
   151  	for {
   152  		port = m.portBase + m.mu.portAdvanced
   153  		if localPortCheck(port) {
   154  			break
   155  		}
   156  		m.mu.portAdvanced++
   157  		if m.mu.portAdvanced > m.reservedSlots {
   158  			panic(fmt.Sprintf("no ports are available between %d and %d",
   159  				m.portBase, m.portBase+m.reservedSlots))
   160  		}
   161  	}
   162  	m.mu.portAdvanced++
   163  	return port
   164  }
   165  
   166  // localPortCheck checks if the local port is available to use. If the port is not used, return
   167  // true; otherwise, return false.
   168  func localPortCheck(port int) bool {
   169  	return !RemoteAddressAvail(fmt.Sprintf(":%d", port), 0)
   170  }
   171  
   172  // RemoteAddressAvail checks if remote address can be connected.
   173  func RemoteAddressAvail(address string, timeout time.Duration) bool {
   174  	conn, err := net.DialTimeout("tcp", address, timeout)
   175  	if err != nil {
   176  		return false
   177  	} else {
   178  		if conn != nil {
   179  			_ = conn.Close()
   180  			return true
   181  		} else {
   182  			return false
   183  		}
   184  	}
   185  }