register MetadataService as internal service (#10148)

* register MetadataService as internal service

* fix ut

* register consumer model when generating proxy for internal service

* check model type directly

* check by looking up service for model type here is not clear

* unify thread configuration
This commit is contained in:
ken.lj 2022-06-17 14:21:32 +08:00 committed by GitHub
parent 14d8931339
commit 2face28201
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 155 additions and 29 deletions

View File

@ -112,6 +112,8 @@ public interface CommonConstants {
String EXECUTOR_SERVICE_COMPONENT_KEY = ExecutorService.class.getName();
String INTERNAL_EXECUTOR_SERVICE_COMPONENT_KEY = "INTERNAL_SERVICE_EXECUTOR";
String THREADPOOL_KEY = "threadpool";
String THREAD_NAME_KEY = "threadname";

View File

@ -30,6 +30,7 @@ import org.apache.dubbo.config.ModuleConfig;
import org.apache.dubbo.config.ProviderConfig;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -43,6 +44,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_EXPORT_THREAD_NUM;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_REFER_THREAD_NUM;
import static org.apache.dubbo.common.constants.CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.INTERNAL_EXECUTOR_SERVICE_COMPONENT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY;
@ -78,7 +80,7 @@ public class DefaultExecutorRepository implements ExecutorRepository, ExtensionA
*/
@Override
public synchronized ExecutorService createExecutorIfAbsent(URL url) {
Map<Integer, ExecutorService> executors = data.computeIfAbsent(EXECUTOR_SERVICE_COMPONENT_KEY, k -> new ConcurrentHashMap<>());
Map<Integer, ExecutorService> executors = data.computeIfAbsent(getExecutorKey(url), k -> new ConcurrentHashMap<>());
// Consumer's executor is sharing globally, key=Integer.MAX_VALUE. Provider's executor is sharing by protocol.
Integer portKey = CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY)) ? Integer.MAX_VALUE : url.getPort();
if (url.getParameter(THREAD_NAME_KEY) == null) {
@ -95,13 +97,30 @@ public class DefaultExecutorRepository implements ExecutorRepository, ExtensionA
return executor;
}
/**
* Return the executor key based on the type (internal or biz) of the current service.
*
* @param url
* @return
*/
private String getExecutorKey(URL url) {
String executorKey = INTERNAL_EXECUTOR_SERVICE_COMPONENT_KEY;
ServiceDescriptor serviceDescriptor = applicationModel.getInternalModule().getServiceRepository().lookupService(url.getServiceInterface());
// if not found in internal service repository, then it's biz service defined by user.
if (serviceDescriptor == null) {
executorKey = EXECUTOR_SERVICE_COMPONENT_KEY;
}
return executorKey;
}
private ExecutorService createExecutor(URL url) {
return (ExecutorService) extensionAccessor.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
}
@Override
public ExecutorService getExecutor(URL url) {
Map<Integer, ExecutorService> executors = data.get(EXECUTOR_SERVICE_COMPONENT_KEY);
Map<Integer, ExecutorService> executors = data.get(getExecutorKey(url));
/*
* It's guaranteed that this method is called after {@link #createExecutorIfAbsent(URL)}, so data should already

View File

@ -16,6 +16,7 @@
*/
package org.apache.dubbo.rpc.model;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.ModuleEnvironment;
import org.apache.dubbo.common.context.ModuleExt;
import org.apache.dubbo.common.deploy.ApplicationDeployer;
@ -28,6 +29,7 @@ import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.config.context.ModuleConfigManager;
import java.util.HashMap;
import java.util.Set;
/**
@ -173,4 +175,22 @@ public class ModuleModel extends ScopeModel {
public void setModuleEnvironment(ModuleEnvironment moduleEnvironment) {
this.moduleEnvironment = moduleEnvironment;
}
public ConsumerModel registerInternalConsumer(Class<?> internalService, URL url) {
ServiceMetadata serviceMetadata = new ServiceMetadata();
serviceMetadata.setVersion(url.getVersion());
serviceMetadata.setGroup(url.getGroup());
serviceMetadata.setDefaultGroup(url.getGroup());
serviceMetadata.setServiceInterfaceName(internalService.getName());
serviceMetadata.setServiceType(internalService);
String servyceKey = URL.buildKey(internalService.getName(), url.getGroup(), url.getVersion());
serviceMetadata.setServiceKey(servyceKey);
ConsumerModel consumerModel = new ConsumerModel(serviceMetadata.getServiceKey(), "jdk", serviceRepository.lookupService(serviceMetadata.getServiceInterfaceName()), null,
this, serviceMetadata, new HashMap<>());
logger.info("Dynamically registering consumer model " + servyceKey + " into model " + this.getDesc());
serviceRepository.registerConsumer(consumerModel);
return consumerModel;
}
}

View File

@ -205,7 +205,7 @@ public class ModuleServiceRepository {
}
public ServiceDescriptor lookupService(String interfaceName) {
if (services.containsKey(interfaceName)) {
if (interfaceName != null && services.containsKey(interfaceName)) {
List<ServiceDescriptor> serviceDescriptors = services.get(interfaceName);
return serviceDescriptors.size() > 0 ? serviceDescriptors.get(0) : null;
} else {

View File

@ -46,8 +46,8 @@ public class ExecutorRepositoryTest {
@Test
public void testGetExecutor() {
testGet(URL.valueOf("dubbo://127.0.0.1:23456"));
testGet(URL.valueOf("dubbo://127.0.0.1:23456?side=consumer"));
testGet(URL.valueOf("dubbo://127.0.0.1:23456/TestService"));
testGet(URL.valueOf("dubbo://127.0.0.1:23456/TestService?side=consumer"));
Assertions.assertNotNull(executorRepository.getSharedExecutor());
Assertions.assertNotNull(executorRepository.getServiceExportExecutor());
@ -68,7 +68,7 @@ public class ExecutorRepositoryTest {
@Test
public void testUpdateExecutor() {
URL url = URL.valueOf("dubbo://127.0.0.1:23456?threads=5");
URL url = URL.valueOf("dubbo://127.0.0.1:23456/TestService?threads=5");
ThreadPoolExecutor executorService = (ThreadPoolExecutor) executorRepository.createExecutorIfAbsent(url);
executorService.setCorePoolSize(3);

View File

@ -33,14 +33,18 @@ import org.apache.dubbo.rpc.ProtocolServer;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static java.util.Collections.emptyList;
import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PORT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.METADATA_SERVICE_PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
/**
* Export metadata service
@ -178,6 +182,11 @@ public class ConfigurableMetadataServiceExporter {
serviceConfig.setMethods(generateMethodConfig());
serviceConfig.setConnections(1); // separate connection
serviceConfig.setExecutes(100); // max tasks running at the same time
Map<String, String> threadParams = new HashMap<>();
threadParams.put(THREADPOOL_KEY, "cached");
threadParams.put(THREADS_KEY, "100");
threadParams.put(CORE_THREADS_KEY, "2");
serviceConfig.setParameters(threadParams);
return serviceConfig;
}

View File

@ -248,6 +248,7 @@ public abstract class AbstractServiceNameMapping implements ServiceNameMapping,
mappingListeners.clear();
mappingLocks.clear();
mappingInitStatus.clear();
}
private class AsyncMappingTask implements Callable<Set<String>> {

View File

@ -0,0 +1,28 @@
/*
* 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.metadata;
import org.apache.dubbo.rpc.model.BuiltinServiceDetector;
public class MetadataServiceDetector implements BuiltinServiceDetector {
@Override
public Class<?> getService() {
return MetadataService.class;
}
}

View File

@ -0,0 +1 @@
metadata=org.apache.dubbo.metadata.MetadataServiceDetector

View File

@ -34,7 +34,8 @@ import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.ProxyFactory;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.ScopeModel;
import org.apache.dubbo.rpc.model.ConsumerModel;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.service.Destroyable;
@ -105,7 +106,7 @@ public class MetadataUtils {
}
}
public static MetadataService referProxy(ServiceInstance instance) {
public static ProxyHolder referProxy(ServiceInstance instance) {
MetadataServiceURLBuilder builder;
ExtensionLoader<MetadataServiceURLBuilder> loader = instance.getApplicationModel()
.getExtensionLoader(MetadataServiceURLBuilder.class);
@ -122,16 +123,19 @@ public class MetadataUtils {
List<URL> urls = builder.build(instance);
if (CollectionUtils.isEmpty(urls)) {
throw new IllegalStateException("Introspection service discovery mode is enabled "
+ instance + ", but no metadata service can build from it.");
+ instance + ", but no metadata service can build from it.");
}
// Simply rely on the first metadata url, as stated in MetadataServiceURLBuilder.
ScopeModel scopeModel = instance.getApplicationModel();
Protocol protocol = scopeModel.getExtensionLoader(Protocol.class).getAdaptiveExtension();
ApplicationModel applicationModel = instance.getApplicationModel();
ModuleModel internalModel = applicationModel.getInternalModule();
ConsumerModel consumerModel = applicationModel.getInternalModule().registerInternalConsumer(MetadataService.class, urls.get(0));
Protocol protocol = applicationModel.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Invoker<MetadataService> invoker = protocol.refer(MetadataService.class, urls.get(0));
ProxyFactory proxyFactory = scopeModel.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
return proxyFactory.getProxy(invoker);
ProxyFactory proxyFactory = applicationModel.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
return new ProxyHolder(consumerModel, proxyFactory.getProxy(invoker), internalModel);
}
public static MetadataInfo getRemoteMetadata(String revision, List<ServiceInstance> instances, MetadataReport metadataReport) {
@ -147,13 +151,12 @@ public class MetadataUtils {
} else {
// change the instance used to communicate to avoid all requests route to the same instance
MetadataService metadataServiceProxy = null;
ProxyHolder proxyHolder = null;
try {
metadataServiceProxy = MetadataUtils.referProxy(instance);
metadataInfo = metadataServiceProxy.getMetadataInfo(ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
proxyHolder = MetadataUtils.referProxy(instance);
metadataInfo = proxyHolder.getProxy().getMetadataInfo(ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
} finally {
if (metadataServiceProxy instanceof Destroyable) {
((Destroyable)metadataServiceProxy).$destroy();
}
MetadataUtils.destroyProxy(proxyHolder);
}
}
} catch (Exception e) {
@ -167,6 +170,12 @@ public class MetadataUtils {
return metadataInfo;
}
public static void destroyProxy(ProxyHolder proxyHolder) {
if (proxyHolder != null) {
proxyHolder.destroy();
}
}
public static MetadataInfo getMetadata(String revision, ServiceInstance instance, MetadataReport metadataReport) {
SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), revision);
@ -194,4 +203,35 @@ public class MetadataUtils {
return instances.get(ThreadLocalRandom.current().nextInt(0, instances.size()));
}
private static class ProxyHolder {
private final ConsumerModel consumerModel;
private final MetadataService proxy;
private final ModuleModel internalModel;
public ProxyHolder(ConsumerModel consumerModel, MetadataService proxy, ModuleModel internalModel) {
this.consumerModel = consumerModel;
this.proxy = proxy;
this.internalModel = internalModel;
}
public void destroy() {
if (proxy instanceof Destroyable) {
((Destroyable) proxy).$destroy();
}
internalModel.getServiceRepository().unregisterConsumer(consumerModel);
}
public ConsumerModel getConsumerModel() {
return consumerModel;
}
public MetadataService getProxy() {
return proxy;
}
public ModuleModel getInternalModel() {
return internalModel;
}
}
}

View File

@ -33,12 +33,15 @@ import java.util.Map;
import static java.util.Collections.emptyMap;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.CORE_THREADS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.RETRIES_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.utils.StringUtils.isBlank;
@ -98,13 +101,16 @@ public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuil
String protocol = params.get(PROTOCOL_KEY);
int port = Integer.parseInt(params.get(PORT_KEY));
URLBuilder urlBuilder = new URLBuilder()
.setHost(host)
.setPort(port)
.setProtocol(protocol)
.setPath(MetadataService.class.getName())
.addParameter(TIMEOUT_KEY, ConfigurationUtils.get(applicationModel, METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE))
.addParameter(SIDE_KEY, CONSUMER)
.addParameter(CONNECTIONS_KEY, 1)
.setHost(host)
.setPort(port)
.setProtocol(protocol)
.setPath(MetadataService.class.getName())
.addParameter(TIMEOUT_KEY, ConfigurationUtils.get(applicationModel, METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE))
.addParameter(SIDE_KEY, CONSUMER)
.addParameter(CONNECTIONS_KEY, 1)
.addParameter(THREADPOOL_KEY, "cached")
.addParameter(THREADS_KEY, "100")
.addParameter(CORE_THREADS_KEY, "2")
.addParameter(RETRIES_KEY, 0);

View File

@ -132,19 +132,19 @@ public class TripleProtocol extends AbstractProtocol {
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
optimizeSerialization(url);
ExecutorService streamExecutor = getOrCreateStreamExecutor(
url.getOrDefaultApplicationModel());
url.getOrDefaultApplicationModel(), url);
TripleInvoker<T> invoker = new TripleInvoker<>(type, url, acceptEncodings,
connectionManager, invokers, streamExecutor);
invokers.add(invoker);
return invoker;
}
private ExecutorService getOrCreateStreamExecutor(ApplicationModel applicationModel) {
private ExecutorService getOrCreateStreamExecutor(ApplicationModel applicationModel, URL url) {
ExecutorService executor = applicationModel.getExtensionLoader(ExecutorRepository.class)
.getDefaultExtension()
.createExecutorIfAbsent(THREAD_POOL_URL);
.createExecutorIfAbsent(url);
Objects.requireNonNull(executor,
String.format("No available executor found in %s", THREAD_POOL_URL));
String.format("No available executor found in %s", url));
return executor;
}