Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >分布式定时任务Elastic-Job框架在SpringBoot工程中的应用实践(一)

分布式定时任务Elastic-Job框架在SpringBoot工程中的应用实践(一)

作者头像
用户2991389
发布于 2018-09-05 06:53:03
发布于 2018-09-05 06:53:03
2.9K10
代码可运行
举报
运行总次数:0
代码可运行

摘要:如何构建具备作业分片和弹性扩缩容的定时任务系统是每个大型业务系统在设计时需要考虑的重要问题? 对于构建一般的业务系统来说,使用Quartz或者Spring Task即可基本满足我们的单体服用应用需要。然而随着线上业务量的不断发展,这两种定时任务已经日渐无法满足我们的需求。一般,使用这两种定时任务框架都会遇到如下的两个痛点问题: (1)如果业务工程采用集群化的部署,可能会多次重复执行定时任务而导致系统的业务逻辑错误,并产生系统故障。 (2)Quartz的集群方案具备HA功能,可以实现定时任务的分发,但是通过增加机器节点数量的方式并不能提高每次定时任务的执行效率,无法实现任务的弹性分片。 一线互联网大厂都有他们自己为其业务定制化研发的分布式定时任务系统,业务研发工程师可以通过在其Web Console的界面上进行简单的任务配置即可使得大型业务系统实现定时任务的调度、分发、分片、监控和扩缩容等功能。那么,业界是否有开源的组件框架同样具备这些功能呢?答案是肯定的!本文将向大家介绍一款开源的分布式定时任务调度框架—Elastic-Job的功能和原理,同时通过一个简单的案例阐述如何在Spring Boot工程完成Elastic-Job的集成。

一、Elastic-Job的简要介绍

Elastic-Job是当当开源的一款分布式弹性定时任务调度框架,它是从ddframe项目中dd-job的作业模块中分离出来的。Elastic-Job本身是基于开源定时任务框架Quartz和分布式应用程序协调服务Zookeeper及其客户端Curator进行二次开发的。

Elastic-Job在2.X版本以后主要分为Elastic-Job-Lite和Elastic-Job-Cloud两个子项目。其中,Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务。而Elastic-Job-Cloud使用Mesos + Docker的解决方案,额外提供资源治理、应用分发以及进程隔离等服务。本文叙述主要以Elastic-Job-Lite这个轻量级分布式任务调度框架为主。如下为Elastic-Job分布式协调框架的系统架构图:

Elasitc-Job架构图.jpg

从Elastic-Job的架构图上基本就可以看出,其以Jar的形式为业务工程(诸如,Spring Boot工程)的快速集成提供了简便的方式。同时,其提供的定时任务分片、弹性扩缩容、失效转移、作业监控和支持多种作业模式等强大的功能,使业务开发人员无需在这些方面花费较大多的精力,而可以更加专注于平台的业务开发。其主要的功能如下: (1)定时任务:基于成熟的定时任务作业框架Quartz cron表达式执行定时任务; (2)作业注册中心:基于Zookeeper和其客户端Curator实现的全局作业注册控制中心;作业注册中心仅用于作业任务注册和监控信息的暂存; (3)定时任务分片:可以将原本一个较大任务分片成为多小的子任务项分别在多个服务器上同时执行,提高总任务的执行处理效率; (4)弹性扩容缩容:运行中定时任务所在的服务器崩溃,或新增加n台作业服务器,作业框架将在下次任务执行前重新进行任务调度分发,不影响当前任务的处理与执行; (5)支持多种任务模式:分别支持Simple、Dataflow和Script类型的定时任务。具体的任务模式后面会详细介绍; (6)失效转移:运行中的定时任务所在的服务器崩溃不会导致重新分片,会在下次定时任务启动时重新分发和调度; (7)运行时定时任务状态收集:监控任务运行时的状态,统计最近一段时间任务处理成功和失败的数量,记录作业上次运行开始时间,结束时间和下次运行时间; (8)支持配置定时任务停止、恢复和禁用:用于操作定时任务的启停,并可以禁止某任务的执行; (9)Spring支持:Elastic-Job-Lite项目完美支持spring的容器,自定义命名空间,支持占位符 (10)运维平台:提供运维界面,方便开发和运维人员管理生产环境上已经发布的定时任务和注册中心;

二、Elastic-Job的应用场景分析与特点介绍

