2-1 分布式文件存储系统Ceph

分布式文件存储系统Ceph

1 分布式文件存储系统Ceph

 Ceph是一个统一的分布式存储系统,设计初衷是提供较好的性能、可靠性和可扩展性。

对比说明TFSFASTDFSMooseFSGlusterFSCEPH
开发语言C++CCCC++
数据存储方式文件/Trunk文件/块对象/文件/块
在线扩容支持支持支持支持支持
冗余备份支持支持支持支持支持
单点故障存在不存在存在不存在不存在
易用性安装复杂,官方文档少安装简单,社区相对活跃安装简单官方文档专业化安装有一定复杂度
适用场景跨集群的小文件单集群的中小文件单集群的大中文件跨集群云存储单集群的大中小文件

1.1 Ceph介绍

​ Ceph项目最早起源于Sage就读博士期间的工作(最早的成果于2004年发表),并随后贡献给开源社区。在经过了数年的发展之后,目前已得到众多云计算厂商的支持并被广泛应用。RedHat及OpenStack都可与Ceph整合以支持虚拟机镜像的后端存储。

Ceph特点:

CRUSH算法:Crush算法是ceph的两大创新之一,简单来说,ceph摒弃了传统的集中式存储元数据寻址的方案,转而使用CRUSH算法完成数据的寻址操作。CRUSH在一致性哈希基础上很好的考虑了容灾域的隔离,能够实现各类负载的副本放置规则,例如跨机房、机架感知等。Crush算法有相当强大的扩展性,理论上支持数千个存储节点。

高性能:Ceph中的数据副本数量可以由管理员自行定义,并可以通过CRUSH算法指定副本的物理存储位置以分隔故障域,支持数据强一致性; ceph可以忍受多种故障场景并自动尝试并行修复。

高扩展性:Ceph本身并没有主控节点,扩展起来比较容易,并且理论上,它的性能会随着磁盘数量的增加而线性增长。
	
特性丰富:Ceph支持三种调用接口:对象存储,块存储,文件系统挂载。三种方式可以一同使用。在国内一些公司的云环境中,通常会采用ceph作为openstack的唯一后端存储来提升数据转发效率。

中文学习网:http://docs.ceph.org.cn/

Ceph架构:

在这里插入图片描述

组件对象讲解:

RADOS:就是这样一个可用于PB级规模数据存储集群的可伸缩的、可靠的对象存储服务,可以理解成Ceph的整个存储对象,包括逻辑对象。

File:用户上传的文件

object:上传的文件被切成N个小文件块对象,RADOS的基本存储单元。

MDS:元数据的内存缓存,为了加快元数据的访问。

CRUSH:Ceph寻址算法,用于计算当前文件存储到哪个PG对应的OSD中。

PG:对object的存储进行组织和位置映射。具体而言,一个PG负责组织若干个object(可以为数千个甚至更多),但一个object只能被映射到一个PG中,即,PG和object之间是“一对多”映射关系。同时,一个PG会被映射到n个OSD上,而每个OSD上都会承载大量的PG,即,PG和OSD之间是“多对多”映射关系。

OSD:RADOS中的存储节点被称为OSD

架构图讲解:

1:文件上传,先将文件切片成N个object(如果开启了cephFS,可以使用MDS缓存)
2:切片后的文件object会存入到Ceph中
3:文件存储前,会经过CRUSH算法,计算当前文件存储归结于哪个PG
4:PG是逻辑概念上对文件存储范围划分的索引
5:根据PG索引将文件存储到指定服务器的OSD中

1.2 Ceph集群搭建

在这里插入图片描述

​ 集群结构如上图,server1作为主节点(Dashbaord、mon、mds、rgw、mgr、osd),server2和server3作为子节点(mon、mds、rgw、mgr、osd)。

节点中信息说明:

dashbaord:Ceph可视化管理界面。
rgw:RADOSGW,Ceph对象网关,使客户端能够利用标准对象存储API来访问Ceph集群。
mgr:ceph-mgr,主要目标实现 ceph 集群的管理,为外界提供统一的入口。

节点信息:
server1:192.168.100.131
server2:192.168.100.132
server3:192.168.100.133

1.2.1 准备工作

