View Javadoc
1   /*
2    * Copyright 2021 TiKV Project Authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  
18  package org.tikv.common;
19  
20  import static org.tikv.common.pd.PDUtils.addrToUri;
21  
22  import com.google.common.annotations.Beta;
23  import io.etcd.jetcd.ByteSequence;
24  import io.etcd.jetcd.Client;
25  import io.etcd.jetcd.KeyValue;
26  import io.etcd.jetcd.kv.GetResponse;
27  import java.net.URI;
28  import java.nio.charset.StandardCharsets;
29  import java.util.List;
30  import java.util.concurrent.CompletableFuture;
31  import java.util.concurrent.ConcurrentHashMap;
32  import java.util.concurrent.ConcurrentMap;
33  import java.util.concurrent.ExecutionException;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  public class DefaultHostMapping implements HostMapping {
38    private static final String NETWORK_MAPPING_PATH = "/client/url-mapping";
39    private final Client etcdClient;
40    private final String networkMappingName;
41    private final ConcurrentMap<String, String> hostMapping;
42    private final Logger logger = LoggerFactory.getLogger(DefaultHostMapping.class);
43  
44    public DefaultHostMapping(Client etcdClient, String networkMappingName) {
45      this.etcdClient = etcdClient;
46      this.networkMappingName = networkMappingName;
47      this.hostMapping = new ConcurrentHashMap<>();
48    }
49  
50    private ByteSequence hostToNetworkMappingKey(String host) {
51      String path = NETWORK_MAPPING_PATH + "/" + networkMappingName + "/" + host;
52      return ByteSequence.from(path, StandardCharsets.UTF_8);
53    }
54  
55    @Beta
56    private String getMappedHostFromPD(String host) {
57      ByteSequence hostKey = hostToNetworkMappingKey(host);
58      for (int i = 0; i < 5; i++) {
59        CompletableFuture<GetResponse> future = etcdClient.getKVClient().get(hostKey);
60        try {
61          GetResponse resp = future.get();
62          List<KeyValue> kvs = resp.getKvs();
63          if (kvs.size() != 1) {
64            break;
65          }
66          return kvs.get(0).getValue().toString(StandardCharsets.UTF_8);
67        } catch (InterruptedException e) {
68          Thread.currentThread().interrupt();
69        } catch (ExecutionException e) {
70          logger.info("failed to get mapped Host from PD: " + host, e);
71          break;
72        } catch (Exception ignore) {
73          // ignore
74          break;
75        }
76      }
77      return host;
78    }
79  
80    public URI getMappedURI(URI uri) {
81      if (networkMappingName.isEmpty()) {
82        return uri;
83      }
84      return addrToUri(
85          hostMapping.computeIfAbsent(uri.getHost(), this::getMappedHostFromPD)
86              + ":"
87              + uri.getPort());
88    }
89  }