微服务应用一般划分哪些租户 微服务应用实例

admin2024-06-04  21

1. SpringCloud概述

1.1 互联网应用架构

1.1.1 单体应用架构
  • 在诞⽣之初,项目的⽤户量、数据量规模都⽐较⼩,项目所有的功能模块都放在一个工程中编码、
    编译、打包并且部署在一个Tomcat容器中的架构模式就是单体应用架构,这样的架构既简单实 ⽤、便
    于维护,成本⼜低,成为了那个时代的主流架构⽅式。

微服务应用一般划分哪些租户 微服务应用实例,微服务应用一般划分哪些租户 微服务应用实例_microservices,第1张

  • 单体项目优点
  • 高效开发:项⽬前期开发节奏快,团队成员少的时候能够快速迭代
  • 架构简单:MVC架构,只需要借助IDE开发、调试即可
  • 易于测试:只需要通过单元测试或者浏览器完成
  • 易于部署:打包成单⼀可执⾏的jar或者打成war包放到容器内启动
  • 单体架构的应用比较容易部署、测试, 在项目的初期,单体应用可以很好地运行。
  • 然而,随着需求的不断增加, 越来越多的人加入开发团队,代码库也在飞速地膨胀。慢慢地,单体应用变得越来越臃肿,可维护性、灵活性逐渐降低,维护成本越来越高。
  • 单体项目的缺点
  • 可靠性差: 某个应用Bug,例如死循环、内存溢出等, 可能会导致整个应用的崩溃
  • 复杂性高: 以一个百万行级别的单体应用为例,整个项目包含的模块多、模块的边界模糊、 依赖关系不清晰、 代码质量参差不齐、 混乱地堆砌在一起。使得整个项目非常复杂。
  • 扩展能力受限: 单体应用只能作为一个整体进行扩展,无法根据业务模块的需要进行伸缩。例如,
    应用中有的模块是计算密集型的,它需要强劲的CPU; 有的模块则是IO密集型的,需要更大的内
    存。 由于这些模块部署在一起,不得不在硬件的选择上做出妥协。
  • 业务量上涨之后,单体应用架构进一步丰富变化,比如应用集群部署、使用Nginx进行负载均衡、增加
    缓存服务器、增加文件服务器、数据库集群并做读写分离等,通过以上措施增强应对高并发的能力、应
    对一定的复杂业务场景,但依然属于单体应用架构。
1.1.2 垂直应用架构
  • 为了避免上⾯提到的那些问题,开始做模块的垂直划分,做垂直划分的原则是基于现有的业务
    特性来做,核心目标标第⼀个是为了业务之间互不影响,第⼆个是在研发团队的壮⼤后为了提⾼效率,
    减少组件之间的依赖。
  • 垂直应用架构优点
  • 系统拆分实现了流量分担,解决了并发问题
  • 可以针对不同模块进⾏优化
  • ⽅便⽔平扩展,负载均衡,容错率提⾼
  • 系统间相互独⽴,互不影响,新的业务迭代时更加⾼效
  • 垂直应用架构缺点
  • 服务之间相互调⽤,如果某个服务的端⼝或者ip地址发⽣改变,调⽤的系统得⼿动改变
  • 搭建集群之后,实现负载均衡⽐较复杂,如:内⽹负载,在迁移机器时会影响调⽤⽅的路 由,导致线上故障
  • 服务之间调⽤⽅式不统⼀,基于 httpclient 、 webservice ,接⼝协议不统⼀
  • 服务监控不到位:除了依靠端⼝、进程的监控,调⽤的成功率、失败率、总耗时等等这些监 控指标
    是没有的
1.1.3 SOA应用架构
  • 在做了垂直划分以后,模块随之增多,维护的成本在也变⾼,⼀些通⽤的业务和模块重复的越来越
    多,为了解决上⾯提到的接⼝协议不统⼀、服务⽆法监控、服务的负载均衡,引⼊了阿⾥巴巴开源的
    Dubbo ,⼀款⾼性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。
  • 它提供了三⼤核⼼能⼒:⾯向接⼝的远程⽅法调⽤,智能容错和负载均衡,以及服务⾃动注册和发现。
  • SOA (Service-Oriented Architecture),即面向服务的架构。根据实际业务,把系统拆分成合适
    的、独立部署的模块,模块之间相互独立(通过Webservice/Dubbo等技术进行通信)。
  • 优点:分布式、松耦合、扩展灵活、可重用。
  • 缺点:服务抽取粒度较大、服务调用方和提供方耦合度较高(接口耦合度)
