Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控等!...

点击上方“朱小厮的博客”,选择“设为星标”

后台回复”1024“获取公众号专属1024GB资料

来源:rrd.me/fJVL7

前言

去年我们项目做了微服务1.0的架构转型,但是服务监控这块却没有跟上。这不,最近我就被分配了要将我们核心的微服务应用全部监控起来的任务。我们的微服务应用都是SpringBoot 应用,因此就自然而然的想到了借助Spring Boot 的Actuator 模块。

本篇是我在完成这个工单之后,对Spring Boot Actuator模块 学习应用的总结。在本篇文章中,你可以学习到:

1、Spring Boot Actuator 的快速使用入门
2、Spring Boot Actuator 的一些重要的endpoints的介绍
3、如何通过Actuator 模块实时查看当前应用的线程 dump信息
4、如何通过Actuator 模块实时查看当前应用的堆信息
5、如何通过Actuator 模块实时修改当前应用的日志打印等级
6、...

之后我还会介绍:

TODO:SpringBoot 微服务应用集成Prometheus + Grafana实现监控告警

一、什么是 Spring Boot Actuator

Spring Boot Actuator 模块提供了生产级别的功能,比如健康检查,审计,指标收集,HTTP 跟踪等,帮助我们监控和管理Spring Boot 应用、Bean加载情况、环境变量、日志信息、线程信息,JVM 堆信息等 。这个模块是一个采集应用内部信息暴露给外部的模块,上述的功能都可以通过HTTP 和 JMX 访问。

因为暴露内部信息的特性,Actuator 也可以和一些外部的应用监控系统整合(Prometheus, Graphite, DataDog, Influx, Wavefront, New Relic等)。这些监控系统提供了出色的仪表板,图形,分析和警报,可帮助你通过一个统一友好的界面,监视和管理你的应用程序。

Actuator使用Micrometer与这些外部应用程序监视系统集成。这样一来,只需很少的配置即可轻松集成外部的监控系统。

Micrometer 为 Java 平台上的性能数据收集提供了一个通用的 API,应用程序只需要使用 Micrometer 的通用 API 来收集性能指标即可。Micrometer 会负责完成与不同监控系统的适配工作。这就使得切换监控系统变得很容易。

对比 Slf4j 之于 Java Logger 中的定位。

二、快速开始,创建一个Spring Boot Actuator Demo

我们先创建一个demo应用。

你可以通过Spring Boot CLI 创建:

spring init -d=web,actuator -n=actuator-demo actuator-demo

或者通过Spring Initializr 创建:

对应的maven依赖:

<dependencies>
    ...
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    ...
</dependencies>

对应的Gradle 依赖:

dependencies {
    compile("org.springframework.boot:spring-boot-starter-actuator")
}

三、Endpoints 介绍

Spring Boot 提供了所谓的 endpoints (下文翻译为端点)给外部来与应用程序进行访问和交互。

打比方来说, /health 端点 提供了关于应用健康情况的一些基础信息。metrics 端点提供了一些有用的应用程序指标(JVM 内存使用、系统CPU使用等)。

这些 Actuator 模块本来就有的端点我们称之为原生端点。根据端点的作用的话,我们大概可以分为三大类:

应用配置类:获取应用程序中加载的应用配置、环境变量、自动化配置报告等与Spring Boot应用密切相关的配置类信息。

度量指标类:获取应用程序运行过程中用于监控的度量指标,比如:内存信息、线程池信息、HTTP请求统计等。

操作控制类:提供了对应用的关闭等操作类功能。

详细的原生端点介绍,请以官网为准,这里就不赘述徒增篇幅。

需要注意的就是:

1、每一个端点都可以通过配置来单独禁用或者启动

2、不同于Actuator 1.x,Actuator 2.x 的大多数端点默认被禁掉。Actuator 2.x 中的默认端点增加了 /actuator前缀。默认暴露的两个端点为 /actuator/health/actuator/info

四、端点暴露配置

我们可以通过以下配置,来配置通过JMX 和 HTTP 暴露的端点。

