唯一標識 Java 執行的例項

candyleer發表於2019-02-23

背景

在企業應用部署監控過程中 ,往往需要通過一些唯一標示來更快的定位有問題的專案或者專案執行的例項.可以實現

  1. 唯一標識,同一機器可以可以部署同一個應用多個例項.
  2. 不受重啟的影響,例項標識能夠沿用到應用的整個生命週期.避免標識爆炸.

獲取執行 IP

如下可以獲取執行的 ip 地址,獲取一次即可快取,無需每次獲取.


    private static final String LOCALHOST_IP = "127.0.0.1";
    private static final String EMPTY_IP = "0.0.0.0";
    private static final Pattern IP_PATTERN = Pattern.compile("[0-9]{1,3}(\\.[0-9]{1,3}){3,}");
    
    /**
     * 獲取本機ip;
     *
     * @return ip;
     * @throws IOException io異常
     */
    public static String getHostIp() throws IOException {
        InetAddress address = getHostAddress();
        return address == null ? null : address.getHostAddress();
    }
    
    /**
     * 獲取hostAddress; 預設是127.0.0.1
     *
     * @return hostAddress;
     * @throws IOException if can't get address
     */
    public static InetAddress getHostAddress() throws IOException {
        InetAddress localAddress;
        localAddress = InetAddress.getLocalHost();
        if (isValidHostAddress(localAddress)) {
            return localAddress;
        }
        Enumeration<NetworkInterface> interfaces;
        interfaces = NetworkInterface.getNetworkInterfaces();
        if (interfaces == null) {
            return localAddress;
        }
        while (interfaces.hasMoreElements()) {
            NetworkInterface network = interfaces.nextElement();
            Enumeration<InetAddress> addresses = network.getInetAddresses();
            while (addresses.hasMoreElements()) {
                InetAddress address = addresses.nextElement();
                if (isValidHostAddress(address)) {
                    return address;
                }
            }
        }
        return localAddress;
    }
    
    private static boolean isValidHostAddress(InetAddress address) {
        if (address == null || address.isLoopbackAddress()) {
            return false;
        }
        String name = address.getHostAddress();
        return (name != null && !EMPTY_IP.equals(name) && !LOCALHOST_IP.equals(name) && IP_PATTERN.matcher(name)
                .matches());
    }

複製程式碼

獲取執行埠

這裡以 tomcat 為例.之所以能從 MBean中獲取到資料,是因為 Tomcat 在啟動過程中將相關的資料註冊到了 MBeanServer 中.

    public static int getTomcatPort() throws Exception {
        MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
        Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"),
                Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
        String port = objectNames.iterator().next().getKeyProperty("port");
        return Integer.valueOf(port);
    }
複製程式碼

建立唯一的 HashCode

獲取當前檔案所在路徑的絕對路徑的hash值,比如當前檔案為AppUtils.class,保證無論以什麼樣的方式啟動,值不會改變,除非改了部署路徑.

    private String getUniqueCode(){
        URL resource = Thread.currentThread().getContextClassLoader().getResource(AppUtils.class.getName().replace(".", "/") + ".class");
        return String.valueOf(Math.abs(resource.toString().hashCode()));
    }
複製程式碼

示例

Web服務使用: IP+埠

對於應用有暴露埠的情況可以使用IP 和埠的形式,即使同一機器部署多個應用,也可以輕鬆區分開. 比如: 10.11.12.13:8080 10.11.12.13:8081

其他服務使用: IP+ HashCode

比如: 10.11.12.13:212134566 10.11.12.13:123134586

總結

有了上面的描述,對應任何 Java 應用來說,都可以找到對於他的唯一編號且不會隨意改變.

public static String getInstanceCode() {
       String ip = getHostIp();
       Integer port = getTomcatPort();
       if (port != null) {
           return ip + ":" + port;
       }
       String uniqueCode = getUniqueCode();
       return ip + ":" + uniqueCode;
   }
複製程式碼

相關文章