maxucheng0 opened a new issue #2574:
URL: https://github.com/apache/servicecomb-java-chassis/issues/2574
**Describe the bug**
背景:当前正在使用servicecomb-java-chassis 2.1.5版本,之前使用servicecomb-java-chassis
1.3.1版本,在1.3.1版本中,MicroserviceVersions会定时(30s)触发pullInstances来更新实例缓存,在2.1.5发现仅能通过实例更新事件触发MicroserviceVersions中的缓存更新,如果更新事件丢失,且后续没有实例新的更新事件,缓存一致性无法保证。
现象:实例注册后更新实例属性成功,在其他服务实例中进行服务间调用时指定transactionControl仅调用存在对应属性的服务实例,Load
balancer报错无法找到可用实例。
原因分析:MicroserviceVersions在初始化时先进行了pullInstances之后将此microserviceVersions放入versionsByName的map中,在执行pullInstances时如果收到了更新事件,会触发MicroserviceManager里面的全量更新方法,此方法是通过遍历versionsByName中的MicroserviceVersions进行pullInstances的调用,此时之前创建的microserviceVersions未放入versionsByName中,导致丢失此次更新事件,之后缓存无法恢复,服务间调用持续失败。
_MicroserviceManager.java_
```
public MicroserviceVersions getOrCreateMicroserviceVersions(String
microserviceName) {
MicroserviceVersions microserviceVersions =
versionsByName.get(microserviceName);
if (microserviceVersions == null) {
synchronized (lock) {
microserviceVersions = versionsByName.get(microserviceName);
if (microserviceVersions == null) {
microserviceVersions = new MicroserviceVersions(appManager, appId,
microserviceName);
microserviceVersions.pullInstances();
versionsByName.put(microserviceName, microserviceVersions);
}
}
}
tryRemoveInvalidMicroservice(microserviceVersions);
return microserviceVersions;
}
...
public void pullInstances() {
for (MicroserviceVersions microserviceVersions :
versionsByName.values()) {
microserviceVersions.pullInstances();
tryRemoveInvalidMicroservice(microserviceVersions);
}
}
public void onMicroserviceInstanceChanged(MicroserviceInstanceChangedEvent
changedEvent) {
for (MicroserviceVersions microserviceVersions :
versionsByName.values()) {
microserviceVersions.onMicroserviceInstanceChanged(changedEvent);
tryRemoveInvalidMicroservice(microserviceVersions);
}
}
```
解决建议:当前可以通过先将初始化后的MicroserviceVersions放入MicroserviceManager的versionsByName中,之后再进行pullInstances,由于阻塞pullInstances的原因是因为流程中的setInstances中存在锁,不一定可以完全保证先执行首次拉取结果的set,再进行更新事件的set,所以仍需要定时pullInstances的操作。
**To Reproduce**
复现方法:通过判断服务名在创建对应MicroserviceVersions缓存时sleep
30s,在sleep期间进行实例属性的更新,之后进行服务间调用时指定更新后的实例属性。
_MicroserviceVersions.java_
```
private void setInstances(List<MicroserviceInstance> pulledInstances, String
rev) {
if ("Service03".equals(getMicroserviceName())) {
try {
TimeUnit.SECONDS.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lock) {
MergedInstances mergedInstances = mergeInstances(pulledInstances,
instances);
instances = mergedInstances.instanceIdMap.values();
// clear cache
versions.entrySet().forEach(versionEntry ->
versionEntry.getValue().setInstances(new ArrayList<>()));
for (Entry<String, List<MicroserviceInstance>> entry :
mergedInstances.microserviceIdMap.entrySet()) {
// ensure microserviceVersion exists
versions.computeIfAbsent(entry.getKey(),
microserviceId -> createMicroserviceVersion(microserviceId,
entry.getValue()))
.setInstances(entry.getValue());
}
for (MicroserviceVersionRule microserviceVersionRule :
versionRules.values()) {
microserviceVersionRule.update(versions, instances);
}
revision = rev;
}
}
```
**Expected behavior**
服务间调用可以成功。
**Platform And Runtime (please complete the following information):**
环境无差别,稳定复现
**Additional context**
无
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]