从第一节内容的介绍中,读者可能还是对Elastic-Job这款分布式弹性定时任务调度框架能够在哪些场景下应用以及其能够为业务系统带来的优点仍然不甚了解。接下来,本节将主要为读者对Elastic-Job的典型应用场景进行分析和介绍。 这里,先举一个典型的业务应用场景,比如在每天的凌晨之前需要对线上用户每日账户余额的增减情况进行汇总统计产生报表。如果数据库中的用户数据较少,那么放在一个普通的Quartz Job中跑这个汇总统计任务一点都不会有任何问题,只要设置适当的Quartz cron定时任务表达式,循环遍历所用用户数据,并对每个用户调用账户余额汇总统计的Api服务即可(这里假设采用微服务构建系统,用户管理为单一服务,账户资产为另外一个服务)。 但是,随着业务系统的发展,用户量越来越大,那么我们很有可能面临在第二天零点之前无法处理完所有用户每日账户余额增减的汇总统计。为了解决单节点部署处理效率较低、定时任务跑超时的问题,那么我们可能采用定时任务Job集群部署,这里需要注意的是多节点部署较容易造成任务的重复执行,在这个例子中,就可能重复累计每个用户账户余额的增减值了。为了解决定时任务重复执行的问题,我们可以对Job加锁来保证每一个时刻仅有同一个任务在执行,但这样也会增加系统的复杂度。

1、Elastic-Job定时任务水平切分

为了根本上解决上述业务的问题,这里就可以引入Elastic-Job。作为业务系统研发人员,可以考虑采用对集群内每一台机器分别处理一部分用户业务的方案来提高整体的处理执行效率。这样,无论用户量有多少,均可以采用增加机器节点数,水平切分总体任务为多个子任务项并调度至不同节点来分别执行,从而最大限度的利用资源。例如,我们想让3台虚拟机跑这个定时任务Job,那么我们可以将总任务分成3个定时分片任务,Elastic-Job分布式弹性定时任务调度框架通过Zookeeper进行协调,最终会让3台虚拟机分别执行0、1、2的三个分片任务,比如server0-->0任务、server1-->1任务、server2-->2任务,其中当server0执行时,可以只针对“id%3==0”的用户进行业务处理;当server1和server2执行时,则分别针对“id%3==1”、 “id%3==2”的用户进行处理。示意图如下:

Elastic-Job定时任务水平切分.png

2、Elastic-Job弹性扩缩容

如果某一天线上生产环境出现故障,集群(server0/ server1/ server2)中有一台机器崩溃(假设server2崩溃),那么跑在server2上的一个定时任务在下一个周期执行时会动态分发至server0或者server1这两个正常的节点,此时就会server0->0任务,server1->1/2任务。以此类推,如果server0或server1中再有一台机器出现故障,则三个分片定时任务将会同时在最后一个正常机器节点上执行。示意图如下:

Elastic-Job弹性扩缩容.png

三、Elastic-Job在Spring Boot中的实践

本节将主要详细介绍在SpringBoot工程中如何集成Elastic-Job这款这款分布式弹性定时任务调度框架组件,并使用其对定时任务的动态分片和弹性扩缩进行一个示例展示。

1、版本环境

Spring Boot 1.4.1.RELEASE、JDK 1.8、Zookeeper3.4.6、Elastic-Job-Lite 2.1.5。

2、添加Elastic-Job的pom依赖

因为当当开源了Elasitc-job-lite的源码,我们可以通过maven仓库来获得jar包依赖。当访问 http://mvnrepository.com/artifact/com.dangdang/elastic-job-lite选择自己项目需要的版本(在本次集成中选择的版本为2.1.5),点击进入后复制maven内容到pom.xml内即可。Pom文件的配置引入如下图所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<elastic-job.version>2.1.5</elastic-job.version>
......
......
......

<!-- elastic-job-core-->
<dependency>
<groupId>com.dangdang</groupId>
    <artifactId>elastic-job-lite-core</artifactId>
    <version>${elastic-job.version}</version>
</dependency>
<!-- elastic-job-lite-spring -->
<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>elastic-job-lite-spring</artifactId>
    <version>${elastic-job.version}</version>
</dependency>

3、Elastic-Job的定时任务编写

引入Elastic-Job的pom配置后,就可以编写第一个分片定时任务了。这里有必要说明下的是,Elastic-job总共提供了三种类型的定时任务:Simple类型定时任务、Dataflow类型定时任务和Script类型定时任务。其中,Script类型作业意为脚本类型作业,支持shell,python和perl等所有类型脚本,应用得不太多。SimpleJob需要实现SimpleJob接口,是未经过任何封装的定时任务简单实现,与quartz原生接口相似,下面示例中用的就是该类型的Job。Dataflow类型的定时任务主要用于处理数据流,需实现DataflowJob接口。该接口可以提供2个方法可供覆盖,分别用于抓取(fetchData)和处理(processData)数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.test.elasticjob

