0

Мне нужно разобрать ответ после совершения HTTP-вызова на сервере. Если ответ не увенчался успехом, попробуйте другой сервер в противном случае проанализировать успешный ответ и заполнить два ConcurrentHashMap и выйти из цикла for. И все серверы будут давать одинаковый точный ответ в том же формате.Как обновить карту только один раз во время первого вызова Singleton?

Ниже мой синглтон класс, который по первому зову в конструкторе ProcConfig, вызывает loadConfig() метод для инициализации все, а затем проверить, есть ли addressToIdMapping карта одну запись в ней или нет. Если он не существует, он выдает исключение. После этого начинается фоновый поток, где каждые 30 минут он будет вызывать loadConfig() метод для обновления addressToIdMapping и processToTcpMapping.

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 навсегда, пока программа не будет запущена. Возможно ли это сделать? Также, как вы видите, я делаю много вещей в моем конструкторе. Есть ли лучший способ сделать то же самое по сравнению с тем, что я делаю?

В целом карта addressToIdMapping всегда будет иметь только одну запись для IP-адреса, в котором работает код. И я в порядке, если карта обновляется каждые 30 минут, если processToTcpMapping.

+0

вы можете использовать метод 'putIfAbsent' на' ConcurrentHashMap', см. [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-адрес будет только обновляться один раз.