1.1.4 微服务架构
  • 微服务架构可以说是SOA架构的一种拓展,这种架构模式下它拆分粒度更小、服务更独立。把应用
    拆分成为一个个微小的服务,不同的服务可以使用不同的开发语言和存储,服务之间往往通过Restful等
  • 轻量级通信。微服务架构关键在于微小、独立、轻量级通信。微服务是在 SOA 上做的升华粒度更加细致,微服务架构强调的⼀个重点是业务需要彻底的组件化和服务化

1.2 微服务架构思想

  • 微服务架构设计的核心思想就是“微”,拆分的粒度相对比较小,这样的话单一职责、开发的耦合度
    就会降低、微小的功能可以独立部署扩展、灵活性强,升级改造影响范围小
  • 微服务架构的优点
  • 微服务很小,便于特定业务功能的聚焦
  • 微服务很小,每个微服务都可以被一个小团队单独实施(开发、测试、部署上线、运维),团队合
    作一定程度解耦,便于实施敏捷开发
  • 微服务很小,便于重用和模块之间的组装
  • 微服务很独立,那么不同的微服务可以使用不同的语言开发,松耦合
  • 微服务架构下,我们更容易引入新技术
  • 微服务架构的缺点
  • 微服务架构下,分布式复杂难以管理,当服务数量增加,管理将越加复杂;
  • 微服务架构下,分布式链路跟踪难等;

1.3 微服务架构核心概念

1.3.1 服务注册与发现
  • 服务注册与服务发现
  • 例如:职位搜索 ->简历服务
  • 服务提供者:简历服务
  • 服务消费者:职位搜索
  • 服务注册: 服务提供者将所提供服务的信息(服务器IP和端口、服务访问协议等)注册/登记到注册
    中心
  • 服务发现: 服务消费者能够从注册中心获取到较为实时的服务列表,然后根究一定的策略选择一个
    服务访问

微服务应用一般划分哪些租户 微服务应用实例,微服务应用一般划分哪些租户 微服务应用实例_java_02,第2张

1.3.2 负载均衡
  • 负载均衡即将请求压力分配到多个服务器(应用服务器、数据库服务器等),以此来提高服务的性能、
    可靠性
1.3.3 链路追踪
  • 微服务架构越发流行,一个项目往往拆分成很多个服务,那么一次请求就需要涉及到很多个服务。不同
    的微服务可能是由不同的团队开发、可能使用不同的编程语言实现、整个项目也有可能部署在了很多服
    务器上(甚至百台、千台)横跨多个不同的数据中心。
  • 所谓链路追踪,就是对一次请求涉及的很多个服务链路进行日志记录、性能监控
1.3.4 API网关
  • 微服务架构下,不同的微服务往往会有不同的访问地址,客户端可能需要调用多个服务的接口才能
    完成一个业务需求,如果让客户端直接与各个微服务通信可能出现:
  • 客户端需要调用不同的url地址,增加了维护调用难度
  • 在一定的场景下,也存在跨域请求的问题(前后端分离就会碰到跨域问题,原本我们在后端采
    用Cors就能解决,现在利用网关,那么就放在网关这层做好了)
  • 每个微服务都需要进行单独的身份认证
  • 那么,API网关就可以较好的统一处理上述问题,API请求调用统一接入API网关层,由网关转发请
    求。API网关更专注在安全、路由、流量等问题的处理上(微服务团队专注于处理业务逻辑即可),它的
    功能比如
  • 统一接入(路由)
  • 安全防护(统一鉴权,负责网关访问身份认证验证,与“访问认证中心”通信,实际认证业务逻辑
    交移“访问认证中心”处理)
  • 黑白名单(实现通过IP地址控制禁止访问网关功能,控制访问)
  • 协议适配(实现通信协议校验、适配转换的功能)
  • 流量管控(限流)
  • 长短链接支持
  • 容错能力(负载均衡)

1.4 SpringCloud介绍

1.4.1 基本概述
  • Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分
    布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,
    都可以用 Spring Boot的开发风格做到一键启动和部署。
  • Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
1.4.2 解决的问题
  • Spring Cloud 规范及实现意图要解决的问题其实就是微服务架构实施过程中存在的一些问题,比如
    微服务架构中的服务注册发现问题、网络问题(比如熔断场景)、统一认证安全授权问题、负载均衡问
    题、链路追踪等问题
  • Distributed/versioned configuration (分布式/版本化配置)
  • Service registration and discovery (服务注册和发现)
  • Routing (智能路由)
  • Service-to-service calls (服务调用)
  • Load balancing (负载均衡)
  • Circuit Breakers (熔断器)
  • Global locks (全局锁)
  • Leadership election and cluster state ( 选举与集群状态管理)
  • Distributed messaging (分布式消息传递平台)

1.5 SpringCloud架构