import com.dangdang.ddframe.job.api.ShardingContext;

import com.dangdang.ddframe.job.api.simple.SimpleJob;

import lombok.extern.slf4j.Slf4j;

@Slf4j

public class MyTestSimpleJob implements SimpleJob {

    @Override

    public void execute(ShardingContext shardingContext) {

log.info(String.format("------Thread ID: %s, 任务总片数: %s, 当前分片项: %s",

            Thread.currentThread().getId(),shardingContext.getShardingTotalCount(), shardingContext.getShardingItem()));

    }

}

4、Spring Boot工程中的Spring配置

在Spring Boot工程中需要在Configuration配置类中将Elastic-Job的Zookeeper作业注册中心、Elastic-Job的数据库日志和上面写的第一个分片定时任务配置进来。具体的配置类和Xml配置文件分别如下(test-elasticjob-cloudbill.properties配置文件中配置了分片定时任务的具体参数和zookeeper注册中心的配置,其中分片定时任务的参数诸如Cron时间表达式、总共分片数量和分片项参数健值对):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
@PropertySource("classpath:op-elasticjob-cloudbill.properties")
@ImportResource("classpath:spring-elasticjob.xml")
public class ElasticJobConfig {
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:reg="http://www.dangdang.com/schema/ddframe/reg"
 xmlns:job="http://www.dangdang.com/schema/ddframe/job"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.dangdang.com/schema/ddframe/reg
http://www.dangdang.com/schema/ddframe/reg/reg.xsd
http://www.dangdang.com/schema/ddframe/job
http://www.dangdang.com/schema/ddframe/job/job.xsd
">
 
<!--配置作业注册中心 -->
<reg:zookeeper id="regCenter" server-lists="${regcenter.zookeeper.serverList}"
namespace="${regcenter.zookeeper.namespace}"
base-sleep-time-milliseconds="${regcenter.zookeeper.base.sleep.time.milliseconds}"
max-sleep-time-milliseconds="${regcenter.zookeeper.max.sleep.time.milliseconds}"
max-retries="${regcenter.zookeeper.max.retries}" />
 
<!-- Elastic-Job在数据库中的日志 -->
<bean id="elasticJobLog" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://{ip}:{port}/res_hw_cloud_bill"/>
        <property name="username" value="root"/>
        <property name="password" value="bcrdb"/>
    </bean>
 
