Merge remote-tracking branch 'upstream/master'
commit
29cfe3537f
@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
|
||||
We recommend using English. If you are non-native English speaker, you can use the translation software.
|
||||
|
||||
**Which Component**
|
||||
eg. Nacos Discovery, Sentinel
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
e.g. MacOS 、Java8 、 Version 0.2.1.RELEASE
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
|
||||
We recommend using English. If you are non-native English speaker, you can use the translation software.
|
||||
|
||||
|
||||
**Which Component**
|
||||
eg. Nacos Discovery, Sentinel
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Question
|
||||
about: how to ask an valid question
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
|
||||
We recommend using English. If you are non-native English speaker, you can use the translation software.
|
||||
|
||||
|
||||
**Which Component**
|
||||
eg. Nacos Discovery, Sentinel
|
||||
|
||||
**Describe what problem you have encountered**
|
||||
A clear and concise description of what you want to do.
|
||||
|
||||
**Describe what information you have read**
|
||||
eg. I have read the reference doc of Sentinel
|
@ -0,0 +1,45 @@
|
||||
|
||||
= Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering an open
|
||||
and welcoming community, we pledge to respect all people who contribute through reporting
|
||||
issues, posting feature requests, updating documentation, submitting pull requests or
|
||||
patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for
|
||||
everyone, regardless of level of experience, gender, gender identity and expression,
|
||||
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
|
||||
religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing other's private information, such as physical or electronic addresses,
|
||||
without explicit permission
|
||||
* Other unethical or unprofessional conduct
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments,
|
||||
commits, code, wiki edits, issues, and other contributions that are not aligned to this
|
||||
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
|
||||
that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
|
||||
consistently applying these principles to every aspect of managing this project. Project
|
||||
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
|
||||
from the project team.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an
|
||||
individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
||||
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
|
||||
be reviewed and investigated and will result in a response that is deemed necessary and
|
||||
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
|
||||
with regard to the reporter of an incident.
|
||||
|
||||
This Code of Conduct is adapted from the
|
||||
http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
|
||||
http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
|
@ -0,0 +1,15 @@
|
||||
|
||||
### Describe what this PR does / why we need it
|
||||
|
||||
|
||||
### Does this pull request fix one issue?
|
||||
|
||||
<!--If that, add "Fixes #xxxx" below in the next line. For example, Fixes #15. Otherwise, add "NONE" -->
|
||||
|
||||
### Describe how you did it
|
||||
|
||||
|
||||
### Describe how to verify it
|
||||
|
||||
|
||||
### Special notes for reviews
|
@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-alibaba</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>0.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-alibaba-dubbo</artifactId>
|
||||
<name>Spring Cloud Alibaba Dubbo</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring Boot dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Cloud dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Dubbo Spring Boot Starter -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.boot</groupId>
|
||||
<artifactId>dubbo-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Netty -->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- REST support dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-netty4</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jackson-provider</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxb-provider</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Feign for JAX-RS 2-->
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-jaxrs2</artifactId>
|
||||
<version>9.7.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Nacos Service Discovery -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Eureka Service Discovery -->
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>org.springframework.cloud</groupId>-->
|
||||
<!--<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.autoconfigure;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.service.MetadataConfigService;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.service.NacosMetadataConfigService;
|
||||
import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Spring Boot Auto-Configuration class for Dubbo Metadata
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@Configuration
|
||||
@Import(DubboServiceMetadataRepository.class)
|
||||
public class DubboMetadataAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(NacosConfigProperties.class)
|
||||
public MetadataConfigService metadataConfigService() {
|
||||
return new NacosMetadataConfigService();
|
||||
}
|
||||
|
||||
}
|
@ -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.autoconfigure;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.FeignMetadataResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.openfeign.DubboFeignClientsConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Dubbo Feign Auto-{@link Configuration Configuration}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@ConditionalOnClass(value = Feign.class)
|
||||
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(defaultConfiguration = DubboFeignClientsConfiguration.class)
|
||||
@Configuration
|
||||
public class DubboOpenFeignAutoConfiguration {
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String currentApplicationName;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MetadataResolver metadataJsonResolver(ObjectProvider<Contract> contract) {
|
||||
return new FeignMetadataResolver(currentApplicationName, contract);
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.autoconfigure;
|
||||
|
||||
import com.alibaba.dubbo.config.spring.ServiceBean;
|
||||
import com.alibaba.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.service.MetadataConfigService;
|
||||
import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent;
|
||||
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}
|
||||
* will be registered Spring Cloud registry.
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
|
||||
@ConditionalOnMissingBean(value = {
|
||||
MetadataResolver.class,
|
||||
MetadataConfigService.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;
|
||||
|
||||
@EventListener(ServiceBeanExportedEvent.class)
|
||||
public void recordRestMetadata(ServiceBeanExportedEvent event) throws JsonProcessingException {
|
||||
ServiceBean serviceBean = event.getServiceBean();
|
||||
serviceRestMetadata.addAll(metadataResolver.resolveServiceRestMetadata(serviceBean));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-handle Spring Cloud application service registered:
|
||||
* <p>
|
||||
* Put <code>restMetadata</code> with the JSON format into
|
||||
* {@link Registration#getMetadata() service instances' metadata}
|
||||
* <p>
|
||||
*
|
||||
* @param event {@link InstancePreRegisteredEvent} instance
|
||||
*/
|
||||
@EventListener(InstancePreRegisteredEvent.class)
|
||||
public void registerRestMetadata(InstancePreRegisteredEvent event) throws Exception {
|
||||
Registration registration = event.getRegistration();
|
||||
metadataConfigService.publishServiceRestMetadata(registration.getServiceId(), serviceRestMetadata);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistryFactory;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* The Dubbo services will be registered as the specified Spring cloud applications that will not be considered
|
||||
* normal ones, but only are used to Dubbo's service discovery even if it is based on Spring Cloud Commons abstraction.
|
||||
* However, current application will be registered by other DiscoveryClientAutoConfiguration.
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboServiceRegistrationApplicationContextInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
// Set ApplicationContext into SpringCloudRegistryFactory before Dubbo Service Register
|
||||
SpringCloudRegistryFactory.setApplicationContext(applicationContext);
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.metadata;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@link Method} Metadata
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class MethodMetadata {
|
||||
|
||||
private String name;
|
||||
|
||||
private String returnType;
|
||||
|
||||
private List<MethodParameterMetadata> params;
|
||||
|
||||
@JsonIgnore
|
||||
private Method method;
|
||||
|
||||
public MethodMetadata() {
|
||||
this.params = new LinkedList<>();
|
||||
}
|
||||
|
||||
public MethodMetadata(Method method) {
|
||||
this.name = method.getName();
|
||||
this.returnType = ClassUtils.getName(method.getReturnType());
|
||||
this.params = initParameters(method);
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
private List<MethodParameterMetadata> initParameters(Method method) {
|
||||
int parameterCount = method.getParameterCount();
|
||||
if (parameterCount < 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<MethodParameterMetadata> params = new ArrayList<>(parameterCount);
|
||||
Parameter[] parameters = method.getParameters();
|
||||
for (int i = 0; i < parameterCount; i++) {
|
||||
Parameter parameter = parameters[i];
|
||||
MethodParameterMetadata param = toMethodParameterMetadata(i, parameter);
|
||||
params.add(param);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private MethodParameterMetadata toMethodParameterMetadata(int index, Parameter parameter) {
|
||||
MethodParameterMetadata metadata = new MethodParameterMetadata();
|
||||
metadata.setIndex(index);
|
||||
metadata.setName(parameter.getName());
|
||||
metadata.setType(parameter.getType().getTypeName());
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getReturnType() {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
public void setReturnType(String returnType) {
|
||||
this.returnType = returnType;
|
||||
}
|
||||
|
||||
public List<MethodParameterMetadata> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(List<MethodParameterMetadata> params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MethodMetadata that = (MethodMetadata) o;
|
||||
return Objects.equals(name, that.name) &&
|
||||
Objects.equals(returnType, that.returnType) &&
|
||||
Objects.equals(params, that.params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, returnType, params);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.metadata;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@link Method} Parameter Metadata
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class MethodParameterMetadata {
|
||||
|
||||
private int index;
|
||||
|
||||
private String name;
|
||||
|
||||
private String type;
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MethodParameterMetadata that = (MethodParameterMetadata) o;
|
||||
return index == that.index &&
|
||||
Objects.equals(name, that.name) &&
|
||||
Objects.equals(type, that.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(index, name, type);
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.metadata;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Request Metadata
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class RequestMetadata {
|
||||
|
||||
private String method;
|
||||
|
||||
private String url;
|
||||
|
||||
private Map<String, Collection<String>> queries;
|
||||
|
||||
private Map<String, Collection<String>> headers;
|
||||
|
||||
public RequestMetadata() {
|
||||
}
|
||||
|
||||
public RequestMetadata(RequestTemplate requestTemplate) {
|
||||
this.method = requestTemplate.method();
|
||||
this.url = requestTemplate.url();
|
||||
this.queries = requestTemplate.queries();
|
||||
this.headers = requestTemplate.headers();
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Map<String, Collection<String>> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, Collection<String>> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public Map<String, Collection<String>> getQueries() {
|
||||
return queries;
|
||||
}
|
||||
|
||||
public void setQueries(Map<String, Collection<String>> queries) {
|
||||
this.queries = queries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RequestMetadata that = (RequestMetadata) o;
|
||||
return Objects.equals(method, that.method) &&
|
||||
Objects.equals(url, that.url) &&
|
||||
Objects.equals(queries, that.queries) &&
|
||||
Objects.equals(headers, that.headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(method, url, queries, headers);
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.metadata;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Method Request Metadata
|
||||
*/
|
||||
public class RestMethodMetadata {
|
||||
|
||||
private MethodMetadata method;
|
||||
|
||||
private RequestMetadata request;
|
||||
|
||||
private Map<Integer, Collection<String>> indexToName;
|
||||
|
||||
public MethodMetadata getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(MethodMetadata method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public RequestMetadata getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public void setRequest(RequestMetadata request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public Map<Integer, Collection<String>> getIndexToName() {
|
||||
return indexToName;
|
||||
}
|
||||
|
||||
public void setIndexToName(Map<Integer, Collection<String>> indexToName) {
|
||||
this.indexToName = indexToName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RestMethodMetadata that = (RestMethodMetadata) o;
|
||||
return Objects.equals(method, that.method) &&
|
||||
Objects.equals(request, that.request) &&
|
||||
Objects.equals(indexToName, that.indexToName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(method, request, indexToName);
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.metadata;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Service Rest Metadata
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see RestMethodMetadata
|
||||
*/
|
||||
public class ServiceRestMetadata {
|
||||
|
||||
private String name;
|
||||
|
||||
private Set<RestMethodMetadata> meta;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<RestMethodMetadata> getMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
public void setMeta(Set<RestMethodMetadata> meta) {
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ServiceRestMetadata that = (ServiceRestMetadata) o;
|
||||
|
||||
if (name != null ? !name.equals(that.name) : that.name != null) return false;
|
||||
return meta != null ? meta.equals(that.meta) : that.meta == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name != null ? name.hashCode() : 0;
|
||||
result = 31 * result + (meta != null ? meta.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.metadata.repository;
|
||||
|
||||
import com.alibaba.dubbo.config.spring.ReferenceBean;
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.service.MetadataConfigService;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceGroup;
|
||||
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceInterface;
|
||||
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceSegments;
|
||||
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceVersion;
|
||||
|
||||
/**
|
||||
* Dubbo Service Metadata {@link Repository}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@Repository
|
||||
public class DubboServiceMetadataRepository {
|
||||
|
||||
/**
|
||||
* Key is application name
|
||||
* Value is Map<RequestMetadata, GenericService>
|
||||
*/
|
||||
private Map<String, Map<RequestMetadata, GenericService>> genericServicesRepository = new HashMap<>();
|
||||
|
||||
private Map<String, Map<RequestMetadata, MethodMetadata>> methodMetadataRepository = new HashMap<>();
|
||||
|
||||
@Autowired
|
||||
private MetadataConfigService metadataConfigService;
|
||||
|
||||
@Value("${dubbo.target.protocol:dubbo}")
|
||||
private String targetProtocol;
|
||||
|
||||
@Value("${dubbo.target.cluster:failover}")
|
||||
private String targetCluster;
|
||||
|
||||
public void updateMetadata(String serviceName) {
|
||||
|
||||
Map<RequestMetadata, GenericService> genericServicesMap = genericServicesRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
|
||||
|
||||
Map<RequestMetadata, MethodMetadata> methodMetadataMap = methodMetadataRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
|
||||
|
||||
Set<ServiceRestMetadata> serviceRestMetadataSet = metadataConfigService.getServiceRestMetadata(serviceName);
|
||||
|
||||
for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) {
|
||||
|
||||
ReferenceBean<GenericService> referenceBean = adaptReferenceBean(serviceRestMetadata);
|
||||
|
||||
serviceRestMetadata.getMeta().forEach(restMethodMetadata -> {
|
||||
RequestMetadata requestMetadata = restMethodMetadata.getRequest();
|
||||
genericServicesMap.put(requestMetadata, referenceBean.get());
|
||||
methodMetadataMap.put(requestMetadata, restMethodMetadata.getMethod());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public GenericService getGenericService(String serviceName, RequestMetadata requestMetadata) {
|
||||
return getGenericServicesMap(serviceName).get(requestMetadata);
|
||||
}
|
||||
|
||||
public MethodMetadata getMethodMetadata(String serviceName, RequestMetadata requestMetadata) {
|
||||
return getMethodMetadataMap(serviceName).get(requestMetadata);
|
||||
}
|
||||
|
||||
private ReferenceBean<GenericService> adaptReferenceBean(ServiceRestMetadata serviceRestMetadata) {
|
||||
String dubboServiceName = serviceRestMetadata.getName();
|
||||
String[] segments = getServiceSegments(dubboServiceName);
|
||||
String interfaceName = getServiceInterface(segments);
|
||||
String version = getServiceVersion(segments);
|
||||
String group = getServiceGroup(segments);
|
||||
|
||||
ReferenceBean<GenericService> referenceBean = new ReferenceBean<GenericService>();
|
||||
referenceBean.setGeneric(true);
|
||||
referenceBean.setInterface(interfaceName);
|
||||
referenceBean.setVersion(version);
|
||||
referenceBean.setGroup(group);
|
||||
referenceBean.setProtocol(targetProtocol);
|
||||
referenceBean.setCluster(targetCluster);
|
||||
|
||||
return referenceBean;
|
||||
}
|
||||
|
||||
private Map<RequestMetadata, GenericService> getGenericServicesMap(String serviceName) {
|
||||
return genericServicesRepository.getOrDefault(serviceName, Collections.emptyMap());
|
||||
}
|
||||
|
||||
private Map<RequestMetadata, MethodMetadata> getMethodMetadataMap(String serviceName) {
|
||||
return methodMetadataRepository.getOrDefault(serviceName, Collections.emptyMap());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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.metadata.resolver;
|
||||
|
||||
import com.alibaba.dubbo.common.URL;
|
||||
import com.alibaba.dubbo.config.spring.ServiceBean;
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Util;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The metadata resolver for {@link Feign}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class FeignMetadataResolver implements BeanClassLoaderAware, SmartInitializingSingleton, MetadataResolver {
|
||||
|
||||
private static final String[] CONTRACT_CLASS_NAMES = {
|
||||
"feign.jaxrs2.JAXRS2Contract",
|
||||
"org.springframework.cloud.openfeign.support.SpringMvcContract",
|
||||
};
|
||||
|
||||
private final String currentApplicationName;
|
||||
|
||||
private final ObjectProvider<Contract> contract;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Feign Contracts
|
||||
*/
|
||||
private Collection<Contract> contracts;
|
||||
|
||||
public FeignMetadataResolver(String currentApplicationName, ObjectProvider<Contract> contract) {
|
||||
this.currentApplicationName = currentApplicationName;
|
||||
this.contract = contract;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
|
||||
LinkedList<Contract> contracts = new LinkedList<>();
|
||||
|
||||
// Add injected Contract if available, for example SpringMvcContract Bean under Spring Cloud Open Feign
|
||||
contract.ifAvailable(contracts::add);
|
||||
|
||||
Stream.of(CONTRACT_CLASS_NAMES)
|
||||
.filter(this::isClassPresent) // filter the existed classes
|
||||
.map(this::loadContractClass) // load Contract Class
|
||||
.map(this::createContract) // create instance by the specified class
|
||||
.forEach(contracts::add); // add the Contract instance into contracts
|
||||
|
||||
this.contracts = Collections.unmodifiableCollection(contracts);
|
||||
}
|
||||
|
||||
private Contract createContract(Class<?> contractClassName) {
|
||||
return (Contract) BeanUtils.instantiateClass(contractClassName);
|
||||
}
|
||||
|
||||
private Class<?> loadContractClass(String contractClassName) {
|
||||
return ClassUtils.resolveClassName(contractClassName, classLoader);
|
||||
}
|
||||
|
||||
private boolean isClassPresent(String className) {
|
||||
return ClassUtils.isPresent(className, classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ServiceRestMetadata> resolveServiceRestMetadata(ServiceBean serviceBean) {
|
||||
|
||||
Object bean = serviceBean.getRef();
|
||||
|
||||
Class<?> beanType = bean.getClass();
|
||||
|
||||
Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
|
||||
|
||||
Set<RestMethodMetadata> methodRestMetadata = resolveMethodRestMetadata(beanType);
|
||||
|
||||
List<URL> urls = serviceBean.getExportedUrls();
|
||||
|
||||
urls.stream()
|
||||
.map(SpringCloudRegistry::getServiceName)
|
||||
.forEach(serviceName -> {
|
||||
ServiceRestMetadata metadata = new ServiceRestMetadata();
|
||||
metadata.setName(serviceName);
|
||||
metadata.setMeta(methodRestMetadata);
|
||||
serviceRestMetadata.add(metadata);
|
||||
});
|
||||
|
||||
return serviceRestMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RestMethodMetadata> resolveMethodRestMetadata(Class<?> targetType) {
|
||||
List<Method> feignContractMethods = selectFeignContractMethods(targetType);
|
||||
return contracts.stream()
|
||||
.map(contract -> contract.parseAndValidatateMetadata(targetType))
|
||||
.flatMap(v -> v.stream())
|
||||
.map(methodMetadata -> resolveMethodRestMetadata(methodMetadata, targetType, feignContractMethods))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Select feign contract methods
|
||||
* <p>
|
||||
* extract some code from {@link Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class)}
|
||||
*
|
||||
* @param targetType
|
||||
* @return non-null
|
||||
*/
|
||||
private List<Method> selectFeignContractMethods(Class<?> targetType) {
|
||||
List<Method> methods = new LinkedList<>();
|
||||
for (Method method : targetType.getMethods()) {
|
||||
if (method.getDeclaringClass() == Object.class ||
|
||||
(method.getModifiers() & Modifier.STATIC) != 0 ||
|
||||
Util.isDefault(method)) {
|
||||
continue;
|
||||
}
|
||||
methods.add(method);
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
protected RestMethodMetadata resolveMethodRestMetadata(MethodMetadata methodMetadata, Class<?> targetType,
|
||||
List<Method> feignContractMethods) {
|
||||
String configKey = methodMetadata.configKey();
|
||||
Method feignContractMethod = getMatchedFeignContractMethod(targetType, feignContractMethods, configKey);
|
||||
|
||||
RestMethodMetadata metadata = new RestMethodMetadata();
|
||||
|
||||
metadata.setRequest(new RequestMetadata(methodMetadata.template()));
|
||||
metadata.setMethod(new org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata(feignContractMethod));
|
||||
metadata.setIndexToName(methodMetadata.indexToName());
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private Method getMatchedFeignContractMethod(Class<?> targetType, List<Method> methods, String expectedConfigKey) {
|
||||
Method matchedMethod = null;
|
||||
for (Method method : methods) {
|
||||
String configKey = Feign.configKey(targetType, method);
|
||||
if (expectedConfigKey.equals(configKey)) {
|
||||
matchedMethod = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return matchedMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.metadata.resolver;
|
||||
|
||||
import com.alibaba.dubbo.config.spring.ServiceBean;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The REST metadata resolver
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public interface MetadataResolver {
|
||||
|
||||
/**
|
||||
* Resolve the {@link ServiceRestMetadata} {@link Set set} from {@link ServiceBean}
|
||||
*
|
||||
* @param serviceBean {@link ServiceBean}
|
||||
* @return non-null {@link Set}
|
||||
*/
|
||||
Set<ServiceRestMetadata> resolveServiceRestMetadata(ServiceBean serviceBean);
|
||||
|
||||
/**
|
||||
* Resolve {@link RestMethodMetadata} {@link Set set} from {@link Class target type}
|
||||
*
|
||||
* @param targetType {@link Class target type}
|
||||
* @return non-null {@link Set}
|
||||
*/
|
||||
Set<RestMethodMetadata> resolveMethodRestMetadata(Class<?> targetType);
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.metadata.service;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Config Service for Metadata
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public interface MetadataConfigService {
|
||||
|
||||
void publishServiceRestMetadata(String serviceName, Set<ServiceRestMetadata> serviceRestMetadata);
|
||||
|
||||
Set<ServiceRestMetadata> getServiceRestMetadata(String serviceName);
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.metadata.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;
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.openfeign;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Dubbo {@link Configuration} for {@link FeignClient FeignClients}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see DubboOpenFeignAutoConfiguration
|
||||
* @see org.springframework.cloud.openfeign.FeignContext#setConfigurations(List)
|
||||
* @see FeignClientsConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
public class DubboFeignClientsConfiguration {
|
||||
|
||||
@Autowired
|
||||
private Contract contract;
|
||||
|
||||
@Autowired
|
||||
private DubboServiceMetadataRepository dubboServiceRepository;
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor beanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof Feign.Builder) {
|
||||
Feign.Builder builder = (Feign.Builder) bean;
|
||||
builder.invocationHandlerFactory(new DubboInvocationHandlerFactory(contract, dubboServiceRepository));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.openfeign;
|
||||
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Dubbo {@link GenericService} for {@link InvocationHandler}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Map<Method, GenericService> genericServicesMap;
|
||||
|
||||
private final Map<Method, MethodMetadata> methodMetadata;
|
||||
|
||||
private final InvocationHandler defaultInvocationHandler;
|
||||
|
||||
public DubboInvocationHandler(Map<Method, GenericService> genericServicesMap,
|
||||
Map<Method, MethodMetadata> methodMetadata,
|
||||
InvocationHandler defaultInvocationHandler) {
|
||||
this.genericServicesMap = genericServicesMap;
|
||||
this.methodMetadata = methodMetadata;
|
||||
this.defaultInvocationHandler = defaultInvocationHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
|
||||
GenericService genericService = genericServicesMap.get(method);
|
||||
|
||||
MethodMetadata methodMetadata = this.methodMetadata.get(method);
|
||||
|
||||
if (genericService == null || methodMetadata == null) {
|
||||
return defaultInvocationHandler.invoke(proxy, method, args);
|
||||
}
|
||||
|
||||
String methodName = methodMetadata.getName();
|
||||
|
||||
String[] parameterTypes = methodMetadata
|
||||
.getParams()
|
||||
.stream()
|
||||
.map(MethodParameterMetadata::getType)
|
||||
.toArray(String[]::new);
|
||||
|
||||
return genericService.$invoke(methodName, parameterTypes, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.openfeign;
|
||||
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
import feign.Contract;
|
||||
import feign.InvocationHandlerFactory;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Target;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static feign.Feign.configKey;
|
||||
|
||||
/**
|
||||
* Dubbo {@link InvocationHandlerFactory}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboInvocationHandlerFactory implements InvocationHandlerFactory {
|
||||
|
||||
private final static InvocationHandlerFactory DEFAULT_INVOCATION_HANDLER_FACTORY =
|
||||
new InvocationHandlerFactory.Default();
|
||||
|
||||
private final Contract contract;
|
||||
|
||||
private final DubboServiceMetadataRepository dubboServiceRepository;
|
||||
|
||||
public DubboInvocationHandlerFactory(Contract contract, DubboServiceMetadataRepository dubboServiceRepository) {
|
||||
this.contract = contract;
|
||||
this.dubboServiceRepository = dubboServiceRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
|
||||
// The target class annotated @FeignClient
|
||||
Class<?> targetType = target.type();
|
||||
// Resolve metadata from current @FeignClient type
|
||||
Map<Method, RequestMetadata> methodRequestMetadataMap = resolveMethodRequestMetadataMap(targetType, dispatch.keySet());
|
||||
// @FeignClient specifies the service name
|
||||
String serviceName = target.name();
|
||||
// Update specified metadata
|
||||
dubboServiceRepository.updateMetadata(serviceName);
|
||||
|
||||
Map<Method, GenericService> genericServicesMap = new HashMap<>();
|
||||
|
||||
Map<Method, org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata> methodMetadataMap = new HashMap<>();
|
||||
|
||||
methodRequestMetadataMap.forEach((method, requestMetadata) -> {
|
||||
GenericService genericService = dubboServiceRepository.getGenericService(serviceName, requestMetadata);
|
||||
org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata methodMetadata =
|
||||
dubboServiceRepository.getMethodMetadata(serviceName, requestMetadata);
|
||||
genericServicesMap.put(method, genericService);
|
||||
methodMetadataMap.put(method, methodMetadata);
|
||||
});
|
||||
|
||||
InvocationHandler defaultInvocationHandler = DEFAULT_INVOCATION_HANDLER_FACTORY.create(target, dispatch);
|
||||
|
||||
DubboInvocationHandler invocationHandler = new DubboInvocationHandler(genericServicesMap, methodMetadataMap,
|
||||
defaultInvocationHandler);
|
||||
|
||||
return invocationHandler;
|
||||
}
|
||||
|
||||
private Map<Method, RequestMetadata> resolveMethodRequestMetadataMap(Class<?> targetType, Set<Method> methods) {
|
||||
Map<String, RequestMetadata> requestMetadataMap = resolveRequestMetadataMap(targetType);
|
||||
return methods.stream().collect(Collectors.toMap(method -> method, method ->
|
||||
requestMetadataMap.get(configKey(targetType, method))
|
||||
));
|
||||
}
|
||||
|
||||
private Map<String, RequestMetadata> resolveRequestMetadataMap(Class<?> targetType) {
|
||||
return contract.parseAndValidatateMetadata(targetType)
|
||||
.stream().collect(Collectors.toMap(MethodMetadata::configKey, this::requestMetadata));
|
||||
}
|
||||
|
||||
private RequestMetadata requestMetadata(MethodMetadata methodMetadata) {
|
||||
return new RequestMetadata(methodMetadata.template());
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.registry;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link Registration} of Dubbo uses an external of {@link ServiceInstance} instance as the delegate.
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class DubboRegistration implements Registration {
|
||||
|
||||
private final ServiceInstance delegate;
|
||||
|
||||
public DubboRegistration(ServiceInstance delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return delegate.getServiceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return delegate.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return delegate.getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return delegate.isSecure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return delegate.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return delegate.getMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScheme() {
|
||||
return delegate.getScheme();
|
||||
}
|
||||
}
|
@ -0,0 +1,443 @@
|
||||
/*
|
||||
* 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.registry;
|
||||
|
||||
import com.alibaba.dubbo.common.Constants;
|
||||
import com.alibaba.dubbo.common.URL;
|
||||
import com.alibaba.dubbo.common.utils.NetUtils;
|
||||
import com.alibaba.dubbo.common.utils.UrlUtils;
|
||||
import com.alibaba.dubbo.registry.NotifyListener;
|
||||
import com.alibaba.dubbo.registry.RegistryFactory;
|
||||
import com.alibaba.dubbo.registry.support.FailbackRegistry;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.alibaba.dubbo.common.Constants.CONFIGURATORS_CATEGORY;
|
||||
import static com.alibaba.dubbo.common.Constants.CONSUMERS_CATEGORY;
|
||||
import static com.alibaba.dubbo.common.Constants.PROTOCOL_KEY;
|
||||
import static com.alibaba.dubbo.common.Constants.PROVIDERS_CATEGORY;
|
||||
import static com.alibaba.dubbo.common.Constants.ROUTERS_CATEGORY;
|
||||
|
||||
/**
|
||||
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class SpringCloudRegistry extends FailbackRegistry {
|
||||
|
||||
/**
|
||||
* All supported categories
|
||||
*/
|
||||
private static final String[] ALL_SUPPORTED_CATEGORIES = of(
|
||||
PROVIDERS_CATEGORY,
|
||||
CONSUMERS_CATEGORY,
|
||||
ROUTERS_CATEGORY,
|
||||
CONFIGURATORS_CATEGORY
|
||||
);
|
||||
|
||||
private static final int CATEGORY_INDEX = 0;
|
||||
|
||||
private static final int PROTOCOL_INDEX = CATEGORY_INDEX + 1;
|
||||
|
||||
private static final int SERVICE_INTERFACE_INDEX = PROTOCOL_INDEX + 1;
|
||||
|
||||
private static final int SERVICE_VERSION_INDEX = SERVICE_INTERFACE_INDEX + 1;
|
||||
|
||||
private static final int SERVICE_GROUP_INDEX = SERVICE_VERSION_INDEX + 1;
|
||||
|
||||
private static final String WILDCARD = "*";
|
||||
|
||||
/**
|
||||
* The separator for service name
|
||||
*/
|
||||
private static final String SERVICE_NAME_SEPARATOR = ":";
|
||||
|
||||
private final ServiceRegistry<Registration> serviceRegistry;
|
||||
|
||||
private final DiscoveryClient discoveryClient;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* {@link ScheduledExecutorService} lookup service names(only for Dubbo-OPS)
|
||||
*/
|
||||
private volatile ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
/**
|
||||
* The interval in second of lookup service names(only for Dubbo-OPS)
|
||||
*/
|
||||
private static final long LOOKUP_INTERVAL = Long.getLong("dubbo.service.names.lookup.interval", 30);
|
||||
|
||||
public SpringCloudRegistry(URL url, ServiceRegistry<Registration> serviceRegistry,
|
||||
DiscoveryClient discoveryClient) {
|
||||
super(url);
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
this.discoveryClient = discoveryClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRegister(URL url) {
|
||||
final String serviceName = getServiceName(url);
|
||||
final Registration registration = createRegistration(serviceName, url);
|
||||
serviceRegistry.register(registration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUnregister(URL url) {
|
||||
final String serviceName = getServiceName(url);
|
||||
final Registration registration = createRegistration(serviceName, url);
|
||||
this.serviceRegistry.deregister(registration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSubscribe(URL url, NotifyListener listener) {
|
||||
List<String> serviceNames = getServiceNames(url, listener);
|
||||
doSubscribe(url, listener, serviceNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUnsubscribe(URL url, NotifyListener listener) {
|
||||
if (isAdminProtocol(url)) {
|
||||
shutdownServiceNamesLookup();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void shutdownServiceNamesLookup() {
|
||||
if (scheduledExecutorService != null) {
|
||||
scheduledExecutorService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private Registration createRegistration(String serviceName, URL url) {
|
||||
return new DubboRegistration(createServiceInstance(serviceName, url));
|
||||
}
|
||||
|
||||
private ServiceInstance createServiceInstance(String serviceName, URL url) {
|
||||
// Append default category if absent
|
||||
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
|
||||
URL newURL = url.addParameter(Constants.CATEGORY_KEY, category);
|
||||
newURL = newURL.addParameter(Constants.PROTOCOL_KEY, url.getProtocol());
|
||||
String ip = NetUtils.getLocalHost();
|
||||
int port = newURL.getParameter(Constants.BIND_PORT_KEY, url.getPort());
|
||||
DefaultServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, ip, port, false);
|
||||
serviceInstance.getMetadata().putAll(new LinkedHashMap<>(newURL.getParameters()));
|
||||
return serviceInstance;
|
||||
}
|
||||
|
||||
public static String getServiceName(URL url) {
|
||||
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
|
||||
return getServiceName(url, category);
|
||||
}
|
||||
|
||||
private static String getServiceName(URL url, String category) {
|
||||
StringBuilder serviceNameBuilder = new StringBuilder(category);
|
||||
appendIfPresent(serviceNameBuilder, url.getParameter(PROTOCOL_KEY, url.getProtocol()));
|
||||
appendIfPresent(serviceNameBuilder, url, Constants.INTERFACE_KEY);
|
||||
appendIfPresent(serviceNameBuilder, url, Constants.VERSION_KEY);
|
||||
appendIfPresent(serviceNameBuilder, url, Constants.GROUP_KEY);
|
||||
return serviceNameBuilder.toString();
|
||||
}
|
||||
|
||||
private static void appendIfPresent(StringBuilder target, URL url, String parameterName) {
|
||||
String parameterValue = url.getParameter(parameterName);
|
||||
appendIfPresent(target, parameterValue);
|
||||
}
|
||||
|
||||
private static void appendIfPresent(StringBuilder target, String parameterValue) {
|
||||
if (StringUtils.hasText(parameterValue)) {
|
||||
target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void filterServiceNames(List<String> serviceNames, URL url) {
|
||||
|
||||
final String[] categories = getCategories(url);
|
||||
|
||||
final String targetServiceInterface = url.getServiceInterface();
|
||||
|
||||
final String targetVersion = url.getParameter(Constants.VERSION_KEY);
|
||||
|
||||
final String targetGroup = url.getParameter(Constants.GROUP_KEY);
|
||||
|
||||
filter(serviceNames, new Filter<String>() {
|
||||
@Override
|
||||
public boolean accept(String serviceName) {
|
||||
// split service name to segments
|
||||
// (required) segments[0] = category
|
||||
// (required) segments[1] = serviceInterface
|
||||
// (required) segments[2] = protocol
|
||||
// (required) segments[3] = version
|
||||
// (optional) segments[4] = group
|
||||
String[] segments = getServiceSegments(serviceName);
|
||||
int length = segments.length;
|
||||
if (length < 4) { // must present 4 segments or more
|
||||
return false;
|
||||
}
|
||||
|
||||
String category = getCategory(segments);
|
||||
if (Arrays.binarySearch(categories, category) > -1) { // no match category
|
||||
return false;
|
||||
}
|
||||
|
||||
String protocol = getProtocol(segments);
|
||||
if (StringUtils.hasText(protocol)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String serviceInterface = getServiceInterface(segments);
|
||||
if (!WILDCARD.equals(targetServiceInterface) &&
|
||||
!Objects.equals(targetServiceInterface, serviceInterface)) { // no match service interface
|
||||
return false;
|
||||
}
|
||||
|
||||
String version = getServiceVersion(segments);
|
||||
if (!WILDCARD.equals(targetVersion) &&
|
||||
!Objects.equals(targetVersion, version)) { // no match service version
|
||||
return false;
|
||||
}
|
||||
|
||||
String group = getServiceGroup(segments);
|
||||
if (group != null && !WILDCARD.equals(targetGroup)
|
||||
&& !Objects.equals(targetGroup, group)) { // no match service group
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static String[] getServiceSegments(String serviceName) {
|
||||
return StringUtils.delimitedListToStringArray(serviceName, SERVICE_NAME_SEPARATOR);
|
||||
}
|
||||
|
||||
public static String getCategory(String[] segments) {
|
||||
return segments[CATEGORY_INDEX];
|
||||
}
|
||||
|
||||
public static String getProtocol(String[] segments) {
|
||||
return segments[PROTOCOL_INDEX];
|
||||
}
|
||||
|
||||
public static String getServiceInterface(String[] segments) {
|
||||
return segments[SERVICE_INTERFACE_INDEX];
|
||||
}
|
||||
|
||||
public static String getServiceVersion(String[] segments) {
|
||||
return segments[SERVICE_VERSION_INDEX];
|
||||
}
|
||||
|
||||
public static String getServiceGroup(String[] segments) {
|
||||
return segments.length > 4 ? segments[SERVICE_GROUP_INDEX] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the categories from {@link URL}
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @return non-null array
|
||||
*/
|
||||
private String[] getCategories(URL url) {
|
||||
return Constants.ANY_VALUE.equals(url.getServiceInterface()) ?
|
||||
ALL_SUPPORTED_CATEGORIES : of(Constants.DEFAULT_CATEGORY);
|
||||
}
|
||||
|
||||
private List<String> getAllServiceNames() {
|
||||
return discoveryClient.getServices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service names from the specified {@link URL url}
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @param listener {@link NotifyListener}
|
||||
* @return non-null
|
||||
*/
|
||||
private List<String> getServiceNames(URL url, NotifyListener listener) {
|
||||
if (isAdminProtocol(url)) {
|
||||
scheduleServiceNamesLookup(url, listener);
|
||||
return getServiceNamesForOps(url);
|
||||
} else {
|
||||
return doGetServiceNames(url);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isAdminProtocol(URL url) {
|
||||
return Constants.ADMIN_PROTOCOL.equals(url.getProtocol());
|
||||
}
|
||||
|
||||
private void scheduleServiceNamesLookup(final URL url, final NotifyListener listener) {
|
||||
if (scheduledExecutorService == null) {
|
||||
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<String> serviceNames = getAllServiceNames();
|
||||
filter(serviceNames, new Filter<String>() {
|
||||
@Override
|
||||
public boolean accept(String serviceName) {
|
||||
boolean accepted = false;
|
||||
for (String category : ALL_SUPPORTED_CATEGORIES) {
|
||||
String prefix = category + SERVICE_NAME_SEPARATOR;
|
||||
if (StringUtils.startsWithIgnoreCase(serviceName, prefix)) {
|
||||
accepted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
});
|
||||
doSubscribe(url, listener, serviceNames);
|
||||
}
|
||||
}, LOOKUP_INTERVAL, LOOKUP_INTERVAL, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void doSubscribe(final URL url, final NotifyListener listener, final List<String> serviceNames) {
|
||||
for (String serviceName : serviceNames) {
|
||||
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
|
||||
notifySubscriber(url, listener, serviceInstances);
|
||||
// TODO Support Update notification event
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> doGetServiceNames(URL url) {
|
||||
String[] categories = getCategories(url);
|
||||
List<String> serviceNames = new ArrayList<String>(categories.length);
|
||||
for (String category : categories) {
|
||||
final String serviceName = getServiceName(url, category);
|
||||
serviceNames.add(serviceName);
|
||||
}
|
||||
return serviceNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the Healthy {@link ServiceInstance service instance} to subscriber.
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @param listener {@link NotifyListener}
|
||||
* @param serviceInstances all {@link ServiceInstance instances}
|
||||
*/
|
||||
private void notifySubscriber(URL url, NotifyListener listener, List<ServiceInstance> serviceInstances) {
|
||||
List<ServiceInstance> healthyInstances = new LinkedList<ServiceInstance>(serviceInstances);
|
||||
// Healthy Instances
|
||||
filterHealthyInstances(healthyInstances);
|
||||
List<URL> urls = buildURLs(url, healthyInstances);
|
||||
this.notify(url, listener, urls);
|
||||
}
|
||||
|
||||
private void filterHealthyInstances(Collection<ServiceInstance> instances) {
|
||||
filter(instances, new Filter<ServiceInstance>() {
|
||||
@Override
|
||||
public boolean accept(ServiceInstance data) {
|
||||
// TODO check the details of status
|
||||
// return serviceRegistry.getStatus(new DubboRegistration(data)) != null;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<URL> buildURLs(URL consumerURL, Collection<ServiceInstance> serviceInstances) {
|
||||
if (serviceInstances.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<URL> urls = new LinkedList<URL>();
|
||||
for (ServiceInstance serviceInstance : serviceInstances) {
|
||||
URL url = buildURL(serviceInstance);
|
||||
if (UrlUtils.isMatch(consumerURL, url)) {
|
||||
urls.add(url);
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
private URL buildURL(ServiceInstance serviceInstance) {
|
||||
URL url = new URL(serviceInstance.getMetadata().get(Constants.PROTOCOL_KEY),
|
||||
serviceInstance.getHost(),
|
||||
serviceInstance.getPort(),
|
||||
serviceInstance.getMetadata());
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service names for Dubbo OPS
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @return non-null
|
||||
*/
|
||||
private List<String> getServiceNamesForOps(URL url) {
|
||||
List<String> serviceNames = getAllServiceNames();
|
||||
filterServiceNames(serviceNames, url);
|
||||
return serviceNames;
|
||||
}
|
||||
|
||||
private <T> void filter(Collection<T> collection, Filter<T> filter) {
|
||||
Iterator<T> iterator = collection.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
T data = iterator.next();
|
||||
if (!filter.accept(data)) { // remove if not accept
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T[] of(T... values) {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter
|
||||
*/
|
||||
private interface Filter<T> {
|
||||
|
||||
/**
|
||||
* Tests whether or not the specified data should be accepted.
|
||||
*
|
||||
* @param data The data to be tested
|
||||
* @return <code>true</code> if and only if <code>data</code>
|
||||
* should be accepted
|
||||
*/
|
||||
boolean accept(T data);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.registry;
|
||||
|
||||
import com.alibaba.dubbo.common.URL;
|
||||
import com.alibaba.dubbo.registry.Registry;
|
||||
import com.alibaba.dubbo.registry.RegistryFactory;
|
||||
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.serviceregistry.Registration;
|
||||
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see RegistryFactory
|
||||
* @see SpringCloudRegistry
|
||||
*/
|
||||
public class SpringCloudRegistryFactory implements RegistryFactory {
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public Registry getRegistry(URL url) {
|
||||
ServiceRegistry<Registration> serviceRegistry = applicationContext.getBean(ServiceRegistry.class);
|
||||
DiscoveryClient discoveryClient = applicationContext.getBean(DiscoveryClient.class);
|
||||
return new SpringCloudRegistry(url, serviceRegistry, discoveryClient);
|
||||
}
|
||||
|
||||
public static void setApplicationContext(ApplicationContext applicationContext) {
|
||||
SpringCloudRegistryFactory.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
spring-cloud=org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistryFactory
|
@ -0,0 +1,7 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestMetadataRegistrationAutoConfiguration
|
||||
|
||||
org.springframework.context.ApplicationContextInitializer=\
|
||||
org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
|
@ -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.springframework.cloud.alibaba.dubbo.autoconfigure;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* {@link DubboServiceRegistrationAutoConfiguration} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@SpringBootTest
|
||||
public class DubboServiceRegistrationAutoConfigurationTest {
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.bootstrap;
|
||||
|
||||
import com.alibaba.dubbo.config.annotation.Reference;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.EchoService;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Dubbo Spring Cloud Bootstrap
|
||||
*/
|
||||
@EnableDiscoveryClient
|
||||
@EnableAutoConfiguration
|
||||
@EnableFeignClients
|
||||
@RestController
|
||||
public class DubboSpringCloudBootstrap {
|
||||
|
||||
@Reference(version = "1.0.0")
|
||||
private EchoService echoService;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private FeignEchoService feignEchoService;
|
||||
|
||||
@GetMapping(value = "/call/echo")
|
||||
public String echo(@RequestParam("message") String message) {
|
||||
return feignEchoService.echo(message);
|
||||
}
|
||||
|
||||
@FeignClient("spring-cloud-alibaba-dubbo")
|
||||
public interface FeignEchoService {
|
||||
|
||||
@GetMapping(value = "/echo")
|
||||
String echo(@RequestParam("message") String message);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationRunner applicationRunner() {
|
||||
return arguments -> {
|
||||
// Dubbo Service call
|
||||
System.out.println(echoService.echo("mercyblitz"));
|
||||
// Spring Cloud Open Feign REST Call
|
||||
System.out.println(feignEchoService.echo("mercyblitz"));
|
||||
};
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(DubboSpringCloudBootstrap.class)
|
||||
.run(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.alibaba.dubbo.rpc.RpcContext;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
|
||||
/**
|
||||
* Default {@link EchoService}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
@Service(version = "1.0.0", protocol = {"dubbo", "rest"})
|
||||
@RestController
|
||||
@Path("/")
|
||||
public class DefaultEchoService implements EchoService {
|
||||
|
||||
@Override
|
||||
@GetMapping(value = "/echo"
|
||||
// consumes = MediaType.APPLICATION_JSON_VALUE,
|
||||
// produces = MediaType.APPLICATION_JSON_UTF8_VALUE
|
||||
)
|
||||
@Path("/echo")
|
||||
@GET
|
||||
// @Consumes("application/json")
|
||||
// @Produces("application/json;charset=UTF-8")
|
||||
public String echo(@RequestParam @QueryParam("message") String message) {
|
||||
return RpcContext.getContext().getUrl() + " [echo] : " + message;
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("/plus")
|
||||
@Path("/plus")
|
||||
@POST
|
||||
public String plus(@RequestParam @QueryParam("a") int a, @RequestParam @QueryParam("b") int b) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Echo Service
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public interface EchoService {
|
||||
|
||||
String echo(String message);
|
||||
|
||||
String plus(int a, int b);
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
dubbo:
|
||||
scan:
|
||||
base-packages: org.springframework.cloud.alibaba.dubbo.service
|
||||
protocols:
|
||||
dubbo:
|
||||
name: dubbo
|
||||
port: 12345
|
||||
rest:
|
||||
name: rest
|
||||
port: 9090
|
||||
server: netty
|
||||
registry:
|
||||
address: spring-cloud://nacos
|
||||
|
||||
server:
|
||||
port: 8080
|
@ -0,0 +1,28 @@
|
||||
spring:
|
||||
application:
|
||||
name: spring-cloud-alibaba-dubbo
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
config:
|
||||
server-addr: 127.0.0.1:8848
|
||||
|
||||
eureka:
|
||||
client:
|
||||
enabled: false
|
||||
|
||||
---
|
||||
spring:
|
||||
profiles: eureka
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
enabled: false
|
||||
register-enabled: false
|
||||
|
||||
eureka:
|
||||
client:
|
||||
enabled: true
|
||||
service-url:
|
||||
defaultZone: http://127.0.0.1:8761/eureka/
|
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>0.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>account-service</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class AccountApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AccountApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import com.alibaba.fescar.core.context.RootContext;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@RestController
|
||||
public class AccountController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AccountController.class);
|
||||
|
||||
private static final String SUCCESS = "SUCCESS";
|
||||
private static final String FAIL = "FAIL";
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private Random random;
|
||||
|
||||
public AccountController(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.random = new Random();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/account", method = RequestMethod.POST, produces = "application/json")
|
||||
public String account(String userId, int money) {
|
||||
LOGGER.info("Account Service ... xid: " + RootContext.getXID());
|
||||
|
||||
if (random.nextBoolean()) {
|
||||
throw new RuntimeException("this is a mock Exception");
|
||||
}
|
||||
|
||||
int result = jdbcTemplate.update(
|
||||
"update account_tbl set money = money - ? where user_id = ?",
|
||||
new Object[] { money, userId });
|
||||
LOGGER.info("Account Service End ... ");
|
||||
if (result == 1) {
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.fescar.rm.datasource.DataSourceProxy;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@Configuration
|
||||
public class DatabaseConfiguration {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public DatabaseConfiguration(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Bean(initMethod = "init", destroyMethod = "close")
|
||||
public DruidDataSource storageDataSource() throws SQLException {
|
||||
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
|
||||
String ip = environment.getProperty("mysql.server.ip");
|
||||
String port = environment.getProperty("mysql.server.port");
|
||||
String dbName = environment.getProperty("mysql.db.name");
|
||||
|
||||
String userName = environment.getProperty("mysql.user.name");
|
||||
String password = environment.getProperty("mysql.user.password");
|
||||
|
||||
DruidDataSource druidDataSource = new DruidDataSource();
|
||||
druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
|
||||
druidDataSource.setUsername(userName);
|
||||
druidDataSource.setPassword(password);
|
||||
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
|
||||
druidDataSource.setInitialSize(0);
|
||||
druidDataSource.setMaxActive(180);
|
||||
druidDataSource.setMaxWait(60000);
|
||||
druidDataSource.setMinIdle(0);
|
||||
druidDataSource.setValidationQuery("Select 'x' from DUAL");
|
||||
druidDataSource.setTestOnBorrow(false);
|
||||
druidDataSource.setTestOnReturn(false);
|
||||
druidDataSource.setTestWhileIdle(true);
|
||||
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
|
||||
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
|
||||
druidDataSource.setRemoveAbandoned(true);
|
||||
druidDataSource.setRemoveAbandonedTimeout(1800);
|
||||
druidDataSource.setLogAbandoned(true);
|
||||
druidDataSource.setFilters("mergeStat");
|
||||
return druidDataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
|
||||
return new DataSourceProxy(druidDataSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
|
||||
|
||||
jdbcTemplate.update("delete from account_tbl where user_id = 'U100001'");
|
||||
jdbcTemplate.update(
|
||||
"insert into account_tbl(user_id, money) values ('U100001', 10000)");
|
||||
|
||||
return jdbcTemplate;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
transport {
|
||||
# tcp udt unix-domain-socket
|
||||
type = "TCP"
|
||||
#NIO NATIVE
|
||||
server = "NIO"
|
||||
#thread factory for netty
|
||||
thread-factory {
|
||||
boss-thread-prefix = "NettyBoss"
|
||||
worker-thread-prefix = "NettyServerNIOWorker"
|
||||
server-executor-thread-prefix = "NettyServerBizHandler"
|
||||
share-boss-worker = false
|
||||
client-selector-thread-prefix = "NettyClientSelector"
|
||||
client-selector-thread-size = 1
|
||||
client-worker-thread-prefix = "NettyClientWorkerThread"
|
||||
# netty boss thread size,will not be used for UDT
|
||||
boss-thread-size = 1
|
||||
#auto default pin or 8
|
||||
worker-thread-size = 8
|
||||
}
|
||||
}
|
||||
service {
|
||||
#vgroup->rgroup
|
||||
vgroup_mapping.account-service-fescar-service-group = "localRgroup"
|
||||
#only support single node
|
||||
localRgroup.grouplist = "127.0.0.1:8091"
|
||||
#degrade current not support
|
||||
enableDegrade = false
|
||||
#disable
|
||||
disable = false
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
spring.application.name=account-service
|
||||
server.port=18084
|
||||
|
||||
mysql.server.ip=127.0.0.1
|
||||
mysql.server.port=3306
|
||||
mysql.db.name=demo
|
||||
|
||||
mysql.user.name=xxxxx
|
||||
mysql.user.password=xxxxx
|
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>0.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>business-service</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients
|
||||
public class BusinessApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BusinessApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@FeignClient(value = "storage", url = "http://127.0.0.1:18082")
|
||||
public interface StorageService {
|
||||
|
||||
@RequestMapping(path = "/storage/{commodityCode}/{count}")
|
||||
String storage(@RequestParam("commodityCode") String commodityCode,
|
||||
@RequestParam("count") int count);
|
||||
|
||||
}
|
||||
|
||||
@FeignClient(value = "order", url = "http://127.0.0.1:18083")
|
||||
public interface OrderService {
|
||||
|
||||
@RequestMapping(path = "/order", method = RequestMethod.POST)
|
||||
String order(@RequestParam("userId") String userId,
|
||||
@RequestParam("commodityCode") String commodityCode,
|
||||
@RequestParam("orderCount") int orderCount);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import com.alibaba.fescar.spring.annotation.GlobalTransactional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.alibaba.cloud.examples.BusinessApplication.OrderService;
|
||||
import org.springframework.cloud.alibaba.cloud.examples.BusinessApplication.StorageService;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@RestController
|
||||
public class HomeController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
|
||||
|
||||
private static final String SUCCESS = "SUCCESS";
|
||||
private static final String FAIL = "FAIL";
|
||||
private static final String USER_ID = "U100001";
|
||||
private static final String COMMODITY_CODE = "C00321";
|
||||
private static final int ORDER_COUNT = 2;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
private final OrderService orderService;
|
||||
private final StorageService storageService;
|
||||
|
||||
public HomeController(RestTemplate restTemplate, OrderService orderService,
|
||||
StorageService storageService) {
|
||||
this.restTemplate = restTemplate;
|
||||
this.orderService = orderService;
|
||||
this.storageService = storageService;
|
||||
}
|
||||
|
||||
@GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx")
|
||||
@RequestMapping(value = "/fescar/rest", method = RequestMethod.GET, produces = "application/json")
|
||||
public String rest() {
|
||||
|
||||
String result = restTemplate.getForObject(
|
||||
"http://127.0.0.1:18082/storage/" + COMMODITY_CODE + "/" + ORDER_COUNT,
|
||||
String.class);
|
||||
|
||||
if (!SUCCESS.equals(result)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
String url = "http://127.0.0.1:18083/order";
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
|
||||
map.add("userId", USER_ID);
|
||||
map.add("commodityCode", COMMODITY_CODE);
|
||||
map.add("orderCount", ORDER_COUNT + "");
|
||||
|
||||
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(
|
||||
map, headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, request,
|
||||
String.class);
|
||||
|
||||
result = response.getBody();
|
||||
|
||||
if (!SUCCESS.equals(result)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx")
|
||||
@RequestMapping(value = "/fescar/feign", method = RequestMethod.GET, produces = "application/json")
|
||||
public String feign() {
|
||||
|
||||
String result = storageService.storage(COMMODITY_CODE, ORDER_COUNT);
|
||||
|
||||
if (!SUCCESS.equals(result)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
result = orderService.order(USER_ID, COMMODITY_CODE, ORDER_COUNT);
|
||||
|
||||
if (!SUCCESS.equals(result)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Order implements Serializable {
|
||||
public long id;
|
||||
public String userId;
|
||||
public String commodityCode;
|
||||
public int count;
|
||||
public int money;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Order{" + "id=" + id + ", userId='" + userId + '\'' + ", commodityCode='"
|
||||
+ commodityCode + '\'' + ", count=" + count + ", money=" + money + '}';
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
transport {
|
||||
# tcp udt unix-domain-socket
|
||||
type = "TCP"
|
||||
#NIO NATIVE
|
||||
server = "NIO"
|
||||
#thread factory for netty
|
||||
thread-factory {
|
||||
boss-thread-prefix = "NettyBoss"
|
||||
worker-thread-prefix = "NettyServerNIOWorker"
|
||||
server-executor-thread-prefix = "NettyServerBizHandler"
|
||||
share-boss-worker = false
|
||||
client-selector-thread-prefix = "NettyClientSelector"
|
||||
client-selector-thread-size = 1
|
||||
client-worker-thread-prefix = "NettyClientWorkerThread"
|
||||
# netty boss thread size,will not be used for UDT
|
||||
boss-thread-size = 1
|
||||
#auto default pin or 8
|
||||
worker-thread-size = 8
|
||||
}
|
||||
}
|
||||
service {
|
||||
#vgroup->rgroup
|
||||
vgroup_mapping.business-service-fescar-service-group = "localRgroup"
|
||||
#only support single node
|
||||
localRgroup.grouplist = "127.0.0.1:8091"
|
||||
#degrade current not support
|
||||
enableDegrade = false
|
||||
#disable
|
||||
disable = false
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
server.port=18081
|
||||
spring.application.name=business-service
|
||||
# The following configuration can be omitted.
|
||||
|
||||
#feign.hystrix.enabled=true
|
||||
#feign.sentinel.enabled=true
|
||||
|
||||
logging.level.com.alibaba.fescar=debug
|
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>0.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>order-service</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.fescar.rm.datasource.DataSourceProxy;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@Configuration
|
||||
public class DatabaseConfiguration {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public DatabaseConfiguration(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Bean(initMethod = "init", destroyMethod = "close")
|
||||
public DruidDataSource storageDataSource() throws SQLException {
|
||||
|
||||
Environment env = applicationContext.getEnvironment();
|
||||
|
||||
String ip = env.getProperty("mysql.server.ip");
|
||||
String port = env.getProperty("mysql.server.port");
|
||||
String dbName = env.getProperty("mysql.db.name");
|
||||
|
||||
String userName = env.getProperty("mysql.user.name");
|
||||
String password = env.getProperty("mysql.user.password");
|
||||
|
||||
DruidDataSource druidDataSource = new DruidDataSource();
|
||||
druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
|
||||
druidDataSource.setUsername(userName);
|
||||
druidDataSource.setPassword(password);
|
||||
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
|
||||
druidDataSource.setInitialSize(0);
|
||||
druidDataSource.setMaxActive(180);
|
||||
druidDataSource.setMaxWait(60000);
|
||||
druidDataSource.setMinIdle(0);
|
||||
druidDataSource.setValidationQuery("Select 'x' from DUAL");
|
||||
druidDataSource.setTestOnBorrow(false);
|
||||
druidDataSource.setTestOnReturn(false);
|
||||
druidDataSource.setTestWhileIdle(true);
|
||||
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
|
||||
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
|
||||
druidDataSource.setRemoveAbandoned(true);
|
||||
druidDataSource.setRemoveAbandonedTimeout(1800);
|
||||
druidDataSource.setLogAbandoned(true);
|
||||
druidDataSource.setFilters("mergeStat");
|
||||
return druidDataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
|
||||
return new DataSourceProxy(druidDataSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
|
||||
|
||||
jdbcTemplate.execute("TRUNCATE TABLE order_tbl");
|
||||
|
||||
return jdbcTemplate;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class OderApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(OderApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Order implements Serializable {
|
||||
public long id;
|
||||
public String userId;
|
||||
public String commodityCode;
|
||||
public int count;
|
||||
public int money;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Order{" + "id=" + id + ", userId='" + userId + '\'' + ", commodityCode='"
|
||||
+ commodityCode + '\'' + ", count=" + count + ", money=" + money + '}';
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Random;
|
||||
|
||||
import com.alibaba.fescar.core.context.RootContext;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.PreparedStatementCreator;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
import org.springframework.jdbc.support.KeyHolder;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@RestController
|
||||
public class OrderController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);
|
||||
private static final String SUCCESS = "SUCCESS";
|
||||
private static final String FAIL = "FAIL";
|
||||
private static final String USER_ID = "U100001";
|
||||
private static final String COMMODITY_CODE = "C00321";
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final RestTemplate restTemplate;
|
||||
private Random random;
|
||||
|
||||
public OrderController(JdbcTemplate jdbcTemplate, RestTemplate restTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.restTemplate = restTemplate;
|
||||
this.random = new Random();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/order", method = RequestMethod.POST, produces = "application/json")
|
||||
public String order(String userId, String commodityCode, int orderCount) {
|
||||
LOGGER.info("Order Service Begin ... xid: " + RootContext.getXID());
|
||||
|
||||
int orderMoney = calculate(commodityCode, orderCount);
|
||||
|
||||
invokerAccountService(orderMoney);
|
||||
|
||||
final Order order = new Order();
|
||||
order.userId = userId;
|
||||
order.commodityCode = commodityCode;
|
||||
order.count = orderCount;
|
||||
order.money = orderMoney;
|
||||
|
||||
KeyHolder keyHolder = new GeneratedKeyHolder();
|
||||
|
||||
int result = jdbcTemplate.update(new PreparedStatementCreator() {
|
||||
|
||||
@Override
|
||||
public PreparedStatement createPreparedStatement(Connection con)
|
||||
throws SQLException {
|
||||
PreparedStatement pst = con.prepareStatement(
|
||||
"insert into order_tbl (user_id, commodity_code, count, money) values (?, ?, ?, ?)",
|
||||
PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
pst.setObject(1, order.userId);
|
||||
pst.setObject(2, order.commodityCode);
|
||||
pst.setObject(3, order.count);
|
||||
pst.setObject(4, order.money);
|
||||
return pst;
|
||||
}
|
||||
}, keyHolder);
|
||||
|
||||
order.id = (long) keyHolder.getKey();
|
||||
|
||||
if (random.nextBoolean()) {
|
||||
throw new RuntimeException("this is a mock Exception");
|
||||
}
|
||||
|
||||
LOGGER.info("Order Service End ... Created " + order);
|
||||
|
||||
if (result == 1) {
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
private int calculate(String commodityId, int orderCount) {
|
||||
return 2 * orderCount;
|
||||
}
|
||||
|
||||
private void invokerAccountService(int orderMoney) {
|
||||
String url = "http://127.0.0.1:18084/account";
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
map.add("userId", USER_ID);
|
||||
map.add("money", orderMoney + "");
|
||||
|
||||
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(
|
||||
map, headers);
|
||||
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, request,
|
||||
String.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
transport {
|
||||
# tcp udt unix-domain-socket
|
||||
type = "TCP"
|
||||
#NIO NATIVE
|
||||
server = "NIO"
|
||||
#thread factory for netty
|
||||
thread-factory {
|
||||
boss-thread-prefix = "NettyBoss"
|
||||
worker-thread-prefix = "NettyServerNIOWorker"
|
||||
server-executor-thread-prefix = "NettyServerBizHandler"
|
||||
share-boss-worker = false
|
||||
client-selector-thread-prefix = "NettyClientSelector"
|
||||
client-selector-thread-size = 1
|
||||
client-worker-thread-prefix = "NettyClientWorkerThread"
|
||||
# netty boss thread size,will not be used for UDT
|
||||
boss-thread-size = 1
|
||||
#auto default pin or 8
|
||||
worker-thread-size = 8
|
||||
}
|
||||
}
|
||||
service {
|
||||
#vgroup->rgroup
|
||||
vgroup_mapping.order-service-fescar-service-group = "localRgroup"
|
||||
#only support single node
|
||||
localRgroup.grouplist = "127.0.0.1:8091"
|
||||
#degrade current not support
|
||||
enableDegrade = false
|
||||
#disable
|
||||
disable = false
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
spring.application.name=order-service
|
||||
server.port=18083
|
||||
|
||||
mysql.server.ip=127.0.0.1
|
||||
mysql.server.port=3306
|
||||
mysql.db.name=demo
|
||||
|
||||
mysql.user.name=xxxxx
|
||||
mysql.user.password=xxxxx
|
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>0.2.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>storage-service</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.1.10</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<check/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.alibaba.fescar.rm.datasource.DataSourceProxy;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@Configuration
|
||||
public class DatabaseConfiguration {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public DatabaseConfiguration(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Bean(initMethod = "init", destroyMethod = "close")
|
||||
public DruidDataSource storageDataSource() throws SQLException {
|
||||
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
|
||||
String ip = environment.getProperty("mysql.server.ip");
|
||||
String port = environment.getProperty("mysql.server.port");
|
||||
String dbName = environment.getProperty("mysql.db.name");
|
||||
|
||||
String userName = environment.getProperty("mysql.user.name");
|
||||
String password = environment.getProperty("mysql.user.password");
|
||||
|
||||
DruidDataSource druidDataSource = new DruidDataSource();
|
||||
druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
|
||||
druidDataSource.setUsername(userName);
|
||||
druidDataSource.setPassword(password);
|
||||
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
|
||||
druidDataSource.setInitialSize(0);
|
||||
druidDataSource.setMaxActive(180);
|
||||
druidDataSource.setMaxWait(60000);
|
||||
druidDataSource.setMinIdle(0);
|
||||
druidDataSource.setValidationQuery("Select 'x' from DUAL");
|
||||
druidDataSource.setTestOnBorrow(false);
|
||||
druidDataSource.setTestOnReturn(false);
|
||||
druidDataSource.setTestWhileIdle(true);
|
||||
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
|
||||
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
|
||||
druidDataSource.setRemoveAbandoned(true);
|
||||
druidDataSource.setRemoveAbandonedTimeout(1800);
|
||||
druidDataSource.setLogAbandoned(true);
|
||||
druidDataSource.setFilters("mergeStat");
|
||||
return druidDataSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
|
||||
return new DataSourceProxy(druidDataSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
|
||||
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
|
||||
|
||||
jdbcTemplate.update("delete from storage_tbl where commodity_code = 'C00321'");
|
||||
jdbcTemplate.update(
|
||||
"insert into storage_tbl(commodity_code, count) values ('C00321', 100)");
|
||||
|
||||
return jdbcTemplate;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class StorageApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(StorageApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.cloud.examples;
|
||||
|
||||
import com.alibaba.fescar.core.context.RootContext;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@RestController
|
||||
public class StorageController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StorageController.class);
|
||||
|
||||
private static final String SUCCESS = "SUCCESS";
|
||||
private static final String FAIL = "FAIL";
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public StorageController(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/storage/{commodityCode}/{count}", method = RequestMethod.GET, produces = "application/json")
|
||||
public String echo(@PathVariable String commodityCode, @PathVariable int count) {
|
||||
LOGGER.info("Storage Service Begin ... xid: " + RootContext.getXID());
|
||||
int result = jdbcTemplate.update(
|
||||
"update storage_tbl set count = count - ? where commodity_code = ?",
|
||||
new Object[] { count, commodityCode });
|
||||
LOGGER.info("Storage Service End ... ");
|
||||
if (result == 1) {
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
transport {
|
||||
# tcp udt unix-domain-socket
|
||||
type = "TCP"
|
||||
#NIO NATIVE
|
||||
server = "NIO"
|
||||
#thread factory for netty
|
||||
thread-factory {
|
||||
boss-thread-prefix = "NettyBoss"
|
||||
worker-thread-prefix = "NettyServerNIOWorker"
|
||||
server-executor-thread-prefix = "NettyServerBizHandler"
|
||||
share-boss-worker = false
|
||||
client-selector-thread-prefix = "NettyClientSelector"
|
||||
client-selector-thread-size = 1
|
||||
client-worker-thread-prefix = "NettyClientWorkerThread"
|
||||
# netty boss thread size,will not be used for UDT
|
||||
boss-thread-size = 1
|
||||
#auto default pin or 8
|
||||
worker-thread-size = 8
|
||||
}
|
||||
}
|
||||
service {
|
||||
#vgroup->rgroup
|
||||
vgroup_mapping.storage-service-fescar-service-group = "localRgroup"
|
||||
#only support single node
|
||||
localRgroup.grouplist = "127.0.0.1:8091"
|
||||
#degrade current not support
|
||||
enableDegrade = false
|
||||
#disable
|
||||
disable = false
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
spring.application.name=storage-service
|
||||
server.port=18082
|
||||
|
||||
mysql.server.ip=127.0.0.1
|
||||
mysql.server.port=3306
|
||||
mysql.db.name=demo
|
||||
|
||||
mysql.user.name=xxxxx
|
||||
mysql.user.password=xxxxx
|
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba</artifactId>
|
||||
<version>0.2.2.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-fescar</artifactId>
|
||||
<name>Spring Cloud Alibaba Fescar</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fescar</groupId>
|
||||
<artifactId>fescar-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fescar</groupId>
|
||||
<artifactId>fescar-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fescar</groupId>
|
||||
<artifactId>fescar-spring</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fescar</groupId>
|
||||
<artifactId>fescar-rm-datasource</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!--spring boot-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.alibaba.fescar")
|
||||
public class FescarProperties {
|
||||
|
||||
// todo support config Fescar server information
|
||||
|
||||
/**
|
||||
* Fescar tx service group.default is ${spring.application.name}-fescar-service-group.
|
||||
*/
|
||||
private String txServiceGroup;
|
||||
|
||||
public String getTxServiceGroup() {
|
||||
return txServiceGroup;
|
||||
}
|
||||
|
||||
public void setTxServiceGroup(String txServiceGroup) {
|
||||
this.txServiceGroup = txServiceGroup;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar;
|
||||
|
||||
import com.alibaba.fescar.spring.annotation.GlobalTransactionScanner;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(FescarProperties.class)
|
||||
public class GlobalTransactionAutoConfiguration {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final FescarProperties fescarProperties;
|
||||
|
||||
public GlobalTransactionAutoConfiguration(ApplicationContext applicationContext,
|
||||
FescarProperties fescarProperties) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.fescarProperties = fescarProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GlobalTransactionScanner globalTransactionScanner() {
|
||||
|
||||
String applicationName = applicationContext.getEnvironment()
|
||||
.getProperty("spring.application.name");
|
||||
|
||||
String txServiceGroup = fescarProperties.getTxServiceGroup();
|
||||
|
||||
if (StringUtils.isEmpty(txServiceGroup)) {
|
||||
txServiceGroup = applicationName + "-fescar-service-group";
|
||||
fescarProperties.setTxServiceGroup(txServiceGroup);
|
||||
}
|
||||
|
||||
return new GlobalTransactionScanner(applicationName, txServiceGroup);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
final class FescarBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final FescarFeignObjectWrapper fescarFeignObjectWrapper;
|
||||
|
||||
FescarBeanPostProcessor(FescarFeignObjectWrapper fescarFeignObjectWrapper) {
|
||||
this.fescarFeignObjectWrapper = fescarFeignObjectWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return this.fescarFeignObjectWrapper.wrap(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
public class FescarContextBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
private FescarFeignObjectWrapper fescarFeignObjectWrapper;
|
||||
|
||||
FescarContextBeanPostProcessor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof FeignContext && !(bean instanceof FescarFeignContext)) {
|
||||
return new FescarFeignContext(getFescarFeignObjectWrapper(),
|
||||
(FeignContext) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
private FescarFeignObjectWrapper getFescarFeignObjectWrapper() {
|
||||
if (this.fescarFeignObjectWrapper == null) {
|
||||
this.fescarFeignObjectWrapper = this.beanFactory
|
||||
.getBean(FescarFeignObjectWrapper.class);
|
||||
}
|
||||
return this.fescarFeignObjectWrapper;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
import feign.Feign;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
final class FescarFeignBuilder {
|
||||
|
||||
private FescarFeignBuilder() {
|
||||
}
|
||||
|
||||
static Feign.Builder builder(BeanFactory beanFactory) {
|
||||
return Feign.builder().client(new FescarFeignClient(beanFactory));
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fescar.core.context.RootContext;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
public class FescarFeignClient implements Client {
|
||||
|
||||
private final Client delegate;
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
FescarFeignClient(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.delegate = new Client.Default(null, null);
|
||||
}
|
||||
|
||||
FescarFeignClient(BeanFactory beanFactory, Client delegate) {
|
||||
this.delegate = delegate;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
|
||||
Request modifiedRequest = getModifyRequest(request);
|
||||
|
||||
try {
|
||||
return this.delegate.execute(modifiedRequest, options);
|
||||
}
|
||||
finally {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Request getModifyRequest(Request request) {
|
||||
|
||||
String xid = RootContext.getXID();
|
||||
|
||||
if (StringUtils.isEmpty(xid)) {
|
||||
return request;
|
||||
}
|
||||
|
||||
Map<String, Collection<String>> headers = new HashMap<>();
|
||||
headers.putAll(request.headers());
|
||||
|
||||
List<String> fescarXid = new ArrayList<>();
|
||||
fescarXid.add(xid);
|
||||
headers.put(RootContext.KEY_XID, fescarXid);
|
||||
|
||||
return Request.create(request.method(), request.url(), headers, request.body(),
|
||||
request.charset());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Client.class)
|
||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||
public class FescarFeignClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnClass(name = "com.netflix.hystrix.HystrixCommand")
|
||||
@ConditionalOnProperty(name = "feign.hystrix.enabled", havingValue = "true")
|
||||
Feign.Builder feignHystrixBuilder(BeanFactory beanFactory) {
|
||||
return FescarHystrixFeignBuilder.builder(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnClass(name = "com.alibaba.csp.sentinel.SphU")
|
||||
@ConditionalOnProperty(name = "feign.sentinel.enabled", havingValue = "true")
|
||||
Feign.Builder feignSentinelBuilder(BeanFactory beanFactory) {
|
||||
return FescarSentinelFeignBuilder.builder(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Scope("prototype")
|
||||
Feign.Builder feignBuilder(BeanFactory beanFactory) {
|
||||
return FescarFeignBuilder.builder(beanFactory);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class FeignBeanPostProcessorConfiguration {
|
||||
|
||||
@Bean
|
||||
FescarBeanPostProcessor fescarBeanPostProcessor(
|
||||
FescarFeignObjectWrapper fescarFeignObjectWrapper) {
|
||||
return new FescarBeanPostProcessor(fescarFeignObjectWrapper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
FescarContextBeanPostProcessor fescarContextBeanPostProcessor(
|
||||
BeanFactory beanFactory) {
|
||||
return new FescarContextBeanPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
FescarFeignObjectWrapper fescarFeignObjectWrapper(BeanFactory beanFactory) {
|
||||
return new FescarFeignObjectWrapper(beanFactory);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Client;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xiaojing
|
||||
*/
|
||||
public class FescarFeignContext extends FeignContext {
|
||||
|
||||
private final FescarFeignObjectWrapper fescarFeignObjectWrapper;
|
||||
private final FeignContext delegate;
|
||||
|
||||
FescarFeignContext(FescarFeignObjectWrapper fescarFeignObjectWrapper,
|
||||
FeignContext delegate) {
|
||||
this.fescarFeignObjectWrapper = fescarFeignObjectWrapper;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInstance(String name, Class<T> type) {
|
||||
T object = this.delegate.getInstance(name, type);
|
||||
if (object instanceof Client) {
|
||||
return object;
|
||||
}
|
||||
return (T) this.fescarFeignObjectWrapper.wrap(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Map<String, T> getInstances(String name, Class<T> type) {
|
||||
Map<String, T> instances = this.delegate.getInstances(name, type);
|
||||
if (instances == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, T> convertedInstances = new HashMap<>();
|
||||
for (Map.Entry<String, T> entry : instances.entrySet()) {
|
||||
if (entry.getValue() instanceof Client) {
|
||||
convertedInstances.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
else {
|
||||
convertedInstances.put(entry.getKey(),
|
||||
(T) this.fescarFeignObjectWrapper.wrap(entry.getValue()));
|
||||
}
|
||||
}
|
||||
return convertedInstances;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import feign.Client;
|
||||
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
public class FescarFeignObjectWrapper {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private CachingSpringLoadBalancerFactory cachingSpringLoadBalancerFactory;
|
||||
private SpringClientFactory springClientFactory;
|
||||
|
||||
FescarFeignObjectWrapper(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
Object wrap(Object bean) {
|
||||
if (bean instanceof Client && !(bean instanceof FescarFeignClient)) {
|
||||
if (bean instanceof LoadBalancerFeignClient) {
|
||||
LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
|
||||
return new FescarLoadBalancerFeignClient(client.getDelegate(), factory(),
|
||||
clientFactory(), this.beanFactory);
|
||||
}
|
||||
return new FescarFeignClient(this.beanFactory, (Client) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
CachingSpringLoadBalancerFactory factory() {
|
||||
if (this.cachingSpringLoadBalancerFactory == null) {
|
||||
this.cachingSpringLoadBalancerFactory = this.beanFactory
|
||||
.getBean(CachingSpringLoadBalancerFactory.class);
|
||||
}
|
||||
return this.cachingSpringLoadBalancerFactory;
|
||||
}
|
||||
|
||||
SpringClientFactory clientFactory() {
|
||||
if (this.springClientFactory == null) {
|
||||
this.springClientFactory = this.beanFactory
|
||||
.getBean(SpringClientFactory.class);
|
||||
}
|
||||
return this.springClientFactory;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import feign.Retryer;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
import feign.Feign;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
final class FescarHystrixFeignBuilder {
|
||||
|
||||
private FescarHystrixFeignBuilder() {
|
||||
}
|
||||
|
||||
static Feign.Builder builder(BeanFactory beanFactory) {
|
||||
return HystrixFeign.builder().retryer(Retryer.NEVER_RETRY)
|
||||
.client(new FescarFeignClient(beanFactory));
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
public class FescarLoadBalancerFeignClient extends LoadBalancerFeignClient {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
FescarLoadBalancerFeignClient(Client delegate,
|
||||
CachingSpringLoadBalancerFactory lbClientFactory,
|
||||
SpringClientFactory clientFactory, BeanFactory beanFactory) {
|
||||
super(wrap(delegate, beanFactory), lbClientFactory, clientFactory);
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
return super.execute(request, options);
|
||||
}
|
||||
|
||||
private static Client wrap(Client delegate, BeanFactory beanFactory) {
|
||||
return (Client) new FescarFeignObjectWrapper(beanFactory).wrap(delegate);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.Retryer;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
|
||||
import org.springframework.cloud.alibaba.sentinel.feign.SentinelFeign;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
final class FescarSentinelFeignBuilder {
|
||||
|
||||
private FescarSentinelFeignBuilder() {
|
||||
}
|
||||
|
||||
static Feign.Builder builder(BeanFactory beanFactory) {
|
||||
return SentinelFeign.builder().retryer(Retryer.NEVER_RETRY)
|
||||
.client(new FescarFeignClient(beanFactory));
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign.hystrix;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(HystrixCommand.class)
|
||||
public class FescarHystrixAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
FescarHystrixConcurrencyStrategy fescarHystrixConcurrencyStrategy() {
|
||||
return new FescarHystrixConcurrencyStrategy();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.feign.hystrix;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.alibaba.fescar.core.context.RootContext;
|
||||
|
||||
import com.netflix.hystrix.strategy.HystrixPlugins;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
public class FescarHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
|
||||
|
||||
private HystrixConcurrencyStrategy delegate;
|
||||
|
||||
public FescarHystrixConcurrencyStrategy() {
|
||||
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
|
||||
HystrixPlugins.reset();
|
||||
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K> Callable<K> wrapCallable(Callable<K> c) {
|
||||
if (c instanceof FescarContextCallable) {
|
||||
return c;
|
||||
}
|
||||
|
||||
Callable<K> wrappedCallable;
|
||||
if (this.delegate != null) {
|
||||
wrappedCallable = this.delegate.wrapCallable(c);
|
||||
}
|
||||
else {
|
||||
wrappedCallable = c;
|
||||
}
|
||||
if (wrappedCallable instanceof FescarContextCallable) {
|
||||
return wrappedCallable;
|
||||
}
|
||||
|
||||
return new FescarContextCallable<>(wrappedCallable);
|
||||
}
|
||||
|
||||
private static class FescarContextCallable<K> implements Callable<K> {
|
||||
|
||||
private final Callable<K> actual;
|
||||
private final String xid;
|
||||
|
||||
FescarContextCallable(Callable<K> actual) {
|
||||
this.actual = actual;
|
||||
this.xid = RootContext.getXID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K call() throws Exception {
|
||||
try {
|
||||
RootContext.bind(xid);
|
||||
return actual.call();
|
||||
}
|
||||
finally {
|
||||
RootContext.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.rest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class FescarRestTemplateAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FescarRestTemplateInterceptor fescarRestTemplateInterceptor() {
|
||||
return new FescarRestTemplateInterceptor();
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
private Collection<RestTemplate> restTemplates;
|
||||
|
||||
@Autowired
|
||||
private FescarRestTemplateInterceptor fescarRestTemplateInterceptor;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (this.restTemplates != null) {
|
||||
for (RestTemplate restTemplate : restTemplates) {
|
||||
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>(
|
||||
restTemplate.getInterceptors());
|
||||
interceptors.add(this.fescarRestTemplateInterceptor);
|
||||
restTemplate.setInterceptors(interceptors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.alibaba.fescar.core.context.RootContext;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.support.HttpRequestWrapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
public class FescarRestTemplateInterceptor implements ClientHttpRequestInterceptor {
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
|
||||
ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
|
||||
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
|
||||
|
||||
String xid = RootContext.getXID();
|
||||
|
||||
if (!StringUtils.isEmpty(xid)) {
|
||||
requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
|
||||
}
|
||||
return clientHttpRequestExecution.execute(requestWrapper, bytes);
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.alibaba.fescar.core.context.RootContext;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*
|
||||
* Fescar HandlerInterceptor, Convert Fescar information into
|
||||
* @see com.alibaba.fescar.core.context.RootContext from http request's header in
|
||||
* {@link org.springframework.web.servlet.HandlerInterceptor#preHandle(HttpServletRequest , HttpServletResponse , Object )},
|
||||
* And clean up Fescar information after servlet method invocation in
|
||||
* {@link org.springframework.web.servlet.HandlerInterceptor#afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)}
|
||||
*/
|
||||
public class FescarHandlerInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(FescarHandlerInterceptor.class);
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
|
||||
String xid = RootContext.getXID();
|
||||
String rpcXid = request.getHeader(RootContext.KEY_XID);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
|
||||
}
|
||||
|
||||
if (xid == null && rpcXid != null) {
|
||||
RootContext.bind(rpcXid);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("bind {} to RootContext", rpcXid);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception e) throws Exception {
|
||||
|
||||
String rpcXid = request.getHeader(RootContext.KEY_XID);
|
||||
|
||||
if (StringUtils.isEmpty(rpcXid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String unbindXid = RootContext.unbind();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("unbind {} from RootContext", unbindXid);
|
||||
}
|
||||
if (!rpcXid.equalsIgnoreCase(unbindXid)) {
|
||||
log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
|
||||
if (unbindXid != null) {
|
||||
RootContext.bind(unbindXid);
|
||||
log.warn("bind {} back to RootContext", unbindXid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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.fescar.web;
|
||||
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
public class FescarHandlerInterceptorConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new FescarHandlerInterceptor()).addPathPatterns("/**");
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.alibaba.fescar.rest.FescarRestTemplateAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.fescar.web.FescarHandlerInterceptorConfiguration,\
|
||||
org.springframework.cloud.alibaba.fescar.GlobalTransactionAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.fescar.feign.FescarFeignClientAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.fescar.feign.hystrix.FescarHystrixAutoConfiguration
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue