美文网首页
SpringCloud踩坑记录二

SpringCloud踩坑记录二

作者: marsjhe | 来源:发表于2019-04-22 20:53 被阅读0次

    前言

    继第一次搭建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这两个端点进行暴露出来,如下图所示。

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

    注意

    1. 使用refresh端点时,它只会针对有@RefreshScope注解的类和方法进行刷新。
    1. 访问这些端点时都需要加上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

    相关文章

      网友评论

          本文标题:SpringCloud踩坑记录二

          本文链接:https://www.haomeiwen.com/subject/jvtpgqtx.html