    <!-- 第一个Elastic-Job分片定时任务 -->
    <job:simple id="mySimpleJob" class="com.test.cloudbill.elastic.jobs.MyTestSimpleJob" registry-center-ref="regCenter"
                cron="${simplejob.cron}"
                sharding-total-count="${simplejob.shardingTotalCount}"
                sharding-item-parameters="${simplejob.shardingItemParameters}"
                description="我的第一个简单作业" overwrite="true" event-trace-rdb-data-source="elasticJobLog"
                job-exception-handler="com.test.cloudbill.elastic.jobs.CustomJobExceptionHandler"/>
</beans>

5、实际环境中的运行Elastic-Job

下面这一节主要来看下在实际环境中运行集成上面示例代码的Elastic-Job的SpringBoot工程的几个主要场景。需要说明的是,在运行工程之前,需要先在另外一台机器上部署上面提到的Zookeeper注册中心。并将部署Zookeeper机器的ip地址和2181端口设置到上面的test-elasticjob-cloudbill.properties配置文件中。 (1)场景1:单台服务器部署工程 当只有一台服务器部署带有定义好两个Elastic-Job分片任务的工程时,两个分片任务会同时调度到这台服务器上,这个跟普通的quartz定时任务没有啥区别。具体可以参考如下的日志输出:

单节点跑elastic_job定时任务的日志截图_1.png

(2)场景2:新增一台部署工程服务器 当在另外一台服务器上部署同样带有定义好两个Elastic-Job分片任务的工程时,Elastic-Job分布式弹性定时任务调度框架会将其中一个分片任务,弹性调度至新增的服务器节点上。此时,两个分片任务就会分别同时运行在两台不同的服务器上,这样极大地提高了整体业务处理的执行效率。两台服务器上的日志输出输出为如下(服务器1的日志和服务器2的日志):

elastic_job增加一个节点后机器1的截图.png

elastic_job增加一个节点后机器2的截图.png

(3)场景3:kill掉一台服务器上tomcat的进程 现在将其中一台服务器上面的tomcat进程kill掉,这台服务器上原先跑的分片定时任务会在下一个执行周期执行前重新调度分发至当前运行正常的服务器上运行,起到分片任务动态缩容的效果。具体可以参考如下的日志输出:

elastic_job双节点机器kill掉一个节点后的截图.png

四、总结

本文主要介绍了一款开源的分布式弹性定时任务调度框架—Elastic-Job的功能特点和框架结构,以及如何在SpringBoot这样的单体应用工程中集成起来,最后根据实际环境中的几种运行场景来的阐述其分片定时任务动态扩缩的功效。由于文章篇幅的原因,本文无法对Elastic-Job框架其他更多的功能点进行介绍,作者将在后面文章中继续进行阐述。同时,限于笔者的才疏学浅,对本文内容可能还有理解不到位的地方,如有阐述不合理之处还望留言一起探讨。 在此顺便为自己打个Call,有兴趣的朋友可以关注下我的个人公众号:“匠心独运的博客”,对于Java并发、Spring和数据库的一些细节、问题的文章将会在这个公众号上发布,欢迎交流与讨论。二维码如下图所示:

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

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

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

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

评论
登录后参与评论
1 条评论
热度
最新
springboot 能不能别用xml配置呀老兄 看着很不和谐
springboot 能不能别用xml配置呀老兄 看着很不和谐
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
分布式作业 Elastic-Job 快速上手指南,从理论到实战一文搞定!
Elastic-Job支持 JAVA API 和 Spring 配置两种方式配置任务,这里我们使用 JAVA API 的形式来创建一个简单的任务入门,现在都是 Spring Boot 时代了,所以不建议使用 Spring 配置文件的形式。 Elastic-Job 需要依赖 Zookeeper 中间件,用于注册和协调作业分布式行为的组件,目前仅支持 Zookeeper。我们已经创建了 Zookeeper 集群! 环境要求 1、Java 请使用 JDK 1.7 及其以上版本。 2、Zookeeper 请使用
Java技术栈
2018/06/04
1.8K0
一文搞懂Elastic-Job(内附源码解析)
Elastic-Job是当当基于Zookepper,Quartz开发并且开源的Java分布式定时任务,解决Quartz不支持分布式的弊端。它由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。
胖虎
2019/06/26
4.1K2
一文搞懂Elastic-Job(内附源码解析)
Spring-boot整合elastic-job分布式调度解决方案
Spring-boot整合elastic-job分布式调度解决方案 文档地址:http://elasticjob.io/docs/elastic-job-lite/00-overview/intro/
林老师带你学编程
2019/05/25
3.6K0
集成elastic-job分布式调度定时任务
定时任务这一组件在工作过程中经常使用到,在单机节点上可以直接选择使用Spring自带的定时任务组件hubble-task,而这种定时任务一旦确定固化了定时触发策略,也无法动态开启关闭,所以后来有了Quartz。
余生大大
2022/11/02
7440
集成elastic-job分布式调度定时任务
elastic-job 新手指南
大多数情况下,定时任务我们一般使用quartz开源框架就能满足应用场景。但如果考虑到健壮性等其它一些因素,就需要自己下点工夫,比如:要避免单点故障,至少得部署2个节点吧,但是部署多个节点,又有其它问题,有些数据在某一个时刻只能处理一次,比如 i = i+1 这些无法保证幂等的操作,run多次跟run一次,完全是不同的效果。 对于上面的问题,我曾经自行设计过一个基于zk分布式锁的解决方案: 1、每类定时job,可以分配一个独立的标识(比如:xxx_job) 2、这类job的实例,部署在多个节点上时,每个节点启
菩提树下的杨过
2018/05/28
2.6K0
定时任务之elastic-job概述
例子中是HelloQuartz。 为什么设计成JobDetail + Job,不直接使用Job?这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
翎野君
2023/05/12
5960
定时任务之elastic-job概述
分布式定时任务 -- elastic-job
在我们的项目当中,使用定时任务是避免不了的,我们在部署定时任务时,通常只部署一台机器。部署多台机器时,同一个任务会执行多次。比如短信提醒,每天定时的给用户下发短信,如果部署了多台,同一个用户将发送多条。只部署一台机器,可用性又无法保证。今天向大家介绍一款开源产品,分布式定时任务解决方案---- elastic-job。
小忽悠
2019/02/25
9100
Elastic Job 入门详解
Elastic job是当当网架构师张亮,曹昊和江树建基于Zookepper、Quartz开发并开源的一个Java分布式定时任务,解决了Quartz不支持分布式的弊端。Elastic job主要的功能有支持弹性扩容,通过Zookepper集中管理和监控job,支持失效转移等,这些都是Quartz等其他定时任务无法比拟的。
全栈程序员站长
2022/07/05
3560
SpringBoot2 整合ElasticJob框架,定制化管理流程
在前面的文章中,说过QuartJob这个定时任务,被广泛应用的定时任务标准。但Quartz核心点在于执行定时任务并不是在于关注的业务模式和场景,缺少高度自定义的功能。Quartz能够基于数据库实现任务的高可用,但是不具备分布式并行调度的功能。
知了一笑
2020/03/12
6170
springboot整合定时框架-Elastic-job-lite
一、前言 本文Github地址 官网地址 当当网张亮主导开发的分布式任务调度框架,结合zookeeper技术解决quartz框架在分布式系统中重复的定时任务导致的不可预见的错误! Code Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。 Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务;Elastic-Job-Cloud采用自研Mesos Fram
MiChong
2020/09/24
2.3K0
分布式定时任务Elastic-Job框架在SpringBoot工程中的应用实践(二)
文章摘要:在生产环境中部署Elastic-Job集群后,那么如何来运维监控线上跑着的定时任务呢? 如果在生产环境的大规模服务器集群上部署了集成Elastic-Job的业务工程,而没有相应的运维监控工具可以来监控定时任务执行状态和动态修改定时任务执行时间,修改相应的配置还得手动更新数据库或者配置文件,那么则会给运维和研发工程师增添不少麻烦。使用过Quartz集群方案的同学应该都有过同样的感触,修改定时任务执行时间配置和监控任务的状态都比较麻烦,想要一个功能齐全的监控运维平台还得自己专门来开发。所幸的是,Elastic-Job开源社区很早就考虑到该问题,在项目发布初期即提供了一个功能相对齐全的Elastic-Job运维监控console平台。
用户2991389
2018/09/05
1.2K1
分布式定时任务Elastic-Job框架在SpringBoot工程中的应用实践(二)
分布式定时任务调度框架选型
任何工具的使用都要结合自身的业务场景,脱落业务场景谈技术选型就是耍流氓。 考虑私有云场景业务量一般,高并发场景很少遇到,同一时间也不会有超大量定时任务同时需要执行,所以考虑自研也未尝不可。 目前自研最急需解决的问题并不是高并发,而是如何避免任务被重复执行; 场景就变成了:
BUG弄潮儿
2021/09/10
3.2K0
分布式定时任务调度框架选型
分布式系统中的定时任务全解(二)
上一篇分布式系统中的定时任务全解(一)中对定时任务和定时任务的基础使用方式进行了说明。这一小节,把分布式场景下的定时任务进行一个大致的讲解。
九州暮云
2019/08/21
1.2K0
分布式系统中的定时任务全解(二)
分布式定时任务调度框架之elastic-job简介
1.elastic-job是什么? elastic-job是当当内部应用框架ddframe中dd-job的作业模块中分离出来的分布式弹性作业框架。 2. 什么是作业调度(定时任务)? 作业即定时任务。
lyb-geek
2018/07/26
3.4K0
分布式定时任务调度框架之elastic-job简介
分布式系统中的定时任务全解(三)
前两篇从java语言中定时任务的基础实现,到第三方框架依赖下的常用实现方式都已经讲到了。
九州暮云
2019/08/21
1.7K0
分布式系统中的定时任务全解(三)
分布式定时任务调度系统技术选型
原文链接:https://blog.csdn.net/guyue35/article/details/84883408
业余草
2019/10/22
3.5K0
分布式定时任务调度系统技术选型
从Zookeeper 到 Elastic Job 的Simple Job使用(二)
按理说,我赋值的是shardingparameter,但是结果确实jobparameter,因为我一开始使用了jobparameter,然后改成shardingparameter
MickyInvQ
2020/09/27
1.6K0
从Zookeeper 到 Elastic Job 的Simple Job使用(二)
elastic-job入门demo
以及在网上找了相关demo,基本上都是复制了官网的简单教程,没有一个明确的demo.
3号攻城狮
2018/05/29
2.1K6
elastic-job入门demo
看完这篇文章,还敢说自己不会分布式任务调度?
以上场景就是任务调度所需要解决的问题,任务调度是为了自动完成特定任务,在约定的特定时刻去执行任务的过程。
上分如喝水
2021/08/16
1.2K0
看完这篇文章,还敢说自己不会分布式任务调度?
房价网是怎么使用分布式作业框架elastic-job
Elastic-Job是什么? Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。 Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务;Elastic-Job-Cloud采用自研Mesos Framework的解决方案,额外提供资源治理、应用分发以及进程隔离等功能。 官网地址:http://elasticjob.io/ Github:https://github
猿天地
2018/04/03
8670
相关推荐
分布式作业 Elastic-Job 快速上手指南,从理论到实战一文搞定!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验