前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【云原生】springcloud11——Hystrix是怎样让微服务“易凡峰顺”的

【云原生】springcloud11——Hystrix是怎样让微服务“易凡峰顺”的

作者头像
用户10127530
发布2022-10-26 19:44:44
4200
发布2022-10-26 19:44:44
举报
文章被收录于专栏:半旧的技术栈半旧的技术栈

前 言 🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端 ☕专栏简介:深入、全面、系统的介绍springcloud与springcloud Alibaba微服务常用技术栈 🌰 文章简介:本文将介绍HyStrix服务熔断、降级,建议收藏备用,创作不易,敬请三连哦 🥒文章推荐: 微服务架构与springcloud 01——微服务入门 微服务架构与springcloud02——父工程构建及支付模块实现 微服务架构与springcloud03——项目热部署与消费者订单模块 微服务架构与springcloud04——Eureka服务注册与发现 springcloud05——Zookeeper实现支付微服务 【云原生】springcloud06——订单服务注册zookeeper 【云原生】springcloud07—Consul的服务注册与发现 【云原生】springcloud08——Ribbon负载均衡调用 【云原生】springcloud09——但愿发长久,空手撕Ribbon 【云原生】springcloud10——人生苦短,我用OpenFeign

文章目录

1 Hystrix简介

1.1 分布式系统面临的问题

1.2 Histrix是什么

1.3 Hystrix能做什么

服务降级、服务熔断,接近实时监控。

官方文档:https://github.com/Netflix/Hystrix/wiki

1.4 停更运维

后面我们会介绍代替的方案,但是必须先学习Hystrix了解其思想。

2.Hystix的重要概念

2.1 服务降级

概念:服务降级一般是指在服务器压力剧增的时候,根据实际业务使用情况以及流量,对一些服务和页面有策略的不处理或者用一种简单的方式进行处理,从而 释放服务器资源的资源以保证核心业务的正常高效运行。

原因: 服务器的资源是有限的,而请求是无限的。在用户使用即并发高峰期,会影响整体服务的性能,严重的话会导致宕机,以至于某些重要服务不可用。故高峰期为了保证核心功能服务的可用性,就需要对某些服务降级处理。可以理解为舍小保大

应用场景: 多用于微服务架构中,一般当整个微服务架构整体的负载超出了预设的上限阈值(和服务器的配置性能有关系),或者即将到来的流量预计会超过预设的阈值时(比如双11、6.18等活动或者秒杀活动)

总结:服务降级是舍小保大,释放服务器资源的资源以保证核心业务的正常高效运行。

2.2 服务熔断

熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。

在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。

这种牺牲局部,保全整体的措施就叫做熔断。

总结:服务熔断相当于断闸

❤服务降级与服务熔断的区别 1.触发原因不一样,服务熔断由链路上某个服务引起的,服务降级是从整体的负载考虑 2.管理目标层次不一样,服务熔断是一个框架层次的处理,服务降级是业务层次的处理 3.实现方式不一样,服务熔断一般是自我熔断恢复,服务降级相当于人工控制 4.触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑; 总结:服务熔断是应对系统服务雪崩的一种保险措施,给出的一种特殊降级措施。而服务降级则是更加宽泛的概念,主要是对系统整体资源的合理分配以应对压力。

2.3服务限流

限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等

总结:服务限流相当于排队

3 Hystrix微服务构建

3.1 准备工作

前面我们使用的都是Eureka集群,为了简化后面的测试、编码工作,避免每次启动的微服务数量过多(方便学习演示),我们暂时将7001的application.yml改造下,改成单机环境。

3.2 建项目

新建项目cloud-provider-hystrix-payment8001

3.3 写pom

<dependencies>
    <!-- hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
    <dependency>
        <groupId>com.wangzhou.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--监控-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--eureka client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <!--   一个Java工具包     -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3.4 写yml

server:
  port: 8001

spring:
  application:
    name: cloud-provider-hystrix-payment

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #单机版
      defaultZone: http://localhost:7001/eureka
      #集群版
#      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka

3.5 主启动

@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
         SpringApplication.run(PaymentHystrixMain8001.class, args);
    }
}

3.6 业务类

(1)Service

节约时间,我们就不写接口了。

@Service
public class PaymentService {