1)机器名称修改(131、132、133都执行)

我们给每台机器一个别名192.168.100.131->CENTOS1,192.168.100.132->CENTOS2,192.168.100.133->CENTOS3。

修改192.168.100.131/etc/hostname,添加CENTOS1

在这里插入图片描述

修改192.168.100.(同上)/etc/hostname,添加CENTOS2(同上)

修改192.168.100.133/etc/hostname,添加CENTOS3(同上)

配置名字解析IP:分别修改131132133/etc/hosts文件,添加如下映射:

192.168.100.131 CENTOS1
192.168.100.132 CENTOS2
192.168.100.133 CENTOS3

2)YUM源修改(131、132、133都执行)

这里采用清华镜像源,提升加载速度。

vi /etc/yum.repos.d/ceph.repo,添加如下内容:

[Ceph]
name=Ceph packages for $basearch
baseurl=https://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/x86_64/
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://download.ceph.com/keys/release.asc

[Ceph-noarch]
name=Ceph noarch packages
# 清华源
baseurl=https://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/noarch/
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://download.ceph.com/keys/release.asc

[ceph-source]
name=Ceph source packages
baseurl=https://mirrors.tuna.tsinghua.edu.cn/ceph/rpm-mimic/el7/SRPMS/
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://download.ceph.com/keys/release.asc

3)ceph与ceph-deploy安装(131)

更新yum源,并安装cephceph-deploy,这个过程非常耗时间,执行如下命令:

yum update && yum -y install ceph ceph-deploy

注意:yum updateyum -y install python2-pip最好在每台机器都更新下yum。

安装过程中, 如果执行ceph-deploy出现ImportError: No module named pkg_resources,则需要安装python2-pip,执行yum -y install python2-pip安装即可。

如果遇到如下错误,安装下epel即可。

在这里插入图片描述

操作命令:(最好先执行该命令)

yum install epel-release -y
 
yum install lttng-ust -y

4)NTP时间同步工具(131执行)

为了保证时间同步,我们需要安装NTP时间同步工具:

yum install ntp ntpdate ntp-doc -y

设为开机启动:

systemctl enable ntpd

设置每隔1小时自动校准同步。编辑 vi /etc/rc.d/rc.local 追加:

/usr/sbin/ntpdate ntp1.aliyun.com > /dev/null 2>&1; /sbin/hwclock -w

配置定时任务, 执行crontab -e 加入:

0 */1 * * * ntpdate ntp1.aliyun.com > /dev/null 2>&1; /sbin/hwclock -w

5)免密配置(131、132、133都执行)

官方建议不用系统内置用户, 创建名为cuser用户, 密码也设为cuser:

useradd -d /home/cuser -m cuser
passwd cuser

设置sudo权限:(免密+只读权限)

echo "cuser ALL = (root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/cuser
sudo chmod 0440 /etc/sudoers.d/cuser

6)生成秘钥:(131执行)

切换用户: su cuser

执行ssh-keygen,一直按默认提示点击生成RSA密钥信息。

在这里插入图片描述

分发密钥至各机器节点

ssh-copy-id cuser@CENTOS1
ssh-copy-id cuser@CENTOS2
ssh-copy-id cuser@CENTOS3

在这里插入图片描述

修改管理节点上的 ~/.ssh/config (当前用户目录下的.ssh/config)文件, 简化SSH远程连接时的输入信息:

管理节点是会有root和cuser多个用户, ssh远程连接默认会以当前用户身份进行登陆, 如果我们是root身份进行远程连接, 还是需要输入密码,我们可以修改配置 使用root远程连接时也不用输入密码。

切换root身份,

su root

编辑config

vi ~/.ssh/config

添加如下内容:

Host CENTOS1
   Hostname CENTOS1
   User cuser
Host CENTOS2
   Hostname CENTOS2
   User cuser
Host CENTOS3
   Hostname CENTOS3
   User cuser

修改文件权限:

chmod 600 ~/.ssh/config

禁用SELINUX:

vi /etc/selinux/config

SELINUX=disabled

1.2.2 集群搭建

安装集群,用root安装,可以避免很多权限问题。

1)创建集群管理目录,作为ceph配置信息存储目录。