1.5.1 核心组件
  • Spring Cloud 生态圈中的组件,按照发展可以分为第一代 Spring Cloud组件和第二代 Spring
    Cloud组件。
1.5.2 体系结构

微服务应用一般划分哪些租户 微服务应用实例,微服务应用一般划分哪些租户 微服务应用实例_spring_03,第3张

  • Spring Cloud中的各组件协同工作,才能够支持一个完整的微服务架构。比如
  • 注册中心负责服务的注册与发现,很好将各服务连接起来
  • API网关负责转发所有外来的请求
  • 断路器负责监控服务之间的调用情况,连续多次失败进行熔断保护。
  • 配置中心提供了统一的配置信息管理服务,可以实时的通知各个服务获取最新的配置信息
1.5.4 与Bubbo对比
  • Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,基于RPC调用,对于目前使用率较高的
    Spring Cloud Netflix来说,它是基于HTTP的,所以效率上没有Dubbo高,但问题在于Dubbo体系的组
    件不全,不能够提供一站式解决方案,比如服务注册与发现需要借助于Zookeeper等实现,而Spring
    Cloud Netflix则是真正的提供了一站式服务化解决方案,且有Spring大家族背景

2. 微服务案例

2.1 案例说明

  • 本部分我们按照普通方式模拟一个微服务之间的调用,后续我们将一步步使用Spring Cloud的组件对案
    例进行改造。
  • 完整业务流程图

2.2 环境准备

2.2.1 数据库搭建
CREATE TABLE products
(
    id          INT PRIMARY KEY AUTO_INCREMENT,
    NAME        VARCHAR(50),  #商品名称
    price       DOUBLE,
    flag        VARCHAR(2),   #上架状态
    goods_desc  VARCHAR(100), #商品描述
    images      VARCHAR(400), #商品图片
    goods_stock INT,          #商品库存
    goods_type  VARCHAR(20)   #商品类型
);
2.2.2 工程架构

微服务应用一般划分哪些租户 微服务应用实例,微服务应用一般划分哪些租户 微服务应用实例_java_04,第4张

2.3 创建父工程

2.3.1 创建项目
  • 创建一个 maven 项目, 项目名为 lg-parent
2.3.2 添加maven配置
  • 添加maven配置 <packaging>pom</packaging> 修改打包方式为 pom
<!-- 将打包方式修改为 pom -->
    <packaging>pom</packaging>
    <!--spring boot 父启动器依赖-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>
    <dependencies>
        <!--web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--日志依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok工具-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
        <!-- Actuator可以帮助你监控和管理Spring Boot应用-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
  • 添加打包插件
<build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <!--打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • 删除 src 目录

2.4 创建公共微服务

2.4.1 创建项目
  • 在 lg-parent下 创建一个maven项目, 项目名 lg-service-common
2.4.2 添加maven配置
  • maven配置如下 :
<?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>lg-parent</artifactId>
        <groupId>cn.knightzz</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lg-service-common</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency> 
        <!--pojo持久化使用-->
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>javax.persistence-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

</project>
2.4.3 生成实体类
  • 需要下载 MybatisX 插件
  • 点击数据库表, 选中插件
  • 生成实体类

微服务应用一般划分哪些租户 微服务应用实例,微服务应用一般划分哪些租户 微服务应用实例_java_05,第5张

  • 实体类代码
package cn.knightzz.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import lombok.Data;

/**
 * 
 * @author knightzz98
 * @TableName products
 */
@TableName(value ="products")
@Data
public class Products implements Serializable {

    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
    private Double price;
    private String flag;
    private String goodsDesc;
    private String images;
    private Integer goodsStock;
    private String goodsType;
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

2.5 创建产品微服务

2.5.1 创建项目
  • 创建名为 lg-service-product 的微服务项目
2.5.2 添加配置
  • 引入公共微服务的依赖
<?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>lg-parent</artifactId>
        <groupId>cn.knightzz</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lg-service-product</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>cn.knightzz</groupId>
            <artifactId>lg-service-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
  • 添加数据库连接配置
server:
  port: 9000
spring:
  application:
    # 微服务名称
    name: lg-service-product
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC&characterEncoding=UTF-8
    username: root
    password: 123456
2.5.2 代码生成

微服务应用一般划分哪些租户 微服务应用实例,微服务应用一般划分哪些租户 微服务应用实例_spring_06,第6张

微服务应用一般划分哪些租户 微服务应用实例,微服务应用一般划分哪些租户 微服务应用实例_微服务应用一般划分哪些租户_07,第7张

  • 这里注意, 因为默认会生成 实体类, 所以我们需要删除 entity 包, 使用 lg-service-common 的实体类
2.5.3 Service层代码
  • ProductsService接口
package cn.knightzz.service;

import cn.knightzz.entity.Products;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 *
 * @author knightzz98
 */
public interface ProductsService extends IService<Products> {


