转载

SpringCloud踩坑记录二

SpringCloud踩坑记录二

前言

继第一次搭建springcloud环境踩坑之后,时隔三个月,第二次踩坑记录也跟着上线了,SpringCloudConfig坑系列。第一次踩坑让我理解了 用户线程和守护线程 这一块的知识盲点,这次踩的坑就是基本就是 配置 上的坑。但是多踩踩坑会让我们更容易理解具体配置起到了什么样的作用。

坑一:抛出异常 :No instances found of configserver (myserver)

出现此错误可以检查一下以下几点:

  1. 需要依赖的config-server服务myserver(自己注册的服务名)是否注册到了eureka注册中心上。
  2. 如果注册上了,检查 spring.cloud.config.discovery.service-id 配置是否和服务名能对应上。
  3. eureka.client.fetch-registry 是否为true(其实默认值就是true,防止手贱误操作)。

回顾下错误发生点:

    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() 方法。

    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;要么通过 vipAddressapplications 查询不出实例结果,即给定的 service-id 在注册中心中不存在。

①注册中心没有可用服务,获取不到服务列表很容易理解。

service-id 对应不上,也很容易理解。就比如拿一个不存在的 key 去一个 collection 中获取 value ,肯定是获取不到服务的。

eureka.client.fetch-registry 配置成了false,这一点需要解释一下:

要知道咱们内存中存储的 applications 列表并不是每次请求都会进行刷新,而是维护了一个 CacheRefreshThread 去定时轮询获取注册中心中的服务,然后塞到 localRegionApps 中,然而,这个线程开启需要一个条件, clientConfig.shouldFetchRegistry()==true ,看方法名就知道需要 eureka.client.fetch-registry=true 任务才会开启。但是默认这个值就是true,当时不晓得是不是脑子抽风了配置成了false,然后找这个bug迷糊了好一会儿。具体开启任务线程的代码如下所示:

   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以上的版本,默认只对 healthinfo 这两个端点进行暴露出来,如下图所示。

SpringCloud踩坑记录二

而对 refresh 端点并未暴露出来,这里就需要咱们自己去手动配置暴露,感兴趣的朋友可以去Endpoints看一下具体有哪些可以暴露的端点,咱们也可以使用 management.endpoints.web.exposure.include=* 将所有端点全部暴露出来,当然,实际生产环境中也不建议如此。目前我测试配置 management.endpoints.web.exposure.include=refresh,info,health 暴露了 refresh,info,health 三个端点。

注意:

  1. 使用refresh端点时,它只会针对有@RefreshScope注解的类和方法进行刷新。

  2. 访问这些端点时都需要加上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 。

END

原文  https://juejin.im/post/5cbdbc1de51d456e46603e3f
正文到此结束
Loading...