    // 正常访问
    public String paymentInfo_OK(Integer id){
        // Hystrix底层调用的是Tomcat的线程池,我们在这里将线程名放回
        return "线程:  "+Thread.currentThread().getName()+"  paymentInfo_OK, id = "+id;
    }

    //超时访问
    public String paymentInfo_Timeout(Integer id) throws InterruptedException {
        int timeout = 3;
        // Hystrix底层调用的是Tomcat的线程池,我们在这里将线程名放回
        TimeUnit.SECONDS.sleep(timeout);
        return "线程:  "+Thread.currentThread().getName()+"  paymentInfo_OK, id = "+id + "\t" + "耗时"+ timeout +"s";
    }

}

(2)Controller

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentOk(@PathVariable("id")Integer id) {
        String result = paymentService.paymentInfo_OK(id);
        log.info("result:" + result);
        return result;
    }

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_timeout(@PathVariable("id") Integer id) throws InterruptedException {
        String result = paymentService.paymentInfo_Timeout(id);
        log.info("********* RESULT:"+ result +"***********");
        return result;
    }
}

3.7 测试

启动7001,8001两个微服务。

访问:http://localhost:7001/,可用看到带熔断的8001Hystrix微服务。

访问ok接口

http://localhost:8001/payment/hystrix/ok/1

没毛病。

timeout接口。

4 JMeter高并发测试

直到目前为止,一切都是"易凡峰顺"的,来点异常场景。

4.1 JMeter简介

JMeter是开源软件Apache基金会下的一个性能测试工具,用来测试部署在服务器端的应用程序的性能。近来,JMeter因为其使用简单,现在也被社区作为接口测试工具… 举个栗子,你开了一个网店,兴冲冲地准备双十一大干一把,没想当天活动的时候大量用户一访问你的网店,你的网店挂了,那怎么办?办法就是在实际搞活动之前,先测试一下以确认系统能承受那么多的用户,当然测试的时候我们不需要请真正的这么多实际用户,否则得花多少钱啊,JMeter就是那个能帮助模拟大量用户访问你网站的一个软件。

4.2 下载、安装JMeter

JMeter下载地址:http://jmeter.apache.org/download_jmeter.cgi

下载后解压到你系统下的任意目录,然后运行%JMETER_HOME%\bin\jmeter.bat文件

4.3 JMeter压力测试

新建测试计划,鼠标右键点击计划设置200*100 = 20000并发量。

新增http请求。

配置如下。

保存,启动压测。

后台不断地看到接收到的请求。

这个时候你访问:http://localhost:8001/payment/hystrix/ok/1

发现也开始转圈圈了,访问速度肉眼可见变慢。

这是因为tomcat的默认工作线程被占满了,没有多余的线程来分解压力和处理了。

如果此时外部消费者80也来访问,只能干等,最终导致消费者80不满意。8001被拖死。

5 客户端测试集成

5.1 建项目

新建cloud-consumer-feign-hystrix-order80(一般将Hystrix用于客户端,但其实她也可以应用于服务端)

5.2 写pom

<dependencies>
    <!-- openfeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--   hystrix     -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <!--eureka client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
     <dependency>
            <groupId>com.wangzhou.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
     </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

5.3 写yml

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7001/eureka

#需要加上,否则会报错
ribbon:
  ReadTimeout: 4000
  ConnectTimeout: 4000

5.4 主启动

@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class, args);
    }
}

5.5 业务类

(1)service

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentOk(@PathVariable("id")Integer id);
    
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_timeout(@PathVariable("id") Integer id);

}

(2)Controller

@Slf4j
@RestController
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;


    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentOk(@PathVariable("id")Integer id) {
        String result = paymentHystrixService.paymentOk(id);
        return result;
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_timeout(@PathVariable("id") Integer id) {
        String result = paymentHystrixService.paymentInfo_timeout(id);
        return result;
    }

}

5.6 正常测试

启动80

http://localhost/consumer/payment/hystrix/ok/1

http://localhost/consumer/payment/hystrix/tmeout/1

5.7 高并发测试

启动JMeter对8001的压力测试,再通过80访问

http://localhost/consumer/payment/hystrix/ok/1

开始转圈圈了。狂点还可能报错。

服务端这么慢,用户不得骂你吗?我们来看看怎么解决。

6 Hystrix的服务降级

6.1 降级容错解决的维度要求

6.2 服务端的降级处理

