继第一次搭建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。