mkdir -p /usr/local/bobo/cephcluster

cd /usr/local/bobo/cephcluster

2)创建集群

ceph-deploy new CENTOS1  CENTOS2 CENTOS3

创建成功后, 会生配置文件和秘钥信息。

在这里插入图片描述

3)修改配置文件

编辑ceph.conf文件vi /usr/local/bobo/cephcluster/ceph.conf,添加如下配置:

#对外开放网段
public network = 192.168.100.0/24
# 设置pool池默认分配数量
osd pool default size = 2
# 容忍更多的时钟误差
mon clock drift allowed = 2
mon clock drift warn backoff = 30
# 允许删除pool
mon_allow_pool_delete = true
[mgr]
# 开启WEB仪表盘
mgr modules = dashboard

注意:Pool是存储对象的逻辑分区,它规定了数据冗余的类型和对应的副本分布策略。

完整内容如下:

在这里插入图片描述

文件修改后执行安装(131执行),此时3台机器都会执行安装执行如下安装命令:

ceph-deploy install  CENTOS1  CENTOS2 CENTOS3

如果出现ceph_deploy][ERROR ] RuntimeError: Failed to execute command: ceph --version错误,可以直接在每个节点单独执行yum -y install ceph进行单独安装。如果没有仓库文件ceph.repo, 按上面的步骤手工创建。

4)初始化Monitor信息

ceph-deploy mon create-initial

此时会生成很多秘钥文件信息
在这里插入图片描述

5)同步管理信息

ceph-deploy admin  CENTOS1  CENTOS2 CENTOS3

6)安装mgr(管理守护进程)

ceph-deploy mgr create CENTOS1  CENTOS2 CENTOS3

7)安装rgw

ceph-deploy rgw create CENTOS1 CENTOS2 CENTOS3

mds服务:

ceph-deploy mds create CENTOS1 CENTOS2 CENTOS3

注意:任意一个环节安装失败了,需要卸载重装:

ceph-deploy purge CENTOS1 CENTOS2 CENTOS3
ceph-deploy purgedata CENTOS1 CENTOS2 CENTOS3
ceph-deploy forgetkeys

将三台节点的mon信息也删除:

rm -rf /var/run/ceph/

如果出现错误:

ceph_deploy][ERROR ] RuntimeError: Failed to execute command: ceph --version

可以在各节点上单独进行安装:

yum -y install ceph 

8)OSD安装

OSD服务是对象存储守护进程, 负责把对象存储到本地文件系统, 必须要有一块独立的磁盘作为存储。如果没有独立磁盘,怎么办? 可以在Linux下面创建一个虚拟磁盘进行挂载

添加磁盘:

执行fdisk -l查看磁盘信息如下,我们需要添加一个磁盘,直接用VMware添加即可。

在这里插入图片描述

使用VMware选择设置->硬盘->添加,如下图:

在这里插入图片描述

一直点击下一步,设置磁盘空间大小为10G即可。操作完后重启虚拟机,并输入fdisk -l查看磁盘信息如下,明显多了/dev/sdb 10G大小。
在这里插入图片描述

执行创建OSD命令:(注意,每条命令都是在131中执行,不要在每台机器中单独执行)

ceph-deploy osd create --data /dev/sdb CENTOS1

ceph-deploy osd create --data /dev/sdb CENTOS2

ceph-deploy osd create --data /dev/sdb CENTOS3

在这里插入图片描述

Monitor查看

/usr/bin下执行./ceph -s可以查看集群状态。

在这里插入图片描述

可以执行ntpdate ntp1.aliyun.com同步各个节点的时间。

如果出现如下情况,执行systemctl restart ceph.target重启每个节点即可(131,132,133都执行)。

在这里插入图片描述

1.2.3 dashboard安装

​ Ceph 提供了原生的Dashboard功能,通过Dashboard可以获取Ceph集群的各种基本状态信息。我们接下来安装一下Dashboard,并使用它的功能。

1)开启dashboard模块

ceph mgr module enable dashboard

2)生成签名

ceph dashboard create-self-signed-cert

3)创建目录

mkdir -p /usr/local/bobo/cephcluster/mgr-dashboard

4)生成密钥对

openssl req -new -nodes -x509   -subj "/O=IT/CN=ceph-mgr-dashboard" -days 3650   -keyout dashboard.key -out dashboard.crt -extensions v3_ca

在这里插入图片描述

5)启动dashboard

ceph mgr module disable dashboard
ceph mgr module enable dashboard

6)设置IP与PORT

ceph config set mgr mgr/dashboard/server_addr 192.168.100.131
ceph config set mgr mgr/dashboard/server_port 9001

7)关闭HTTPS

ceph config set mgr mgr/dashboard/ssl false

8)查看服务信息

ceph mgr services

在这里插入图片描述

9)设置管理员账号密码

ceph dashboard set-login-credentials admin admin

10)访问<https://192.168.100.131:8443/#/dashboard>
在这里插入图片描述

11)RGW访问

访问 http://192.168.100.131:7480/ 效果如下:

在这里插入图片描述

1.3 Cephfs管理

集群创建完后, 默认没有文件系统, 我们创建一个Cephfs可以支持对外访问的文件系统。

1)创建两个存储池, 执行两条命令:

ceph osd pool create cephfs_data 128
ceph osd pool create cephfs_metadata 64

少于5个OSD可把pg_num设置为128

OSD数量在5到10,可以设置pg_num为512

OSD数量在10到50,可以设置pg_num为4096

OSD数量大于50,需要计算pg_num的值

通过下面命令可以列出当前创建的存储池:

ceph osd lspools

2)创建fs, 名称为fs_test:

ceph fs new fs_test cephfs_metadata cephfs_data

3)状态查看, 以下信息代表正常

ceph fs ls

name: fs_test, metadata pool: cephfs_metadata, data pools: [cephfs_data ]

ceph mds stat:

fs_test-0/0/1 up

4)fuse挂载

先确定ceph-fuse命令能执行, 如果没有, 则安装:

 yum -y install ceph-fuse

创建挂载目录

mkdir -p /usr/local/gupao/cephfs_directory

挂载cephfs

ceph-fuse -k /etc/ceph/ceph.client.admin.keyring -m 192.168.100.131:6789 /usr/local/gupao/cephfs_directory

出现下面信息表示挂载成功了:

ceph-fuse[28003]: starting fuse

5)挂载信息查看

[root@CENTOS1 cephcluster]# df -h
Filesystem               Size  Used Avail Use% Mounted on
devtmpfs                 4.1G     0  4.1G   0% /dev
tmpfs                    4.1G     0  4.1G   0% /dev/shm
tmpfs                    4.1G   20M  4.1G   1% /run
tmpfs                    4.1G     0  4.1G   0% /sys/fs/cgroup
/dev/mapper/centos-root   17G  2.0G   16G  12% /
/dev/sda1               1014M  189M  826M  19% /boot
tmpfs                    4.1G   28K  4.1G   1% /var/lib/ceph/osd/ceph-0
tmpfs                    838M     0  838M   0% /run/user/0
ceph-fuse                 13G     0   13G   0% /usr/local/gupao/cephfs_directory

1.4 Ceph Swift API接口开发

  Swift是由Rackspace开发的用来为云计算提供可扩展存储的项目。专注于对象存储, 并提供一套REST风格的Api来访问, 与Ceph强一致性不同, 它是最终一致性。两者都是优秀的开源项目, 并无明显优劣之分,在使用场景上有所不同, 如果是专注于对象存储, 那么可以选择swift即可满足需要, 如果还有块存储要求, 那么选择Ceph更为合适。这里选择Ceph, 因为通过网关可以适配兼容swift api, 同时在数据访问上具有较强的扩展性。

1.4.1 准备工作

创建Swift用户, 用于接口请求认证

sudo radosgw-admin user create --subuser="cephtester:subtester" --uid="cephtester" --display-name="cephtester" --key-type=swift --secret="gupao" --access=full

uid 为主用户, subuser为子用户信息, secret指定密钥, 不指定则随机生成, access拥有权限设定,代码中需使用返回信息中的user和secret_key。

在这里插入图片描述

swift_keys:

"swift_keys": [
    {
        "user": "cephtester:subtester",
        "secret_key": "gupao"
    }
],

创建管理员账号:

