1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

Polish spring-cloud-incubator/spring-cloud-alibaba#376 : Dubbo Metadata Configuration replaces Nacos 's implementation

This commit is contained in:
mercyblitz 2019-02-22 11:09:05 +08:00
parent 80252f8278
commit 918e052cd6
10 changed files with 305 additions and 155 deletions

View File

@ -16,15 +16,22 @@
*/
package org.springframework.cloud.alibaba.dubbo.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.alibaba.dubbo.service.MetadataConfigService;
import org.springframework.cloud.alibaba.dubbo.service.NacosMetadataConfigService;
import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServiceProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import java.util.Collection;
import java.util.Iterator;
import static com.alibaba.dubbo.common.Constants.DEFAULT_PROTOCOL;
/**
* Spring Boot Auto-Configuration class for Dubbo Metadata
*
@ -32,12 +39,39 @@ import org.springframework.context.annotation.Import;
*/
@Configuration
@Import(DubboServiceMetadataRepository.class)
@DubboComponentScan(basePackages = "org.springframework.cloud.alibaba.dubbo.service")
public class DubboMetadataAutoConfiguration {
@Bean
@ConditionalOnBean(NacosConfigProperties.class)
public MetadataConfigService metadataConfigService() {
return new NacosMetadataConfigService();
public static final String METADATA_PROTOCOL_BEAN_NAME = "metadata";
/**
* Build an alias Bean for {@link ProtocolConfig}
*
* @param protocols {@link ProtocolConfig} Beans
* @return {@link ProtocolConfig} bean
*/
@Bean(name = METADATA_PROTOCOL_BEAN_NAME)
public ProtocolConfig protocolConfig(Collection<ProtocolConfig> protocols) {
ProtocolConfig protocolConfig = null;
for (ProtocolConfig protocol : protocols) {
String protocolName = protocol.getName();
if (DEFAULT_PROTOCOL.equals(protocolName)) {
protocolConfig = protocol;
break;
}
}
if (protocolConfig == null) { // If The ProtocolConfig bean named "dubbo" is absent, take first one of them
Iterator<ProtocolConfig> iterator = protocols.iterator();
protocolConfig = iterator.hasNext() ? iterator.next() : null;
}
return protocolConfig;
}
@Bean
@ConditionalOnMissingBean
public DubboMetadataConfigServiceProxy dubboMetadataConfigServiceProxy(DubboGenericServiceFactory factory) {
return new DubboMetadataConfigServiceProxy(factory);
}
}

View File

@ -24,17 +24,12 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver;
import org.springframework.cloud.alibaba.dubbo.service.MetadataConfigService;
import org.springframework.cloud.alibaba.dubbo.service.PublishingDubboMetadataConfigService;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* The Auto-Configuration class for Dubbo REST metadata registration,
* REST metadata that is a part of {@link Registration#getMetadata() Spring Cloud service instances' metadata}
@ -44,24 +39,17 @@ import java.util.Set;
*/
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@ConditionalOnBean(value = {
MetadataResolver.class,
MetadataConfigService.class
MetadataResolver.class
})
@AutoConfigureAfter(value = {DubboMetadataAutoConfiguration.class})
@Configuration
public class DubboRestMetadataRegistrationAutoConfiguration {
/**
* A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service,
* the value is a JSON content of JAX-RS or Spring MVC REST metadata from the annotated methods.
*/
private final Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
@Autowired
private MetadataResolver metadataResolver;
@Autowired
private MetadataConfigService metadataConfigService;
private PublishingDubboMetadataConfigService dubboMetadataConfigService;
@Value("${spring.application.name:application}")
private String currentApplicationName;
@ -69,15 +57,6 @@ public class DubboRestMetadataRegistrationAutoConfiguration {
@EventListener(ServiceBeanExportedEvent.class)
public void recordRestMetadata(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();
serviceRestMetadata.addAll(metadataResolver.resolveServiceRestMetadata(serviceBean));
}
/**
* Publish <code>serviceRestMetadata</code> with the JSON format into
* {@link Registration#getMetadata() service instances' metadata} when The Spring Application is started.
*/
@EventListener(ApplicationStartedEvent.class)
public void registerRestMetadata() {
metadataConfigService.publishServiceRestMetadata(currentApplicationName, serviceRestMetadata);
dubboMetadataConfigService.publishServiceRestMetadata(metadataResolver.resolveServiceRestMetadata(serviceBean));
}
}

View File

@ -16,6 +16,8 @@
*/
package org.springframework.cloud.alibaba.dubbo.metadata.repository;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -23,11 +25,14 @@ import org.springframework.cloud.alibaba.dubbo.http.matcher.RequestMetadataMatch
import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.service.MetadataConfigService;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigService;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServiceProxy;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Repository;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@ -44,6 +49,8 @@ public class DubboServiceMetadataRepository {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ObjectMapper objectMapper = new ObjectMapper();
/**
* Key is application name
* Value is Map<RequestMetadata, DubboServiceMetadata>
@ -51,7 +58,7 @@ public class DubboServiceMetadataRepository {
private Map<String, Map<RequestMetadataMatcher, DubboServiceMetadata>> repository = newHashMap();
@Autowired
private MetadataConfigService metadataConfigService;
private DubboMetadataConfigServiceProxy dubboMetadataConfigServiceProxy;
/**
* Initialize the specified service's Dubbo Service Metadata
@ -64,7 +71,7 @@ public class DubboServiceMetadataRepository {
return;
}
Set<ServiceRestMetadata> serviceRestMetadataSet = metadataConfigService.getServiceRestMetadata(serviceName);
Set<ServiceRestMetadata> serviceRestMetadataSet = getServiceRestMetadataSet(serviceName);
if (isEmpty(serviceRestMetadataSet)) {
if (logger.isWarnEnabled()) {
@ -145,6 +152,23 @@ public class DubboServiceMetadataRepository {
return getMap(repository, serviceName);
}
private Set<ServiceRestMetadata> getServiceRestMetadataSet(String serviceName) {
DubboMetadataConfigService dubboMetadataConfigService = dubboMetadataConfigServiceProxy.newProxy(serviceName);
String serviceRestMetadataJsonConfig = dubboMetadataConfigService.getServiceRestMetadata();
Set<ServiceRestMetadata> metadata;
try {
metadata = objectMapper.readValue(serviceRestMetadataJsonConfig,
TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class));
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
metadata = Collections.emptySet();
}
return metadata;
}
private static <K, V> Map<K, V> getMap(Map<String, Map<K, V>> repository, String key) {
return getOrDefault(repository, key, newHashMap());
}
@ -161,4 +185,5 @@ public class DubboServiceMetadataRepository {
private static <K, V> Map<K, V> newHashMap() {
return new LinkedHashMap<>();
}
}

View File

@ -127,12 +127,22 @@ public class DubboServiceBeanMetadataResolver implements BeanClassLoaderAware, S
public Set<RestMethodMetadata> resolveMethodRestMetadata(Class<?> targetType) {
List<Method> feignContractMethods = selectFeignContractMethods(targetType);
return contracts.stream()
.map(contract -> contract.parseAndValidatateMetadata(targetType))
.map(contract -> parseAndValidateMetadata(contract, targetType))
.flatMap(v -> v.stream())
.map(methodMetadata -> resolveMethodRestMetadata(methodMetadata, targetType, feignContractMethods))
.collect(Collectors.toSet());
}
private List<MethodMetadata> parseAndValidateMetadata(Contract contract, Class<?> targetType) {
List<MethodMetadata> methodMetadataList = Collections.emptyList();
try {
methodMetadataList = contract.parseAndValidatateMetadata(targetType);
} catch (Throwable ignored) {
// ignore
}
return methodMetadataList;
}
/**
* Select feign contract methods
* <p>

View File

@ -50,18 +50,18 @@ public class DubboGenericServiceFactory {
public GenericService create(DubboServiceMetadata dubboServiceMetadata,
DubboTransportedMetadata dubboTransportedMetadata) {
Integer key = Objects.hash(dubboServiceMetadata, dubboTransportedMetadata);
ReferenceBean<GenericService> referenceBean = cache.get(key);
if (referenceBean == null) {
referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTransportedMetadata);
cache.putIfAbsent(key, referenceBean);
}
ReferenceBean<GenericService> referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTransportedMetadata);
return referenceBean == null ? null : referenceBean.get();
}
public GenericService create(String serviceName, Class<?> serviceClass) {
String interfaceName = serviceClass.getName();
ReferenceBean<GenericService> referenceBean = build(interfaceName, serviceName, null,
"dubbo", "failover");
return referenceBean.get();
}
private ReferenceBean<GenericService> build(ServiceRestMetadata serviceRestMetadata,
DubboTransportedMetadata dubboTransportedMetadata) {
@ -70,14 +70,28 @@ public class DubboGenericServiceFactory {
String interfaceName = getServiceInterface(segments);
String version = getServiceVersion(segments);
String group = getServiceGroup(segments);
String protocol = dubboTransportedMetadata.getProtocol();
String cluster = dubboTransportedMetadata.getCluster();
ReferenceBean<GenericService> referenceBean = new ReferenceBean<GenericService>();
return build(interfaceName, version, group, protocol, cluster);
}
private ReferenceBean<GenericService> build(String interfaceName, String version, String group, String protocol,
String cluster) {
Integer key = Objects.hash(interfaceName, version, group, protocol, cluster);
ReferenceBean<GenericService> referenceBean = cache.get(key);
if (referenceBean == null) {
referenceBean = new ReferenceBean<>();
referenceBean.setGeneric(true);
referenceBean.setInterface(interfaceName);
referenceBean.setVersion(version);
referenceBean.setGroup(group);
referenceBean.setProtocol(dubboTransportedMetadata.getProtocol());
referenceBean.setCluster(dubboTransportedMetadata.getCluster());
referenceBean.setProtocol(protocol);
referenceBean.setCluster(cluster);
}
return referenceBean;
}

View File

@ -21,13 +21,16 @@ import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import java.util.Set;
/**
* Config Service for Metadata
* Dubbo Metadata Configuration Service
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface MetadataConfigService {
public interface DubboMetadataConfigService {
void publishServiceRestMetadata(String serviceName, Set<ServiceRestMetadata> serviceRestMetadata);
Set<ServiceRestMetadata> getServiceRestMetadata(String serviceName);
/**
* Get The json content of {@link ServiceRestMetadata} {@link Set}
*
* @return the non-null String
*/
String getServiceRestMetadata();
}

View File

@ -0,0 +1,54 @@
/*
* 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.springframework.cloud.alibaba.dubbo.service;
import com.alibaba.dubbo.rpc.service.GenericService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* {@link DubboMetadataConfigService} {@link InvocationHandler}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
class DubboMetadataConfigServiceInvocationHandler implements InvocationHandler {
/**
* The method name of {@link DubboMetadataConfigService#getServiceRestMetadata()}
*/
private static final String METHOD_NAME = "getServiceRestMetadata";
private static final String[] PARAMETER_TYPES = new String[0];
private static final String[] PARAMETER_VALUES = new String[0];
private final GenericService genericService;
public DubboMetadataConfigServiceInvocationHandler(String serviceName, DubboGenericServiceFactory dubboGenericServiceFactory) {
this.genericService = dubboGenericServiceFactory.create(serviceName, DubboMetadataConfigService.class);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (METHOD_NAME.equals(methodName)) {
return genericService.$invoke(methodName, PARAMETER_TYPES, PARAMETER_VALUES);
}
return method.invoke(proxy, args);
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.springframework.cloud.alibaba.dubbo.service;
import org.springframework.beans.factory.BeanClassLoaderAware;
import static java.lang.reflect.Proxy.newProxyInstance;
/**
* The proxy of {@link DubboMetadataConfigService}
*/
public class DubboMetadataConfigServiceProxy implements BeanClassLoaderAware {
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private ClassLoader classLoader;
public DubboMetadataConfigServiceProxy(DubboGenericServiceFactory dubboGenericServiceFactory) {
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
}
/**
* New proxy instance of {@link DubboMetadataConfigService} via the specified service name
*
* @param serviceName the service name
* @return a {@link DubboMetadataConfigService} proxy
*/
public DubboMetadataConfigService newProxy(String serviceName) {
return (DubboMetadataConfigService) newProxyInstance(classLoader, new Class[]{DubboMetadataConfigService.class},
new DubboMetadataConfigServiceInvocationHandler(serviceName, dubboGenericServiceFactory));
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
}

View File

@ -1,98 +0,0 @@
/*
* 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.springframework.cloud.alibaba.dubbo.service;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
/**
* Nacos {@link MetadataConfigService}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class NacosMetadataConfigService implements MetadataConfigService {
private final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private NacosConfigProperties nacosConfigProperties;
private ConfigService configService;
@PostConstruct
public void init() {
this.configService = nacosConfigProperties.configServiceInstance();
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
/**
* Get the data Id of service rest metadata
*/
private static String getServiceRestMetadataDataId(String serviceName) {
return "metadata:rest:" + serviceName + ".json";
}
@Override
public void publishServiceRestMetadata(String serviceName, Set<ServiceRestMetadata> serviceRestMetadata) {
String dataId = getServiceRestMetadataDataId(serviceName);
String json = writeValueAsString(serviceRestMetadata);
try {
configService.publishConfig(dataId, DEFAULT_GROUP, json);
} catch (NacosException e) {
throw new RuntimeException(e);
}
}
@Override
public Set<ServiceRestMetadata> getServiceRestMetadata(String serviceName) {
Set<ServiceRestMetadata> metadata = Collections.emptySet();
String dataId = getServiceRestMetadataDataId(serviceName);
try {
String json = configService.getConfig(dataId, DEFAULT_GROUP, 1000 * 3);
metadata = objectMapper.readValue(json,
TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
return metadata;
}
private String writeValueAsString(Object object) {
String content = null;
try {
content = objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
return content;
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.springframework.cloud.alibaba.dubbo.service;
import com.alibaba.dubbo.config.annotation.Service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.util.CollectionUtils;
import javax.annotation.PostConstruct;
import java.util.LinkedHashSet;
import java.util.Set;
import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration.METADATA_PROTOCOL_BEAN_NAME;
/**
* Publishing {@link DubboMetadataConfigService} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Service(version = "${spring.application.name}", protocol = METADATA_PROTOCOL_BEAN_NAME)
// Use current Spring application name as the Dubbo Service version
public class PublishingDubboMetadataConfigService implements DubboMetadataConfigService {
/**
* A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service,
* the value is a JSON content of JAX-RS or Spring MVC REST metadata from the annotated methods.
*/
private final Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
private final ObjectMapper objectMapper = new ObjectMapper();
@PostConstruct
public void init() {
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
/**
* Publish the {@link Set} of {@link ServiceRestMetadata}
*
* @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata}
*/
public void publishServiceRestMetadata(Set<ServiceRestMetadata> serviceRestMetadataSet) {
for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) {
if (!CollectionUtils.isEmpty(serviceRestMetadata.getMeta())) {
this.serviceRestMetadata.add(serviceRestMetadata);
}
}
}
@Override
public String getServiceRestMetadata() {
String serviceRestMetadataJsonConfig = null;
try {
serviceRestMetadataJsonConfig = objectMapper.writeValueAsString(serviceRestMetadata);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return serviceRestMetadataJsonConfig;
}
}