Skip to content

Commit 1026955

Browse files
authored
Merge pull request #1620 from jasonk000/jkoch/backport-perf-2x
perf: Backport allocation reduction optimizations from 2.x
2 parents 459fcf5 + b8ff519 commit 1026955

File tree

10 files changed

+1328
-246
lines changed

10 files changed

+1328
-246
lines changed

eureka-client/src/main/java/com/netflix/discovery/AbstractAzToRegionMapper.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public List<String> get() {
4040
});
4141

4242
private final Map<String, String> availabilityZoneVsRegion = new ConcurrentHashMap<String, String>();
43+
private final Map<String, String> parsedAzCache = new ConcurrentHashMap<String, String>();
4344
private String[] regionsToFetch;
4445

4546
protected AbstractAzToRegionMapper(EurekaClientConfig clientConfig) {
@@ -53,6 +54,7 @@ public synchronized void setRegionsToFetch(String[] regionsToFetch) {
5354
this.regionsToFetch = regionsToFetch;
5455
logger.info("Fetching availability zone to region mapping for regions {}", (Object) regionsToFetch);
5556
availabilityZoneVsRegion.clear();
57+
parsedAzCache.clear();
5658
for (String remoteRegion : regionsToFetch) {
5759
Set<String> availabilityZones = getZonesForARegion(remoteRegion);
5860
if (null == availabilityZones
@@ -83,6 +85,7 @@ public synchronized void setRegionsToFetch(String[] regionsToFetch) {
8385
} else {
8486
logger.info("Regions to fetch is null. Erasing older mapping if any.");
8587
availabilityZoneVsRegion.clear();
88+
parsedAzCache.clear();
8689
this.regionsToFetch = EMPTY_STR_ARRAY;
8790
}
8891
}
@@ -96,9 +99,15 @@ public synchronized void setRegionsToFetch(String[] regionsToFetch) {
9699

97100
@Override
98101
public String getRegionForAvailabilityZone(String availabilityZone) {
99-
String region = availabilityZoneVsRegion.get(availabilityZone);
102+
String region = parsedAzCache.get(availabilityZone);
100103
if (null == region) {
101-
return parseAzToGetRegion(availabilityZone);
104+
region = availabilityZoneVsRegion.get(availabilityZone);
105+
if (null == region) {
106+
region = parseAzToGetRegion(availabilityZone);
107+
}
108+
if (region != null) {
109+
parsedAzCache.put(availabilityZone, region);
110+
}
102111
}
103112
return region;
104113
}

eureka-client/src/main/java/com/netflix/discovery/DiscoveryClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,15 +1001,15 @@ private boolean fetchRegistry(boolean forceFullRegistryFetch) {
10011001
|| (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))
10021002
|| forceFullRegistryFetch
10031003
|| (applications == null)
1004-
|| (applications.getRegisteredApplications().size() == 0)
1004+
|| applications.isRegisteredApplicationsEmpty()
10051005
|| (applications.getVersion() == -1)) //Client application does not have latest library supporting delta
10061006
{
10071007
logger.info("Disable delta property : {}", clientConfig.shouldDisableDelta());
10081008
logger.info("Single vip registry refresh property : {}", clientConfig.getRegistryRefreshSingleVipAddress());
10091009
logger.info("Force full registry fetch : {}", forceFullRegistryFetch);
10101010
logger.info("Application is null : {}", (applications == null));
10111011
logger.info("Registered Applications size is zero : {}",
1012-
(applications.getRegisteredApplications().size() == 0));
1012+
applications.isRegisteredApplicationsEmpty());
10131013
logger.info("Application version is -1: {}", (applications.getVersion() == -1));
10141014
getAndStoreFullRegistry();
10151015
} else {

eureka-client/src/main/java/com/netflix/discovery/converters/EurekaJacksonCodec.java

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,15 @@ protected void autoMarshalEligible(Object o, JsonGenerator jgen) {
424424
public static class InstanceInfoDeserializer extends JsonDeserializer<InstanceInfo> {
425425
private static char[] BUF_AT_CLASS = "@class".toCharArray();
426426

427+
/** Extract uppercase from current JsonParser cursor. Avoids lambda capture. */
428+
private static String toUpperCase(JsonParser jp) {
429+
try {
430+
return jp.getText().toUpperCase();
431+
} catch (IOException e) {
432+
throw new RuntimeJsonMappingException(e.getMessage());
433+
}
434+
}
435+
427436
enum InstanceInfoField {
428437
HOSTNAME(ELEM_HOST),
429438
INSTANCE_ID(ELEM_INSTANCE_ID),
@@ -515,14 +524,7 @@ public InstanceInfo deserialize(JsonParser jp, DeserializationContext context) t
515524
break;
516525
case APP:
517526
builder.setAppNameForDeser(
518-
intern.apply(jp, CacheScope.APPLICATION_SCOPE,
519-
()->{
520-
try {
521-
return jp.getText().toUpperCase();
522-
} catch (IOException e) {
523-
throw new RuntimeJsonMappingException(e.getMessage());
524-
}
525-
}));
527+
intern.apply(jp, CacheScope.APPLICATION_SCOPE, InstanceInfoDeserializer::toUpperCase));
526528
break;
527529
case IP:
528530
builder.setIPAddr(intern.apply(jp));
@@ -590,14 +592,8 @@ public InstanceInfo deserialize(JsonParser jp, DeserializationContext context) t
590592
builder.setHealthCheckUrlsForDeser(null, intern.apply(jp.getText()));
591593
break;
592594
case APPGROUPNAME:
593-
builder.setAppGroupNameForDeser(intern.apply(jp, CacheScope.GLOBAL_SCOPE,
594-
()->{
595-
try {
596-
return jp.getText().toUpperCase();
597-
} catch (IOException e) {
598-
throw new RuntimeJsonMappingException(e.getMessage());
599-
}
600-
}));
595+
builder.setAppGroupNameForDeser(
596+
intern.apply(jp, CacheScope.GLOBAL_SCOPE, InstanceInfoDeserializer::toUpperCase));
601597
break;
602598
case HOMEPAGEURL:
603599
builder.setHomePageUrlForDeser(intern.apply(jp.getText()));
@@ -638,7 +634,9 @@ public InstanceInfo deserialize(JsonParser jp, DeserializationContext context) t
638634
String key = intern.apply(jp, CacheScope.GLOBAL_SCOPE);
639635
jsonToken = jp.nextToken();
640636
String value = intern.apply(jp, CacheScope.APPLICATION_SCOPE );
641-
metadataMap = Optional.ofNullable(metadataMap).orElseGet(METADATA_MAP_SUPPLIER);
637+
if (metadataMap == null) {
638+
metadataMap = METADATA_MAP_SUPPLIER.get();
639+
}
642640
metadataMap.put(key, value);
643641
}
644642
};

eureka-client/src/main/java/com/netflix/discovery/shared/Application.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Set;
2929
import java.util.concurrent.ConcurrentHashMap;
3030
import java.util.concurrent.atomic.AtomicReference;
31+
import java.util.function.Consumer;
3132

3233
import com.fasterxml.jackson.annotation.JsonCreator;
3334
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -135,7 +136,11 @@ public void removeInstance(InstanceInfo i) {
135136
*/
136137
@JsonProperty("instance")
137138
public List<InstanceInfo> getInstances() {
138-
return Optional.ofNullable(shuffledInstances.get()).orElseGet(this::getInstancesAsIsFromEureka);
139+
List<InstanceInfo> instances = shuffledInstances.get();
140+
if (instances == null) {
141+
instances = this.getInstancesAsIsFromEureka();
142+
}
143+
return instances;
139144
}
140145

141146
/**
@@ -152,6 +157,18 @@ public List<InstanceInfo> getInstancesAsIsFromEureka() {
152157
}
153158
}
154159

160+
/**
161+
* Iterate over instances without creating a defensive copy.
162+
* Package-private to avoid exposing unsynchronized iteration to external callers.
163+
* Callers should be sure that this is a quick iteration.
164+
*/
165+
void forEachInstance(Consumer<InstanceInfo> consumer) {
166+
synchronized (instances) {
167+
for (InstanceInfo info : instances) {
168+
consumer.accept(info);
169+
}
170+
}
171+
}
155172

156173
/**
157174
* Get the instance info that matches the given id.

0 commit comments

Comments
 (0)