Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.servicecomb.http.client.common;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.servicecomb.http.client.event.RefreshEndpointEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
public class AbstractAddressManager {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAddressManager.class);
public static final String DEFAULT_PROJECT = "default";
public static final String V4_PREFIX = "/v4/";
private static final String V3_PREFIX = "/v3/";
private static final int DEFAULT_METRICS_WINDOW_TIME = 1;
private static final int ISOLATION_THRESHOLD = 3;
private List addresses = new ArrayList<>();
private int index;
private String projectName;
// if address in same zone will be true; others will be false.
private final Map addressCategory = new HashMap<>();
// recording continuous times of failure of an address.
private final Map addressFailureStatus = new ConcurrentHashMap<>();
// recording address isolation status, if isolated will be false
private final Map addressIsolated = new ConcurrentHashMap<>();
// recording address isolation status, if isolated will be false
private Cache addressIsolationStatus = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
private volatile List availableZone = new ArrayList<>();
private volatile List availableRegion = new ArrayList<>();
private final List defaultAddress = new ArrayList<>();
private boolean addressAutoRefreshed = false;
private final Object lock = new Object();
private Random random = new Random();
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1,
new ThreadFactoryBuilder()
.setNameFormat("check-available-address-%d")
.build());
public AbstractAddressManager(List addresses) {
this.projectName = DEFAULT_PROJECT;
this.addresses.addAll(addresses);
this.defaultAddress.addAll(addresses);
this.index = addresses.size() > 0 ? random.nextInt(addresses.size()) : 0;
}
public AbstractAddressManager(String projectName, List addresses) {
this.projectName = StringUtils.isEmpty(projectName) ? DEFAULT_PROJECT : projectName;
this.addresses = this.transformAddress(addresses);
this.defaultAddress.addAll(this.addresses);
this.index = addresses.size() > 0 ? random.nextInt(addresses.size()) : 0;
}
@VisibleForTesting
Cache getAddressIsolationStatus() {
return addressIsolationStatus;
}
@VisibleForTesting
void setAddressIsolationStatus(Cache addressIsolationStatus) {
this.addressIsolationStatus = addressIsolationStatus;
}
@VisibleForTesting
Map getAddressFailureStatus() {
return addressFailureStatus;
}
public List getAddresses() {
return addresses;
}
public List getAvailableZone() {
return availableZone;
}
public List getAvailableRegion() {
return availableRegion;
}
private void startCheck() {
executorService.scheduleAtFixedRate(this::checkHistory,
0,
DEFAULT_METRICS_WINDOW_TIME,
TimeUnit.MINUTES);
}
public String formatUrl(String url, boolean absoluteUrl, String address) {
return absoluteUrl ? address + url : formatAddress(address) + url;
}
public String address() {
if (!addressAutoRefreshed) {
return getDefaultAddress();
} else {
return getAvailableZoneAddress();
}
}
public boolean sslEnabled() {
return address().startsWith("https://");
}
protected List transformAddress(List addresses) {
return addresses.stream().map(this::formatAddress).collect(Collectors.toList());
}
protected String getUrlPrefix(String address) {
return address + V3_PREFIX;
}
protected String formatAddress(String address) {
try {
return getUrlPrefix(address) + HttpUtils.encodeURLParam(this.projectName);
} catch (Exception e) {
throw new IllegalStateException("not possible");
}
}
private String getDefaultAddress() {
List addresses = getAvailableAddress(defaultAddress);
if (!addresses.isEmpty()) {
return getCurrentAddress(addresses);
}
return getInitAddress();
}
private String getAvailableZoneAddress() {
List addresses = getAvailableZoneIpPorts();
if (!addresses.isEmpty()) {
return getCurrentAddress(addresses);
}
return getInitAddress();
}
// when all available address is fail, it will use all the initial addresses for polling.
private String getInitAddress() {
if (addresses.isEmpty()) {
return null;
}
return getCurrentAddress(addresses);
}
private String getCurrentAddress(List addresses) {
synchronized (this) {
this.index++;
if (this.index >= addresses.size()) {
this.index = 0;
}
return addresses.get(index);
}
}
private List getAvailableZoneIpPorts() {
List results = new ArrayList<>();
if (!availableZone.isEmpty()) {
results.addAll(getAvailableAddress(availableZone));
} else {
results.addAll(getAvailableAddress(availableRegion));
}
return results;
}
private List getAvailableAddress(List endpoints) {
return endpoints.stream().filter(uri -> !addressIsolated.containsKey(uri) || addressIsolated.get(uri))
.collect(Collectors.toList());
}
protected String normalizeUri(String endpoint) {
return new URLEndPoint(endpoint).toString();
}
public void refreshEndpoint(RefreshEndpointEvent event, String key) {
if (null == event || !event.getName().equals(key)) {
return;
}
availableZone = event.getSameZone().stream().map(this::normalizeUri).collect(Collectors.toList());
availableRegion = event.getSameRegion().stream().map(this::normalizeUri).collect(Collectors.toList());
availableZone.forEach(address -> addressCategory.put(address, true));
availableRegion.forEach(address -> addressCategory.put(address, false));
startCheck();
addressAutoRefreshed = true;
}
public void recordFailState(String address) {
synchronized (lock) {
if (!addressFailureStatus.containsKey(address)) {
addressFailureStatus.put(address, 1);
return;
}
int number = addressFailureStatus.get(address) + 1;
if (number < ISOLATION_THRESHOLD) {
addressFailureStatus.put(address, number);
} else {
removeAddress(address);
}
}
}
public void recordSuccessState(String address) {
addressFailureStatus.put(address, 0);
}
@VisibleForTesting
protected void checkHistory() {
addressIsolated.keySet().stream().filter(this::judgeIsolation).forEach(s -> {
if (telnetTest(s)) {
rejoinAddress(s);
} else {
addressIsolationStatus.put(s, false);
}
});
}
private Boolean judgeIsolation(String address) {
try {
return addressIsolationStatus.get(address, () -> true);
} catch (ExecutionException e) {
return true;
}
}
protected boolean telnetTest(String address) {
URI ipPort = parseIpPortFromURI(address);
try (Socket s = new Socket()) {
s.connect(new InetSocketAddress(ipPort.getHost(), ipPort.getPort()), 3000);
return true;
} catch (IOException e) {
LOGGER.warn("ping endpoint {} failed, It will be quarantined again.", address);
}
return false;
}
private URI parseIpPortFromURI(String uri) {
try {
return new URI(uri);
} catch (URISyntaxException e) {
return null;
}
}
//Query whether the current address belongs to the same AZ or region through azmap,
// add it to the sequence of, and delete the record in history
@VisibleForTesting
void rejoinAddress(String address) {
if (!addressAutoRefreshed) {
defaultAddress.add(address);
addressFailureStatus.put(address, 0);
addressIsolated.remove(address);
return;
}
if (addressCategory.get(address) == null) {
LOGGER.warn("may not happen {}-{}", addressCategory.size(), address);
return;
}
if (addressCategory.get(address)) {
availableZone.add(address);
} else {
availableRegion.add(address);
}
addressFailureStatus.put(address, 0);
addressIsolated.remove(address);
}
//Query whether the current address belongs to the same AZ or the same region through AZMap,
// and delete it from the record. At the same time, add records in history and cache
@VisibleForTesting
void removeAddress(String address) {
if (!addressAutoRefreshed) {
defaultAddress.remove(address);
addressIsolated.put(address, false);
addressIsolationStatus.put(address, false);
return;
}
if (addressCategory.get(address) == null) {
LOGGER.warn("may not happen {}-{}", addressCategory.size(), address);
return;
}
if (addressCategory.get(address)) {
availableZone.remove(address);
} else {
availableRegion.remove(address);
}
addressIsolated.put(address, false);
addressIsolationStatus.put(address, false);
}
}