All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.servicecomb.foundation.common.net.NetUtils Maven / Gradle / Ivy

There is a newer version: 3.2.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.servicecomb.foundation.common.net;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.VisibleForTesting;

public final class NetUtils {

  private static final Logger LOGGER = LoggerFactory.getLogger(NetUtils.class);

  private static final String IPV4_KEY = "_v4";

  private static final String IPV6_KEY = "_v6";

  private static final String PREFERRED_INTERFACE = "eth";

  // one interface can bind to multiple address
  // we only save one ip for each interface name.
  // eg:
  // 1. eth0 -> ip1 ip2
  //    last data is eth0 -> ip2
  // 2. eth0 -> ip1
  //    eth0:0 -> ip2
  //    eth0:1 -> ip3
  //    on interface name conflict, all data saved

  // key is network interface name and type
  private static Map allInterfaceAddresses = new HashMap<>();

  private static String hostName;

  private static String hostAddress;

  private static String hostAddressIpv6;

  static {
    doGetHostNameAndHostAddress();
  }

  private static void doGetHostNameAndHostAddress() {
    try {
      doGetAddressFromNetworkInterface();
      // getLocalHost will throw exception in some docker image and sometimes will do a hostname lookup and time consuming
      InetAddress localHost = InetAddress.getLocalHost();
      hostName = localHost.getHostName();
      LOGGER.info("localhost hostName={}, hostAddress={}.", hostName, localHost.getHostAddress());

      if (!isLocalAddress(localHost)) {
        if (Inet6Address.class.isInstance(localHost)) {
          hostAddressIpv6 = trimIpv6(localHost.getHostAddress());
          hostAddress = tryGetHostAddressFromNetworkInterface(false, localHost);
          LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6);
          return;
        }
        hostAddress = localHost.getHostAddress();
        hostAddressIpv6 = trimIpv6(tryGetHostAddressFromNetworkInterface(true, localHost));
        LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6);
        return;
      }
      hostAddressIpv6 = trimIpv6(tryGetHostAddressFromNetworkInterface(true, localHost));
      hostAddress = tryGetHostAddressFromNetworkInterface(false, localHost);
      LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6);
    } catch (Exception e) {
      LOGGER.error("got exception when trying to get addresses:", e);
      if (allInterfaceAddresses.size() >= 1) {
        InetAddress entry = allInterfaceAddresses.entrySet().iterator().next().getValue();
        // get host name will do a reverse name lookup and is time consuming
        hostName = entry.getHostName();
        hostAddress = entry.getHostAddress();
        LOGGER.info("add host name from interfaces:" + hostName + ",host address:" + hostAddress);
      }
    }
  }

  private static String tryGetHostAddressFromNetworkInterface(boolean isIpv6, InetAddress localhost) {
    InetAddress result = null;
    for (Entry entry : allInterfaceAddresses.entrySet()) {
      if (isIpv6 && entry.getKey().endsWith(IPV6_KEY)) {
        result = entry.getValue();
        if (entry.getKey().startsWith(PREFERRED_INTERFACE)) {
          return result.getHostAddress();
        }
      } else if (!isIpv6 && entry.getKey().endsWith(IPV4_KEY)) {
        result = entry.getValue();
        if (entry.getKey().startsWith(PREFERRED_INTERFACE)) {
          return result.getHostAddress();
        }
      }
    }

    if (result == null) {
      return localhost.getHostAddress();
    }

    return result.getHostAddress();
  }

  private NetUtils() {
  }

  /**
   * docker环境中,有时无法通过InetAddress.getLocalHost()获取 ,会报unknown host Exception, system error
   * 此时,通过遍历网卡接口的方式规避,出来的数据不一定对
   */
  private static void doGetAddressFromNetworkInterface() throws SocketException {
    Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces();

    while (networkInterfaces.hasMoreElements()) {
      NetworkInterface network = networkInterfaces.nextElement();

      if (!network.isUp() || network.isLoopback() || network.isVirtual()) {
        continue;
      }

      Enumeration addresses = network.getInetAddresses();
      while (addresses.hasMoreElements()) {
        InetAddress address = addresses.nextElement();

        if (isLocalAddress(address)) {
          continue;
        }

        if (address instanceof Inet4Address) {
          LOGGER.info("add ipv4 network interface:" + network.getName() + ",host address:" + address.getHostAddress());
          allInterfaceAddresses.put(network.getName() + IPV4_KEY, address);
        } else if (address instanceof Inet6Address) {
          LOGGER.info("add ipv6 network interface:" + network.getName() + ",host address:" + address.getHostAddress());
          allInterfaceAddresses.put(network.getName() + IPV6_KEY, address);
        }
      }
    }
  }

  private static String trimIpv6(String hostAddress) {
    int index = hostAddress.indexOf("%");
    if (index >= 0) {
      return hostAddress.substring(0, index);
    }
    return hostAddress;
  }

  private static boolean isLocalAddress(InetAddress address) {
    return address.isAnyLocalAddress() || address.isLoopbackAddress() || address.isMulticastAddress();
  }

  /**
   * The format of address should be {@code IPv4:port} or {@code [IPv6]:port}, or {@code host:port},
   * or you will not get expected result.
   *
   * Note that the IPv6 address should be wrapped by square brackets.
   * @return IpPort parsed from input param, or {@code null} if the param is null.
   */
  public static IpPort parseIpPort(String address) {
    if (address == null) {
      return null;
    }

    URI uri = URI.create("http://" + address);
    return parseIpPort(uri, true);
  }

  /**
   * Parse a {@link URI} into an {@link IpPort}.
   *
   * 

* A uri without port is allowed, in which case the port will be inferred from the scheme. {@code http} is 80, and * {@code https} is 443. *

*

* The host of the {@code uri} should not be null, or it will be treated as an illegal param, * and an {@link IllegalArgumentException} will be thrown. *

*/ public static IpPort parseIpPort(URI uri) { return parseIpPort(uri, false); } /** * Parse a {@link URI} into an {@link IpPort} * @param uri a uri representing {@link IpPort} * @param ignorePortUndefined whether the port should be inferred from scheme, when the port part of {@code uri} is {@code -1}. * If {@code true} the undefined port is ignored; * otherwise a port will be inferred from scheme: {@code http} is 80, and {@code https} is 443. */ public static IpPort parseIpPort(URI uri, boolean ignorePortUndefined) { if (null == uri.getHost()) { // if the format of address is legal but the value is out of range, URI#create(String) will not throw exception // but return a URI with null host. throw new IllegalArgumentException("Illegal uri: [" + uri + "]"); } IpPort ipPort = new IpPort(uri.getHost(), uri.getPort()); if (-1 != ipPort.getPort() || ignorePortUndefined) { return ipPort; } if (uri.getScheme().equals("http")) { ipPort.setPort(80); } if (uri.getScheme().equals("https")) { ipPort.setPort(443); } return ipPort; } /** * @param uriAddress the address containing IP and port info. * @return IpPort parsed from input param, or {@code null} if the param is null. */ public static IpPort parseIpPortFromURI(String uriAddress) { if (uriAddress == null) { return null; } try { return parseIpPort(new URI(uriAddress)); } catch (URISyntaxException e) { return null; } } public static IpPort parseIpPort(String scheme, String authority) { if (authority == null) { return null; } return parseIpPort(URI.create(scheme + "://" + authority)); } /** * 对于配置为0.0.0.0的地址,let it go * schema, e.g. http * adddress, e.g 0.0.0.0:8080 * return 实际监听的地址 */ public static String getRealListenAddress(String schema, String address) { if (address == null) { return null; } try { URI originalURI = new URI(schema + "://" + address); // validate original url NetUtils.parseIpPort(originalURI); return originalURI.toString(); } catch (URISyntaxException e) { LOGGER.error("address {} is not valid.", address); return null; } } public static String getHostName() { //If failed to get host name ,micro-service will registry failed //So I add retry mechanism if (hostName == null) { doGetHostNameAndHostAddress(); } return hostName; } @VisibleForTesting static void resetHostName() { hostName = null; } public static String getHostAddress() { //If failed to get host address ,micro-service will registry failed //So I add retry mechanism if (hostAddress == null) { doGetHostNameAndHostAddress(); } return hostAddress; } public static String getIpv6HostAddress() { //If failed to get host address ,micro-service will registry failed //So I add retry mechanism if (hostAddressIpv6 == null) { doGetHostNameAndHostAddress(); } return hostAddressIpv6; } public static InetAddress getInterfaceAddress(String interfaceName) { return allInterfaceAddresses.get(interfaceName); } public static InetAddress ensureGetInterfaceAddress(String interfaceName) { InetAddress address = allInterfaceAddresses.get(interfaceName); if (address == null) { throw new IllegalArgumentException("Can not find address for interface name: " + interfaceName); } return address; } @SuppressWarnings({"unused", "try"}) public static boolean canTcpListen(InetAddress address, int port) { try (ServerSocket ss = new ServerSocket(port, 0, address)) { return true; } catch (IOException e) { return false; } } public static String humanReadableBytes(long bytes) { int unit = 1024; if (bytes < unit) { return String.valueOf(bytes); } int exp = (int) (Math.log(bytes) / Math.log(unit)); char pre = "KMGTPE".charAt(exp - 1); return String.format("%.3f%c", bytes / Math.pow(unit, exp), pre); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy