“Sentinel控制台页面中的微服务数据空白” 问题的处理

整合步骤完成后,发现 Sentinel 控制台页面中的微服务数据空白,意外的问题出现了。写案例的时候正常,一放到具体的项目里就不能正常使用了。

紧接着,开始检查代码,看是不是配置有问题,结果一切正常。之后又刷新了 Maven 依赖,重启项目和 Sentinel Server,问题依然存在。

错误的解决思路

这个时候,笔者尝试了修改配置项,不使用懒加载的方式,在微服务实例启动时就将信息上报给 Sentinel 控制台,于是在配置文件中增加了如下配置项:

spring.cloud.sentinel.eager=true

再次重启项目,进入 Sentinel 控制台,页面左侧的微服务名称出现了,如图 11-2 所示。

image 2025 04 28 14 28 10 411
Figure 1. 图11-2 Sentinel 控制台页面左侧的微服务名称正常显示

微服务数据虽然出现了,但是每个微服务中 “实时监控”、“簇点数据” 页面中的数据都是空的,如图 11-3 所示。

image 2025 04 28 14 28 45 272
Figure 2. 图11-3 微服务的监控数据显示为空

不管发起多少次请求,这些页面都空空如也。因此,微服务架构项目中整合 Sentinel 还是有问题,根本没有监控到任何数据。此时,笔者推测出两个问题:一是实例中的 Sentinel ClientSentinel Server 间的通信有问题,二是实例中的 Sentinel Client 根本没有向 Sentinel Server 上报实例的监控信息。

Sentinel 控制台左侧的微服务列表中,单击 “机器列表” 选项,可以看到微服务实例的信息,而且微服务实例与 Sentinel Server 都部署在本地,也就是同一个网络环境中,通信肯定不存在问题。那么很大的可能就是 Sentinel ClientSentinel Server 上报实例的监控信息步骤时出现了问题。

笔者查看实例的启动日志,发现了一条警告级别的日志(不是报错日志):

Bean 'com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration' of type [com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

当时,笔者觉得这可能是个突破口,于是按照这条日志查询了相关的问题,并尝试着解决 “Sentinel 控制台页面中微服务数据空白” 的问题,但花了不少时间,尝试了好几种解决方案,都无功而返。

好了,以上就是笔者对这个问题的错误思路,花了不少时间,但是都没能处理好,这里就不再赘述了。

正确的解决思路

开发人员应该都有过 “发现了问题却不知道如何下手才能解决” 的无力感,整个人都会变得焦躁起来。

当感觉可能解决不了问题的时候,笔者忽然想到是不是自己的思路错了?犹记得之前整合 Seata 时也出现了类似的情况,最后发现问题就是没有配置拦截器而已。这次不会又如此吧!于是,笔者赶紧查了一下 spring-cloud-starter-alibaba-sentinel 的源代码,如图 11-4 所示,最终发现依然是拦截器失效的问题。

因为 newbee-mall-cloud-user-webnewbee-mall-cloud-recommend-webnewbee-mall-cloud-order-webnewbee-mall-cloud-shop-cart-webnewbee-mall-cloud-goods-web 5 个微服务实例工程中都自定义了 xxxWebMvcConfigurer,如果想要 Sentinel 中的这个拦截器生效,就需要分别在各个微服务实例中定义,代码修改如下:

image 2025 04 28 14 31 36 898
Figure 3. 图11-4 SentinelWebMvcConfigurer 代码截图

UserServiceWebMvcConfigurer 代码如下:

@Configuration
public class UserServiceWebMvcConfigurer extends WebMvcConfigurationSupport {
    private static final Logger log = LoggerFactory.getLogger(UserServiceWebMvcConfigurer.class);

    @Autowired
    private SentinelProperties sentinelProperties;
    @Autowired
    private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;

    @Autowired
    private TokenToAdminUserMethodArgumentResolver tokenToAdminUserMethodArgumentResolver;

    @Autowired
    private TokenToMallUserMethodArgumentResolver tokenToMallUserMethodArgumentResolver;

    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(tokenToAdminUserMethodArgumentResolver);
        argumentResolvers.add(tokenToMallUserMethodArgumentResolver);
    }

    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.
                addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
                .resourceChain(false);
    }

    public void addInterceptors(InterceptorRegistry registry) {
        // 配置 Sentinel 拦截器
        if (this.sentinelWebInterceptorOptional.isPresent()) {
            SentinelProperties.Filter filterConfig = this.sentinelProperties.getFilter();
            registry.addInterceptor((HandlerInterceptor) this.
                            sentinelWebInterceptorOptional.get())
                    .order(filterConfig.getOrder())
                    .addPathPatterns(filterConfig.getUrlPatterns());
            log.info("[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: {}", filterConfig.getUrlPatterns());
        }
    }
}

