blob: 48fe0deea3440a689c902674e19f35b4110135f4 [file] [log] [blame]
/*
* 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.registry.client.metadata;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metadata.MetadataInfo;
import org.apache.dubbo.metadata.MetadataService;
import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
import org.apache.dubbo.metadata.report.MetadataReport;
import org.apache.dubbo.metadata.report.MetadataReportInstance;
import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
import org.apache.dubbo.registry.client.ServiceInstance;
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.ConsumerModel;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.service.Destroyable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.PROXY_CLASS_REF;
import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
public class MetadataUtils {
public static final Logger logger = LoggerFactory.getLogger(MetadataUtils.class);
public static void publishServiceDefinition(URL url, ServiceDescriptor serviceDescriptor, ApplicationModel applicationModel) {
if (getMetadataReports(applicationModel).size() == 0) {
String msg = "Remote Metadata Report Server is not provided or unavailable, will stop registering service definition to remote center!";
logger.warn(msg);
}
try {
String side = url.getSide();
if (PROVIDER_SIDE.equalsIgnoreCase(side)) {
String serviceKey = url.getServiceKey();
FullServiceDefinition serviceDefinition = serviceDescriptor.getFullServiceDefinition(serviceKey);
if (StringUtils.isNotEmpty(serviceKey) && serviceDefinition != null) {
serviceDefinition.setParameters(url.getParameters());
for (Map.Entry<String, MetadataReport> entry : getMetadataReports(applicationModel).entrySet()) {
MetadataReport metadataReport = entry.getValue();
if (!metadataReport.shouldReportDefinition()) {
logger.info("Report of service definition is disabled for " + entry.getKey());
continue;
}
metadataReport.storeProviderMetadata(
new MetadataIdentifier(
url.getServiceInterface(),
url.getVersion() == null ? "" : url.getVersion(),
url.getGroup() == null ? "" : url.getGroup(),
PROVIDER_SIDE,
applicationModel.getApplicationName())
, serviceDefinition);
}
}
} else {
for (Map.Entry<String, MetadataReport> entry : getMetadataReports(applicationModel).entrySet()) {
MetadataReport metadataReport = entry.getValue();
if (!metadataReport.shouldReportDefinition()) {
logger.info("Report of service definition is disabled for " + entry.getKey());
continue;
}
metadataReport.storeConsumerMetadata(
new MetadataIdentifier(
url.getServiceInterface(),
url.getVersion() == null ? "" : url.getVersion(),
url.getGroup() == null ? "" : url.getGroup(),
CONSUMER_SIDE,
applicationModel.getApplicationName()),
url.getParameters());
}
}
} catch (Exception e) {
//ignore error
logger.error("publish service definition metadata error.", e);
}
}
public static ProxyHolder referProxy(ServiceInstance instance) {
MetadataServiceURLBuilder builder;
ExtensionLoader<MetadataServiceURLBuilder> loader = instance.getApplicationModel()
.getExtensionLoader(MetadataServiceURLBuilder.class);
Map<String, String> metadata = instance.getMetadata();
// METADATA_SERVICE_URLS_PROPERTY_NAME is a unique key exists only on instances of spring-cloud-alibaba.
String dubboUrlsForJson = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME);
if (metadata.isEmpty() || StringUtils.isEmpty(dubboUrlsForJson)) {
builder = loader.getExtension(StandardMetadataServiceURLBuilder.NAME);
} else {
builder = loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME);
}
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.");
}
URL url = urls.get(0);
// Simply rely on the first metadata url, as stated in MetadataServiceURLBuilder.
ApplicationModel applicationModel = instance.getApplicationModel();
ModuleModel internalModel = applicationModel.getInternalModule();
ConsumerModel consumerModel = applicationModel.getInternalModule().registerInternalConsumer(MetadataService.class, url);
Protocol protocol = applicationModel.getExtensionLoader(Protocol.class).getAdaptiveExtension();
url.setServiceModel(consumerModel);
Invoker<MetadataService> invoker = protocol.refer(MetadataService.class, url);
ProxyFactory proxyFactory = applicationModel.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
MetadataService metadataService = proxyFactory.getProxy(invoker);
consumerModel.getServiceMetadata().setTarget(metadataService);
consumerModel.getServiceMetadata().addAttribute(PROXY_CLASS_REF, metadataService);
consumerModel.setProxyObject(metadataService);
consumerModel.initMethodModels();
return new ProxyHolder(consumerModel, metadataService, internalModel);
}
public static MetadataInfo getRemoteMetadata(String revision, List<ServiceInstance> instances, MetadataReport metadataReport) {
ServiceInstance instance = selectInstance(instances);
String metadataType = ServiceInstanceMetadataUtils.getMetadataStorageType(instance);
MetadataInfo metadataInfo;
try {
if (logger.isDebugEnabled()) {
logger.debug("Instance " + instance.getAddress() + " is using metadata type " + metadataType);
}
if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
metadataInfo = MetadataUtils.getMetadata(revision, instance, metadataReport);
} else {
// change the instance used to communicate to avoid all requests route to the same instance
ProxyHolder proxyHolder = null;
try {
proxyHolder = MetadataUtils.referProxy(instance);
metadataInfo = proxyHolder.getProxy().getMetadataInfo(ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
} finally {
MetadataUtils.destroyProxy(proxyHolder);
}
}
} catch (Exception e) {
logger.error("Failed to get app metadata for revision " + revision + " for type " + metadataType + " from instance " + instance.getAddress(), e);
metadataInfo = null;
}
if (metadataInfo == null) {
metadataInfo = MetadataInfo.EMPTY;
}
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);
if (metadataReport == null) {
throw new IllegalStateException("No valid remote metadata report specified.");
}
String registryCluster = instance.getRegistryCluster();
Map<String, String> params = new HashMap<>(instance.getExtendParams());
if (registryCluster != null && !registryCluster.equalsIgnoreCase(params.get(REGISTRY_CLUSTER_KEY))) {
params.put(REGISTRY_CLUSTER_KEY, registryCluster);
}
return metadataReport.getAppMetadata(identifier, params);
}
private static Map<String, MetadataReport> getMetadataReports(ApplicationModel applicationModel) {
return applicationModel.getBeanFactory().getBean(MetadataReportInstance.class).getMetadataReports(false);
}
private static ServiceInstance selectInstance(List<ServiceInstance> instances) {
if (instances.size() == 1) {
return instances.get(0);
}
return instances.get(ThreadLocalRandom.current().nextInt(0, instances.size()));
}
public 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;
}
}
}