Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ public <RequestType> HttpClientRequest<RequestType> onRequestSubmitted(final Htt
LOG.trace("Found service URI: %s", serviceURI);
List<ServiceHint> hints = Lists.newArrayList();
for (HttpClientHeader header: request.getHeaders()) {
String name = header.getName();
//Extract any service discovery hints
if (name.startsWith("X-Ness-SDHint-") && name.length() > "X-Ness-SDHint-".length()) {
hints.add(new ServiceHint(name.substring("X-Ness-SDHint-".length(), name.length()), header.getValue()));
}
String name = header.getName();
//Extract any service discovery hints
if (name.startsWith("X-Ness-SDHint-") && name.length() > "X-Ness-SDHint-".length()) {
hints.add(new ServiceHint(name.substring("X-Ness-SDHint-".length(), name.length()), header.getValue()));
}
}
final URI newUri = serviceUriConverter.convertServiceURI(serviceURI, hints.toArray(new ServiceHint[hints.size()]));
final HttpClientRequest.Builder<RequestType> builder = HttpClientRequest.Builder.fromRequest(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,29 @@
* @author steven
*/
public class ServiceHint {
public static final String VERSION_HINT = "Version";
public static final String QUALIFIER_HINT = "Qualifier";
public static final String CONSISTENTHASH_HINT = "ConsistentHash";
private final String name;
private final String value;

ServiceHint(String name, String value) {
this.name = name;
this.value = value;
public static final String VERSION_HINT = "Version";
public static final String QUALIFIER_HINT = "Qualifier";
public static final String CONSISTENTHASH_HINT = "ConsistentHash";
private final String name;
private final String value;

ServiceHint(String name, String value) {
this.name = name;
this.value = value;
}

public String getName() {
return name;
}

public String getValue() {
return value;
}

public String getName() {
return name;
}

public String getValue() {
return value;
}

/*
* Factory methods
*/

/**
* Select a service with a particular qualifier
*/
Expand All @@ -55,13 +55,13 @@ public static ServiceHint withQualifier(String qualifier) {
public static ServiceHint withVersion(int version) {
return new ServiceHint(VERSION_HINT, String.valueOf(version));
}

/** Selects a service that serves the requested hash key.
*
*
* @param hashKey (example: user id, or another consistent identifier)
* @return
*/
public static ServiceHint servesKey(String hashKey) {
return new ServiceHint(CONSISTENTHASH_HINT, hashKey);
return new ServiceHint(CONSISTENTHASH_HINT, hashKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,82 +37,82 @@
*
*/
public class ConsistentRingGroup extends AbstractCollection<ConsistentHashRing> {
private final Map<String, ConsistentHashRing> rings = Maps.newHashMap();
private final Random rand = new Random();
private final int totalServers;
private final Map<String, ConsistentHashRing> rings = Maps.newHashMap();
private final Random rand = new Random();
private final int totalServers;

public ConsistentRingGroup(Collection<ServiceInformation> servers) {
totalServers = servers.size();
Map<String, List<ServiceInformation>> serverGroups = Maps.newHashMap();
String serviceName = null;
//Sort the servers by type
for (ServiceInformation info: servers) {
Preconditions.checkArgument(serviceName == null || StringUtils.equals(serviceName, info.getServiceName()),
"All services must have the same name: " + servers);
serviceName = info.getServiceName();
//Hashmaps allow null keys, so null service types should map correctly.
if (!serverGroups.containsKey(info.getServiceType())) {
serverGroups.put(info.getServiceType(), new ArrayList<ServiceInformation>());
}
serverGroups.get(info.getServiceType()).add(info);
}
public ConsistentRingGroup(Collection<ServiceInformation> servers) {
totalServers = servers.size();
Map<String, List<ServiceInformation>> serverGroups = Maps.newHashMap();
String serviceName = null;
//Sort the servers by type
for (ServiceInformation info: servers) {
Preconditions.checkArgument(serviceName == null || StringUtils.equals(serviceName, info.getServiceName()),
"All services must have the same name: " + servers);
serviceName = info.getServiceName();
//Hashmaps allow null keys, so null service types should map correctly.
if (!serverGroups.containsKey(info.getServiceType())) {
serverGroups.put(info.getServiceType(), new ArrayList<ServiceInformation>());
}
serverGroups.get(info.getServiceType()).add(info);
}

for (Map.Entry<String, List<ServiceInformation>> entry: serverGroups.entrySet()) {
rings.put(entry.getKey(), new ConsistentHashRing(entry.getValue()));
}
}
for (Map.Entry<String, List<ServiceInformation>> entry: serverGroups.entrySet()) {
rings.put(entry.getKey(), new ConsistentHashRing(entry.getValue()));
}
}

/** Get the server ring for a particular type.
*
* Notes on running time:
* if type != null: O(1)
* if type == null and there is at least 1 service with null type: O(1)
* otherwise: O(N) where N is the number of types
*
* @param type
* @return
*/
public ConsistentHashRing getRing(String type) {
ConsistentHashRing ring = rings.get(type);
if (ring != null) {
return ring;
}
if (type == null) {
//If there's no ring without a type, then any type will do
//Use weighted random among the types, based on how many servers are serving each type
int selection = rand.nextInt() % totalServers;
for (ConsistentHashRing candidateRing: rings.values()) {
if (selection <= 0) {
return candidateRing;
} else {
selection -= candidateRing.size();
}
}
//It's possible there are no rings
Preconditions.checkState(totalServers == 0, "It shouldn't be possible to get here, " +
"unless there are no rings");
return null;
} else {
//If there's no server for this type, then pick one without a type
return rings.get(null);
}
}
/** Get the server ring for a particular type.
*
* Notes on running time:
* if type != null: O(1)
* if type == null and there is at least 1 service with null type: O(1)
* otherwise: O(N) where N is the number of types
*
* @param type
* @return
*/
public ConsistentHashRing getRing(String type) {
ConsistentHashRing ring = rings.get(type);
if (ring != null) {
return ring;
}
if (type == null) {
//If there's no ring without a type, then any type will do
//Use weighted random among the types, based on how many servers are serving each type
int selection = rand.nextInt() % totalServers;
for (ConsistentHashRing candidateRing: rings.values()) {
if (selection <= 0) {
return candidateRing;
} else {
selection -= candidateRing.size();
}
}
//It's possible there are no rings
Preconditions.checkState(totalServers == 0, "It shouldn't be possible to get here, " +
"unless there are no rings");
return null;
} else {
//If there's no server for this type, then pick one without a type
return rings.get(null);
}
}

public List<ServiceInformation> getAll() {
List<ServiceInformation> result = Lists.newArrayList();
for (Collection<ServiceInformation> info: this) {
result.addAll(info);
}
return result;
}
public List<ServiceInformation> getAll() {
List<ServiceInformation> result = Lists.newArrayList();
for (Collection<ServiceInformation> info: this) {
result.addAll(info);
}
return result;
}

@Override
public Iterator<ConsistentHashRing> iterator() {
return rings.values().iterator();
}
@Override
public Iterator<ConsistentHashRing> iterator() {
return rings.values().iterator();
}

@Override
public int size() {
return rings.size();
}
@Override
public int size() {
return rings.size();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ public void processResult(final int rc, final String path, final Object ctx, fin

Map<String, ConsistentRingGroup> serviceGroups = Maps.newHashMap();
for (Map.Entry<String, List<ServiceInformation>> entry: serviceMap.entrySet()) {
ConsistentRingGroup currentGroup = stateHolder.getState().get(entry.getKey());
//Rebuilding a group is kind of expensive, so reuse the old group if it hasn't changed
if (currentGroup != null && Sets.newHashSet(entry.getValue()).equals(Sets.newHashSet(currentGroup.getAll()))) {
serviceGroups.put(entry.getKey(), currentGroup);
} else {
serviceGroups.put(entry.getKey(), new ConsistentRingGroup(entry.getValue()));
}
ConsistentRingGroup currentGroup = stateHolder.getState().get(entry.getKey());
//Rebuilding a group is kind of expensive, so reuse the old group if it hasn't changed
if (currentGroup != null && Sets.newHashSet(entry.getValue()).equals(Sets.newHashSet(currentGroup.getAll()))) {
serviceGroups.put(entry.getKey(), currentGroup);
} else {
serviceGroups.put(entry.getKey(), new ConsistentRingGroup(entry.getValue()));
}
}
stateHolder.setState(serviceGroups);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void unannounce(final ServiceInformation serviceInformation)
final Map<String, ConsistentRingGroup> worldOrder = getStateOfTheWorldHolder().getState();
ConsistentRingGroup group = worldOrder.get(serviceInformation.getServiceName());
if (group == null) {
return;
return;
}
Set<ServiceInformation> services = Sets.newHashSet(group.getAll());
services.remove(serviceInformation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ public Builder<Type> addServiceInformation(final ServiceInformation serviceInfor
final String serviceName = serviceInformation.getServiceName();
List<ServiceInformation> serviceInformations = Lists.newArrayList();
ConsistentRingGroup currentGroup = newWorldOrder.get(serviceName);
if (currentGroup != null) {
serviceInformations.addAll(currentGroup.getAll());
if (currentGroup != null) {
serviceInformations.addAll(currentGroup.getAll());
}
serviceInformations.add(serviceInformation);
newWorldOrder.put(serviceName, new ConsistentRingGroup(serviceInformations));
serviceInformations.add(serviceInformation);
newWorldOrder.put(serviceName, new ConsistentRingGroup(serviceInformations));
return this;
}

Expand Down