Fix MD5Utils concurrent error (#9176)
This commit is contained in:
parent
ef2c0b3fcc
commit
68381ba766
|
|
@ -36,9 +36,9 @@ public class MD5Utils {
|
|||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||
};
|
||||
|
||||
private static MessageDigest mdInst;
|
||||
private MessageDigest mdInst;
|
||||
|
||||
static {
|
||||
public MD5Utils() {
|
||||
try {
|
||||
mdInst = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
|
|
@ -46,9 +46,17 @@ public class MD5Utils {
|
|||
}
|
||||
}
|
||||
|
||||
public static String getMd5(String value) {
|
||||
mdInst.update(value.getBytes(UTF_8));
|
||||
byte[] md5 = mdInst.digest();
|
||||
/**
|
||||
* Calculation md5 value of specify string
|
||||
* @param input
|
||||
*/
|
||||
public String getMd5(String input) {
|
||||
byte[] md5;
|
||||
// MessageDigest instance is NOT thread-safe
|
||||
synchronized (mdInst) {
|
||||
mdInst.update(input.getBytes(UTF_8));
|
||||
md5 = mdInst.digest();
|
||||
}
|
||||
|
||||
int j = md5.length;
|
||||
char str[] = new char[j * 2];
|
||||
|
|
@ -61,5 +69,4 @@ public class MD5Utils {
|
|||
return new String(str);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.dubbo.common.utils;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class MD5UtilsTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
MD5Utils sharedMd5Utils = new MD5Utils();
|
||||
final String[] input = {"provider-appgroup-one/org.apache.dubbo.config.spring.api.HelloService:dubboorg.apache.dubbo.config.spring.api.HelloService{REGISTRY_CLUSTER=registry-one, anyhost=true, application=provider-app, background=false, compiler=javassist, deprecated=false, dubbo=2.0.2, dynamic=true, file.cache=false, generic=false, group=group-one, interface=org.apache.dubbo.config.spring.api.HelloService, logger=slf4j, metadata-type=remote, methods=sayHello, organization=test, owner=com.test, release=, service-name-mapping=true, side=provider}",
|
||||
"provider-appgroup-two/org.apache.dubbo.config.spring.api.DemoService:dubboorg.apache.dubbo.config.spring.api.DemoService{REGISTRY_CLUSTER=registry-two, anyhost=true, application=provider-app, background=false, compiler=javassist, deprecated=false, dubbo=2.0.2, dynamic=true, file.cache=false, generic=false, group=group-two, interface=org.apache.dubbo.config.spring.api.DemoService, logger=slf4j, metadata-type=remote, methods=sayName,getBox, organization=test, owner=com.test, release=, service-name-mapping=true, side=provider}"};
|
||||
final String[] result = {sharedMd5Utils.getMd5(input[0]), new MD5Utils().getMd5(input[1])};
|
||||
|
||||
System.out.println("Expected result: " + Arrays.asList(result));
|
||||
int nThreads = 8;
|
||||
CountDownLatch latch = new CountDownLatch(nThreads);
|
||||
List<Throwable> errors = Collections.synchronizedList(new ArrayList<>());
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
|
||||
try {
|
||||
|
||||
for (int i = 0; i < nThreads; i++) {
|
||||
MD5Utils md5Utils = i < nThreads / 2 ? sharedMd5Utils : new MD5Utils();
|
||||
executorService.submit(new Md5Task(input[i % 2], result[i % 2], md5Utils, latch, errors));
|
||||
}
|
||||
latch.await();
|
||||
Assertions.assertEquals(Collections.EMPTY_LIST, errors);
|
||||
Assertions.assertEquals(0, latch.getCount());
|
||||
} catch (Throwable e) {
|
||||
Assertions.fail(StringUtils.toString(e));
|
||||
} finally {
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
static class Md5Task implements Runnable {
|
||||
|
||||
private final String input;
|
||||
private final String expected;
|
||||
private final MD5Utils md5Utils;
|
||||
private final CountDownLatch latch;
|
||||
private final List<Throwable> errorCollector;
|
||||
|
||||
public Md5Task(String input, String expected, MD5Utils md5Utils, CountDownLatch latch, List<Throwable> errorCollector) {
|
||||
this.input = input;
|
||||
this.expected = expected;
|
||||
this.md5Utils = md5Utils;
|
||||
this.latch = latch;
|
||||
this.errorCollector = errorCollector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int i = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
for (; i < 200; i++) {
|
||||
Assertions.assertEquals(expected, md5Utils.getMd5(input));
|
||||
md5Utils.getMd5("test#" + i);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
errorCollector.add(e);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
System.out.println("[" + Thread.currentThread().getName() + "] progress: " + i + ", cost: " + cost);
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,6 +85,8 @@ public class NacosDynamicConfiguration implements DynamicConfiguration {
|
|||
*/
|
||||
private final Map<String, NacosConfigListener> watchListenerMap;
|
||||
|
||||
private MD5Utils md5Utils = new MD5Utils();
|
||||
|
||||
NacosDynamicConfiguration(URL url) {
|
||||
this.nacosProperties = buildNacosProperties(url);
|
||||
this.configService = buildConfigService(url);
|
||||
|
|
@ -221,7 +223,7 @@ public class NacosDynamicConfiguration implements DynamicConfiguration {
|
|||
String content = getConfig(key, group);
|
||||
String casMd5 = "";
|
||||
if (StringUtils.isNotEmpty(content)) {
|
||||
casMd5 = MD5Utils.getMd5(content);
|
||||
casMd5 = md5Utils.getMd5(content);
|
||||
}
|
||||
return new ConfigItem(content, casMd5);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ public class RevisionResolver {
|
|||
|
||||
public static final String EMPTY_REVISION = "0";
|
||||
|
||||
private static MD5Utils md5Utils = new MD5Utils();
|
||||
|
||||
public static String calRevision(String metadata) {
|
||||
return MD5Utils.getMd5(metadata);
|
||||
return md5Utils.getMd5(metadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
|
||||
package org.apache.dubbo.metadata.store.nacos;
|
||||
|
||||
import com.alibaba.nacos.api.NacosFactory;
|
||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||
import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
|
||||
import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
|
||||
|
|
@ -36,12 +41,6 @@ import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
|
|||
import org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
||||
import com.alibaba.nacos.api.NacosFactory;
|
||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||
import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
|
@ -80,6 +79,7 @@ public class NacosMetadataReport extends AbstractMetadataReport {
|
|||
|
||||
private Map<String, MappingDataListener> casListenerMap = new ConcurrentHashMap<>();
|
||||
|
||||
private MD5Utils md5Utils = new MD5Utils();
|
||||
|
||||
public NacosMetadataReport(URL url) {
|
||||
super(url);
|
||||
|
|
@ -226,7 +226,7 @@ public class NacosMetadataReport extends AbstractMetadataReport {
|
|||
String content = getConfig(key, group);
|
||||
String casMd5 = "";
|
||||
if (StringUtils.isNotEmpty(content)) {
|
||||
casMd5 = MD5Utils.getMd5(content);
|
||||
casMd5 = md5Utils.getMd5(content);
|
||||
}
|
||||
return new ConfigItem(content, casMd5);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue