繼第一次搭建springcloud環境踩坑以後,時隔三個月,第二次踩坑記錄也跟着上線了,SpringCloudConfig坑系列。第一次踩坑讓我理解了用戶線程和守護線程這一塊的知識盲點,此次踩的坑就是基本就是配置上的坑。可是多踩踩坑會讓咱們更容易理解具體配置起到了什麼樣的做用。html
出現此錯誤能夠檢查一下如下幾點:git
spring.cloud.config.discovery.service-id
配置是否和服務名能對應上。eureka.client.fetch-registry
是否爲true(其實默認值就是true,防止手賤誤操做)。回顧下錯誤發生點:github
public List<ServiceInstance> getConfigServerInstances(String serviceId) {
logger.debug("Locating configserver (" + serviceId + ") via discovery");
List<ServiceInstance> instances = this.client.getInstances(serviceId);
if (instances.isEmpty()) {
throw new IllegalStateException(
"No instances found of configserver (" + serviceId + ")");
}
logger.debug("Located configserver (" + serviceId
+ ") via discovery. No of instances found: " + instances.size());
return instances;
}
複製代碼
從上方代碼能夠看出,在this.client.getInstances(serviceId)
獲取到實例爲空的時候會拋出此異常,一步步追蹤一下,發現最終會調用到DiscoveryClient.getInstancesByVipAddress()
方法。web
public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure,
@Nullable String region) {
if (vipAddress == null) {
throw new IllegalArgumentException(
"Supplied VIP Address cannot be null");
}
Applications applications;
if (instanceRegionChecker.isLocalRegion(region)) {
applications = this.localRegionApps.get();
} else {
applications = remoteRegionVsApps.get(region);
if (null == applications) {
logger.debug("No applications are defined for region {}, so returning an empty instance list for vip "
+ "address {}.", region, vipAddress);
return Collections.emptyList();
}
}
if (!secure) {
return applications.getInstancesByVirtualHostName(vipAddress);
} else {
return applications.getInstancesBySecureVirtualHostName(vipAddress);
}
}
複製代碼
從這裏能夠明顯看出,要麼applications
爲空,即註冊中心沒有可用服務或者eureka.client.fetch-registry
配置成了false;要麼經過vipAddress
在applications
查詢不出實例結果,即給定的service-id
在註冊中心中不存在。spring
①註冊中心沒有可用服務,獲取不到服務列表很容易理解。
②service-id
對應不上,也很容易理解。就好比拿一個不存在的key
去一個collection
中獲取value
,確定是獲取不到服務的。
③eureka.client.fetch-registry
配置成了false,這一點須要解釋一下:bootstrap
要知道我們內存中存儲的applications
列表並非每次請求都會進行刷新,而是維護了一個CacheRefreshThread
去定時輪詢獲取註冊中心中的服務,而後塞到localRegionApps
中,然而,這個線程開啓須要一個條件,clientConfig.shouldFetchRegistry()==true
,看方法名就知道須要eureka.client.fetch-registry=true
任務纔會開啓。可是默認這個值就是true,當時不曉得是否是腦子抽風了配置成了false,而後找這個bug迷糊了好一下子。具體開啓任務線程的代碼以下所示:springboot
private void initScheduledTasks() {
if (clientConfig.shouldFetchRegistry()) {
// registry cache refresh timer
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
scheduler.schedule(
new TimedSupervisorTask(
"cacheRefresh",
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
),
registryFetchIntervalSeconds, TimeUnit.SECONDS);
}
...
}
複製代碼
訪問ip:port/actuator/refresh返回404。在搭建的過程當中,不少老版本的教程都只是說引入下方依賴便可。bash
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
複製代碼
但在springboot 2.x以上的版本,默認只對health
和info
這兩個端點進行暴露出來,以下圖所示。app
而對refresh
端點並未暴露出來,這裏就須要我們本身去手動配置暴露,感興趣的朋友能夠去Endpoints看一下具體有哪些能夠暴露的端點,我們也可使用 management.endpoints.web.exposure.include=*
將全部端點所有暴露出來,固然,實際生產環境中也不建議如此。目前我測試配置management.endpoints.web.exposure.include=refresh,info,health
暴露了refresh,info,health三個端點。spring-boot
注意:
使用refresh端點時,它只會針對有@RefreshScope註解的類和方法進行刷新。
訪問這些端點時都須要加上actuator這個basePath。
最後附上config-server端和config-client端的bootstrap.yml配置。
server端:
spring:
cloud:
config:
server:
git:
uri: https://github.com/crazyStrongboy/config/
searchPaths: foo
application:
name: myserver
server:
port: 8003
eureka:
instance:
hostname: TTT-HJ
instance-id: ${spring.application.name}:${server.port}
client:
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:8000/eureka/
複製代碼
client端:
spring:
application:
name: application
cloud:
config:
discovery:
service-id: myserver
enabled: true
profile: dev
server:
port: 8004
eureka:
instance:
hostname: TTT-HJ
instance-id: ${spring.application.name}:${server.port}
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:8000/eureka/
fetch-registry: true
management:
endpoints:
web:
exposure:
include: refresh,info,health
複製代碼
目前僅僅只是簡單的測試一下springcloud config註冊中心,後續會加上springcloud bus消息總線安排一下,看看還有木有坑點繼續分享~~,具體案例見github。