-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support spring cloud loadbalancer (#28)
* support spring cloud loadbalancer * update docs * add SPRING_RETRY_PRESENT * upgrade plugins version * upgrade gradle 8.5 * update ci
- Loading branch information
1 parent
1ae3b45
commit 7843f44
Showing
20 changed files
with
487 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
dependencies { | ||
implementation("org.springframework.boot:spring-boot-starter-web") | ||
implementation(project(":starters:httpexchange-spring-boot-starter")) | ||
implementation("org.springframework.cloud:spring-cloud-starter-loadbalancer") | ||
|
||
implementation("org.springframework:spring-webflux") | ||
implementation("org.springframework.retry:spring-retry") | ||
|
||
testImplementation("org.springframework.boot:spring-boot-starter-test") | ||
} |
24 changes: 24 additions & 0 deletions
24
examples/load-balancer/src/main/java/com/example/LoadBalancerApp.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.example; | ||
|
||
import io.github.danielliu1123.httpexchange.EnableExchangeClients; | ||
import java.util.List; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.boot.builder.SpringApplicationBuilder; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@SpringBootApplication | ||
@EnableExchangeClients | ||
@RestController | ||
public class LoadBalancerApp implements UserApi { | ||
|
||
public static void main(String[] args) { | ||
new SpringApplicationBuilder(LoadBalancerApp.class) | ||
.properties("server.port=0") | ||
.run(args); | ||
} | ||
|
||
@Override | ||
public UserApi.UserDTO getById(String id) { | ||
return new UserApi.UserDTO(id, "Freeman", List.of("Coding", "Reading")); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
examples/load-balancer/src/main/java/com/example/UserApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.example; | ||
|
||
import java.util.List; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.service.annotation.GetExchange; | ||
import org.springframework.web.service.annotation.HttpExchange; | ||
|
||
@HttpExchange("/user") | ||
public interface UserApi { | ||
record UserDTO(String id, String name, List<String> hobbies) {} | ||
|
||
@GetExchange("/getById/{id}") | ||
UserDTO getById(@PathVariable("id") String id); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
spring: | ||
application: | ||
name: load-balancer | ||
cloud: | ||
discovery: | ||
client: | ||
simple: | ||
instances: | ||
user: | ||
- host: localhost | ||
port: ${server.port} | ||
- host: localhost | ||
port: ${random.int(50000,60000)} # Simulate an unavailable instance | ||
http-exchange: | ||
channels: | ||
- base-url: user # service id | ||
clients: | ||
- com.example.*Api |
150 changes: 150 additions & 0 deletions
150
examples/load-balancer/src/test/java/com/example/LoadBalancerAppTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package com.example; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import java.net.ServerSocket; | ||
import lombok.SneakyThrows; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
import org.springframework.boot.builder.SpringApplicationBuilder; | ||
|
||
/** | ||
* Spring Cloud load balancer is based on Spring Retry or Reactor. | ||
* | ||
* <p> If using sync clients, the retry depends on Spring Retry. | ||
* <p> If using async clients, the retry depends on Reactor. | ||
*/ | ||
class LoadBalancerAppTests { | ||
|
||
/** | ||
* When using sync clients, the retry depends on Spring Retry. | ||
*/ | ||
@ParameterizedTest | ||
@ValueSource(strings = {"rest_client", "rest_template"}) | ||
void testLoadBalancer_whenRetryDependsOnSpringRetry_thenAllRequestOK(String clientType) { | ||
int port = getRandomPort(); | ||
|
||
var ctx = new SpringApplicationBuilder(LoadBalancerApp.class) | ||
.properties("server.port=" + port) | ||
.properties("http-exchange.client-type=" + clientType) | ||
.run(); | ||
|
||
UserApi userApi = ctx.getBean(UserApi.class); | ||
|
||
int success = 0; | ||
int failure = 0; | ||
for (int i = 0; i < 4; i++) { | ||
try { | ||
userApi.getById("1"); | ||
success++; | ||
} catch (Exception e) { | ||
failure++; | ||
} | ||
} | ||
|
||
assertThat(success).isEqualTo(4); | ||
assertThat(failure).isZero(); | ||
|
||
ctx.close(); | ||
} | ||
|
||
/** | ||
* When using async clients, the retry depends on Reactor. | ||
*/ | ||
@ParameterizedTest | ||
@ValueSource(strings = {"web_client"}) | ||
void testLoadBalancer_whenRetryDependsOnReactor_thenAllRequestOK(String clientType) { | ||
int port = getRandomPort(); | ||
|
||
var ctx = new SpringApplicationBuilder(LoadBalancerApp.class) | ||
.properties("server.port=" + port) | ||
.properties("http-exchange.client-type=" + clientType) | ||
.properties("spring.cloud.loadbalancer.retry.enabled=true") | ||
.run(); | ||
|
||
UserApi userApi = ctx.getBean(UserApi.class); | ||
|
||
int success = 0; | ||
int failure = 0; | ||
for (int i = 0; i < 4; i++) { | ||
try { | ||
userApi.getById("1"); | ||
success++; | ||
} catch (Exception e) { | ||
failure++; | ||
} | ||
} | ||
|
||
assertThat(success).isEqualTo(4); | ||
assertThat(failure).isZero(); | ||
|
||
ctx.close(); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = {"rest_client", "rest_template", "web_client"}) | ||
void testLoadBalancer_whenDisableRetry_thenHalfOKHalfFailed(String clientType) { | ||
int port = getRandomPort(); | ||
|
||
var ctx = new SpringApplicationBuilder(LoadBalancerApp.class) | ||
.properties("server.port=" + port) | ||
.properties("http-exchange.client-type=" + clientType) | ||
.properties("spring.cloud.loadbalancer.retry.enabled=false") | ||
.run(); | ||
|
||
UserApi userApi = ctx.getBean(UserApi.class); | ||
|
||
int success = 0; | ||
int failure = 0; | ||
for (int i = 0; i < 4; i++) { | ||
try { | ||
userApi.getById("1"); | ||
success++; | ||
} catch (Exception e) { | ||
failure++; | ||
} | ||
} | ||
|
||
assertThat(success).isEqualTo(2); | ||
assertThat(failure).isEqualTo(2); | ||
|
||
ctx.close(); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = {"rest_client", "rest_template", "web_client"}) | ||
void testLoadBalancer_whenDisabled(String clientType) { | ||
int port = getRandomPort(); | ||
|
||
var ctx = new SpringApplicationBuilder(LoadBalancerApp.class) | ||
.properties("server.port=" + port) | ||
.properties("http-exchange.client-type=" + clientType) | ||
.properties("spring.cloud.loadbalancer.enabled=false") | ||
.run(); | ||
|
||
UserApi userApi = ctx.getBean(UserApi.class); | ||
|
||
int success = 0; | ||
int failure = 0; | ||
for (int i = 0; i < 4; i++) { | ||
try { | ||
userApi.getById("1"); | ||
success++; | ||
} catch (Exception e) { | ||
failure++; | ||
} | ||
} | ||
|
||
assertThat(success).isZero(); | ||
assertThat(failure).isEqualTo(4); | ||
|
||
ctx.close(); | ||
} | ||
|
||
@SneakyThrows | ||
private static int getRandomPort() { | ||
try (ServerSocket ss = new ServerSocket(0)) { | ||
return ss.getLocalPort(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.