diff --git a/STATUS.adoc b/STATUS.adoc index d2168eb..f20b04d 100644 --- a/STATUS.adoc +++ b/STATUS.adoc @@ -47,6 +47,11 @@ h|test |image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-cr-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-cr-app-test] |image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/context-refresh-http-test] +|loadbalancing +|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/loadbalancing-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/loadbalancing-app-test] +|image:https://ci.spring.io/api/v1/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/loadbalancing-cr-app-test/badge[link=https://ci.spring.io/teams/spring-checkpoint-restore-smoke-tests/pipelines/spring-checkpoint-restore-smoke-tests-3.2.x/jobs/loadbalancing-cr-app-test] +| + |=== == Data diff --git a/ci/smoke-tests.yml b/ci/smoke-tests.yml index ecc8981..b8d011d 100644 --- a/ci/smoke-tests.yml +++ b/ci/smoke-tests.yml @@ -18,6 +18,9 @@ groups: - name: context-refresh-http app_test: true test: true + - name: loadbalancing + app_test: true + test: false - name: data smoke_tests: - name: data-jdbc diff --git a/cloud/loadbalancing/build.gradle b/cloud/loadbalancing/build.gradle new file mode 100644 index 0000000..66428ea --- /dev/null +++ b/cloud/loadbalancing/build.gradle @@ -0,0 +1,26 @@ +plugins { + id "java" + id "org.springframework.boot" + id "org.springframework.cr.smoke-test" +} + +// Not using Spring Cloud bom due to existing issues with dependency management +ext { + set('springCloudCommonsVerions', "4.1.0-SNAPSHOT") +} + +dependencies { + implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + implementation("org.springframework.boot:spring-boot-starter-webflux") + implementation("org.crac:crac:$cracVersion") + implementation(project(":cr-listener")) + implementation("org.springframework.cloud:spring-cloud-starter-loadbalancer:${springCloudCommonsVerions}") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + + appTestImplementation(project(":cr-smoke-test-support")) +} + +crSmokeTest { + webApplication = true +} diff --git a/cloud/loadbalancing/docker-compose.yml b/cloud/loadbalancing/docker-compose.yml new file mode 100644 index 0000000..a3c7224 --- /dev/null +++ b/cloud/loadbalancing/docker-compose.yml @@ -0,0 +1,9 @@ +services: + test-service: + image: springcloud/test-service:latest + ports: + - "8081" + demo-service: + image: springcloud/demo-service:latest + ports: + - "8082" diff --git a/cloud/loadbalancing/src/appTest/java/com/example/loadbalancing/LoadBalancerApplicationTests.java b/cloud/loadbalancing/src/appTest/java/com/example/loadbalancing/LoadBalancerApplicationTests.java new file mode 100644 index 0000000..f84abe2 --- /dev/null +++ b/cloud/loadbalancing/src/appTest/java/com/example/loadbalancing/LoadBalancerApplicationTests.java @@ -0,0 +1,23 @@ +package com.example.loadbalancing; + +import org.junit.jupiter.api.Test; + +import org.springframework.cr.smoketest.support.junit.ApplicationTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.assertj.core.api.Assertions.assertThat; + +@ApplicationTest +class LoadBalancerApplicationTests { + + @Test + void loadBalancing(WebTestClient webClient) { + webClient.get() + .exchange() + .expectStatus() + .isOk() + .expectBody() + .consumeWith(result -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("testdemo")); + } + +} diff --git a/cloud/loadbalancing/src/main/java/com/example/loadbalancing/LoadBalancerApplication.java b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/LoadBalancerApplication.java new file mode 100644 index 0000000..d6a585e --- /dev/null +++ b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/LoadBalancerApplication.java @@ -0,0 +1,32 @@ +package com.example.loadbalancing; + +import com.example.loadbalancing.service.LoadBalancerClientTestService; +import reactor.core.publisher.Flux; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +public class LoadBalancerApplication { + + public static void main(String[] args) { + SpringApplication.run(LoadBalancerApplication.class, args); + } + + @RestController + static class TestController { + + @Autowired + private LoadBalancerClientTestService testService; + + @GetMapping("/") + public Flux test() { + return testService.callServices(); + } + + } + +} diff --git a/cloud/loadbalancing/src/main/java/com/example/loadbalancing/client/DemoServiceClient.java b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/client/DemoServiceClient.java new file mode 100644 index 0000000..08c4f93 --- /dev/null +++ b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/client/DemoServiceClient.java @@ -0,0 +1,24 @@ +package com.example.loadbalancing.client; + +import java.net.URI; + +import reactor.core.publisher.Mono; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Component +public class DemoServiceClient { + + private final WebClient webClient; + + public DemoServiceClient(@LoadBalanced WebClient.Builder builder) { + this.webClient = builder.build(); + } + + public Mono demo() { + return webClient.get().uri(URI.create("http://demo-service")).retrieve().bodyToMono(String.class); + } + +} diff --git a/cloud/loadbalancing/src/main/java/com/example/loadbalancing/client/TestServiceClient.java b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/client/TestServiceClient.java new file mode 100644 index 0000000..421e4d6 --- /dev/null +++ b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/client/TestServiceClient.java @@ -0,0 +1,24 @@ +package com.example.loadbalancing.client; + +import java.net.URI; + +import reactor.core.publisher.Mono; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Component +public class TestServiceClient { + + private final WebClient webClient; + + public TestServiceClient(@LoadBalanced WebClient.Builder builder) { + this.webClient = builder.build(); + } + + public Mono test() { + return webClient.get().uri(URI.create("http://test-service")).retrieve().bodyToMono(String.class); + } + +} diff --git a/cloud/loadbalancing/src/main/java/com/example/loadbalancing/config/CustomLoadBalancerConfiguration.java b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/config/CustomLoadBalancerConfiguration.java new file mode 100644 index 0000000..4e305ce --- /dev/null +++ b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/config/CustomLoadBalancerConfiguration.java @@ -0,0 +1,47 @@ +package com.example.loadbalancing.config; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.CompletionContext; +import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle; +import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.context.annotation.Bean; + +public class CustomLoadBalancerConfiguration { + + @Bean + LoadBalancerLifecycle loadBalancerLifecycle() { + return new TestLoadBalancerLifecycle(); + } + + static class TestLoadBalancerLifecycle implements LoadBalancerLifecycle { + + private static final Log LOG = LogFactory.getLog(TestLoadBalancerLifecycle.class); + + @Override + public void onStart(Request request) { + if (LOG.isInfoEnabled()) { + LOG.info("On Start: " + request); + } + } + + @Override + public void onStartRequest(Request request, Response lbResponse) { + if (LOG.isInfoEnabled()) { + LOG.info("On Start Request: " + request + ", LB response: " + lbResponse); + } + } + + @Override + public void onComplete(CompletionContext completionContext) { + if (LOG.isInfoEnabled()) { + LOG.info("On Complete: " + completionContext); + } + } + + } + +} diff --git a/cloud/loadbalancing/src/main/java/com/example/loadbalancing/config/WebClientConfig.java b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/config/WebClientConfig.java new file mode 100644 index 0000000..acb7a2e --- /dev/null +++ b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/config/WebClientConfig.java @@ -0,0 +1,19 @@ +package com.example.loadbalancing.config; + +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +@LoadBalancerClient(name = "test-service", configuration = CustomLoadBalancerConfiguration.class) +public class WebClientConfig { + + @LoadBalanced + @Bean + WebClient.Builder webClientBuilder() { + return WebClient.builder(); + } + +} diff --git a/cloud/loadbalancing/src/main/java/com/example/loadbalancing/service/LoadBalancerClientTestService.java b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/service/LoadBalancerClientTestService.java new file mode 100644 index 0000000..7b2b596 --- /dev/null +++ b/cloud/loadbalancing/src/main/java/com/example/loadbalancing/service/LoadBalancerClientTestService.java @@ -0,0 +1,26 @@ +package com.example.loadbalancing.service; + +import com.example.loadbalancing.client.DemoServiceClient; +import com.example.loadbalancing.client.TestServiceClient; +import reactor.core.publisher.Flux; + +import org.springframework.stereotype.Component; + +@Component +public class LoadBalancerClientTestService { + + private final TestServiceClient testServiceClient; + + private final DemoServiceClient demoServiceClient; + + public LoadBalancerClientTestService(TestServiceClient testServiceClient, DemoServiceClient demoServiceClient) { + this.testServiceClient = testServiceClient; + this.demoServiceClient = demoServiceClient; + + } + + public Flux callServices() { + return testServiceClient.test().concatWith(demoServiceClient.demo()); + } + +} diff --git a/cloud/loadbalancing/src/main/resources/application.yml b/cloud/loadbalancing/src/main/resources/application.yml new file mode 100644 index 0000000..0873c7c --- /dev/null +++ b/cloud/loadbalancing/src/main/resources/application.yml @@ -0,0 +1,12 @@ +spring: + application: + name: loadbalancer-client + cloud: + discovery: + client: + simple: + instances: + test-service: + - uri: http://${TEST-SERVICE_HOST:localhost}:${TEST-SERVICE_PORT_8081:8081} + demo-service: + - uri: http://${DEMO-SERVICE_HOST:localhost}:${DEMO-SERVICE_PORT_8082:8082}