    /**
     * 根据id查询产品信息
     * @param id
     * @return
     */
    public Products findById(Integer id);
}
  • ProductsServiceImpl
package cn.knightzz.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.knightzz.entity.Products;
import cn.knightzz.service.ProductsService;
import cn.knightzz.mapper.ProductsMapper;
import org.springframework.stereotype.Service;

/**
 *
 * @author knightzz98
 */
@Service
public class ProductsServiceImpl extends ServiceImpl<ProductsMapper, Products>
    implements ProductsService{

    @Override
    public Products findById(Integer id) {
        // 可以直接使用 baseMapper 查询数据
        Products products = baseMapper.selectById(id);
        return products;
    }
}
2.5.4 Controller层代码
package cn.knightzz.controller;

import cn.knightzz.entity.Products;
import cn.knightzz.service.ProductsService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author 王天赐
 * @title: ProductController
 * @projectName springcloud-lg
 * @description:
 * @website http://knightzz.cn/
 * @github https://github.com/knightzz1998
 * @date 2022/2/11 10:56
 */
@RestController
@RequestMapping("/products")
public class ProductController {

    @Resource
    ProductsService productsService;

    @RequestMapping("/query/{id}")
    public Products query(@PathVariable Integer id){
        return productsService.findById(id);
    }
}
2.5.5 创建启动类
package cn.knightzz;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("cn.knightzz.mapper")
public class ProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }
}
2.5.5 代码测试
  • 打开 apipost 或者 postman 进行测试, 注意, 需要自己向数据库里面添加一些数据

2.6 页面静态化微服务

2.6.1 创建项目
  • 创建一个名为 lg-service-page 的maven项目
2.6.2 添加配置
  • 添加 maven 配置
<dependencies>
        <dependency>
            <groupId>cn.knightzz</groupId>
            <artifactId>lg-service-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
  • application.yml
server:
  port: 9100
spring:
  application:
    # 微服务名称
    name: lg-service-page
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC&characterEncoding=UTF-8
    username: root
    password: 123456
2.6.3 PageController
  • PageController
package cn.knightzz.page.controller;

import cn.knightzz.entity.Products;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;


/**
 * @author 王天赐
 * @title: PageController
 * @projectName springcloud-lg
 * @description:
 * @website http://knightzz.cn/
 * @github https://github.com/knightzz1998
 * @date 2022/2/11 11:55
 */
@RestController
@RequestMapping("/page")
public class PageController {

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/getData/{id}")
    public Products findDataById(@PathVariable Integer id){

        Products product = restTemplate.getForObject("http://localhost:9000/products/query/" + id, Products.class);
        System.out.println("从 lg-service-product 模块得到的数据 " + product);
        return product;
    }
}
2.6.4 页面启动类
  • 页面启动类
package cn.knightzz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class PageApplication {

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

    @Bean
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}

2.7 案例代码存在的问题

  • 我们在页面静态化微服务中使用RestTemplate调用商品微服务的商品状态接口时(Restful API 接
    口)。在微服务分布式集群环境下会存在什么问题呢?怎么解决?
  • 存在的问题:
  • 在服务消费者中,我们把url地址硬编码到代码中,不方便后期维护。
  • 服务提供者只有一个服务,即便服务提供者形成集群,服务消费者还需要自己实现负载均衡。
  • 在服务消费者中,不清楚服务提供者的状态。
  • 服务消费者调用服务提供者时候,如果出现故障能否及时发现不向用户抛出异常页面?
  • RestTemplate这种请求调用方式是否还有优化空间?能不能类似于Dubbo那样玩?
  • 这么多的微服务统一认证如何实现?
  • 配置文件每次都修改好多个很麻烦!?
  • 上述分析出的问题,其实就是微服务架构中必然面临的一些问题:
  • 服务管理:自动注册与发现、状态监管
  • 服务负载均衡
  • 熔断
  • 远程过程调用
  • 网关拦截、路由转发
  • 统一认证
  • 集中式配置管理,配置信息实时自动更新
  • 这些问题,Spring Cloud 体系都有解决方案

3. 初代SpringCloud核心组件

  • 说明:上面提到网关组件Zuul性能一般,未来将退出Spring Cloud 生态圈
  • 各组件整体结构如下:

从形式上来说,Feign一个顶三,Feign = RestTemplate + Ribbon + Hystrix

微服务应用一般划分哪些租户 微服务应用实例,微服务应用一般划分哪些租户 微服务应用实例_微服务应用一般划分哪些租户_08,第8张


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!