RecommendServiceWebMvcConfigurer 代码如下:

@Configuration
public class RecommendServiceWebMvcConfigurer extends WebMvcConfigurationSupport {

    private static final Logger log = LoggerFactory.getLogger(RecommendServiceWebMvcConfigurer.class);

    @Autowired
    private SentinelProperties sentinelProperties;
    @Autowired
    private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;
    @Autowired
    private TokenToAdminUserMethodArgumentResolver tokenToAdminUserMethodArgumentResolver;

    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(tokenToAdminUserMethodArgumentResolver);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.
                addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
                .resourceChain(false);
    }

    public void addInterceptors(InterceptorRegistry registry) {
        // 配置 Sentinel 拦截器
        if (this.sentinelWebInterceptorOptional.isPresent()) {
            SentinelProperties.Filter filterConfig = this.sentinelProperties.getFilter();
            registry.addInterceptor((HandlerInterceptor) this.sentinelWebInterceptorOptional.get())
                    .order(filterConfig.getOrder())
                    .addPathPatterns(filterConfig.getUrlPatterns());
            log.info("[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: {}", filterConfig.getUrlPatterns());
        }
    }
}

OrderServiceWebMvcConfigurer 代码如下:

@Configuration
public class OrderServiceWebMvcConfigurer extends WebMvcConfigurationSupport {

    private static final Logger log = LoggerFactory.getLogger(OrderServiceWebMvcConfigurer.class);

    @Autowired
    private SentinelProperties sentinelProperties;
    @Autowired
    private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;

    @Autowired
    private TokenToAdminUserMethodArgumentResolver tokenToAdminUserMethodArgumentResolver;

    @Autowired
    private TokenToMallUserMethodArgumentResolver tokenToMallUserMethodArgumentResolver;

    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(tokenToAdminUserMethodArgumentResolver);
        argumentResolvers.add(tokenToMallUserMethodArgumentResolver);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
                .resourceChain(false);
    }

    public void addInterceptors(InterceptorRegistry registry) {
        // 配置 Sentinel 拦截器
        if (this.sentinelWebInterceptorOptional.isPresent()) {
            SentinelProperties.Filter filterConfig = this.sentinelProperties.getFilter();
            registry.addInterceptor((HandlerInterceptor) this)
                    .order(filterConfig.getOrder())
                    .addPathPatterns(filterConfig.getUrlPatterns());
            log.info("[Sentinel Starter] Register SentinelWebInterceptor with urlPatterns: {}.", filterConfig.getUrlPatterns());
        }
    }
}

ShopCartServiceWebMvcConfigurer 代码如下:

@Configuration
public class ShopCartServiceWebMvcConfigurer extends WebMvcConfigurationSupport {

    private static final Logger log = LoggerFactory.getLogger
            (ShopCartServiceWebMvcConfigurer.class);

    @Autowired
    private SentinelProperties sentinelProperties;

    @Autowired
    private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;

