0

我必须在服务器上进行HTTP调用后解析响应。如果响应不成功,则尝试另一个服务器,否则解析成功响应并填充两个ConcurrentHashMap并跳出for循环。所有的服务器将以相同的格式提供相同的确切响应。如何在第一次调用Singleton的过程中只更新一次地图?

下面是我的单身类上的ProcConfig构造函数在第一次调用,调用loadConfig()方法来初始化一切,然后检查addressToIdMapping地图是否有与否的一个条目。如果不存在,则抛出异常。之后它启动后台线程,每隔30分钟它会调用loadConfig()方法更新addressToIdMappingprocessToTcpMapping图。

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()方法总是返回地图中的第一个更新过程中有什么在那里。但是现在它会在每次更新30分钟后返回地图中的任何内容。例如:当第一次调用这个类时,它会更新地图,所以我想永远保持相同的值,直到程序运行。这可能吗?也正如你所看到的,我在我的构造函数中做了很多东西。与我正在做的事情相比,是否有更好的方法来做同样的事情?

一般来说addressToIdMapping地图总是只有一个IP地址的条目在运行代码。而且我很好,如果processToTcpMapping地图每30分钟更新一次。

+0

你可以在'ConcurrentHashMap'上使用'putIfAbsent'方法参见[doc](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#putIfAbsent-KV - ) – wwajerowicz

+0

@wwajerowicz'putIfAbsent'在这里需要做什么? – user1950349

+0

它只会在条目不存在的情况下更新条目,这意味着只有一个IP地址的条目才会更新一次。 – wwajerowicz

回答

1

可以使用putIfAbsent方法上ConcurrentHashMap看到documentation

,如果不存在的话,这意味着对于一个IP地址条目将只能获得一次更新它只会更新条目。