initial sidecar implementation
This commit is contained in:
15
pom.xml
15
pom.xml
@@ -31,6 +31,7 @@
|
||||
<module>spring-cloud-netflix-eureka-server</module>
|
||||
<module>spring-cloud-netflix-turbine</module>
|
||||
<module>spring-cloud-netflix-zuul-server</module>
|
||||
<module>spring-cloud-netflix-sidecar</module>
|
||||
<module>docs</module>
|
||||
</modules>
|
||||
|
||||
@@ -263,6 +264,20 @@
|
||||
<turbine.version>1.0.0</turbine.version>
|
||||
<zuul.version>1.0.28</zuul.version>
|
||||
<netflix.rxjava.version>0.20.7</netflix.rxjava.version>
|
||||
<java.version>1.7</java.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,6 +2,8 @@ package org.springframework.cloud.client.discovery;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@@ -10,4 +12,6 @@ public interface DiscoveryClient {
|
||||
* @return ServiceInstance with information used to register the local service
|
||||
*/
|
||||
public ServiceInstance getLocalServiceInstance();
|
||||
|
||||
public List<ServiceInstance> getInstances(String serviceId);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.collect.Iterables.*;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@@ -12,6 +20,9 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
|
||||
@Autowired
|
||||
private EurekaInstanceConfigBean config;
|
||||
|
||||
@Autowired
|
||||
private com.netflix.discovery.DiscoveryClient discovery;
|
||||
|
||||
@Override
|
||||
public ServiceInstance getLocalServiceInstance() {
|
||||
return new ServiceInstance() {
|
||||
@@ -31,4 +42,40 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServiceInstance> getInstances(String serviceId) {
|
||||
List<InstanceInfo> infos = discovery.getInstancesByVipAddress(serviceId, false);
|
||||
Iterable<ServiceInstance> instances = transform(infos, new Function<InstanceInfo, ServiceInstance>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public ServiceInstance apply(@Nullable InstanceInfo info) {
|
||||
return new EurekaServiceInstance(info);
|
||||
}
|
||||
});
|
||||
return Lists.newArrayList(instances);
|
||||
}
|
||||
|
||||
static class EurekaServiceInstance implements ServiceInstance {
|
||||
InstanceInfo instance;
|
||||
|
||||
EurekaServiceInstance(InstanceInfo instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return instance.getAppName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return instance.getHostName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return instance.getPort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
77
spring-cloud-netflix-sidecar/pom.xml
Normal file
77
spring-cloud-netflix-sidecar/pom.xml
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-cloud-netflix-sidecar</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Netflix Sidecar</name>
|
||||
<url>http://projects.spring.io/spring-cloud/</url>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-javanica</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-eureka</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.zuul</groupId>
|
||||
<artifactId>zuul-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<!-- Only needed at compile time -->
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@EnableHystrix
|
||||
@EnableEurekaClient
|
||||
@EnableZuulProxy
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(SidecarConfiguration.class)
|
||||
public @interface EnableSidecar {
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class LocalApplicationHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
@Autowired
|
||||
SidecarProperties properties;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
URI uri = properties.getLocalHealthUri();
|
||||
if (uri == null) {
|
||||
builder.up();
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> map = new RestTemplate().getForObject(uri, Map.class);
|
||||
Object status = map.get("status");
|
||||
|
||||
if (status != null && status instanceof String) {
|
||||
builder.status(status.toString());
|
||||
} else if (status != null && status instanceof Map) {
|
||||
Map<String, Object> statusMap = (Map<String, Object>) status;
|
||||
Object code = statusMap.get("code");
|
||||
if (code != null) {
|
||||
builder.status(code.toString());
|
||||
} else {
|
||||
getWarning(builder);
|
||||
}
|
||||
} else {
|
||||
getWarning(builder);
|
||||
}
|
||||
}
|
||||
|
||||
private Health.Builder getWarning(Health.Builder builder) {
|
||||
return builder.unknown().withDetail("warning", "no status field in response");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnExpression("${sidecar.enabled:true}")
|
||||
public class SidecarConfiguration {
|
||||
|
||||
@Bean
|
||||
public SidecarProperties sidecarProperties() {
|
||||
return new SidecarProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocalApplicationHealthIndicator localApplicationHealthIndicator() {
|
||||
return new LocalApplicationHealthIndicator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SidecarController sidecarController() {
|
||||
return new SidecarController();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RestController
|
||||
public class SidecarController {
|
||||
|
||||
@Autowired
|
||||
DiscoveryClient discovery;
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
String appName;
|
||||
|
||||
@RequestMapping("/ping")
|
||||
public String ping() {
|
||||
return "OK";
|
||||
}
|
||||
|
||||
@RequestMapping("/hosts")
|
||||
public List<ServiceInstance> hosts(@RequestParam("appName") String appName) {
|
||||
List<ServiceInstance> instances = discovery.getInstances(appName);
|
||||
return instances;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/", produces = "text/html")
|
||||
public String home() {
|
||||
return "<head><title>Sidecar</title></head><body>\n" +
|
||||
"<a href='/ping'>ping</a><br/>\n" +
|
||||
"<a href='/health'>health</a><br/>\n" +
|
||||
"<a href='/hosts?appName="+appName+"'>hosts?appName="+appName+"</a><br/>\n" +
|
||||
"</body>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("sidecar")
|
||||
public class SidecarProperties {
|
||||
private URI localHealthUri;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableSidecar
|
||||
@RestController
|
||||
public class SidecarApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SidecarApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.cloud.netflix.sidecar;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = SidecarApplication.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest("server.port=0")
|
||||
public class SidecarApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
server:
|
||||
port: 5678
|
||||
spring:
|
||||
application:
|
||||
name: sidecarTest
|
||||
|
||||
sidecar:
|
||||
local-health-uri: http://localhost:8081/health
|
||||
|
||||
zuul:
|
||||
proxy:
|
||||
route:
|
||||
stores: /stores
|
||||
customers: /customers
|
||||
|
||||
Reference in New Issue
Block a user