@LoadBalanced RestTemplate 调用嵌套上下文端点

@LoadBalanced RestTemplate to call nested context endpoints

下面的指南页面很棒,可以作为 spring 启动应用程序中功能区的基本案例。

https://spring.io/guides/gs/client-side-load-balancing/

一旦端点映射嵌套,该示例就会停止工作 - 例如添加

@RequestMapping(value = "/welcome")

class 级别

@RestController
@SpringBootApplication
@RequestMapping(value = "/welcome") //<------------- ADDED --->
public class SayHelloApplication {

  private static Logger log = LoggerFactory.getLogger(SayHelloApplication.class);

  @RequestMapping(value = "/greeting")
  public String greet() {

然后将客户端中的@LoadBalanced RestTemplate调用改为

String greeting = this.restTemplate.getForObject("http://say-hello/greeting", String.class);

String greeting = this.restTemplate.getForObject("http://say-hello/welcome/greeting", String.class);

调用因附加的堆栈跟踪而失败,而直接访问 http://localhost:8090/welcome/greeting 仍然可以正常工作。什么是配置功能区以负载平衡请求到长和嵌套 URL 端点(例如域)的合适方法。com/x/y/z/p/q?

堆栈跟踪:

java.lang.IllegalStateException: No instances available for say-hello
    at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:79) ~[spring-cloud-netflix-core-1.1.4.RELEASE.jar:1.1.4.RELEASE]
    at org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor.intercept(LoadBalancerInterceptor.java:46) ~[spring-cloud-commons-1.1.1.RELEASE.jar:1.1.1.RELEASE]
    at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:85) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:69) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:596) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:264) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at hello.UserApplication.hi(UserApplication.java:31) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]

问题是通过将 @RequestMapping 添加到 class,您还更改了 / 处理程序以从根移动到 /welcome/。为了使负载平衡器能够继续工作,您必须更新在用户应用程序的 SayHelloConfiguration 中使用的 PingUrl。做到 new PingUrl(false, "/welcome/")

我还使用这个示例应用程序开始使用 Ribbon,它很棒。

为了说清楚,我想在设计上多花点时间:

  1. “/complete/user”文件夹下的用户应用程序是 “client”应用程序,我们可以通过“curl 访问它 http://{host}:8888"; 而 say-hello 应用程序,在 “/complete/say-hello”文件夹,就是“服务商”。作为 按照示例的指示,我们应该通过以下方式启动 3 个实例 {host}:8090, {host}:9092 and {host}:9999 -- 我们可以看看 /complete/user/src/main/resources/application.yml看看;
  2. Ribbon,内嵌在"client" user应用中,会维护一系列的负载均衡服务实例(如果我们像上面提到的那样启动实例,这里会是3个)通过默认的 Ping 策略,它将通过调用特定的 URL 定期 ping 服务实例。默认情况下是“/”,正如我们在这里看到的代码(同样,也可以通过指定 URI 进行配置): @Bean public IPing ribbonPing(IClientConfig config) { return new PingUrl(); } 现在,让我们回到您的问题。

通过显式添加 @RequestMapping(value = "/welcome") 更改 SayHelloApplication.java 中的 URI 映射后,

中“/”的映射
@RequestMapping(value = "/")
public String home() {
  log.info("Access /");
  return "Hi!";
}

将表示“/welcome”下的根路径是“/welcome/”,而不是 say-hello 应用程序的“/”。

那么我们没有真正的“/”的任何映射,例如,'http://{host}:8090/'。在这种情况下,Ping 将一个接一个地失败,最终 Ribbon 会将所有服务实例标记为不健康,因此您最终会得到 "No instances available for say-hello".