radosgw-admin user create --uid=mgruser --display-name=mgruser  --system

返回信息如下:

{
    "user_id": "mgruser",
    "display_name": "mgruser",
    "email": "",
    "suspended": 0,
    "max_buckets": 1000,
    "auid": 0,
    "subusers": [],
    "keys": [
        {
            "user": "mgruser",
            "access_key": "AZ6L40PH9WB37EKVVMCZ",
            "secret_key": "rk8PEjtYaMTo7nMDM62hqqN1tOnZPBEe4GA0LQMW"
        }
    ],
    "swift_keys": [],
    "caps": [],
    "op_mask": "read, write, delete",
    "system": "true",
    "default_placement": "",
    "placement_tags": [],
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "user_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "temp_url_keys": [],
    "type": "rgw",
    "mfa_ids": []
}

根据生成的access_key与secret_key, 执行:

ceph dashboard set-rgw-api-access-key AZ6L40PH9WB37EKVVMCZ
ceph dashboard set-rgw-api-secret-key rk8PEjtYaMTo7nMDM62hqqN1tOnZPBEe4GA0LQMW

打开管理界面,http://192.168.100.131:9001/#/rgw/user 可以查看到我们刚才创建的两个用户:

在这里插入图片描述

1.4.2 文件服务搭建

  我们搭建一个单独的工程,专门用于实现文件上传和文件下载,工程坐标如下:

<groupId>com.bobo.vip.mall</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>mall-file-service</artifactId>

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mall-service</artifactId>
        <groupId>com.gupaoedu.vip.mall</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>mall-file-service</artifactId>
    <description>
        文件上传微服务
    </description>

    <dependencies>
        <!-- Rados Java Api依赖 -->
        <dependency>
            <groupId>com.ceph</groupId>
            <artifactId>rados</artifactId>
            <version>0.6.0</version>
        </dependency>
        <!-- Cephfs 文件系统依赖 -->
        <dependency>
            <groupId>com.ceph</groupId>
            <artifactId>libcephfs</artifactId>
            <version>0.80.5</version>
        </dependency>

        <!--swift-->
        <dependency>
            <groupId>org.javaswift</groupId>
            <artifactId>joss</artifactId>
            <version>0.10.2</version>
        </dependency>
    </dependencies>
</project>

bootstrap.yml:

server:
  port: 8082
spring:
  application:
    name: mall-file
  cloud:
    nacos:
      config:
        file-extension: yaml
        server-addr: 192.168.100.130:8848
      discovery:
        #Nacos的注册地址
        server-addr: 192.168.100.130:8848

ceph:
  username: cephtester:subtester  #Ceph配置 主用户名:子用户名
  password: gupao #秘钥
  authUrl: http://192.168.100.131:7480/auth/1.0 #接口访问路径
  defaultContainerName: user_datainfo #默认容器名字
#图片路径
cephurl: http://localhost:8082/file/download/

#日志配置
logging:
  pattern:
    console: "%msg%n"

创建com.bobo.vip.mall.file.ceph.ContainerConfig配置类,在类中创建AccountContainer对象,代码如下:

@Configuration
@ConfigurationProperties(prefix = "ceph")
@Data
public class ContainerConfig {

    private String username;
    private String password;
    private String authUrl;
    private String defaultContainerName;

    /***
     * Ceph的账户信息配置
     * @return
     */
    @Bean
    public Account account(){
        // Ceph的账户信息配置
        AccountConfig config = new AccountConfig();
        config.setUsername(username);
        config.setPassword(password);
        config.setAuthUrl(authUrl);
        config.setAuthenticationMethod(AuthenticationMethod.BASIC);
        return  new AccountFactory(config).createAccount();
    }

    /***
     * 容器对象
     * @return
     */
    @Bean
    public Container container(){
        // 获取容器信息
        Container newContainer = account().getContainer(defaultContainerName);
        if(!newContainer.exists()) {
            return newContainer.create();
        }else {
            return newContainer;
        }
    }
}

创建文件上传下载工具类com.gupaoedu.vip.mall.file.ceph.FileHandler,代码如下:

@Component
public class FileHandler {

    @Autowired
    private Container container;

