前言
繼第一次搭建springcloud環(huán)境踩坑之后,時(shí)隔三個(gè)月古胆,第二次踩坑記錄也跟著上線了堕伪,SpringCloudConfig坑系列。第一次踩坑讓我理解了用戶線程和守護(hù)線程這一塊的知識盲點(diǎn)肮街,這次踩的坑就是基本就是配置上的坑。但是多踩踩坑會(huì)讓我們更容易理解具體配置起到了什么樣的作用判导。
坑一:拋出異常 :No instances found of configserver (myserver)
出現(xiàn)此錯(cuò)誤可以檢查一下以下幾點(diǎn):
- 需要依賴的config-server服務(wù)myserver(自己注冊的服務(wù)名)是否注冊到了eureka注冊中心上嫉父。
- 如果注冊上了沛硅,檢查
spring.cloud.config.discovery.service-id
配置是否和服務(wù)名能對應(yīng)上。 -
eureka.client.fetch-registry
是否為true(其實(shí)默認(rèn)值就是true绕辖,防止手賤誤操作)摇肌。
回顧下錯(cuò)誤發(fā)生點(diǎn):
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)
獲取到實(shí)例為空的時(shí)候會(huì)拋出此異常仪际,一步步追蹤一下围小,發(fā)現(xiàn)最終會(huì)調(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
查詢不出實(shí)例結(jié)果成榜,即給定的service-id
在注冊中心中不存在框舔。
①注冊中心沒有可用服務(wù),獲取不到服務(wù)列表很容易理解伦连。
②service-id
對應(yīng)不上雨饺,也很容易理解。就比如拿一個(gè)不存在的key
去一個(gè)collection
中獲取value
惑淳,肯定是獲取不到服務(wù)的额港。
③eureka.client.fetch-registry
配置成了false,這一點(diǎn)需要解釋一下:
要知道咱們內(nèi)存中存儲(chǔ)的applications
列表并不是每次請求都會(huì)進(jìn)行刷新歧焦,而是維護(hù)了一個(gè)CacheRefreshThread
去定時(shí)輪詢獲取注冊中心中的服務(wù)移斩,然后塞到localRegionApps
中,然而绢馍,這個(gè)線程開啟需要一個(gè)條件向瓷,clientConfig.shouldFetchRegistry()==true
,看方法名就知道需要eureka.client.fetch-registry=true
任務(wù)才會(huì)開啟。但是默認(rèn)這個(gè)值就是true舰涌,當(dāng)時(shí)不曉得是不是腦子抽風(fēng)了配置成了false猖任,然后找這個(gè)bug迷糊了好一會(huì)兒。具體開啟任務(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以上的版本搁痛,默認(rèn)只對health
和info
這兩個(gè)端點(diǎn)進(jìn)行暴露出來长搀,如下圖所示。
而對refresh
端點(diǎn)并未暴露出來鸡典,這里就需要咱們自己去手動(dòng)配置暴露源请,感興趣的朋友可以去Endpoints看一下具體有哪些可以暴露的端點(diǎn),咱們也可以使用 management.endpoints.web.exposure.include=*
將所有端點(diǎn)全部暴露出來,當(dāng)然谁尸,實(shí)際生產(chǎn)環(huán)境中也不建議如此舅踪。目前我測試配置management.endpoints.web.exposure.include=refresh,info,health
暴露了refresh,info,health三個(gè)端點(diǎn)。
注意:
- 使用refresh端點(diǎn)時(shí)症汹,它只會(huì)針對有@RefreshScope注解的類和方法進(jìn)行刷新硫朦。
- 訪問這些端點(diǎn)時(shí)都需要加上actuator這個(gè)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ù)會(huì)加上springcloud bus消息總線安排一下泽裳,看看還有木有坑點(diǎn)繼續(xù)分享~~瞒斩,具體案例見github。