現象:
?
從這里可以看到是當前服務在調用product service服務是出現了連接拒絕connection refused
走讀一下原始代碼:
可以看到請求是由FeignClient代理發出的?,但問題在于為什么Feign請求的時候會產生connection refused錯誤?
上面的日志又沒有給出詳細信息。我們需要看到feign請求網址的完整信息。
解決辦法:新建一個配置類
import feign.Logger; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration public class FeignConfig {
? ? ? ? ?@Bean
? ? ? ? ? ?Logger.Level feignLoggerLevel() {
? ? ? ? ? ?return Logger.Level.FULL; // 設置Feign日志級別為FULL
? ? }
}
然后把這個配置類寫到@FeignClient注解里的configration里面
@FeignClient(name = "your-service", configuration = FeignConfig.class)
public interface YourServiceClient { // Feign客戶端的方法定義 }
這樣配置后,Feign將以Full日志級別打印詳細的HTTP請求和響應信息,包括完整的URL
因為我的@FeignClient注解里面的configration里面已經有配置類了,所以我把上面的那個bean直接追加到那個配置類里面:
然后再來查看日志果然發現:feign請求的地址有問題:竟然請求到當前自身ip 127.0.0.1了,
難怪會連接拒絕。
?2023-11-21 02:14:07.336 ?INFO 1 --- [oduct-service-1] c.n.l.DynamicServerListLoadBalancer ? ? ?: DynamicServerListLoadBalancer for client product-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=product-service,current list of Servers=[127.0.0.1:8081],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; ?Instance count:1; ? ? ? Active connections count: 0; ? ? ?Circuit breaker tripped count: 0; ? ? ? Active connections per server: 0.0;]
},Server stats: [[Server:127.0.0.1:8081; ? ? ? ?Zone:defaultZone; ? ? ? Total Requests:0; ? ? ? Successive connection failure:0; ?Total blackout seconds:0; ? ? ? Last connection made:Thu Jan 01 00:00:00 GMT 1970; ? ? ?First connection made: Thu Jan 01 00:00:00 GMT 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]?
看了一下springboot配置文件
原來是ip-address設置在作怪,因為它主動上報eureka自身的ip地址為127.0.0.1, 那其他服務調用該服務時從eureka拿到的地址就會是127.0.0.1, 從而向自身發起調用,這樣必須會產生連接拒絕錯誤(因為你本來就是跨服務調用,自身怎么會有你需要的服務)
注意:?不要手動設置ip-address,而應該讓Eureka自動獲取。這樣,服務將以實際的網絡地址注冊到Eureka,Feign在調用時將獲得正確的服務地址
然后去掉這個ip-address配置以后,仍然報錯:java.net.UnknownHostException: product-service-deploy-674d77cffd-4rj4m
?2023-11-21 04:57:26.847 ERROR 1 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] ? ?: Servlet.service() for servlet [dispatcherServlet] in co ?ntext with path [] threw exception [Request processing failed; nested exception is com.netflix.hystrix.exception.HystrixRuntimeException: ProductService ?Api#findProductsByCategory(Integer) failed and no fallback available.] with root cause
java.net.UnknownHostException: product-service-deploy-674d77cffd-4rj4m
這回看起來feign請求的時候沒有請求ip , 但是解析到k8s的pod ip了。
為什么會這樣?
使用下面的api查看一下product-service在eureka中注冊的詳細信息:
${eureka-IP}:port/eukeka/apps/${server-name}
?像下面這樣: 這里可以很清晰的看到produc-service在eureka中的詳細信息,
特別是hostName和ipAddr, 從上面的情況來看,應該是eureka返回給服務調用方的是被調用服務的hostName而不是ip
?然后同時測試一下服務需求方是否能正常從k8s的內網ip ping通被調用服務,也就是測試服務之間的網絡連通性。
?可以看到兩個服務之間網絡聯通正常,然后從下圖可以看到在宿主機上也可以通過內網ip和端口訪問到該服務。
問題還是出現在eureka在springboot的配置中
eureka的client注冊到server時默認是使用hostname而不是ip,這就導致client在多臺機器時,服務間相互調用時也會使用hostname進行調用,從而調用失敗。
?為解決這個問題,eureka必須配置prefer-ip-address: true, 要求優先使用ip地址向eureka注冊。
那么最后調用方從eureka拿到的就是ip地址了而不是hostname.
為什么會這樣?因為eureka源碼就是這樣定義的(EurekaInstanceConfigBean.class),如下所示
?
加上prefer-ip-address以后,再通過eureka api查詢上述服務的信息,發現之前的hostName也變成了ip地址:
把上面的prefer-ip-address: true配置加上去以后,以為問題解決了, 但是又報錯如下:
?大概意思請求失敗,沒有fallback降級服務,但是我明明有寫降級服務啊:
后來發現原來是application沒有配置熔斷降級,加上去就可以了:
feign:hystrix:enabled: true #啟用熔斷降級
?最后問題終于解決,沒再報錯了。
總結一下解決辦法:
1. springboot eureka配置中不要手動設置ip-address,此配置必須去掉!
2.?springboot eureka必須配置prefer-ip-address: true
3.? 當feign調用報錯信息不明時,應設置Feign日志級別為FULL,查看詳細的服務調用信息,包括請求url等,這樣有利于診斷故障