    /****
     * 文件上传
     */
    public void upload(String filename,byte[] buffer) {
        //获取容器对象
        StoredObject object = container.getObject(filename);
        //文件上传
        object.uploadObject(buffer);
    }

    /***
     * 文件下载
     */
    public byte[] download(String filename){
        //获取容器中远程存储的信息
        StoredObject object = container.getObject(filename);
        //执行文件下载
        byte[] bytes = object.downloadObject();
        return bytes;
    }
}

控制器创建:com.gupaoedu.vip.mall.file.controller.FileController

@RestController
@RequestMapping(value = "/file")
public class FileController {
    @Autowired
    private FileHandler fileHandler;

    @Value("${cephurl}")
    private String cephurl;
    /***
     * 文件上传
     * @param file
     * @return
     */
    @PostMapping(value = "/upload")
    public RespResult upload(MultipartFile file) throws IOException {
        //上传
        fileHandler.upload(file.getOriginalFilename(),file.getBytes());
        return RespResult.ok(cephurl+file.getOriginalFilename());
    }

    /***
     * 下载
     * @return
     */
    @GetMapping(value = "/download/{filename}")
    public void download(@PathVariable String filename, HttpServletResponse response) throws IOException {
        //下载
        byte[] bytes = fileHandler.download(filename);
        //输出文件
        ServletOutputStream os = response.getOutputStream();
        os.write(bytes);
    }
}

创建启动类:com.gupaoedu.vip.mall.file.MallFileApplication

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MallFileApplication {

    public static void main(String[] args) {
        SpringApplication.run(MallFileApplication.class,args);
    }
}

文件上传测试http://localhost:8082/file/upload

在这里插入图片描述

文件下载测试

在这里插入图片描述

搭建成功~!

相关推荐
<p> <span style="font-size:20px;">   <span style="font-size:18px;">通过本次课程的学习您将会对openstack的部署有一个全新的认识,openstack的部署一切变的那么容易,您只需要修改一些配置文件即可轻松部署一套openstack环境,同时本次课程介绍了企业生产环境中常用的后端分布式存储系统CEPH作为openstack的后端存储,从而实现了云主机的热迁移,同时可以在CEPH dashboard页面可以查看整个CEPH集群的状态,</span></span><span style="font-size:20px;color:#C00000;"><span style="font-size:18px;">本次课程环境是基于</span><span style="font-size:18px;">https://edu.csdn.net/course/detail/24768</span></span><span style="font-size:20px;color:#C00000;"></span><span style="font-size:18px;">课程的升级版本,</span><span style="font-size:20px;color:#C00000;"><span style="font-size:18px;">本次课程不再对openstack的每一个组件进行详细的讲解,针对openstack每个组件的详细讲解,请购买(</span><u><span style="font-size:18px;">https://edu.csdn.net/course/detail/24768</span></u><a href="https://edu.51cto.com/course/17586.html),%E6%9C%AC%E6%AC%A1%E8%AF%BE%E7%A8%8B%E7%9A%84%E9%87%8D%E7%82%B9%E6%98%AFCEPH%E9%9B%86%E7%BE%A4%E9%83%A8%E7%BD%B2%E4%BB%A5%E5%8F%8ACEPHyu"><u><span style="font-size:18px;"></span></u><span style="font-size:18px;">),本次课程的重点是CEPH集群部署以及CEPH</span></a><span style="font-size:18px;">与openstack的对接和对接之后的功能测试。</span></span> </p> <p> <span style="font-size:20px;color:#C00000;"><span style="font-size:18px;"><img src="https://img-bss.csdn.net/201909191344127214.png" alt="" /><br /> </span></span> </p> <p> <span style="font-size:20px;color:#C00000;"><span style="font-size:18px;"><img src="https://img-bss.csdn.net/201909191344248085.png" alt="" /><br /> </span></span> </p> <p> <span style="font-size:20px;color:#C00000;"><span style="font-size:18px;"><img src="https://img-bss.csdn.net/201909191344372094.png" alt="" /><br /> </span></span> </p> <p> <span style="font-size:20px;color:#C00000;"><span style="font-size:18px;"><img src="https://img-bss.csdn.net/201909191344472072.png" alt="" /><br /> </span></span> </p>
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页
实付 29.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值