问题描述
我正在尝试使用spring-cloud(Finchley.SR1)进行spring-boot(2.0.5),并尝试使用Eureka作为发现服务器并使用Feign/Ribbon作为客户端来设置两个服务之间的通信.设置非常简单(尽管由于各种原因和我尝试过的其他答案而有些困惑):
Eureka的application.yml
spring:
application:
name: eureka-service
server:
port: 8761
eureka:
instance:
hostname: localhost
preferIpAddress: true
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
第二项服务的bootstrap.yml
spring:
application:
name: secondservice
eureka:
instance:
hostname: ${spring.application.name}
preferIpAddress: true
instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
statusPageUrlPath: ${server.servlet.context-path}/actuator/info
healthCheckUrlPath: ${server.servlet.context-path}/actuator/health
leaseRenewalIntervalInSeconds: 15
leaseExpirationDurationInSeconds: 45
metadata-map:
server.servlet.context-path: ${server.servlet.context-path}
client:
enabled: true
serviceUrl:
defaultZone: http://localhost:8761/eureka
我的测试/模板服务的bootstrap.yml
spring:
application:
name: templateservice
eureka:
instance:
hostname: ${spring.application.name}
preferIpAddress: true
instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
statusPageUrlPath: ${server.servlet.context-path}/actuator/info
healthCheckUrlPath: ${server.servlet.context-path}/actuator/health
leaseRenewalIntervalInSeconds: 15
leaseExpirationDurationInSeconds: 45
metadata-map:
server.servlet.context-path: ${server.servlet.context-path}
client:
enabled: true
serviceUrl:
defaultZone: http://localhost:8761/eureka
logging:
level:
com...MessageServiceClient: DEBUG
我的假客户
@FeignClient(name = "secondservice", configuration = FeignConfig.class)
public interface MessageServiceClient {
@RequestMapping(method = RequestMethod.GET, value = "/dummy")
public String getMessage();
}
我的服务班级:
@Autowired MessageServiceClient messageServiceClient;
@Autowired private LoadBalancerClient loadBalancer;
public String getDummyMessage() {
ServiceInstance instance = loadBalancer.choose("secondservice");
URI secondServiceUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
System.out.println(secondServiceUri); // logs http://192.168.0.205:8090, check log below
return messageServiceClient.getMessage(); // throws 404??
}
在FeignConfig中,唯一要做的就是将日志级别设置为FULL.日志如下所示:
2018-10-08 11:14:59.511 INFO [templateservice,,,] 16801 --- [onPool-worker-2] s.c.a.AnnotationConfigApplicationContext : Refreshing SpringClientFactory-secondservice: startup date [Mon Oct 08 11:14:59 IST 2018]; parent: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@451f35ad
2018-10-08 11:14:59.683 INFO [templateservice,,,] 16801 --- [onPool-worker-2] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2018-10-08 11:15:00.042 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.netflix.config.ChainedDynamicProperty : Flipping property: secondservice.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-10-08 11:15:00.095 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-secondservice
2018-10-08 11:15:00.146 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.netflix.loadbalancer.BaseLoadBalancer : Client: secondservice instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=secondservice,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2018-10-08 11:15:00.189 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2018-10-08 11:15:00.287 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.netflix.config.ChainedDynamicProperty : Flipping property: secondservice.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-10-08 11:15:00.291 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client secondservice initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=secondservice,current list of Servers=[192.168.0.205:8090, 192.168.0.205:8090],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:192.168.0.205:8090; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 05:30:00 IST 1970; First connection made: Thu Jan 01 05:30:00 IST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@3625959e
http://192.168.0.205:8090
2018-10-08 11:15:01.215 INFO [templateservice,,,] 16801 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: secondservice.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-10-08 11:15:07.951 DEBUG [templateservice,,,] 16801 --- [onPool-worker-2] c.e.m.t.clients.MessageServiceClient : [MessageServiceClient#getMessage] ---> GET http://secondservice/dummy HTTP/1.1
2018-10-08 11:15:12.527 DEBUG [templateservice,,,] 16801 --- [onPool-worker-2] c.e.m.t.clients.MessageServiceClient : [MessageServiceClient#getMessage] <--- HTTP/1.1 404 (4575ms)
2018-10-08 11:15:12.559 ERROR [templateservice,7004692c56b2e643,7004692c56b2e643,false] 16801 --- [nio-8080-exec-4] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
问题是,这将引发404,这当然是因为它试图访问的URL是http://secondservice/dummy
,并且没有这样的东西.它可能会帮助您注意,如果我在FeignClient上设置url
,它会起作用,但是Eureka有什么意义呢?另外,当这可行时,客户会自动使用上下文路径吗?还是我必须在客户端的每个网址中都指定它?
更新1 :响应: http://localhost:8761/eureka/apps/secondservice
找到了!它与发现或通常与config无关,这是因为feign不支持上下文路径!
为了尝试哑巴",我继续将每一个配置删除到最低限度,以保持服务正常运行.当我删除第二个服务的上下文路径时,它突然工作了.如果Feign + Ribbon由其他服务设置,则不支持自定义上下文路径.这是一个旧错误,仍未修复. >
有两种可能的解决方案:
- 删除上下文路径.
- 在您的Feign客户中添加上下文路径.因此,基本上,您的Feign客户将成为:
//这需要在这里,以便下面的格式正确
@FeignClient(name = "secondservice/secondservice", configuration = FeignConfig.class)
public interface MessageServiceClient {
@RequestMapping(method = RequestMethod.GET, value = "/dummy")
public String getMessage();
}
我个人不喜欢任何一种解决方案.我喜欢使用上下文路径,以便为url提供上下文,它随上下文而变得不言自明.但这是其他服务(secondservice)的属性,应由该服务选择/更改.因此,不应在依赖服务中对其进行硬编码.我希望它得到支持,但与此同时,我要去:
@FeignClient(name = "${dependencies.secondservice.url}")
public interface MessageServiceClient {....}
在application.properties中:dependencies.secondservice.url=secondservice/secondservice
.这清楚表明该属性归依赖项所有,而不是此服务所有.
更多注意事项: 1.我可以将请求跟踪到SynchronousMethodHandler#executeAndDecode
,response = client.execute(request, options);
.直到此处,URL都已解析为注释. 2.正在记录的url:GET http://secondservice/secondservice/dummy
实际上是正确的URL,第一个secondservice
字符串在log语句后被替换为IP.以下是支持该文档的文档: https ://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#_using_ribbon .注意传递给restTemplate的URL.这就是引发寻找替代原因的原因.
I am trying spring-boot (2.0.5) with spring-cloud (Finchley.SR1) and trying to setup communication between two services using Eureka as discovery server and Feign/Ribbon as clients. The setup is quite straightforward (although a bit muddied by the various things and other answers I tried):
Eureka's application.yml
spring:
application:
name: eureka-service
server:
port: 8761
eureka:
instance:
hostname: localhost
preferIpAddress: true
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
second service's bootstrap.yml
spring:
application:
name: secondservice
eureka:
instance:
hostname: ${spring.application.name}
preferIpAddress: true
instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
statusPageUrlPath: ${server.servlet.context-path}/actuator/info
healthCheckUrlPath: ${server.servlet.context-path}/actuator/health
leaseRenewalIntervalInSeconds: 15
leaseExpirationDurationInSeconds: 45
metadata-map:
server.servlet.context-path: ${server.servlet.context-path}
client:
enabled: true
serviceUrl:
defaultZone: http://localhost:8761/eureka
my test/template service's bootstrap.yml
spring:
application:
name: templateservice
eureka:
instance:
hostname: ${spring.application.name}
preferIpAddress: true
instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
statusPageUrlPath: ${server.servlet.context-path}/actuator/info
healthCheckUrlPath: ${server.servlet.context-path}/actuator/health
leaseRenewalIntervalInSeconds: 15
leaseExpirationDurationInSeconds: 45
metadata-map:
server.servlet.context-path: ${server.servlet.context-path}
client:
enabled: true
serviceUrl:
defaultZone: http://localhost:8761/eureka
logging:
level:
com...MessageServiceClient: DEBUG
My Feign client
@FeignClient(name = "secondservice", configuration = FeignConfig.class)
public interface MessageServiceClient {
@RequestMapping(method = RequestMethod.GET, value = "/dummy")
public String getMessage();
}
My Service class:
@Autowired MessageServiceClient messageServiceClient;
@Autowired private LoadBalancerClient loadBalancer;
public String getDummyMessage() {
ServiceInstance instance = loadBalancer.choose("secondservice");
URI secondServiceUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
System.out.println(secondServiceUri); // logs http://192.168.0.205:8090, check log below
return messageServiceClient.getMessage(); // throws 404??
}
In FeignConfig, only thing being done is setting log level to FULL. The log looks like this:
2018-10-08 11:14:59.511 INFO [templateservice,,,] 16801 --- [onPool-worker-2] s.c.a.AnnotationConfigApplicationContext : Refreshing SpringClientFactory-secondservice: startup date [Mon Oct 08 11:14:59 IST 2018]; parent: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@451f35ad
2018-10-08 11:14:59.683 INFO [templateservice,,,] 16801 --- [onPool-worker-2] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2018-10-08 11:15:00.042 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.netflix.config.ChainedDynamicProperty : Flipping property: secondservice.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-10-08 11:15:00.095 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-secondservice
2018-10-08 11:15:00.146 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.netflix.loadbalancer.BaseLoadBalancer : Client: secondservice instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=secondservice,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2018-10-08 11:15:00.189 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2018-10-08 11:15:00.287 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.netflix.config.ChainedDynamicProperty : Flipping property: secondservice.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-10-08 11:15:00.291 INFO [templateservice,,,] 16801 --- [onPool-worker-2] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client secondservice initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=secondservice,current list of Servers=[192.168.0.205:8090, 192.168.0.205:8090],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:192.168.0.205:8090; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 05:30:00 IST 1970; First connection made: Thu Jan 01 05:30:00 IST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@3625959e
http://192.168.0.205:8090
2018-10-08 11:15:01.215 INFO [templateservice,,,] 16801 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: secondservice.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-10-08 11:15:07.951 DEBUG [templateservice,,,] 16801 --- [onPool-worker-2] c.e.m.t.clients.MessageServiceClient : [MessageServiceClient#getMessage] ---> GET http://secondservice/dummy HTTP/1.1
2018-10-08 11:15:12.527 DEBUG [templateservice,,,] 16801 --- [onPool-worker-2] c.e.m.t.clients.MessageServiceClient : [MessageServiceClient#getMessage] <--- HTTP/1.1 404 (4575ms)
2018-10-08 11:15:12.559 ERROR [templateservice,7004692c56b2e643,7004692c56b2e643,false] 16801 --- [nio-8080-exec-4] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
The issue is, this throws a 404, of course cause the url it is trying to hit is http://secondservice/dummy
, and there is no such thing. It might help noting, that if I set url
on the FeignClient, it works, but then what is the point of Eureka?Also, for when this works, would fiegn clients automatically use the context path? or I have to specify it in every url on client?
UPDATE 1: Response to: http://localhost:8761/eureka/apps/secondservice
Found it! Its nothing to do with discovery, or config in general, it is because feign does not support context path!
In an attempt to 'dumb' down, I went on removing every single config to bare-minimum to keep the services up. It suddenly worked when I removed the context path of the second service. Thing is Feign+Ribbon do not support custom context path if set by other service. This is an old bug, still not fixed.
There are two possible solutions:
- Remove context path.
- Add context path in your Feign clients. So basically your Feign client becomes:
// This needs to be here for the formatting below to be right
@FeignClient(name = "secondservice/secondservice", configuration = FeignConfig.class)
public interface MessageServiceClient {
@RequestMapping(method = RequestMethod.GET, value = "/dummy")
public String getMessage();
}
I personally don't like either of the solutions. I like to have context path, well, to give context to a url, it becomes self-explanatory with a context. But it is a property of the other service (secondservice) and should be chosen/changed by that service. And so should not be hardcoded in dependent services. I would have liked it to be supported, but meanwhile, I am going to go for:
@FeignClient(name = "${dependencies.secondservice.url}")
public interface MessageServiceClient {....}
And in application.properties: dependencies.secondservice.url=secondservice/secondservice
.This makes it clear that the property is owned by the dependency and not by this service.
Few more notes: 1. I could trace the request to SynchronousMethodHandler#executeAndDecode
, response = client.execute(request, options);
. Till here the url is note resolved. 2. The url being logged: GET http://secondservice/secondservice/dummy
is in fact the correct URL, the first secondservice
string gets replaced with IP after the log statement. Here is the documentation supporting that: https://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#_using_ribbon. Notice the url passed to restTemplate. This is what triggered the search for alternative causes.
这篇关于Spring Boot 2 + Feign + Eureka客户端无法将服务名称解析为URL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!