Add LoadBalancer WebFlux sample

See gh-114
This commit is contained in:
Olga Maciaszek-Sharma
2022-09-12 18:18:42 +02:00
committed by Andy Wilkinson
parent 9ea77ab8e1
commit 9e3970e46c
12 changed files with 285 additions and 0 deletions

View File

@@ -29,6 +29,7 @@ groups:
- cloud-discovery-zookeeper
- cloud-function-web
- cloud-function-webflux
- cloud-loadbalancing-webflux
- cloud-stream-kafka
- cloud-stream-rabbit
- cloud-task

View File

@@ -0,0 +1,39 @@
plugins {
id 'java'
id 'org.springframework.boot'
id 'org.springframework.aot.smoke-test'
id 'org.graalvm.buildtools.native'
}
ext {
set('springCloudVersion', "2022.0.0-SNAPSHOT")
}
dependencies {
implementation(platform(
org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
implementation(
platform("org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"))
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.cloud:spring-cloud-starter-loadbalancer")
testImplementation("org.springframework.boot:spring-boot-starter-test")
aotSmokeTestImplementation(project(":aot-smoke-test-support"))
aotSmokeTestImplementation("org.awaitility:awaitility:4.2.0")
}
aotSmokeTest {
webApplication = true
}
graalvmNative {
binaries {
main {
buildArgs.add("--exclude-config")
buildArgs.add("/netty-.*")
buildArgs.add("META-INF/native-image/.*")
}
}
}

View File

@@ -0,0 +1,10 @@
version: '3'
services:
test-service:
image: springcloud/test-service:latest
ports:
- "8081"
demo-service:
image: springcloud/demo-service:latest
ports:
- "8082"

View File

@@ -0,0 +1,29 @@
package com.example.cloud.loadbalancing;
import java.time.Duration;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
import org.springframework.aot.smoketest.support.assertj.AssertableOutput;
import org.springframework.aot.smoketest.support.junit.AotSmokeTest;
import static org.assertj.core.api.Assertions.assertThat;
@AotSmokeTest
public class CloudLoadBalancerClientWebFluxApplicationAotTests {
@Test
void shouldRetrieveInstance(AssertableOutput output) {
Awaitility.await().atMost(Duration.ofSeconds(30)).untilAsserted(() -> {
assertThat(output).hasLineContaining("On Start: ");
assertThat(output).hasLineContaining("On Start Request: ");
assertThat(output).hasLineContaining("On Complete: ");
assertThat(output).hasLineContaining("c.e.c.l.s.LoadBalancerClientTestService : demo");
assertThat(output).hasLineContaining("c.e.c.l.s.LoadBalancerClientTestService : test");
assertThat(output).hasNoLinesContaining("ERROR");
});
}
}

View File

@@ -0,0 +1,37 @@
package com.example.cloud.loadbalancing;
import com.example.cloud.loadbalancing.service.LoadBalancerClientTestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
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 CloudLoadBalancerClientWebFluxApplication implements CommandLineRunner {
@Autowired
LoadBalancerClientTestService testService;
public static void main(String[] args) {
SpringApplication.run(CloudLoadBalancerClientWebFluxApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
testService.callServices();
}
@RestController
static class TestController {
@GetMapping("/")
public String test() {
return "test";
}
}
}

View File

@@ -0,0 +1,24 @@
package com.example.cloud.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<String> demo() {
return webClient.get().uri(URI.create("http://demo-service")).retrieve().bodyToMono(String.class);
}
}

View File

@@ -0,0 +1,24 @@
package com.example.cloud.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<String> test() {
return webClient.get().uri(URI.create("http://test-service")).retrieve().bodyToMono(String.class);
}
}

View File

@@ -0,0 +1,47 @@
package com.example.cloud.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<Object, Object, ServiceInstance> loadBalancerLifecycle() {
return new TestLoadBalancerLifecycle();
}
static class TestLoadBalancerLifecycle implements LoadBalancerLifecycle<Object, Object, ServiceInstance> {
private static final Log LOG = LogFactory.getLog(TestLoadBalancerLifecycle.class);
@Override
public void onStart(Request<Object> request) {
if (LOG.isInfoEnabled()) {
LOG.info("On Start: " + request);
}
}
@Override
public void onStartRequest(Request<Object> request, Response<ServiceInstance> lbResponse) {
if (LOG.isInfoEnabled()) {
LOG.info("On Start Request: " + request + ", LB response: " + lbResponse);
}
}
@Override
public void onComplete(CompletionContext<Object, ServiceInstance, Object> completionContext) {
if (LOG.isInfoEnabled()) {
LOG.info("On Complete: " + completionContext);
}
}
}
}

View File

@@ -0,0 +1,19 @@
package com.example.cloud.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();
}
}

View File

@@ -0,0 +1,38 @@
package com.example.cloud.loadbalancing.service;
import com.example.cloud.loadbalancing.client.DemoServiceClient;
import com.example.cloud.loadbalancing.client.TestServiceClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;
@Component
public class LoadBalancerClientTestService {
private static final Log LOG = LogFactory.getLog(LoadBalancerClientTestService.class);
private final TestServiceClient testServiceClient;
private final DemoServiceClient demoServiceClient;
public LoadBalancerClientTestService(TestServiceClient testServiceClient, DemoServiceClient demoServiceClient) {
this.testServiceClient = testServiceClient;
this.demoServiceClient = demoServiceClient;
}
public void callServices() {
testServiceClient.test().subscribe(responseBody -> {
if (LOG.isInfoEnabled()) {
LOG.info(responseBody);
}
});
demoServiceClient.demo().subscribe(responseBody -> {
if (LOG.isInfoEnabled()) {
LOG.info(responseBody);
}
});
}
}

View File

@@ -0,0 +1,16 @@
spring:
application:
name: loadbalancer-client
cloud:
loadbalancer:
eager-load:
clients:
- demo-service
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}

View File

@@ -44,6 +44,7 @@ include "cloud-discovery-consul"
include "cloud-discovery-zookeeper"
include "cloud-function-web"
include "cloud-function-webflux"
include 'cloud-loadbalancing-webflux'
include "cloud-stream-kafka"
include "cloud-stream-rabbit"
include "cloud-task"