PropertyDefault
management.endpoints.jmx.exposure.exclude
management.endpoints.jmx.exposure.include*
management.endpoints.web.exposure.exclude
management.endpoints.web.exposure.includeinfo,healt

可以打开所有的监控点

management.endpoints.web.exposure.include=*

也可以选择打开部分,"*" 代表暴露所有的端点,如果指定多个端点,用","分开

management.endpoints.web.exposure.exclude=beans,trace

Actuator 默认所有的监控点路径都在 /actuator/*,当然如果有需要这个路径也支持定制。

management.endpoints.web.base-path=/minitor

设置完重启后,再次访问地址就会变成 /minitor/*

现在我们按照如下配置:

# "*" 代表暴露所有的端点 如果指定多个端点,用","分开
management.endpoints.web.exposure.include=*
# 赋值规则同上
management.endpoints.web.exposure.exclude=

启动DEMO程序,访问 http://localhost:8080/actuator,查看暴露出来的端点:


上面这样显示是因为chrome 浏览器安装了 JSON-handle 插件,实际上就是返回一大段json

下面,我会着重介绍几个比较重要的端点。

五、重要端点解析

5.1 /health端点

/health端点会聚合你程序的健康指标,来检查程序的健康情况。端点公开的应用健康信息取决于:

management.endpoint.health.show-details=always

该属性可以使用以下值之一进行配置:

NameDescription
never不展示详细信息,up或者down的状态,默认配置
when-authorized详细信息将会展示给通过认证的用户。授权的角色可以通过 management.endpoint.health.roles配置
always对所有用户暴露详细信息

按照上述配置,配置成 always之后,我们启动项目,访问 http://localhost:8080/actuator/health端口,可以看到这样的信息:

是不是感觉好像健康信息有点少?先别急,那是因为我们创建的是一个最基础的Demo项目,没有依赖很多的组件。

/health端点有很多自动配置的健康指示器:如redis、rabbitmq、db等组件。当你的项目有依赖对应组件的时候,这些健康指示器就会被自动装配,继而采集对应的信息。如上面的 diskSpace 节点信息就是 DiskSpaceHealthIndicator 在起作用。

上述截图取自官方文档

这是我另一个项目的 /health端点信息。

当如上的组件有一个状态异常,应用服务的整体状态即为down。我们也可以通过配置禁用某个组件的健康监测。

management.health.mongo.enabled: false

或者禁用所有自动配置的健康指示器:

management.health.defaults.enabled: false

⭐自定义 Health Indicator

当然你也可以自定义一个Health Indicator,只需要实现 HealthIndicator 接口或者继承 AbstractHealthIndicator类。

/**
 * @author Richard_yyf
 * @version 1.0 2020/1/16
 */
@Component
public class CustomHealthIndicator extends AbstractHealthIndicator {
    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        // 使用 builder 来创建健康状态信息
        // 如果你throw 了一个 exception,那么status 就会被置为DOWN,异常信息会被记录下来
        builder.up()
                .withDetail("app", "这个项目很健康")
                .withDetail("error", "Nothing, I'm very good");
    }
}

最终效果:

5.2 /metrics端点

/metrics端点用来返回当前应用的各类重要度量指标,比如:内存信息、线程信息、垃圾回收信息、tomcat、数据库连接池等。

{
    "names": [
        "tomcat.threads.busy",
        "jvm.threads.states",
        "jdbc.connections.active",
        "jvm.gc.memory.promoted",
        "http.server.requests",
        "hikaricp.connections.max",
        "hikaricp.connections.min",
        "jvm.memory.used",
        "jvm.gc.max.data.size",
        "jdbc.connections.max",
         ....
    ]
}

不同于1.x,Actuator在这个界面看不到具体的指标信息,只是展示了一个指标列表。为了获取到某个指标的详细信息,我们可以请求具体的指标信息,像这样:

http://localhost:8080/actuator/metrics/{MetricName}

比如我访问 /actuator/metrics/jvm.memory.max,返回信息如下:

你也可以用query param的方式查看单独的一块区域。比如你可以访问 /actuator/metrics/jvm.memory.max?tag=id:Metaspace。结果就是:

5.3 /loggers端点