在8001的timeout接口新增@HystrixCommand注解,进行如下改造,设置兜底机制。

   /*
      通过@HystrixCommand来指定哪个方法由Hystrix来接管
         fallbackMethod属性: 指定哪个方法作为兜底方法
     */
    @HystrixCommand(fallbackMethod ="paymentInfo_TimeoutHandler", commandProperties = {
            //设置自身超时调用时间的峰值为 3 秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_Timeout(Integer id) throws InterruptedException {
        int timeout = 3;
        // Hystrix底层调用的是Tomcat的线程池,我们在这里将线程名放回
        TimeUnit.SECONDS.sleep(timeout);
        return "线程:  "+Thread.currentThread().getName()+"  paymentInfo_OK, id = "+id + "\t" + "耗时"+ timeout +"s";
    }

    public String paymentInfo_TimeoutHandler(Integer id) {
        return "线程:  "+Thread.currentThread().getName()+"  paymentInfo_TimeoutHandler, id = "+id + "\t/(ㄒoㄒ)/~~";
    }

主启动类增加@EnableCircuitBreaker注解触发熔断功能

测试下,访问:http://localhost/consumer/payment/hystrix/timeout/1

可以注意到,此时处理的线程也变成了Hystrix...开头了。说明对于超时的情况使用了其它线程池的线程进行单独处理了。

将接口的核心方法改造下。从超时的情况改造称为异常。

    @HystrixCommand(fallbackMethod ="paymentInfo_TimeoutHandler", commandProperties = {
            //设置自身超时调用时间的峰值为 3 秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_Timeout(Integer id) throws InterruptedException {
        int timeout = 3;
        int temp = 3 / 0;
        // Hystrix底层调用的是Tomcat的线程池,我们在这里将线程名放回
//        TimeUnit.SECONDS.sleep(timeout);
        return "线程:  "+Thread.currentThread().getName()+"  paymentInfo_OK, id = "+id + "\t" + "耗时"+ timeout +"s";
    }

再测试。

这说明对于异常或者超时的情况都将会使用兜底方案。

6.3 客户端的降级处理

之前我们说过,一般会将服务降级放在客户端,这是为了在上游及时发先问题,及时处理。现在就来实践下。

在80的yml中添加。

feign:
  hystrix:
    enabled: true  #老师说是要设置这个,我设置一个不管怎么范围都是超时

在主启动类添加@EnableHystrix注解。

@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
@EnableHystrix
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class, args);
    }
}

改造80 controller中的paymentInfo_timeout接口。

   @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod ="paymentInfo_TimeoutHandler", commandProperties = {
            //设置自身超时调用时间的峰值为 2 秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
    })
    public String paymentInfo_timeout(@PathVariable("id") Integer id) {
        String result = paymentHystrixService.paymentInfo_timeout(id);
        return result;
    }

    public String paymentInfo_TimeoutHandler(Integer id) {
        return "我是消费者80,对方系统繁忙,请稍后再试,/(ㄒoㄒ)/~~";
    }

测试。

在测试之前记得将支付微服务8001的paymentInfo_timeout逻辑从异常改为sleep哟。

测试结果如下。

6.4 全局服务降级的配置

上面的代码有如下问题。

  • 业务逻辑和异常处理被我们混在一块了,耦合度极高。
  • 每一个方法都需要有兜底方法

解决方法,使用@DefaultProperties设置全局的fallback方法。

80的OrderHystrixController中添加全局fallback方法:

    //全局fallback方法,不能有传参
    public String payment_Global_FallbackMethod(){
        return "Global异常处理信息,请稍后再试!";
    }

并在OrderHystrixController类上加上@DefaultProperties(defaultFallback = “payment_Global_FallbackMethod”),指定设置全局fallback方法。

将之前的@HystrixCommand注掉,稍微对方法进行下改动。重启微服务80.

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
//    @HystrixCommand(fallbackMethod ="paymentInfo_TimeoutHandler", commandProperties = {
//            //设置自身超时调用时间的峰值为 2 秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
//            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
//    })
    @HystrixCommand
    public String paymentInfo_timeout(@PathVariable("id") Integer id) {
        int var = 10/0;
        String result = paymentHystrixService.paymentInfo_timeout(id);
        return result;
    }

结果如下。

6.5 通配服务降级

前面一节我们已经解决了代码膨胀的问题,接下来解决下代码耦合度过高的问题。