    @Autowired
    private TokenToMallUserMethodArgumentResolver tokenToMallUserMethodArgumentResolver;

    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(tokenToMallUserMethodArgumentResolver);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjar/springfox-swagger-ui/")
                .resourceChain(false);
    }

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SeataHandlerInterceptor())
                .addPathPatterns("/**");

        // 增加对 Sentinel 拦截器的配置
        if (this.sentinelWebInterceptorOptional.isPresent()) {
            SentinelProperties.Filter filterConfig =
                    this.sentinelWebInterceptorOptional.get().getFilter();
            registry.addInterceptor((HandlerInterceptor) this.sentinelWebInterceptorOptional.get())
                    .order(filterConfig.getOrder())
                    .addPathPatterns(filterConfig.getUrlPatterns());
            log.info("[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: {}.", filterConfig.getUrlPatterns());
        }
    }
}

GoodsServiceWebMvcConfigurer 代码如下:

@Configuration
public class GoodsServiceWebMvcConfigurer extends
        WebMvcConfigurationSupport {

    private static final Logger log = LoggerFactory.getLogger(GoodsServiceWebMvcConfigurer.class);

    @Autowired
    private SentinelProperties sentinelProperties;
    @Autowired
    private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;

    @Autowired
    private TokenToAdminUserMethodArgumentResolver tokenToAdminUserMethodArgumentResolver;

    @Autowired
    private TokenToMallUserMethodArgumentResolver tokenToMallUserMethodArgumentResolver;


    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(tokenToAdminUserMethodArgumentResolver);
        argumentResolvers.add(tokenToMallUserMethodArgumentResolver);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
                .resourceChain(false);
    }

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SeataHandlerInterceptor());

        // 增加对Sentinel 埋点或者流控的配置
        if (this.sentinelWebInterceptorOptional.isPresent()) {
            SentinelProperties.FilterConfig filterConfig = this.sentinelWebInterceptorOptional.getFilter();
            registry.addInterceptor((HandlerInterceptor) this.sentinelWebInterceptorOptional.get())
                    .order(filterConfig.getOrder())
                    .addPathPatterns(filterConfig.getUrlPatterns());
            log.info("[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: {}, filterConfig.getUrlPatterns());
        }
    }
}

修改完成后,重启所有的微服务实例。访问后,所有的服务信息都出现在 SentinelDashboard 左侧的列表中,并且 “实例监控”、“簇点链路” 等信息都正常显示,如图 11-5 所示。

image 2025 04 28 14 50 29 244
Figure 4. 图11-5 监控信息正常显示

“Sentinel Dashboard 页面中微服务数据空白” 的问题解决了。到这里,才算真正地把 Sentinel 整合到微服务架构项目中。之后,就可以对一些资源进行限流配置及降级熔断配置了。

扩展一下这个知识点,除未配置 Sentinel 拦截器会出现 “Sentinel Dashboard 页面中微服务数据空白” 的问题外,还有如下几种情形也会出现该问题。

  1. 代码配置错误或配置项有遗漏,导致页面空白。解决办法:检查配置,主要是 IP 地址和端口号,若因为粗心漏掉了一些,修改正确即可。

  2. 默认的懒加载原因,导致页面空白。解决办法:发起几次请求就可以了。

  3. 微服务实例与 Sentinel Dashboard 间的网络不通。比如,一个在内网,另一个在公网,或者由于防火墙原因导致二者不能正常通信。解决办法:保证网络畅通,都部署到内网或都部署到公网,抑或使用内网穿透技术。

  4. 在使用 DockerKubernetes 等容器化技术部署时导致的网络不通,和问题③类似。

网上有很多这个问题的解决办法,不过大部分都是关于上述 4 种情形的。笔者并未搜索到未配置 Sentinel 拦截器导致的页面空白问题,因此导致在错误的思路上花费了很多的时间。

本章主要讲解微服务架构项目中整合 Sentinel 的相关编码过程,对实战部分的讲解做补充和优化。之后对整合 Sentinel 时遇到的一个 “坑” 做了复盘,详细地记录了遇到问题后笔者的处理过程和思考过程,希望对读者有一些启发。