Skip to content

Commit b476bb8

Browse files
feat: support incremental configuration synchronization client (#90)
* feat: support incremental configuration synchronization client * code format * add Unknown sync mode * code format * code format * code format * code format * code format * 更新 RemoteConfigRepository.java Co-authored-by: Jason Song <[email protected]> * 更新 ApolloConfig.java Co-authored-by: Jason Song <[email protected]> * 更新 ApolloConfig.java Co-authored-by: Jason Song <[email protected]> * 更新 ConfigurationChange.java Co-authored-by: Jason Song <[email protected]> * 更新 ConfigSyncType.java Co-authored-by: Jason Song <[email protected]> * 更新 ConfigSyncType.java Co-authored-by: Jason Song <[email protected]> * 更新 ConfigSyncType.java Co-authored-by: Jason Song <[email protected]> * 更新 ConfigurationChange.java Co-authored-by: Jason Song <[email protected]> * 更新 ConfigurationChangeType.java Co-authored-by: Jason Song <[email protected]> * 更新 ConfigurationChangeTypeUtils.java Co-authored-by: Jason Song <[email protected]> * 更新 ConfigSyncType.java Co-authored-by: Jason Song <[email protected]> * code format * code format * add UnknownSync action * Apply suggestions from code review --------- Co-authored-by: Jason Song <[email protected]>
1 parent eff90aa commit b476bb8

File tree

8 files changed

+450
-1
lines changed

8 files changed

+450
-1
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Apollo Java 2.4.0
1313
* [Feature support pulling configuration information from multiple AppIds](https://github.com/apolloconfig/apollo-java/pull/70)
1414
* [Fix monitor arg cause npe](https://github.com/apolloconfig/apollo-java/pull/86)
1515
* [Fix the concurrent issue in SpringValueRegistry.scanAndClean](https://github.com/apolloconfig/apollo-java/pull/95)
16+
* [Feature support incremental configuration synchronization client](https://github.com/apolloconfig/apollo-java/pull/90)
1617

1718
------------------
1819
All issues and pull requests are [here](https://github.com/apolloconfig/apollo-java/milestone/4?closed=1)

apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigRepository.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
import com.ctrip.framework.apollo.core.ConfigConsts;
2424
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
2525
import com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;
26+
import com.ctrip.framework.apollo.core.dto.ConfigurationChange;
2627
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
28+
import com.ctrip.framework.apollo.core.enums.ConfigSyncType;
29+
import com.ctrip.framework.apollo.core.enums.ConfigurationChangeType;
2730
import com.ctrip.framework.apollo.core.schedule.ExponentialSchedulePolicy;
2831
import com.ctrip.framework.apollo.core.schedule.SchedulePolicy;
2932
import com.ctrip.framework.apollo.core.signature.Signature;
@@ -49,6 +52,7 @@
4952
import com.google.common.util.concurrent.RateLimiter;
5053
import com.google.gson.Gson;
5154
import java.util.Collections;
55+
import java.util.HashMap;
5256
import java.util.List;
5357
import java.util.Map;
5458
import java.util.Properties;
@@ -255,6 +259,24 @@ private ApolloConfig loadApolloConfig() {
255259

256260
ApolloConfig result = response.getBody();
257261

262+
if (result != null) {
263+
ConfigSyncType configSyncType = ConfigSyncType.fromString(result.getConfigSyncType());
264+
265+
if (configSyncType == ConfigSyncType.INCREMENTAL_SYNC) {
266+
ApolloConfig previousConfig = m_configCache.get();
267+
Map<String, String> previousConfigurations =
268+
(previousConfig != null) ? previousConfig.getConfigurations() : null;
269+
result.setConfigurations(
270+
mergeConfigurations(previousConfigurations, result.getConfigurationChanges()));
271+
} else if (configSyncType == ConfigSyncType.UNKNOWN) {
272+
String message = String.format(
273+
"Invalid config sync type - %s",
274+
result.getConfigSyncType());
275+
throw new ApolloConfigException(message, exception);
276+
}
277+
278+
}
279+
258280
logger.debug("Loaded config for {}: {}", m_namespace, result);
259281

260282
return result;
@@ -269,7 +291,7 @@ private ApolloConfig loadApolloConfig() {
269291
statusCodeException = new ApolloConfigStatusCodeException(ex.getStatusCode(),
270292
message);
271293
Tracer.logEvent(APOLLO_CLIENT_NAMESPACE_NOT_FOUND,m_namespace);
272-
294+
273295
}
274296
Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(statusCodeException));
275297
transaction.setStatus(statusCodeException);
@@ -363,4 +385,34 @@ private List<ServiceDTO> getConfigServices() {
363385

364386
return services;
365387
}
388+
389+
Map<String, String> mergeConfigurations(Map<String, String> previousConfigurations,
390+
List<ConfigurationChange> configurationChanges) {
391+
Map<String, String> newConfigurations = new HashMap<>();
392+
393+
if (previousConfigurations != null) {
394+
newConfigurations = Maps.newHashMap(previousConfigurations);
395+
}
396+
397+
if (configurationChanges == null) {
398+
return newConfigurations;
399+
}
400+
401+
for (ConfigurationChange change : configurationChanges) {
402+
switch (ConfigurationChangeType.fromString(change.getConfigurationChangeType())) {
403+
case ADDED:
404+
case MODIFIED:
405+
newConfigurations.put(change.getKey(), change.getNewValue());
406+
break;
407+
case DELETED:
408+
newConfigurations.remove(change.getKey());
409+
break;
410+
default:
411+
//do nothing
412+
break;
413+
}
414+
}
415+
416+
return newConfigurations;
417+
}
366418
}

apollo-client/src/test/java/com/ctrip/framework/apollo/internals/RemoteConfigRepositoryTest.java

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
3535
import com.ctrip.framework.apollo.core.dto.ApolloConfigNotification;
3636
import com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;
37+
import com.ctrip.framework.apollo.core.dto.ConfigurationChange;
3738
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
39+
import com.ctrip.framework.apollo.core.enums.ConfigSyncType;
3840
import com.ctrip.framework.apollo.core.signature.Signature;
3941
import com.ctrip.framework.apollo.enums.ConfigSourceType;
4042
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
@@ -53,6 +55,7 @@
5355
import com.google.common.util.concurrent.SettableFuture;
5456
import com.google.gson.Gson;
5557
import java.lang.reflect.Type;
58+
import java.util.ArrayList;
5659
import java.util.List;
5760
import java.util.Map;
5861
import java.util.Properties;
@@ -154,6 +157,141 @@ public void testLoadConfig() throws Exception {
154157
assertEquals(ConfigSourceType.REMOTE, remoteConfigRepository.getSourceType());
155158
}
156159

160+
@Test
161+
public void testLoadConfigWithIncrementalSync() throws Exception {
162+
163+
String someKey = "someKey";
164+
String someValue = "someValue";
165+
String someKey1 = "someKey1";
166+
String someValue1 = "someKey1";
167+
Map<String, String> configurations = Maps.newHashMap();
168+
configurations.put(someKey, someValue);
169+
configurations.put(someKey1, someValue1);
170+
ApolloConfig someApolloConfig = assembleApolloConfig(configurations);
171+
172+
when(someResponse.getStatusCode()).thenReturn(200);
173+
when(someResponse.getBody()).thenReturn(someApolloConfig);
174+
175+
RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
176+
someNamespace);
177+
178+
remoteConfigRepository.sync();
179+
180+
List<ConfigurationChange> configurationChanges = new ArrayList<>();
181+
String someNewValue = "someNewValue";
182+
configurationChanges.add(new ConfigurationChange(someKey, someNewValue, "MODIFIED"));
183+
configurationChanges.add(new ConfigurationChange(someKey1, null, "DELETED"));
184+
String someKey2 = "someKey2";
185+
String someValue2 = "someValue2";
186+
configurationChanges.add(new ConfigurationChange(someKey2, someValue2, "ADDED"));
187+
ApolloConfig someApolloConfigWithIncrementalSync = assembleApolloConfigWithIncrementalSync(
188+
configurationChanges);
189+
190+
when(someResponse.getStatusCode()).thenReturn(200);
191+
when(someResponse.getBody()).thenReturn(someApolloConfigWithIncrementalSync);
192+
193+
remoteConfigRepository.sync();
194+
195+
Properties config = remoteConfigRepository.getConfig();
196+
197+
assertEquals(2, config.size());
198+
assertEquals("someNewValue", config.getProperty("someKey"));
199+
assertEquals("someValue2", config.getProperty("someKey2"));
200+
assertEquals(ConfigSourceType.REMOTE, remoteConfigRepository.getSourceType());
201+
remoteConfigLongPollService.stopLongPollingRefresh();
202+
}
203+
204+
@Test
205+
public void testMergeConfigurations() throws Exception {
206+
String key1 = "key1";
207+
String value1 = "value1";
208+
String anotherValue1 = "anotherValue1";
209+
210+
String key3 = "key3";
211+
String value3 = "value3";
212+
Map<String, String> previousConfigurations = ImmutableMap.of(key1, value1, key3, value3);
213+
214+
List<ConfigurationChange> configurationChanges = new ArrayList<>();
215+
configurationChanges.add(new ConfigurationChange(key1, anotherValue1, "MODIFIED"));
216+
String key2 = "key2";
217+
String value2 = "value2";
218+
configurationChanges.add(new ConfigurationChange(key2, value2, "ADDED"));
219+
configurationChanges.add(new ConfigurationChange(key3, null, "DELETED"));
220+
221+
RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
222+
someNamespace);
223+
Map<String, String> result = remoteConfigRepository.mergeConfigurations(previousConfigurations,
224+
configurationChanges);
225+
226+
assertEquals(2, result.size());
227+
assertEquals(anotherValue1, result.get(key1));
228+
assertEquals(value2, result.get(key2));
229+
}
230+
231+
@Test
232+
public void testMergeConfigurationWithPreviousConfigurationsIsNULL() throws Exception {
233+
String key1 = "key1";
234+
String value1 = "value1";
235+
236+
String key3 = "key3";
237+
String value3 = "value3";
238+
239+
Map<String, String> previousConfigurations = ImmutableMap.of(key1, value1, key3, value3);
240+
241+
RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
242+
someNamespace);
243+
Map<String, String> result = remoteConfigRepository.mergeConfigurations(previousConfigurations,
244+
null);
245+
246+
assertEquals(2, result.size());
247+
assertEquals(value1, result.get(key1));
248+
assertEquals(value3, result.get(key3));
249+
}
250+
251+
@Test
252+
public void testMergeConfigurationWithChangesIsNULL() throws Exception {
253+
String key1 = "key1";
254+
String value1 = "value1";
255+
String anotherValue1 = "anotherValue1";
256+
257+
String key3 = "key3";
258+
String value3 = "value3";
259+
260+
List<ConfigurationChange> configurationChanges = new ArrayList<>();
261+
configurationChanges.add(new ConfigurationChange(key1, anotherValue1, "MODIFIED"));
262+
String key2 = "key2";
263+
String value2 = "value2";
264+
configurationChanges.add(new ConfigurationChange(key2, value2, "ADDED"));
265+
configurationChanges.add(new ConfigurationChange(key3, null, "DELETED"));
266+
267+
RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
268+
someNamespace);
269+
Map<String, String> result = remoteConfigRepository.mergeConfigurations(null,
270+
configurationChanges);
271+
272+
assertEquals(2, result.size());
273+
assertEquals(anotherValue1, result.get(key1));
274+
assertEquals(value2, result.get(key2));
275+
}
276+
277+
@Test(expected = ApolloConfigException.class)
278+
public void testGetRemoteConfigWithUnknownSync() throws Exception {
279+
280+
ApolloConfig someApolloConfigWithUnknownSync = assembleApolloConfigWithUnknownSync(
281+
new ArrayList<>());
282+
283+
when(someResponse.getStatusCode()).thenReturn(200);
284+
when(someResponse.getBody()).thenReturn(someApolloConfigWithUnknownSync);
285+
286+
RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someAppId,
287+
someNamespace);
288+
289+
//must stop the long polling before exception occurred
290+
remoteConfigLongPollService.stopLongPollingRefresh();
291+
292+
remoteConfigRepository.getConfig();
293+
}
294+
157295
@Test
158296
public void testLoadConfigWithOrderedProperties() throws Exception {
159297
String someKey = "someKey";
@@ -371,6 +509,32 @@ private ApolloConfig assembleApolloConfig(Map<String, String> configurations) {
371509
return apolloConfig;
372510
}
373511

512+
private ApolloConfig assembleApolloConfigWithIncrementalSync(
513+
List<ConfigurationChange> configurationChanges) {
514+
String someAppId = "appId";
515+
String someClusterName = "cluster";
516+
String someReleaseKey = "1";
517+
ApolloConfig apolloConfig =
518+
new ApolloConfig(someAppId, someClusterName, someNamespace, someReleaseKey);
519+
520+
apolloConfig.setConfigSyncType(ConfigSyncType.INCREMENTAL_SYNC.getValue());
521+
apolloConfig.setConfigurationChanges(configurationChanges);
522+
return apolloConfig;
523+
}
524+
525+
private ApolloConfig assembleApolloConfigWithUnknownSync(
526+
List<ConfigurationChange> configurationChanges) {
527+
String someAppId = "appId";
528+
String someClusterName = "cluster";
529+
String someReleaseKey = "1";
530+
ApolloConfig apolloConfig =
531+
new ApolloConfig(someAppId, someClusterName, someNamespace, someReleaseKey);
532+
533+
apolloConfig.setConfigSyncType(ConfigSyncType.UNKNOWN.getValue());
534+
apolloConfig.setConfigurationChanges(configurationChanges);
535+
return apolloConfig;
536+
}
537+
374538
public static class MockConfigUtil extends ConfigUtil {
375539

376540
@Override

apollo-core/src/main/java/com/ctrip/framework/apollo/core/dto/ApolloConfig.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package com.ctrip.framework.apollo.core.dto;
1818

19+
import java.util.List;
1920
import java.util.Map;
2021

2122
/**
@@ -33,6 +34,10 @@ public class ApolloConfig {
3334

3435
private String releaseKey;
3536

37+
private String configSyncType;
38+
39+
private List<ConfigurationChange> configurationChanges;
40+
3641
public ApolloConfig() {
3742
}
3843

@@ -62,6 +67,14 @@ public String getReleaseKey() {
6267
return releaseKey;
6368
}
6469

70+
public String getConfigSyncType() {
71+
return configSyncType;
72+
}
73+
74+
public List<ConfigurationChange> getConfigurationChanges() {
75+
return configurationChanges;
76+
}
77+
6578
public Map<String, String> getConfigurations() {
6679
return configurations;
6780
}
@@ -82,10 +95,18 @@ public void setReleaseKey(String releaseKey) {
8295
this.releaseKey = releaseKey;
8396
}
8497

98+
public void setConfigSyncType(String configSyncType) {
99+
this.configSyncType = configSyncType;
100+
}
101+
85102
public void setConfigurations(Map<String, String> configurations) {
86103
this.configurations = configurations;
87104
}
88105

106+
public void setConfigurationChanges(List<ConfigurationChange> configurationChanges) {
107+
this.configurationChanges = configurationChanges;
108+
}
109+
89110
@Override
90111
public String toString() {
91112
final StringBuilder sb = new StringBuilder("ApolloConfig{");

0 commit comments

Comments
 (0)