如何在第一次调用 Singleton 时只更新一次地图?
How to update map only once during the first call to Singleton?
我必须在服务器上进行 HTTP 调用后解析响应。如果响应不成功,则尝试另一个服务器,否则解析成功的响应并填充两个 ConcurrentHashMap 并跳出 for 循环。所有的服务器都会以相同的格式给出相同的准确响应。
下面是我的单例class,它在ProcConfig
的构造函数中第一次调用时,调用loadConfig()
方法初始化所有东西,然后检查addressToIdMapping
映射是否有是否有一个条目。如果它不存在则抛出异常。之后它启动一个后台线程,每 30 分钟调用 loadConfig()
方法来更新 addressToIdMapping
和 processToTcpMapping
map.
public class ProcConfig {
private static final Splitter SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Map<String, Short> addressToIdMapping = new ConcurrentHashMap<>();
private final Map<DatacenterEnum, List<String>> processToTcpMapping = new ConcurrentHashMap<>();
private static class Holder {
private static final ProcConfig INSTANCE = new ProcConfig();
}
public static ProcConfig getInstance() {
return Holder.INSTANCE;
}
private ProcConfig() {
loadConfig();
checkArgument(!MapUtils.isEmpty(addressToIdMapping), "cannot find id, found '%s'.", addressToIdMapping);
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
loadConfig();
} catch (Exception ex) {
// log error
}
}
}, 60, 30, TimeUnit.MINUTES);
}
private void loadConfig() {
// current ipAddress where the program is running
Optional<String> ipAddress = Utils.getIPAddress();
List<String> servers = getServers();
for (String server : servers) {
try {
String response = HttpClient.getInstance().execute(makeUrl(server));
if (Strings.isNullOrEmpty(response) || response.equalsIgnoreCase("KEEP OUT")
|| response.equalsIgnoreCase("NOTHING FOUND")) {
continue;
}
parseConfig(response, ipAddress.get());
break;
} catch (Exception ex) {
// log error
}
}
}
private void parseConfig(final String response, final String ipAddress) throws IOException {
List<String> lines = IOUtils.readLines(new StringReader(response));
for (String line : lines) {
if (line.contains(ipAddress)) {
List<String> config = SPLITTER.splitToList(line);
Short id = Short.parseShort(config.get(2));
// this map will only have one entry for the ip address where it is running
addressToIdMapping.put(ipAddress, id);
} else if (line.contains("process_")) {
List<String> config = SPLITTER.splitToList(line);
String procAddr = config.get(0);
int datacenter = Integer.parseInt(config.get(1));
int portNumber = Integer.parseInt(config.get(3));
int numberOfPorts = Integer.parseInt(config.get(4));
DatacenterEnum colo = Utils.isProd() ? DatacenterEnum.name((byte) datacenter) : DatacenterEnum.DEV;
List<String> address = makeTcpAddress(procAddr, colo, portNumber, numberOfPorts);
processToTcpMapping.put(colo, address);
}
}
}
public Optional<Short> getId() {
Optional<String> ipAddress = Utils.getIPAddress();
return Optional.fromNullable(addressToIdMapping.get(ipAddress.get()));
}
}
现在我的问题是:我只想在第一次调用单例时更新我的 addressToIdMapping
映射一次,这样 getId()
方法总是 return 第一次调用时有什么在地图上更新。但是现在它会 return 每更新 30 分钟后地图中的任何内容。例如:当第一次调用这个 class 时,它会更新地图,所以我想在 addressToIdMapping
地图中永远保持相同的值,直到程序为 运行。这可能吗?同样如您所见,我在我的构造函数中做了很多事情。有没有比我现在做的更好的方法来做同样的事情?
一般来说,addressToIdMapping
地图总是只有一个 IP 地址条目,代码在 运行 中。如果 processToTcpMapping
地图每 30 分钟更新一次,我就很好.
您可以在 ConcurrentHashMap
上使用 putIfAbsent
方法,请参阅 documentation
如果条目不存在,它只会更新条目,这意味着一个 IP 地址的条目只会更新一次。
我必须在服务器上进行 HTTP 调用后解析响应。如果响应不成功,则尝试另一个服务器,否则解析成功的响应并填充两个 ConcurrentHashMap 并跳出 for 循环。所有的服务器都会以相同的格式给出相同的准确响应。
下面是我的单例class,它在ProcConfig
的构造函数中第一次调用时,调用loadConfig()
方法初始化所有东西,然后检查addressToIdMapping
映射是否有是否有一个条目。如果它不存在则抛出异常。之后它启动一个后台线程,每 30 分钟调用 loadConfig()
方法来更新 addressToIdMapping
和 processToTcpMapping
map.
public class ProcConfig {
private static final Splitter SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Map<String, Short> addressToIdMapping = new ConcurrentHashMap<>();
private final Map<DatacenterEnum, List<String>> processToTcpMapping = new ConcurrentHashMap<>();
private static class Holder {
private static final ProcConfig INSTANCE = new ProcConfig();
}
public static ProcConfig getInstance() {
return Holder.INSTANCE;
}
private ProcConfig() {
loadConfig();
checkArgument(!MapUtils.isEmpty(addressToIdMapping), "cannot find id, found '%s'.", addressToIdMapping);
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
loadConfig();
} catch (Exception ex) {
// log error
}
}
}, 60, 30, TimeUnit.MINUTES);
}
private void loadConfig() {
// current ipAddress where the program is running
Optional<String> ipAddress = Utils.getIPAddress();
List<String> servers = getServers();
for (String server : servers) {
try {
String response = HttpClient.getInstance().execute(makeUrl(server));
if (Strings.isNullOrEmpty(response) || response.equalsIgnoreCase("KEEP OUT")
|| response.equalsIgnoreCase("NOTHING FOUND")) {
continue;
}
parseConfig(response, ipAddress.get());
break;
} catch (Exception ex) {
// log error
}
}
}
private void parseConfig(final String response, final String ipAddress) throws IOException {
List<String> lines = IOUtils.readLines(new StringReader(response));
for (String line : lines) {
if (line.contains(ipAddress)) {
List<String> config = SPLITTER.splitToList(line);
Short id = Short.parseShort(config.get(2));
// this map will only have one entry for the ip address where it is running
addressToIdMapping.put(ipAddress, id);
} else if (line.contains("process_")) {
List<String> config = SPLITTER.splitToList(line);
String procAddr = config.get(0);
int datacenter = Integer.parseInt(config.get(1));
int portNumber = Integer.parseInt(config.get(3));
int numberOfPorts = Integer.parseInt(config.get(4));
DatacenterEnum colo = Utils.isProd() ? DatacenterEnum.name((byte) datacenter) : DatacenterEnum.DEV;
List<String> address = makeTcpAddress(procAddr, colo, portNumber, numberOfPorts);
processToTcpMapping.put(colo, address);
}
}
}
public Optional<Short> getId() {
Optional<String> ipAddress = Utils.getIPAddress();
return Optional.fromNullable(addressToIdMapping.get(ipAddress.get()));
}
}
现在我的问题是:我只想在第一次调用单例时更新我的 addressToIdMapping
映射一次,这样 getId()
方法总是 return 第一次调用时有什么在地图上更新。但是现在它会 return 每更新 30 分钟后地图中的任何内容。例如:当第一次调用这个 class 时,它会更新地图,所以我想在 addressToIdMapping
地图中永远保持相同的值,直到程序为 运行。这可能吗?同样如您所见,我在我的构造函数中做了很多事情。有没有比我现在做的更好的方法来做同样的事情?
一般来说,addressToIdMapping
地图总是只有一个 IP 地址条目,代码在 运行 中。如果 processToTcpMapping
地图每 30 分钟更新一次,我就很好.
您可以在 ConcurrentHashMap
上使用 putIfAbsent
方法,请参阅 documentation
如果条目不存在,它只会更新条目,这意味着一个 IP 地址的条目只会更新一次。