在80的service包下新建PaymentFallbackService类,实现PaymentHystrixService接口

public class PaymentFallbackService implements PaymentHystrixService{
    @Override
    public String paymentOk(Integer id) {
        return "------PaymentFallbackService  paymentOk /(ㄒoㄒ)/~~";
    }

    @Override
    public String paymentInfo_timeout(Integer id) {
        return "------PaymentFallbackService  paymentInfo_timeout /(ㄒoㄒ)/~~";

    }
}

PaymentHystrixService中的@FeignClient注解增加fallback参数即可。

访问ok接口:http://localhost/consumer/payment/hystrix/ok/1,没有任何毛病

关闭8001服务,模拟服务器宕机。请读者自测。

7 Hystrix的服务熔断

7.1 熔断理论

前面介绍过服务熔断,这里再解释下,帮助读者理解。

(1)调用正常方法失败会触发降级,而降级会触发fallback方法

(2)但无论如何,降级一定是先调用正常方法,再调用fallback方法

(3)假如单位时间降级次数过多,会触发熔断

(4)熔断以后将会跳过正常方法直接调用fallback方法

(5)所谓的熔断后服务不可用,就是因为跳过了正常方法,而直接执行了fallback方法

7.2 服务熔断案例

在8001的PaymentService中添加

 //====服务熔断
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),                      //开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),         //请求总数阈值(默认20)
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),   //休眠时间窗口期(休眠多久进入半开模式(单位毫秒,默认5秒))
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),       //请求次数的错误率达到多少跳闸(百分率%,默认50%)
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
        if(id < 0){
            throw  new RuntimeException("****id 不能为负数");
        }
        String serialNumber = IdUtil.simpleUUID();

        return  Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + serialNumber;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
        return "id 不能为负数,请稍后再试, id: " + id;
    }

对上面的参数解释如下。

HystrixCommandProperties.class可用看到所有可配置的参数。

在8001的PaymentController中添加

@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
    String result = paymentService.paymentCircuitBreaker(id);
    log.info("******result:" + result);
    return result;
}

启动8001,执行测试。

正数放行,http://localhost:8001/payment/circuit/1

负数,http://localhost:8001/payment/circuit/-1

借助Jmeter大量进行如上请求,使服务熔断,熔断10秒内就算是正确的请求也返回错误信息

10秒后进入半开模式,对请求进行处理,此时如果是正确的请求,那么就关闭熔断,否则再次进入熔断,10秒后再次开启半开模式,对请求进行处理,直到半开模式处理到正确请求。

7.3 熔断规则总结

(1)熔断状态的变化

下图总结了熔断的状态变化的规则

官网还给出了详细的流程图

(2)熔断开启的条件

(3)断路器打开之后

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-09-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1 Hystrix简介
    • 1.1 分布式系统面临的问题
      • 1.2 Histrix是什么
        • 1.3 Hystrix能做什么
          • 1.4 停更运维
          • 2.Hystix的重要概念
            • 2.1 服务降级
              • 2.2 服务熔断
                • 2.3服务限流
                • 3 Hystrix微服务构建
                  • 3.1 准备工作
                    • 3.2 建项目
                      • 3.3 写pom
                        • 3.4 写yml
                          • 3.5 主启动
                            • 3.6 业务类
                              • 3.7 测试
                              • 4 JMeter高并发测试
                                • 4.1 JMeter简介
                                  • 4.2 下载、安装JMeter
                                    • 4.3 JMeter压力测试
                                    • 5 客户端测试集成
                                      • 5.1 建项目
                                        • 5.2 写pom
                                          • 5.3 写yml
                                            • 5.4 主启动
                                              • 5.5 业务类
                                                • 5.6 正常测试
                                                  • 5.7 高并发测试
                                                  • 6 Hystrix的服务降级
                                                    • 6.1 降级容错解决的维度要求
                                                      • 6.2 服务端的降级处理
                                                        • 6.3 客户端的降级处理
                                                          • 6.4 全局服务降级的配置
                                                            • 6.5 通配服务降级
                                                            • 7 Hystrix的服务熔断
                                                              • 7.1 熔断理论
                                                                • 7.2 服务熔断案例
                                                                  • 7.3 熔断规则总结
                                                                  相关产品与服务
                                                                  负载均衡
                                                                  负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
                                                                  领券
                                                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档