目录
bean:IdMeta,IdMetaFactory,IdType
该模块实现了发号器接口的核心项目
该模块内部包结构如下:
service
impl
bean:IdMeta,IdMetaFactory,IdType
IdType:枚举类有三种取值类型,分别对应三种Id类型
package com.robert.vesta.service.impl.bean;
public enum IdType {
SECONDS("seconds"), MILLISECONDS("milliseconds"), SHORTID("short_id");
private String name;
private IdType(String name) {
this.name = name;
}
public long value() {
switch (this) {
case SECONDS:
return 0;
case MILLISECONDS:
return 1;
case SHORTID:
return 2;
default:
return 0;
}
}
@Override
public String toString() {
return this.name;
}
public static IdType parse(String name) {
if ("seconds".equals(name)) {
return SECONDS;
} else if ("milliseconds".equals(name)) {
return MILLISECONDS;
} else if ("short_id".equals(name)) {
return SHORTID;
}
throw new IllegalArgumentException("Illegal IdType name <[" + name
+ "]>, available names are seconds and milliseconds");
}
public static IdType parse(long type) {
if (type == 1) {
return MILLISECONDS;
} else if (type == 0) {
return SECONDS;
}
else if(type==2){
return SHORTID;
}
throw new IllegalArgumentException("Illegal IdType value <[" + type
+ "]>, available values are 0 (for seconds) and 1 (for milliseconds)");
}
}
IdMeta:该类用来描述当前ID类型的具体信息
package com.robert.vesta.service.impl.bean;
public class IdMeta {
private byte machineBits;
private byte seqBits;
private byte timeBits;
private byte genMethodBits;
private byte typeBits;
private byte versionBits;
public IdMeta(byte machineBits, byte seqBits, byte timeBits, byte genMethodBits, byte typeBits, byte versionBits) {
super();
this.machineBits = machineBits;
this.seqBits = seqBits;
this.timeBits = timeBits;
this.genMethodBits = genMethodBits;
this.typeBits = typeBits;
this.versionBits = versionBits;
}
public byte getMachineBits() {
return machineBits;
}
public void setMachineBits(byte machineBits) {
this.machineBits = machineBits;
}
public long getMachineBitsMask() {
return -1L ^ -1L << machineBits;
}
public byte getSeqBits() {
return seqBits;
}
public void setSeqBits(byte seqBits) {
this.seqBits = seqBits;
}
public long getSeqBitsStartPos() {
return machineBits;
}
public long getSeqBitsMask() {
return -1L ^ -1L << seqBits;
}
public byte getTimeBits() {
return timeBits;
}
public void setTimeBits(byte timeBits) {
this.timeBits = timeBits;
}
public long getTimeBitsStartPos() {
return machineBits + seqBits;
}
public long getTimeBitsMask() {
return -1L ^ -1L << timeBits;
}
public byte getGenMethodBits() {
return genMethodBits;
}
public void setGenMethodBits(byte genMethodBits) {
this.genMethodBits = genMethodBits;
}
public long getGenMethodBitsStartPos() {
return machineBits + seqBits + timeBits;
}
public long getGenMethodBitsMask() {
return -1L ^ -1L << genMethodBits;
}
public byte getTypeBits() {
return typeBits;
}
public void setTypeBits(byte typeBits) {
this.typeBits = typeBits;
}
public long getTypeBitsStartPos() {
return machineBits + seqBits + timeBits + genMethodBits;
}
public long getTypeBitsMask() {
return -1L ^ -1L << typeBits;
}
public byte getVersionBits() {
return versionBits;
}
public void setVersionBits(byte versionBits) {
this.versionBits = versionBits;
}
public long getVersionBitsStartPos() {
return machineBits + seqBits + timeBits + genMethodBits + typeBits;
}
public long getVersionBitsMask() {
return -1L ^ -1L << versionBits;
}
}
getXxxxBitsMask():
假设某个字段有五个二进制位
-1L的补码: 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
-1 << xxxBits: 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1110 0000
-1L ^ -1L << machineBits:000... 0001 1111
IdMetaFactory用来产生描述ID类型信息的对象:
package com.robert.vesta.service.impl.bean;
public class IdMetaFactory {
private static IdMeta maxPeak = new IdMeta((byte) 10, (byte) 20, (byte) 30, (byte) 1, (byte) 1, (byte) 1);
private static IdMeta minGranularity = new IdMeta((byte) 10, (byte) 10, (byte) 40, (byte) 1, (byte) 1, (byte) 1);
private static IdMeta shortId=new IdMeta((byte)10,(byte) 10,(byte) 30,(byte)1,(byte) 1,(byte) 1);
public static IdMeta getIdMeta(IdType type) {
if (IdType.SECONDS.equals(type)) {
return maxPeak;
} else if (IdType.MILLISECONDS.equals(type)) {
return minGranularity;
} else if(IdType.SHORTID.equals(type)){
return shortId;
}
return null;
}
}
Converter:ID元数据与长整型ID的互相转换
IdConverter:
package com.robert.vesta.service.impl.converter;
import com.robert.vesta.service.bean.Id;
import com.robert.vesta.service.impl.bean.IdMeta;
public interface IdConverter {
long convert(Id id, IdMeta idMeta);
Id convert(long id, IdMeta idMeta);
}
IdConverterImpl:
package com.robert.vesta.service.impl.converter;
import com.robert.vesta.service.bean.Id;
import com.robert.vesta.service.impl.bean.IdMeta;
public class IdConverterImpl implements IdConverter {
public IdConverterImpl() {
}
public long convert(Id id, IdMeta idMeta) {
return doConvert(id, idMeta);
}
//把id转换成long
protected long doConvert(Id id, IdMeta idMeta) {
long ret = 0;
ret |= id.getMachine();
ret |= id.getSeq() << idMeta.getSeqBitsStartPos();
ret |= id.getTime() << idMeta.getTimeBitsStartPos();
ret |= id.getGenMethod() << idMeta.getGenMethodBitsStartPos();
ret |= id.getType() << idMeta.getTypeBitsStartPos();
ret |= id.getVersion() << idMeta.getVersionBitsStartPos();
return ret;
}
//把long转换成Id
public Id convert(long id, IdMeta idMeta) {
return doConvert(id, idMeta);
}
protected Id doConvert(long id, IdMeta idMeta) {
Id ret = new Id();
ret.setMachine(id & idMeta.getMachineBitsMask());
ret.setSeq((id >>> idMeta.getSeqBitsStartPos()) & idMeta.getSeqBitsMask());
ret.setTime((id >>> idMeta.getTimeBitsStartPos()) & idMeta.getTimeBitsMask());
ret.setGenMethod((id >>> idMeta.getGenMethodBitsStartPos()) & idMeta.getGenMethodBitsMask());
ret.setType((id >>> idMeta.getTypeBitsStartPos()) & idMeta.getTypeBitsMask());
ret.setVersion((id >>> idMeta.getVersionBitsStartPos()) & idMeta.getVersionBitsMask());
return ret;
}
}
long doConvert(Id id, IdMeta idMeta)转换器根据ID元数据(IdMate)的信息对象获取每个属性所在ID的位数,然后通过左移来实现将各个属性拼接到一个长整型字里。
Id doConvert(long id, IdMeta idMeta)根据ID元数据(IdMate)的信息对象将long类型的ID内部的字段分解到对应Id类型的对象中:
首先将ID整体右移,将想要得到的字段右移到从第0位开始:id >>> idMeta.getTypeBitsStartPos(),注意这里使用的是无符号右移操作。
然后和BitsMask进行&操作,得到对应的字段:id>>>idMeta.getTypeBitsStartPos()&idMeta.getTypeBitsMask()
populater:Id产生方案
IdPopulator:
package com.robert.vesta.service.impl.populater;
import com.robert.vesta.service.bean.Id;
import com.robert.vesta.service.impl.bean.IdMeta;
import com.robert.vesta.service.impl.timer.Timer;
/**
* 需要计算构成唯一ID的格式中的时间和序列号,提供了由Synchronized锁,ReentrantLock,CAS无锁技术来提供
*/
public interface IdPopulator {
void populateId(Timer timer, Id id, IdMeta idMeta);
}
ResetPopulator:reset方法在子类中负责将序列号字段和时间字段复位
package com.robert.vesta.service.impl.populater;
public interface ResetPopulator {
void reset();
}
BasePopulator:
package com.robert.vesta.service.impl.populater;
import com.robert.vesta.service.bean.Id;
import com.robert.vesta.service.impl.bean.IdMeta;
import com.robert.vesta.service.impl.timer.Timer;
/**
* 提供锁同步的公共模板
*/
public abstract class BasePopulator implements IdPopulator, ResetPopulator {
protected long sequence = 0;//本个时间单位内的序列号
protected long lastTimestamp = -1;//时间字段,用来记录上一个时间
public BasePopulator() {
super();
}
/**
* idMeta里存储着ID的每个字段的信息
* @param timer
* @param id
* @param idMeta
*/
public void populateId(Timer timer, Id id, IdMeta idMeta) {
long timestamp = timer.genTime();//timer来获取当前时间
timer.validateTimestamp(lastTimestamp, timestamp);//校验时间是否正确
/**
* 如果当前时间和上一个时间相同,则直接对序列号累加
* 否则,序列号清零,然后获得当前的时间,并且更新上一个时间。
*/
if (timestamp == lastTimestamp) {
sequence++;
sequence &= idMeta.getSeqBitsMask();
//如果当前时间段内产生的序列号达到最大值,则使用乐观锁一直等待到下一个时间值
if (sequence == 0) {
timestamp = timer.tillNextTimeUnit(lastTimestamp);
}
} else {
lastTimestamp = timestamp;
sequence = 0;
}
id.setSeq(sequence);
id.setTime(timestamp);
}
public void reset() {
this.sequence = 0;
this.lastTimestamp = -1;
}
}
LockIdPopulator:
package com.robert.vesta.service.impl.populater;
import com.robert.vesta.service.bean.Id;
import com.robert.vesta.service.impl.bean.IdMeta;
import com.robert.vesta.service.impl.timer.Timer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockIdPopulator extends BasePopulator {
private Lock lock = new ReentrantLock();
public LockIdPopulator() {
super();
}
public void populateId(Timer timer, Id id, IdMeta idMeta) {
//采用Reentranlock可重入锁进行同步,性能高于synchronized
lock.lock();
try {
super.populateId(timer, id, idMeta);
} finally {
lock.unlock();
}
}
}
SyncIdPopulator:
package com.robert.vesta.service.impl.populater;
import com.robert.vesta.service.bean.Id;
import com.robert.vesta.service.impl.bean.IdMeta;
import com.robert.vesta.service.impl.timer.Timer;
public class SyncIdPopulator extends BasePopulator {
public SyncIdPopulator() {
super();
}
public synchronized void populateId(Timer timer, Id id, IdMeta idMeta) {
super.populateId(timer, id, idMeta);
}
}
AtomicIdPopulator:采用CAS底层基础设施实现了无锁版本,CAS无锁版本在高并发的场景下,能够高性能的处理唯一ID的产生,但是要安全并发的修改两个变量:时间字段和序列号字段,这里通过原子变量引用来实现。
首先定义一个联合的数据结构:
class Variant {
private long sequence = 0;
private long lastTimestamp = -1;
}
package com.robert.vesta.service.impl.populater;
import com.robert.vesta.service.bean.Id;
import com.robert.vesta.service.impl.bean.IdMeta;
import com.robert.vesta.service.impl.timer.Timer;
import java.util.concurrent.atomic.AtomicReference;
public class AtomicIdPopulator implements IdPopulator, ResetPopulator {
class Variant {
private long sequence = 0;
private long lastTimestamp = -1;
}
//这里定义一个原子变量的引用,这个引用可以保证CAS操作中Variant的
//sequence和lastTimestamp字段中的任意一个被修改了,都可以安全的被更新
private AtomicReference<Variant> variant = new AtomicReference<Variant>(new Variant());
public AtomicIdPopulator() {
super();
}
public void populateId(Timer timer, Id id, IdMeta idMeta) {
Variant varOld, varNew;
long timestamp, sequence;
while (true) {
// 获得原来的变量,变量中包含原来的时间和序列号版本
varOld = variant.get();
//这部分对时间字段和序列号字段的更新逻辑和用ReentranLock和Synchronized实现的版本相同
timestamp = timer.genTime();
timer.validateTimestamp(varOld.lastTimestamp, timestamp);
sequence = varOld.sequence;
if (timestamp == varOld.lastTimestamp) {
sequence++;
sequence &= idMeta.getSeqBitsMask();
if (sequence == 0) {
timestamp = timer.tillNextTimeUnit(varOld.lastTimestamp);
}
} else {
sequence = 0;
}
// 保存新的变量
varNew = new Variant();
varNew.sequence = sequence;
varNew.lastTimestamp = timestamp;
//CAS更新原来的变量
if (variant.compareAndSet(varOld, varNew)) {
id.setSeq(sequence);
id.setTime(timestamp);
break;
}
}
}
public void reset() {
variant = new AtomicReference<Variant>(new Variant());
}
}
timer:时间操作
Timer:假设在某个秒中序列号被用光了怎么办?这种情况下,我们认为等待的时间不会太长,而且阻塞线程没有任何意义,所以采用自旋锁直到时间到下一秒中,这样就减少了因线程切换而导致的性能损耗。
package com.robert.vesta.service.impl.timer;
import com.robert.vesta.service.impl.bean.IdMeta;
import com.robert.vesta.service.impl.bean.IdType;
import java.util.Date;
public interface Timer {
long EPOCH = 1514736000000L;
void init(IdMeta idMeta, IdType idType);
Date transTime(long time);
void validateTimestamp(long lastTimestamp, long timestamp);
long tillNextTimeUnit(long lastTimestamp);
long genTime();
}
SimpleTimer:
package com.robert.vesta.service.impl.timer;
import com.robert.vesta.service.impl.bean.IdMeta;
import com.robert.vesta.service.impl.bean.IdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
public class SimpleTimer implements Timer {
protected static final Logger log = LoggerFactory.getLogger(SimpleTimer.class);
protected IdMeta idMeta;
protected IdType idType;
protected long maxTime;
protected long epoch = EPOCH;
public void init(IdMeta idMeta, IdType idType) {
this.idMeta = idMeta;
this.maxTime = (1L << idMeta.getTimeBits()) - 1;
this.idType = idType;
this.genTime();
this.timerUsedLog();
}
public void timerUsedLog(){
Date expirationDate = transTime(maxTime);
long days = ((expirationDate.getTime() - System.currentTimeMillis())/(1000 * 60 * 60 * 24));
log.info("The current time bit length is {}, the expiration date is {}, this can be used for {} days.",
idMeta.getTimeBits(), expirationDate, days);
}
public void setEpoch(long epoch) {
this.epoch = epoch;
}
public Date transTime(long time) {
if (idType == IdType.MILLISECONDS) {
return new Date(time + epoch);
} else {
return new Date(time * 1000 + epoch);
}
}
/**
* 校验机器时间是否被调慢了
* @param lastTimestamp
* @param timestamp
*/
public void validateTimestamp(long lastTimestamp, long timestamp) {
if (timestamp < lastTimestamp) {
if (log.isErrorEnabled())
log.error(String
.format("Clock moved backwards. Refusing to generate id for %d second/milisecond.",
lastTimestamp - timestamp));
throw new IllegalStateException(
String.format(
"Clock moved backwards. Refusing to generate id for %d second/milisecond.",
lastTimestamp - timestamp));
}
}
//获得下一个时间值
public long tillNextTimeUnit(final long lastTimestamp) {
if (log.isInfoEnabled())
log.info(String
.format("Ids are used out during %d. Waiting till next second/milisencond.",
lastTimestamp));
//乐观锁一直等待到下一个时间值
long timestamp = genTime();
while (timestamp <= lastTimestamp) {
timestamp = genTime();
}
if (log.isInfoEnabled())
log.info(String.format("Next second/milisencond %d is up.",
timestamp));
return timestamp;
}
//通过id类型来确认时间单位,并且通过epoch来压缩
public long genTime() {
long time;
if (idType == IdType.MILLISECONDS) {
time = (System.currentTimeMillis() - epoch);
} else {
time = (System.currentTimeMillis() - epoch) / 1000;
}
validateTimestamp(time);
return time;
}
protected void validateTimestamp(long timestamp){
if (timestamp > maxTime) {
String error = String.format(
"The current timestamp (%s >= %s) has overflowed, Vesta Service will be terminate.", timestamp, maxTime);
log.error(error);
throw new RuntimeException(error);
}
}
}
provider:机器ID的产生
MachineIdProvider
package com.robert.vesta.service.impl.provider;
public interface MachineIdProvider {
public long getMachineId();
}
MachineIdsProvider
package com.robert.vesta.service.impl.provider;
public interface MachineIdsProvider extends MachineIdProvider {
long getNextMachineId();
}
PropertyMachineIdProvider:基于属性配置进行实现的,也是一种用于测试环境的方式,使用这种方式时,需要在部署的每台机器上配置不同的机器号
package com.robert.vesta.service.impl.provider;
public class PropertyMachineIdProvider implements MachineIdProvider {
private long machineId;
public long getMachineId() {
return machineId;
}
public void setMachineId(long machineId) {
this.machineId = machineId;
}
}
PropertyMachineIdsProvider
package com.robert.vesta.service.impl.provider;
public class PropertyMachineIdsProvider implements MachineIdsProvider {
private long[] machineIds;
private int currentIndex;
public long getNextMachineId() {
return getMachineId();
}
public long getMachineId() {
return machineIds[currentIndex++%machineIds.length];
}
public void setMachineIds(long[] machineIds) {
this.machineIds = machineIds;
}
}
IpConfigurableMachineIdProvider:通过IP的机器列表位每个机器生成一个唯一的ID号,主要是和服务节点比较少的情况,生成ID是轻量级的服务,不会需要太大的服务池。
这种实现方式通过发布前配置所有的服务节点IP的映射,每个服务节点必须具有相同的映射,运行时每个服务节点根据本机IP取得在IP映射中的位置,作为自己的机器号。
package com.robert.vesta.service.impl.provider;
import com.robert.vesta.util.IpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
public class IpConfigurableMachineIdProvider implements MachineIdProvider {
private static final Logger log = LoggerFactory
.getLogger(IpConfigurableMachineIdProvider.class);
private long machineId;
private Map<String, Long> ipsMap = new HashMap<String, Long>();
public IpConfigurableMachineIdProvider() {
log.debug("IpConfigurableMachineIdProvider constructed.");
}
public IpConfigurableMachineIdProvider(String ips) {
setIps(ips);
init();
}
public void init() {
String ip = IpUtils.getHostIp();
if (StringUtils.isEmpty(ip)) {
String msg = "Fail to get host IP address. Stop to initialize the IpConfigurableMachineIdProvider provider.";
log.error(msg);
throw new IllegalStateException(msg);
}
if (!ipsMap.containsKey(ip)) {
String msg = String
.format("Fail to configure ID for host IP address %s. Stop to initialize the IpConfigurableMachineIdProvider provider.",
ip);
log.error(msg);
throw new IllegalStateException(msg);
}
machineId = ipsMap.get(ip);
log.info("IpConfigurableMachineIdProvider.init ip {} id {}", ip,
machineId);
}
public void setIps(String ips) {
log.debug("IpConfigurableMachineIdProvider ips {}", ips);
if (!StringUtils.isEmpty(ips)) {
String[] ipArray = ips.split(",");
for (int i = 0; i < ipArray.length; i++) {
ipsMap.put(ipArray[i], (long) i);
}
}
}
public long getMachineId() {
return machineId;
}
public void setMachineId(long machineId) {
this.machineId = machineId;
}
}
DbMachineIdProvider:通过在数据库中配置机器ID来实现,适用情况范围光,但是使用起来需要依赖数据库。
package com.robert.vesta.service.impl.provider;
import com.robert.vesta.util.IpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.StringUtils;
public class DbMachineIdProvider implements MachineIdProvider {
private static final Logger log = LoggerFactory
.getLogger(DbMachineIdProvider.class);
private long machineId;
private JdbcTemplate jdbcTemplate;
public DbMachineIdProvider() {
log.debug("IpConfigurableMachineIdProvider constructed.");
}
public void init() {
String ip = IpUtils.getHostIp();
if (StringUtils.isEmpty(ip)) {
String msg = "Fail to get host IP address. Stop to initialize the DbMachineIdProvider provider.";
log.error(msg);
throw new IllegalStateException(msg);
}
Long id = null;
try {
id = jdbcTemplate.queryForObject(
"select ID from DB_MACHINE_ID_PROVIDER where IP = ?",
new Object[]{ip}, Long.class);
} catch (EmptyResultDataAccessException e) {
// Ignore the exception
log.error("No allocation before for ip {}.", ip);
}
if (id != null) {
machineId = id;
return;
}
log.info(
"Fail to get ID from DB for host IP address {}. Next step try to allocate one.",
ip);
int count = jdbcTemplate
.update("update DB_MACHINE_ID_PROVIDER set IP = ? where IP is null limit 1",
ip);
if (count <= 0 || count > 1) {
String msg = String
.format("Fail to allocte ID for host IP address {}. The {} records are updated. Stop to initialize the DbMachineIdProvider provider.",
ip, count);
log.error(msg);
throw new IllegalStateException(msg);
}
try {
id = jdbcTemplate.queryForObject(
"select ID from DB_MACHINE_ID_PROVIDER where IP = ?",
new Object[]{ip}, Long.class);
} catch (EmptyResultDataAccessException e) {
// Ignore the exception
log.error("Fail to do allocation for ip {}.", ip);
}
if (id == null) {
String msg = String
.format("Fail to get ID from DB for host IP address {} after allocation. Stop to initialize the DbMachineIdProvider provider.",
ip);
log.error(msg);
throw new IllegalStateException(msg);
}
machineId = id;
}
public long getMachineId() {
return machineId;
}
public void setMachineId(long machineId) {
this.machineId = machineId;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
util工具类
CommonUtils:
package com.robert.vesta.util;
import java.util.Arrays;
public class CommonUtils {
public static String[] SWITCH_ON_EXP = new String[]{"ON", "TRUE", "on", "true"};
public static String[] SWITCH_OFF_EXP = new String[]{"OFF", "FALSE", "off", "false"};
public static boolean isOn(String swtch) {
if (Arrays.asList(SWITCH_ON_EXP).contains(swtch)) {
return true;
}
return false;
}
public static boolean isPropKeyOn(String key) {
String prop = System.getProperty(key);
if (Arrays.asList(SWITCH_ON_EXP).contains(prop)) {
return true;
}
return false;
}
}
IpUtils:
package com.robert.vesta.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
public class IpUtils {
private static final Logger log = LoggerFactory.getLogger(IpUtils.class);
public static String getHostIp() {
String ip = null;
try {
Enumeration<NetworkInterface> en = NetworkInterface
.getNetworkInterfaces();
while (en.hasMoreElements()) {
NetworkInterface intf = (NetworkInterface) en.nextElement();
Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses();
while (enumIpAddr.hasMoreElements()) {
InetAddress inetAddress = (InetAddress) enumIpAddr
.nextElement();
if (!inetAddress.isLoopbackAddress()
&& !inetAddress.isLinkLocalAddress()
&& inetAddress.isSiteLocalAddress()) {
ip = inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
log.error("Fail to get IP address.", e);
}
return ip;
}
public static String getHostName() {
String hostName = null;
try {
Enumeration<NetworkInterface> en = NetworkInterface
.getNetworkInterfaces();
while (en.hasMoreElements()) {
NetworkInterface intf = (NetworkInterface) en.nextElement();
Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses();
while (enumIpAddr.hasMoreElements()) {
InetAddress inetAddress = (InetAddress) enumIpAddr
.nextElement();
if (!inetAddress.isLoopbackAddress()
&& !inetAddress.isLinkLocalAddress()
&& inetAddress.isSiteLocalAddress()) {
hostName = inetAddress.getHostName();
}
}
}
} catch (SocketException e) {
log.error("Fail to get host name.", e);
}
return hostName;
}
}