/loggers 端点暴露了我们程序内部配置的所有logger的信息。我们访问 /actuator/loggers可以看到,

你也可以通过下述方式访问单独一个logger,

http://localhost:8080/actuator/loggers/{name}

比如我现在访问 root logger, http://localhost:8080/actuator/loggers/root

{
    "configuredLevel": "INFO",
    "effectiveLevel": "INFO"
}

⭐改变运行时的日志等级

/loggers端点我最想提的就是这个功能,能够动态修改你的日志等级。

比如,我们可以通过下述方式来修改 root logger的日志等级。我们只需要发起一个URL 为 http://localhost:8080/actuator/loggers/rootPOST请求,POST报文如下:

{
   "configuredLevel": "DEBUG"
}

仔细想想,这个功能是不是非常有用。如果在生产环境中,你想要你的应用输出一些Debug信息以便于你诊断一些异常情况,你你只需要按照上述方式就可以修改,而不需要重启应用。

如果想重置成默认值,把value 改成 null

5.4 /info端点

/info端点可以用来展示你程序的信息。我理解过来就是一些程序的基础信息。并且你可以按照自己的需求在配置文件 application.properties中个性化配置(默认情况下,该端点只会返回一个空的json内容。):

info.app.name=actuator-test-demo
info.app.encoding=UTF-8
info.app.java.source=1.8
info.app.java.target=1.8
# 在 maven 项目中你可以直接用下列方式引用 maven properties的值
# info.app.encoding=@project.build.sourceEncoding@
# info.app.java.source=@java.version@
# info.app.java.target=@java.version@

启动项目,访问 http://localhost:8080/actuator/info

{
    "app": {
        "encoding": "UTF-8",
        "java": {
            "source": "1.8.0_131",
            "target": "1.8.0_131"
        },
        "name": "actuator-test-demo"
    }
}

5.5 /beans端点

/beans端点会返回Spring 容器中所有bean的别名、类型、是否单例、依赖等信息。

访问 http://localhost:8080/actuator/beans,返回如下:

5.6 /heapdump 端点

访问:http://localhost:8080/actuator/heapdump会自动生成一个 Jvm 的堆文件 heapdump。我们可以使用 JDK 自带的 Jvm 监控工具 VisualVM 打开此文件查看内存快照。

5.7 /threaddump 端点

这个端点我个人觉得特别有用,方便我们在日常定位问题的时候查看线程的情况。主要展示了线程名、线程ID、线程的状态、是否等待锁资源、线程堆栈等信息。就是可能查看起来不太直观。访问 http://localhost:8080/actuator/threaddump返回如下:

5.8 /shutdown端点

这个端点属于操作控制类端点,可以优雅关闭 Spring Boot 应用。要使用这个功能首先需要在配置文件中开启:

management.endpoint.shutdown.enabled=true

由于 shutdown 接口默认只支持 POST 请求,我们启动Demo项目,向 http://localhost:8080/actuator/shutdown发起 POST请求。返回信息:

{
    "message": "Shutting down, bye..."
}

然后应用程序被关闭。

由于开放关闭应用的操作本身是一件非常危险的事,所以真正在线上使用的时候,我们需要对其加入一定的保护机制,比如:定制Actuator的端点路径、整合Spring Security进行安全校验等。(不是特别必要的话,这个端点不用开)

六、整合Spring Security 对端点进行安全校验

由于端点的信息和产生的交互都是非常敏感的,必须防止未经授权的外部访问。如果您的应用程序中存在Spring Security的依赖,则默认情况下使用基于表单的HTTP身份验证来保护端点。

如果没有,只需要增加对应的依赖即可:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

添加之后,我们需要定义安全校验规则,来覆盖Spring Security 的默认配置。

这里我给出了两个版本的模板配置:

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.actuate.context.ShutdownEndpoint;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
 * @author Richard_yyf
 */
@Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
    /*
     * version1:
     * 1. 限制 '/shutdown'端点的访问,只允许ACTUATOR_ADMIN访问
     * 2. 允许外部访问其他的端点
     * 3. 允许外部访问静态资源
     * 4. 允许外部访问 '/'
     * 5. 其他的访问需要被校验
     * version2:
     * 1. 限制所有端点的访问,只允许ACTUATOR_ADMIN访问
     * 2. 允许外部访问静态资源
     * 3. 允许外部访问 '/'
     * 4. 其他的访问需要被校验
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // version1
//        http
//                .authorizeRequests()
//                    .requestMatchers(EndpointRequest.to(ShutdownEndpoint.class))
//                        .hasRole("ACTUATOR_ADMIN")
//                .requestMatchers(EndpointRequest.toAnyEndpoint())
//                    .permitAll()
//                .requestMatchers(PathRequest.toStaticResources().atCommonLocations())
//                    .permitAll()
//                .antMatchers("/")
//                    .permitAll()
//                .antMatchers("/**")
//                    .authenticated()
//                .and()
//                .httpBasic();
        // version2
        http
                .authorizeRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint())
                    .hasRole("ACTUATOR_ADMIN")
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations())
                    .permitAll()
                .antMatchers("/")
                    .permitAll()
                .antMatchers("/**")
                    .authenticated()
                .and()
                .httpBasic();
    }
}

application.properties的相关配置如下:

# Spring Security Default user name and password
spring.security.user.name=actuator
spring.security.user.password=actuator
spring.security.user.roles=ACTUATOR_ADMIN

结语

本篇文章内容就到这里。

对应的源码可以Github上看到。

https://github.com/Richard-yyf/springboot-actuator-prometheus-test

如果本文有帮助到你,希望能点个赞,这是对我的最大动力????????????????。

参考

https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html

http://www.ityouknow.com/springboot/2018/02/06/spring-boot-actuator.html

想知道更多?描下面的二维码关注我

【精彩推荐】

朕已阅 

  • 1
    点赞
  • 0
    评论
  • 8
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

<p> <span style="color:#4d4d4d;">当前课程中博客项目的实战源码是我在 GitHub上开源项目 My-Blog目前已有 2000 多个 star:</span> </p> <p> <span style="color:#4d4d4d;"><img src="https://img-bss.csdnimg.cn/202103310649344285.png" alt="" /><br /> </span> </p> <p> <span style="color:#4d4d4d;">本课程是一个 Spring Boot 技术栈的实战类课程课程共分为 3 大部分前面两个部分为基础环境准备和相关概念介绍第三个部分是 Spring Boot 个人博客项目功能的讲解<span style="color:#565656;">通过本课程的学习不仅仅让你掌握基本的 Spring Boot 开发能力以及 Spring Boot 项目的大部分开发使用场景同时帮你提前甄别和处理掉将要遇到的技术难点认真学完这个课程后你将会对 Spring Boot 有更加深入而全面的了解同时你也会得到一个大家都在使用的博客系统源码你可以根据自己的需求和想法进行改造也可以直接使用它来作为自己的个人网站这个课程一定会给你带来巨大的收获。</span></span> </p> <p> <span style="color:#4d4d4d;"><span style="color:#565656;"> </span></span> </p> <p> <span style="color:#e53333;"><span style="color:#e53333;"><strong>课程特色</strong></span></span> </p> <p> <span style="color:#e53333;"><span style="color:#e53333;"><strong> </strong></span></span> </p> <p> <span style="color:#4d4d4d;"><span style="color:#565656;"> </span></span> </p> <ol> <li> <span style="color:#565656;">课程内容紧贴 Spring Boot 技术栈涵盖大部分 Spring Boot 使用场景。</span> </li> <li> <span style="color:#565656;">开发教程详细完整、文档资源齐全、实验过程循序渐进简单明了。</span> </li> <li> <span style="color:#565656;">实践项目页面美观且实用交互效果完美。</span> </li> <li> <span style="color:#565656;">包含从零搭建项目、以及完整的后台管理系统和博客展示系统两个系统的功能开发流程。</span> </li> <li> <span style="color:#565656;">技术栈新颖且知识点丰富学习后可以提升大家对于知识的理解和掌握对于提升你的市场竞争力有一定的帮助。</span> </li> </ol> <p> <strong>实战项目预览</strong> </p> <p> <span style="color:#4d4d4d;"><span style="color:#565656;"><span style="color:#e53333;"><strong> </strong></span></span></span> </p> <p> <span style="color:#4d4d4d;"><img src="https://img-bss.csdn.net/202005150303066258.png" alt="" /><br /> </span> </p> <p>   </p> <p> <span style="color:#4d4d4d;"> </span> </p> <p> <span style="color:#4d4d4d;"><img src="https://img-bss.csdn.net/202005150305396930.png" alt="" /><br /> </span> </p> <p> <span style="color:#4d4d4d;"> </span> </p> <p> <span style="color:#4d4d4d;"><img src="https://img-bss.csdn.net/202005150305528842.png" alt="" /><br /> </span> </p> <p> <span style="color:#4d4d4d;"> </span> </p> <p> <span style="color:#4d4d4d;"><img src="https://img-bss.csdn.net/202005150306056323.png" alt="" /><br /> </span> </p>
相关推荐
<p> <span style="color:#333333;"><strong></strong></span> </p> <span style="font-size:24px;">一、简介</span> <p> <span style="font-size:12px;"></span> </p> <p> <span style="font-size:16px;">通过这个课程带大家从零开发一款功能全面的后台管理系统包括项目搭建、功能实现到最后的Linux系统部署全过程。本课程使用SpringMVC + Spring + Mybatis作为主体框架使用AdminLTE作为前端框架使用主流关系型数据库Mysql作为存储数据库使用非关系型数据库Redis作为缓存数据库并集成SpringSecuriy安全框架做权限的动态管理集成Swagger2自动生成接口文档集成Druid连接池进行SQL性能监控集成ActiveMQ消息中间件进行异步解耦提高性能。最后使用linux系统进行服务部署并搭建nginx反向代理服务器提高网站性能。</span> </p> <p> <br /> </p> <p> <span style="font-size:24px;"><strong>二、学习目标</strong></span> </p> <p> <span style="font-size:16px;">通过本课程的学习带大家掌握SSM框架的开发流程并熟练使用SpringSecurity做为安全框架进行权限管理整合相关优秀的开源框架进行功能开发。还在项目中带大家学习前端相关的Jquery、Bootstrap知识。课程结束之后希望大家能做到独立进行开发项目的目的增强解决问题的能力具备功能落地实现的能力。</span> </p> <p> <span style="font-size:16px;"><span style="font-size:24px;"><strong>三、课程涉及知识点</strong></span></span> </p> <p> <span style="font-size:16px;"></span> </p> <ul> <li> SpringMVC源码分析 </li> <li> Mybatis源码分析 </li> <li> 通用Mapper </li> <li> Mysql数据库 </li> <li> Redis缓存实现 </li> <li> ActiveMQ消息中间件 </li> <li> SpringSecurity鉴权 </li> <li> Swagger2接口文档生成 </li> <li> 自定义注解 </li> <li> AOP切面编程 </li> <li> 自定义过滤器 </li> <li> Logback日志整合 </li> <li> Druid性能监控 </li> <li> Linux系统 </li> <li> Nginx反向代理 </li> <li> Ajax异步请求技术 </li> <li> Jquery基本使用 </li> <li> AdminLTE前端框架 </li> <li> Chart图表-线状图和饼状图 </li> <li> 百度地图定位城市 </li> <li> BootStrap前端框架 </li> <li> BootStrap-Table插件 </li> <li> BootStrap-Treeview插件 </li> <li> Markdown编辑器 </li> <li> 403、404、500错误页面配置 </li> <li> 数据库事务 </li> <li> 消息提示插件toastr.js </li> <li> 图片上传插件bootstrap fileinput </li> <li> 数字滚动效果 </li> <li> pv/uv流量统计 </li> <li> ... </li> </ul> <p> <br /> </p> <p> <span style="font-size:16px;"><span style="font-size:24px;"><strong>四、</strong></span><span style="font-size:24px;"><strong>课程部分内容截图如下</strong></span><span style="font-size:24px;"><strong></strong></span></span> </p> <p> <span style="font-size:18px;"><span style="font-size:18px;"><strong>1、首页</strong></span></span> </p> <p> <span style="font-size:16px;"><span style="font-size:24px;"><strong><span></span><span><img alt="" src="https://imgconvert.csdnimg.cn/aHR0cDovL3d3dy5kcmVhbWxhbmQud2FuZy9pbWFnZXMvaW1hZ2UvMjAxOTA5MTkvMjAxOTA5MTkxMTE1MTJfMTM5LnBuZw?x-oss-process=image/format,png" /></span></strong></span></span> </p> <p> <span style="font-size:24px;"><span style="font-size:24px;"><strong><span style="font-size:24px;">2、菜单管理</span></strong></span></span> </p> <p> <span style="font-size:24px;"><span style="font-size:24px;"><strong><span style="font-size:24px;"><span></span><span><img alt="" src="https://imgconvert.csdnimg.cn/aHR0cDovL3d3dy5kcmVhbWxhbmQud2FuZy9pbWFnZXMvaW1hZ2UvMjAxOTA5MTkvMjAxOTA5MTkxMTE3MjBfNzE5LnBuZw?x-oss-process=image/format,png" /></span><br /> </span></strong></span></span> </p> <p> <span></span> </p> <p> <span><span style="font-size:24px;"><strong>3、图床管理</strong></span><span style="font-size:24px;"><strong></strong></span><br /> </span> </p> <p> <span><span style="font-size:24px;"><strong><span></span><span><img alt="" src="https://imgconvert.csdnimg.cn/aHR0cDovL3d3dy5kcmVhbWxhbmQud2FuZy9pbWFnZXMvaW1hZ2UvMjAxOTA5MTkvMjAxOTA5MTkxMTIwMTJfNjYwLnBuZw?x-oss-process=image/format,png" /><span></span></span><span></span><br /> </strong></span></span> </p> <p> <span><span style="font-size:24px;"><strong>4、图标管理<br /> </strong></span></span> </p> <p> <span><span style="font-size:24px;"><strong><span></span><span><img alt="" src="https://imgconvert.csdnimg.cn/aHR0cDovL3d3dy5kcmVhbWxhbmQud2FuZy9pbWFnZXMvaW1hZ2UvMjAxOTA5MTkvMjAxOTA5MTkxMTIxMDBfMTcucG5n?x-oss-process=image/format,png" /><span></span></span><span></span></strong></span></span> </p> <p> <span><span style="font-size:24px;"><strong>5、留言反馈管理<br /> </strong></span></span> </p> <p> <span><span style="font-size:24px;"><strong><span></span><span><img alt="" src="https://imgconvert.csdnimg.cn/aHR0cDovL3d3dy5kcmVhbWxhbmQud2FuZy9pbWFnZXMvaW1hZ2UvMjAxOTA5MjEvMjAxOTA5MjExMDEwMTJfMzQ2LnBuZw?x-oss-process=image/format,png" /><span></span></span><span></span></strong></span></span> </p> <p> <span><span style="font-size:24px;"><strong>6、druid监控<br /> </strong></span></span> </p> <p> <span></span><span><img alt="" src="https://imgconvert.csdnimg.cn/aHR0cDovL3d3dy5kcmVhbWxhbmQud2FuZy9pbWFnZXMvaW1hZ2UvMjAxOTA5MTkvMjAxOTA5MTkxMTIzMzVfNTgwLnBuZw?x-oss-process=image/format,png" /></span> </p> <p> <br /> </p> <p> <span style="font-size:24px;"><strong></strong></span><span style="font-size:24px;"><strong>7、登录日志</strong></span><span style="font-size:24px;"><strong></strong></span> </p> <p> <span style="font-size:24px;"><strong><span></span><span><img alt="" src="https://imgconvert.csdnimg.cn/aHR0cDovL3d3dy5kcmVhbWxhbmQud2FuZy9pbWFnZXMvaW1hZ2UvMjAxOTA5MTkvMjAxOTA5MTkxMTI0MzJfOTQyLnBuZw?x-oss-process=image/format,png" /><span></span></span><span></span><br /> </strong></span> </p> <p> <br /> </p>
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值