
前言
繼第一次搭建springcloud環(huán)境踩坑之后,時隔三個月,第二次踩坑記錄也跟著上線了,SpringCloudConfig坑系列。第一次踩坑讓我理解了用戶線程和守護線程這一塊的知識盲點,這次踩的坑就是基本就是配置上的坑。但是多踩踩坑會讓我們更容易理解具體配置起到了什么樣的作用。
坑一:拋出異常 :No instances found of configserver (myserver)
出現(xiàn)此錯誤可以檢查一下以下幾點:
- 需要依賴的config-server服務(wù)myserver(自己注冊的服務(wù)名)是否注冊到了eureka注冊中心上。
- 如果注冊上了,檢查
spring.cloud.config.discovery.service-id配置是否和服務(wù)名能對應(yīng)上。 -
eureka.client.fetch-registry是否為true(其實默認值就是true,防止手賤誤操作)。
回顧下錯誤發(fā)生點:
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)獲取到實例為空的時候會拋出此異常,一步步追蹤一下,發(fā)現(xiàn)最終會調(diào)用到DiscoveryClient.getInstancesByVipAddress()方法。
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為空,即注冊中心沒有可用服務(wù)或者eureka.client.fetch-registry配置成了false;要么通過vipAddress在applications查詢不出實例結(jié)果,即給定的service-id在注冊中心中不存在。
①注冊中心沒有可用服務(wù),獲取不到服務(wù)列表很容易理解。
②service-id對應(yīng)不上,也很容易理解。就比如拿一個不存在的key去一個collection中獲取value,肯定是獲取不到服務(wù)的。
③eureka.client.fetch-registry配置成了false,這一點需要解釋一下:
要知道咱們內(nèi)存中存儲的applications列表并不是每次請求都會進行刷新,而是維護了一個CacheRefreshThread去定時輪詢獲取注冊中心中的服務(wù),然后塞到localRegionApps中,然而,這個線程開啟需要一個條件,clientConfig.shouldFetchRegistry()==true,看方法名就知道需要eureka.client.fetch-registry=true任務(wù)才會開啟。但是默認這個值就是true,當時不曉得是不是腦子抽風了配置成了false,然后找這個bug迷糊了好一會兒。具體開啟任務(wù)線程的代碼如下所示:
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);
}
...
}
坑二:refresh的endpoints訪問不到了
訪問ip:port/actuator/refresh返回404。在搭建的過程中,很多老版本的教程都只是說引入下方依賴即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
但在springboot 2.x以上的版本,默認只對health和info這兩個端點進行暴露出來,如下圖所示。

而對refresh端點并未暴露出來,這里就需要咱們自己去手動配置暴露,感興趣的朋友可以去Endpoints看一下具體有哪些可以暴露的端點,咱們也可以使用 management.endpoints.web.exposure.include=*將所有端點全部暴露出來,當然,實際生產(chǎn)環(huán)境中也不建議如此。目前我測試配置management.endpoints.web.exposure.include=refresh,info,health暴露了refresh,info,health三個端點。
注意:
- 使用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
后續(xù)
目前僅僅只是簡單的測試一下springcloud config注冊中心,后續(xù)會加上springcloud bus消息總線安排一下,看看還有木有坑點繼續(xù)分享~~,具體案例見github。