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

Temporary commit

This commit is contained in:
小马哥 2019-01-14 10:44:11 +08:00
parent 53b677f3b7
commit 7aa17d3760
11 changed files with 309 additions and 52 deletions

View File

@ -37,14 +37,14 @@ import javax.ws.rs.Path;
@Configuration @Configuration
public class DubboRestAutoConfiguration { public class DubboRestAutoConfiguration {
/** // /**
* A Feign Contract bean for JAX-RS if available // * A Feign Contract bean for JAX-RS if available
*/ // */
@ConditionalOnClass(Path.class) // @ConditionalOnClass(Path.class)
@Bean // @Bean
public Contract jaxrs2Contract() { // public Contract jaxrs2Contract() {
return new JAXRS2Contract(); // return new JAXRS2Contract();
} // }
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@ -52,14 +52,14 @@ public class DubboRestAutoConfiguration {
return new ObjectMapper(); return new ObjectMapper();
} }
/** // /**
* A Feign Contract bean for Spring MVC if available // * A Feign Contract bean for Spring MVC if available
*/ // */
@ConditionalOnClass(RequestMapping.class) // @ConditionalOnClass(RequestMapping.class)
@Bean // @Bean
public Contract springMvcContract() { // public Contract springMvcContract() {
return new SpringMvcContract(); // return new SpringMvcContract();
} // }
@Bean @Bean
public RestMetadataResolver metadataJsonResolver(ObjectMapper objectMapper) { public RestMetadataResolver metadataJsonResolver(ObjectMapper objectMapper) {

View File

@ -16,10 +16,23 @@
*/ */
package org.springframework.cloud.alibaba.dubbo.autoconfigure; package org.springframework.cloud.alibaba.dubbo.autoconfigure;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import feign.Client;
import feign.Request;
import feign.Response;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataResolver;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.context.named.NamedContextFactory; import org.springframework.cloud.context.named.NamedContextFactory;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
@ -27,8 +40,18 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/** /**
* The Auto-Configuration class for Dubbo REST Discovery * The Auto-Configuration class for Dubbo REST Discovery
@ -41,28 +64,148 @@ import java.util.Map;
DubboRestMetadataRegistrationAutoConfiguration.class}) DubboRestMetadataRegistrationAutoConfiguration.class})
public class DubboRestDiscoveryAutoConfiguration { public class DubboRestDiscoveryAutoConfiguration {
@Autowired @Autowired
private DiscoveryClient discoveryClient; private DiscoveryClient discoveryClient;
// 1. Get all service names from Spring beans that was annotated by @FeignClient @Autowired
// 2. Get all service instances by echo specified service name private RestMetadataResolver restMetadataResolver;
// 3. Get Rest metadata from service instance
// 4. Resolve REST metadata from the @FeignClient instance @Autowired(required = false)
private ObjectMapper objectMapper = new ObjectMapper();
/**
* Feign Request -> Dubbo ReferenceBean
*/
private Map<String, ReferenceBean> referenceBeanCache = new HashMap<>();
@Autowired
private ApplicationConfig applicationConfig;
@Value("${spring.cloud.nacos.discovery.server-addr}")
private String nacosServerAddress;
private volatile boolean initialized = false;
@Autowired
private ListableBeanFactory beanFactory;
@Scheduled(initialDelay = 10 * 1000, fixedRate = 5000)
public void init() {
if (initialized) {
return;
}
Map<String, NamedContextFactory.Specification> specifications =
beanFactory.getBeansOfType(NamedContextFactory.Specification.class);
ServiceAnnotationBeanPostProcessor
// 1. Get all service names from Spring beans that was annotated by @FeignClient
List<String> serviceNames = new LinkedList<>();
specifications.forEach((beanName, specification) -> {
String serviceName = beanName.substring(0, beanName.indexOf("."));
serviceNames.add(serviceName);
// 2. Get all service instances by echo specified service name
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
if (!serviceInstances.isEmpty()) {
ServiceInstance serviceInstance = serviceInstances.get(0);
// 3. Get Rest metadata from service instance
Map<String, String> metadata = serviceInstance.getMetadata();
// 4. Resolve REST metadata from the @FeignClient instance
String restMetadataJson = metadata.get("restMetadata");
/**
* {
* "providers:org.springframework.cloud.alibaba.dubbo.service.EchoService:1.0.0": [
* "{\"method\":\"POST\",\"url\":\"/plus?a={a}&b={b}\",\"headers\":{}}",
* "{\"method\":\"GET\",\"url\":\"/echo?message={message}\",\"headers\":{}}"
* ]
* }
*/
try {
Map<String, List<String>> restMetadata = objectMapper.readValue(restMetadataJson, Map.class);
restMetadata.forEach((dubboServiceName, restJsons) -> {
restJsons.stream().map(restMetadataResolver::resolveRequest).forEach(request -> {
referenceBeanCache.put(request.toString(), buildReferenceBean(dubboServiceName));
});
});
} catch (IOException e) {
throw new RuntimeException(e);
}
//
}
});
initialized = true;
}
private ReferenceBean buildReferenceBean(String dubboServiceName) {
ReferenceBean referenceBean = new ReferenceBean();
applicationConfig.setName("service-consumer");
referenceBean.setApplication(applicationConfig);
RegistryConfig registryConfig = new RegistryConfig();
// requires dubbo-registry-nacos
registryConfig.setAddress("nacos://" + nacosServerAddress);
referenceBean.setRegistry(registryConfig);
String[] parts = StringUtils.delimitedListToStringArray(dubboServiceName, ":");
referenceBean.setInterface(parts[1]);
referenceBean.setVersion(parts[2]);
referenceBean.setGroup(parts.length > 3 ? parts[3] : null);
referenceBean.get();
return referenceBean;
}
@Bean @Bean
public SmartInitializingSingleton onBeansInitialized(ListableBeanFactory beanFactory) { public BeanPostProcessor wrapClientBeanPostProcessor() {
return () -> { return new BeanPostProcessor() {
Map<String, Object> feignClientBeans = beanFactory.getBeansWithAnnotation(FeignClient.class); public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
feignClientBeans.forEach((beanName, bean) -> { if (bean instanceof Client) {
if (bean instanceof NamedContextFactory.Specification) { Client client = (Client) bean;
NamedContextFactory.Specification specification = (NamedContextFactory.Specification) bean; // wrapper
String serviceName = specification.getName(); return new DubboFeignClientProxy(client);
} }
}); return bean;
}
}; };
} }
class DubboFeignClientProxy implements Client {
private final Client delegate;
DubboFeignClientProxy(Client delegate) {
this.delegate = delegate;
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
ReferenceBean referenceBean = referenceBeanCache.get(request.toString());
if (referenceBean != null) {
Object dubboClient = referenceBean.get();
Method method = null;
Object[] params = null;
try {
Object result = method.invoke(dubboClient, params);
// wrapper as a Response
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return delegate.execute(request, options);
}
}
@EventListener(ContextRefreshedEvent.class) @EventListener(ContextRefreshedEvent.class)
public void onContextRefreshed(ContextRefreshedEvent event) { public void onContextRefreshed(ContextRefreshedEvent event) {

View File

@ -23,17 +23,23 @@ import com.alibaba.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Contract; import feign.Contract;
import feign.jaxrs2.JAXRS2Contract;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataResolver; import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataResolver;
import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent; import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.util.ClassUtils;
import javax.annotation.PostConstruct;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -51,7 +57,7 @@ import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegist
@Configuration @Configuration
@AutoConfigureAfter(value = { @AutoConfigureAfter(value = {
DubboRestAutoConfiguration.class, DubboServiceRegistrationAutoConfiguration.class}) DubboRestAutoConfiguration.class, DubboServiceRegistrationAutoConfiguration.class})
public class DubboRestMetadataRegistrationAutoConfiguration { public class DubboRestMetadataRegistrationAutoConfiguration implements BeanClassLoaderAware {
/** /**
* A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service, * A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service,
@ -60,17 +66,38 @@ public class DubboRestMetadataRegistrationAutoConfiguration {
private final Map<String, Set<String>> restMetadata = new LinkedHashMap<>(); private final Map<String, Set<String>> restMetadata = new LinkedHashMap<>();
/** /**
* Autowire Feign Contract Beans * Feign Contracts
*/ */
@Autowired(required = false)
private Collection<Contract> contracts = Collections.emptyList(); private Collection<Contract> contracts = Collections.emptyList();
private ClassLoader classLoader;
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired @Autowired
private RestMetadataResolver restMetadataResolver; private RestMetadataResolver restMetadataResolver;
@PostConstruct
public void init() {
contracts = initFeignContracts();
}
private Collection<Contract> initFeignContracts() {
Collection<Contract> contracts = new LinkedList<>();
if (ClassUtils.isPresent("javax.ws.rs.Path", classLoader)) {
contracts.add(new JAXRS2Contract());
}
if (ClassUtils.isPresent("org.springframework.web.bind.annotation.RequestMapping", classLoader)) {
contracts.add(new SpringMvcContract());
}
return contracts;
}
@EventListener(ServiceBeanExportedEvent.class) @EventListener(ServiceBeanExportedEvent.class)
public void recordRestMetadata(ServiceBeanExportedEvent event) { public void recordRestMetadata(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean(); ServiceBean serviceBean = event.getServiceBean();
@ -105,4 +132,9 @@ public class DubboRestMetadataRegistrationAutoConfiguration {
String restMetadataJson = objectMapper.writeValueAsString(restMetadata); String restMetadataJson = objectMapper.writeValueAsString(restMetadata);
serviceInstanceMetadata.put("restMetadata", restMetadataJson); serviceInstanceMetadata.put("restMetadata", restMetadataJson);
} }
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
} }

View File

@ -1,3 +1,5 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestAutoConfiguration,\ org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestMetadataRegistrationAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestDiscoveryAutoConfiguration

View File

@ -29,8 +29,16 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.alibaba.dubbo.service.EchoService; import org.springframework.cloud.alibaba.dubbo.service.EchoService;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 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.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
@ -39,18 +47,37 @@ import java.util.List;
*/ */
@EnableDiscoveryClient @EnableDiscoveryClient
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableFeignClients
@EnableScheduling
@RestController
public class DubboSpringCloudBootstrap { public class DubboSpringCloudBootstrap {
@Reference(version = "1.0.0") @Reference(version = "1.0.0")
private EchoService echoService; private EchoService echoService;
@Reference(version = "1.0.0") @Autowired
private EchoService echoServiceForRest; @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 @Bean
public ApplicationRunner applicationRunner() { public ApplicationRunner applicationRunner() {
return arguments -> { return arguments -> {
// Dubbo Service call
System.out.println(echoService.echo("mercyblitz")); System.out.println(echoService.echo("mercyblitz"));
// Spring Cloud Open Feign REST Call
System.out.println(feignEchoService.echo("mercyblitz"));
}; };
} }

View File

@ -43,13 +43,14 @@ import javax.ws.rs.QueryParam;
public class DefaultEchoService implements EchoService { public class DefaultEchoService implements EchoService {
@Override @Override
@GetMapping(value = "/echo", @GetMapping(value = "/echo"
consumes = MediaType.APPLICATION_JSON_VALUE, // consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) // produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
@Path("/echo") @Path("/echo")
@GET @GET
@Consumes("application/json") // @Consumes("application/json")
@Produces("application/json;charset=UTF-8") // @Produces("application/json;charset=UTF-8")
public String echo(@RequestParam @QueryParam("message") String message) { public String echo(@RequestParam @QueryParam("message") String message) {
return RpcContext.getContext().getUrl() + " [echo] : " + message; return RpcContext.getContext().getUrl() + " [echo] : " + message;
} }

View File

@ -10,4 +10,4 @@ dubbo:
port: 9090 port: 9090
server: netty server: netty
registry: registry:
address: spring-cloud://dummy address: spring-cloud://nacos

View File

@ -5,7 +5,6 @@ spring:
nacos: nacos:
discovery: discovery:
server-addr: 127.0.0.1:8848 server-addr: 127.0.0.1:8848
port: 12345
eureka: eureka:
client: client:
enabled: false enabled: false

View File

@ -16,15 +16,31 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency> </dependency>
<!-- Dubbo Nacos registry dependency -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>0.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dubbo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
@ -40,10 +56,10 @@
<artifactId>spring-cloud-starter-openfeign</artifactId> <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> </dependency>
<dependency> <!--<dependency>-->
<groupId>org.springframework.cloud</groupId> <!--<groupId>org.springframework.cloud</groupId>-->
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <!--<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>-->
</dependency> <!--</dependency>-->
</dependencies> </dependencies>

View File

@ -0,0 +1,42 @@
package org.springframework.cloud.alibaba.cloud.examples.demos;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
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;
@EnableAutoConfiguration // 激活自动装配
@EnableDiscoveryClient // 激活服务注册和发现
@EnableFeignClients // 激活 @FeignClients注册
public class SpringCloudRestClientBootstrap {
@FeignClient("spring-cloud-alibaba-dubbo")
public interface FeignEchoService {
@GetMapping(value = "/echo")
String echo(@RequestParam("message") String message);
}
@RestController
public static class EchoServiceController {
@Autowired
private FeignEchoService feignEchoService;
@GetMapping("/call/echo")
public String echo(@RequestParam("message") String message) {
return feignEchoService.echo(message);
}
}
public static void main(String[] args) {
SpringApplication.run(SpringCloudRestClientBootstrap.class, args);
}
}

View File

@ -3,10 +3,5 @@ server.port=18083
management.endpoints.web.exposure.include=* management.endpoints.web.exposure.include=*
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
feign.sentinel.enabled=true dubbo.registry.address= spring-cloud://nacos
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.eager=true
spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json