浅谈springcloud入门(六)-服务网关Gateway

一、GateWay的出现

在SpringCloud中网关的实现主要包括两种:gateway和zuul

Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。

Zuul是Netflix的开源项目,Spring Cloud将其收纳成为自己的一个子组件。zuul用的是多线程阻塞模型,它本质上就是一个同步 Servlet,这样的模型比较简单,他都问题是多线程之间上下文切换是有开销的,线程越多开销就越大。线程池数量固定意味着能力接受的请求数固定,当后台请求变慢,面对大量的请求,线程池中的线程容易被耗尽,后续的请求会被拒绝。

在Zuul 2.0中它采用了 Netty 实现异步非阻塞编程模型,异步非阻塞模式对线程的消耗比较少,对线程上线文切换的消耗也比较小,并且可以接受更多的请求。它的问题就是线程模型比较复杂,要求深究底层原理需要花一些功夫。

Spring Cloud Gateway是Spring Cloud自己的产物,基于Spring 5 和Spring Boot 2.0 开发,Spring Cloud Gateway的出现是为了代替zuul,在Spring Cloud 高版本中没有对zuul 2.0进行集成,SpringCloud Gateway使用了高性能的Reactor模式通信框架Netty。

Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流

所以说其实Gateway和zuul 2.0差别不是特别大,都是采用Netty高性能通信框架,性能都挺不错。

二、GateWay的作用

网关(Gateway)又称网间连接器、协议转换器。默认网关在网络层以上实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关的结构也和路由器类似,不同的是互连层。网关既可以用于广域网互连,也可以用于局域网互连。

网关是一种充当转换重任的计算机系统或设备。使用在不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。与网桥只是简单地传达信息不同,网关对收到的信息要重新打包,以适应目的系统的需求。同层–应用层。

网关(Gateway)是将两个使用不同协议的网络段连接在一起的设备。
网关的作用就是对两个网络段中的使用不同传输协议的数据进行互相的翻译转换。

Gateway网关是我们服务的守门神,所有微服务的统一入口

三、GateWay的核心组成

Spring Cloud Gataway有几个核心组成:

  • Filter(过滤器):

Spring Cloud Gateway的Filter和Zuul的过滤器类似,可以在请求发出前后进行一些业务上的处理 ,这里分为两种类型的Filter,分别是Gateway Filter网关filter和Global Filter全局Filter, 他们的区别在后续会讲到。

  • Route(路由):

网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。说白了就是把url请求路由到对应的资源(服务),或者说是一个请求过来Gateway应该怎么把这个请求转发给下游的微服务,转发给谁。

  • Predicate(断言):

这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。简单理解就是处理HTTP请求的匹配规则,在什么样的请情况下才能命中资源继续访问。

四、GateWay的工作原理

下图描述了网关如何工作,当网关收到请求的时候,会根据定义好的路由映射进行匹配,查看当前的请求地址能够匹配那个mapping, 然后将相关的请求发送到下面的web handler, handler中有一些过滤器组成了责任链对请求进行pre处理,然后最终送达到响应的服务器,在目标返回结果之后再通过过滤器进行post处理,最终返回给调用这个,过滤器的虚线用于区分是前置处理还是后置处理,其实整体过程类似于SpringMVC。

五、GateWay的使用

1.创建工程导入依赖
<dependencies>
        <!--服务注册与发现-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>
2.启动类配置
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayServerApplication
{
    public static void main( String[] args )
    {
        SpringApplication.run(GatewayServerApplication .class);
    }
}
3.yml配置

该配置中将断言,跨域,超时一并配置,详情请看代码说明。

server:
  port: 10060 # user服务端口号

eureka:
  client:
    serviceUrl: # Eureka客户端配置,指向注册中心地址
      defaultZone: http://localhost:10010/eureka/
  instance: # 打开IP注册
    instance-id: ${spring.application.name}:${server.port} # 设置实例名称
    prefer-ip-address: true # 开启IP注册

# 指定服务名称,此服务下的集群所有服务都叫此服务名
spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s
      discovery:
        locator:
          enabled: false #开放服务名访问方式
          lower-case-service-id: true #服务名小写
      routes:
        - id: order-server #指定服务名
          uri: lb://order-server #去注册中心找这个服务名
          predicates: #断言,匹配访问的路径 如果路径中有/order/ 那么请求就会给lb://order-server
            - Path=/server/order/**    #服务访问路径
          filters:
            - StripPrefix=2    #请求转发的时候会去掉 /order访问路径
    globalcors: #跨域配置
      cors-configurations:
        '[/**]':
          allowedOrigins: "*" #允许的站点
          allowedMethods:
            - GET
            - POST
            - DELETE
            - PUT
            - HEAD
            - CONNECT
            - TRACE
            - OPTIONS
          allowHeaders:
            - Content-Type

这里除了要注册到Eureak以外,还需要配置Gataway的路由

  • spring.cloud.gateway.discovery.locator.enabled=false: 不开放服务名访问方式

  • spring.cloud.gateway.discovery.locator.lower-case-service-id: true 忽略服务名大小写,大写小写都可以匹配

  • spring.cloud.gateway.routes.id : 指定了路由的服务名,可以自己定义

  • spring.cloud.gateway.routes.uri=lb://user-server : 去注册中心找服务,采用负载均衡的方式请求。其实就是找要调用的服务。

  • spring.cloud.gateway.routes.predicates: 断言,这里使用的Path=/user/**,即匹配访问的路径如果匹配/user/就可以将请求路由(分发)到user-server这个服务上。

  • spring.cloud.gateway.routes.filters :这里使用StripPrefix=1主要是处理前缀 /user ,访问目标服务的时候会去掉前缀访问。这个需要根据url情况来定义。

4.测试

启动注册中心,启动用户服务,启动网关访问:http://localhost:10060/server/order/order/1,请求将会打到用户服务上,并返回用户数据。

六、Predicate断言工厂

1.什么是断言工厂

什么是断言工程,在Spring Cloud Gateway官方文档有如下解释:

Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping基础架构的一部分进行匹配。Spring Cloud Gateway包括许多内置的路由断言工厂。所有这些断言都与HTTP请求的不同属性匹配。您可以将多个路由断言工厂与逻辑and语句结合使用。

这里不难理解,其实断言工厂就是用来判断http请求的匹配方式。比如我们再上面案例中配置的:“Path=/user/**” ,就是使用的是 “Path Route Predicate Factory” 路径匹配工厂,意思是http请求的资源地址必须是 /user 才会被匹配到对应的路由,然后继续执行对应的服务获取资源。

在Spring Cloud Gateway中,针对不同的场景内置的路由断言工厂,比如

  • Query Route Predicate Factory:根据查询参数来做路由匹配

  • RemoteAddr Route Predicate Factory:根据ip来做路由匹配

  • Header Route Predicate Factory:根据请求头中的参数来路由匹配

  • Host Route Predicate Factory:根据主机名来进行路由匹配

  • Method Route Predicate Factory :根据方法来路由匹配

  • Cookie Route Predicate Factory:根据cookie中的属性值来匹配

  • Before Route Predicate Factory:指定时间之间才能匹配

  • After Route Predicate Factory: 指定时间之前才能匹配

  • Weight Route Predicate Factory: 根据权重把流量分发到不同的主机

2.根据查询参数断言- Query Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

上面配置表达的试试是如果请求参数中包含了 green,那么就会断言成功,从而执行uri后面的地址。

3.根据path断言-Path Route Predicate Factory(重要)
spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

请求路径如: /red/1/red/blue/blue/green 就可以断言成功

4.根据权重比例断言-Weight Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

大约80%的请求转发到weighthigh.org,将大约20%的流量转发weightlow.org。

5.根据远程ip断言 - RemoteAddr Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

如果请求的远程地址为 192.168.1.1192.168.1.24之间,则此路由匹配

6.指定时间之后断言-After Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

在atfer配置的时间之后才能访问

7.在指定时间之前断言-Before Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

在before配置的时间之前才能访问

8.在指定时间段之间断言-Between Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

请求时间在两个时间之内者允许访问

9.根据cookie断言-Cookie Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

cookies中必须有Cookie配置的属性才能匹配

10.根据请求头断言-Header Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

请求头必须出现 X-Request-Id 才可以访问

11.根据主机断言-Host Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

如果请求的主机头具有值.somehost.org,或者.anotherhost.org这匹配路由

12.根据请求方式断言-Method Route Predicate Factory
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

只允许 GET和POST请求。

七、Gateway 的 Filter 过滤器

Gateway的Filter的zuul的Filter有相似之处,与zuul不同的是,Gateway的filter从生命周期上可以为“pre”和“post”类型。根据作用范围可分为针对于单个路由的gateway filter,和针对于所有路由的Global Filer

gateway中过滤器主要分为网关(路由)过滤器和全局过滤器两个,网关(路由)过滤器主要是个某个服务指定的过滤器,全局过滤器主要是给所有的路由设置的。

1.自定义网关过滤器

网关过滤器用于拦截并通过责任链处理web请求,修改http的请求传入数据和http响应的传出数据,可以实现AOP处理我们的与应用无关的操作,比如安全控制等,主要包含路径过滤器,header过滤器,参数过滤器,body过滤器,状态过滤器,会话过滤器,重定向过滤器,重试过滤器,限流RateLimiter过滤器,熔断器过滤器。

在Spring Cloud Gateway自定义过滤器,过滤器需要实现GatewayFilter和Ordered这两个接口。我们下面来演示自定义filter计算请求的耗时。

public class RequestTimeFilter implements GatewayFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //开始时间
        long beginTime = new Date().getTime();
        //执行完成之后
        return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    //结束时间
                    long endTime = new Date().getTime();
                    //时间差
                    long time = endTime - beginTime;
                    System.out.println("当前请求:" + exchange.getRequest().getURI().getPath() + "耗时:" + time);
                    
                })
        );
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

getOrder返回filter的优先级,越大的值优先级越低 , 在filterI方法中计算了请求的开始时间和结束时间。

最后我们还需要把该Filter配置在对应的路由上,配置如下:

@Configuration
public class FilterConfig {

    //配置Filter作用于那个访问规则上
    @Bean
    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {

        return builder.routes().route(r -> r.path("/server/user/**")
                //去掉2个前缀
                        .filters(f -> f.stripPrefix(2)
                        .filter(new RequestTimeFilter())
                        .addResponseHeader("X-Response-test", "test"))
                        .uri("lb://user-server")
                        .order(0)
                        .id("test-RequestTimeFilter")
                ).build();
    }
}
2.全局过滤器

全局过滤器和网关过滤器一样,但是全局过滤器不需要通过配置文件或者配置类进行设置,它作用在所有路由上面,经过GatewayFilterAdapter包装为GatewayFilterChain责任链,它主要将我们的请求转换为真实的请求地址并进行访问,不需要经过配置,会自动在系统启动的时候进行加载。

这里我们模拟了一个登陆检查的Filter。

@Component
public class TimeGlobleFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //拿到请求对象
        ServerHttpRequest request = exchange.getRequest();
        //拿到token
        List<String> token = request.getHeaders().get("token");
        if(token == null || token.isEmpty()){
            //响应对象
            ServerHttpResponse response = exchange.getResponse();


            DataBuffer buffer = null;
            try {
                byte[] bytes = "登录".getBytes("utf-8");
                buffer = response.bufferFactory().wrap(bytes);

                //设置完成相应,不会继续执行后面的filter
                //response.setComplete();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            //把结果写给客户端
            return response.writeWith(Mono.just(buffer));
        }
        //放行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

总结:

网关搭建步骤:

创建项目,引入nacos服务发现和gateway依赖

配置application.yml,包括服务基本信息、nacos地址、路由

路由配置包括:

路由id:路由的唯一标示

路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡

路由断言(predicates):判断路由的规则,

路由过滤器(filters):对请求或响应做处理
 

新闻网页贴吧知道MP3图片视频百科文库 帮助设置 首页 自然 文化 地理 历史 生活 社会 艺术 人物 经济 科学 体育 欧冠 核心用户 进程管理 百科名片 引是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说CPU(寄存器),IO,内存,网络资源等。很多人在回答进程的概念的时候,往往只会说它是一个运行的实体,而会忽略掉进程所占据的资源。比如说,同样一个程序,同一时刻被两次运行了,那么他们就是两个独立的进程。linux下查看系统进程的命令是ps。 目录 进程的分类1.基本系统进程 2.常见系统进程解释 (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) 进程管理进程的分类 1.基本系统进程 2.常见系统进程解释 (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) (27) (28) 进程管理 展开 编辑本段进程的分类 1.基本系统进程   Csrss.exe:这是子系统服务器进程,负责控制Windows创建或删除线程以及16位的虚拟DOS环境。   System Idle Process:这个进程是作为单线程运行在每个处理器上,并在系统不处理其它线程的时候分派处理器的时间。   Smss.exe:这是一个会话管理子系统,负责启动用户会话。   Services.exe:系统服务的管理工具。   Lsass.exe:本地的安全授权服务。   Explorer.exe:资源管理器。   Spoolsv.exe:管理缓冲区中的打印和传真作业。   Svchost.exe:这个进程要着重说明一下,有不少朋友都有这种错觉:若是在“任务管理器”中看到多个Svchost.exe在运行,就觉得是有病毒了。其实并不一定,系统启动的时候,Svchost.exe将检查注册表中的位置来创建需要加载的服务列表,如果多个Svchost.exe同时运行,则表明当前有多组服务处于活动状态;多个DLL文件正在调用它。 2.常见系统进程解释 (1)   system process   进程文件: system process   进程名称: Windows内存处理系统进程   描述: Windows页面内存管理进程,拥有0级优先。   是否为系统进程: 是 (2)   alg.exe   进程文件: alg or alg.exe   进程名称: 应用层网关服务   描述: 这是一个应用层网关服务用于网络共享。   是否为系统进程: 是 (3)   csrss.exe   进程文件: csrss or csrss.exe   进程名称: Client/Server Runtime Server Subsystem   描述: 客户端服务子系统,用以控制Windows图形相关子系统。   是否为系统进程: 是 (4)   ddhelp.exe   进程文件: ddhelp or ddhelp.exe   进程名称: DirectDraw Helper   描述: DirectDraw Helper是DirectX这个用于图形服务的一个组成部分。   是否为系统进程: 是 (5)   dllhost.exe   进程文件: dllhost or dllhost.exe   进程名称: DCOM DLL Host进程   描述: DCOM DLL Host进程支持基于COM对象支持DLL以运行Windows程序。   是否为系统进程: 是 (6)   inetinfo.exe   进程文件: inetinfo or inetinfo.exe   进程名称: IIS Admin Service Helper   描述: InetInfo是Microsoft Internet Infomation Services (IIS)的一部分,用于Debug调试除错。   是否为系统进程: 是 (7)   internat.exe   进程文件: internat or internat.exe   进程名称: Input Locales   描述: 这个输入控制图标用于更改类似国家设置、键盘类型和日期格式。   是否为系统进程: 是 (8)   kernel32.dll   进程文件: kernel32 or kernel32.dll   进程名称: Windows壳进程   描述: Windows壳进程用于管理多线程、内存和资源。   是否为系统进程: 是 (9)   lsass.exe   进程文件: lsass or lsass.exe   进程名称: 本地安全权限服务   描述: 这个本地安全权限服务控制Windows安全机制。   是否为系统进程: 是 (10)   mdm.exe   进程文件: mdm or mdm.exe   进程名称: Machine Debug Manager   描述: Debug除错管理用于调试应用程序和Microsoft Office中的Microsoft Script Editor脚本编辑器。   是否为系统进程: 是 (11)   mmtask.tsk   进程文件: mmtask or mmtask.tsk   进程名称: 多媒体支持进程   描述: 这个Windows多媒体后台程序控制多媒体服务,例如MIDI。   是否为系统进程: 是 (12)   mprexe.exe   进程文件: mprexe or mprexe.exe   进程名称: Windows路由进程   描述: Windows路由进程包括向适当的网络部分发出网络请求。   是否为系统进程: 是 (13)   msgsrv32.exe   进程文件: msgsrv32 or msgsrv32.exe   进程名称: Windows信使服务   描述: Windows信使服务调用Windows驱动和程序管理在启动。   是否为系统进程: 是 (14)   mstask.exe   进程文件: mstask or mstask.exe   进程名称: Windows计划任务   描述: Windows计划任务用于设定继承在什么时间或者什么日期备份或者运行。   是否为系统进程: 是 (15)   regsvc.exe   进程文件: regsvc or regsvc.exe   进程名称: 远程注册表服务   描述: 远程注册表服务用于访问在远程计算机的注册表。   是否为系统进程: 是 (16)   rpcss.exe   进程文件: rpcss or rpcss.exe   进程名称: RPC Portmapper   描述: Windows 的RPC端口映射进程处理RPC调用(远程模块调用)然后把它们映射给指定的服务提供者。   是否为系统进程: 是 (17)   services.exe   进程文件: services or services.exe   进程名称: Windows Service Controller   描述: 管理Windows服务。   是否为系统进程: 是 (18)   smss.exe   进程文件: smss or smss.exe   进程名称: Session Manager Subsystem   描述: 该进程为会话管理子系统用以初始化系统变量,MS-DOS驱动名称类似LPT1以及COM,调用Win32壳子系统和运行在Windows登陆过程。   是否为系统进程: 是 (19)   snmp.exe   进程文件: snmp or snmp.exe   进程名称: Microsoft SNMP Agent   描述: Windows简单的网络协议代理(SNMP)用于监听和发送请求到适当的网络部分。   是否为系统进程: 是 (20)   spool32.exe   进程文件: spool32 or spool32.exe   进程名称: Printer Spooler   描述: Windows打印任务控制程序,用以打印机就绪。   是否为系统进程: 是 (21)   spoolsv.exe   进程文件: spoolsv or spoolsv.exe   进程名称: Printer Spooler Service   描述: Windows打印任务控制程序,用以打印机就绪。   是否为系统进程: 是 (22)   stisvc.exe   进程文件: stisvc or stisvc.exe   进程名称: Still Image Service   描述: Still Image Service用于控制扫描仪和数码相机连接在Windows。   是否为系统进程: 是 (23)   svchost.exe   进程文件: svchost or svchost.exe   进程名称: Service Host Process   描述: Service Host Process是一个标准的动态连接库主机处理服务。   是否为系统进程: 是 (24)   system   进程文件: system or system   进程名称: Windows System Process   描述: Microsoft Windows系统进程。   是否为系统进程: 是 (25)   taskmon.exe   进程文件: taskmon or taskmon.exe   进程名称: Windows Task Optimizer   描述: windows任务优化器监视你使用某个程序的频率,并且通过加载那些经常使用的程序来整理优化硬盘。   是否为系统进程: 是 (26)   tcpsvcs.exe   进程文件: tcpsvcs or tcpsvcs.exe   进程名称: TCP/IP Services   描述: TCP/IP Services Application支持透过TCP/IP连接局域网和Internet。   是否为系统进程: 是 (27)   winlogon.exe   进程文件: winlogon or winlogon.exe   进程名称: Windows Logon Process   描述: Windows NT用户登陆程序。   是否为系统进程: 是 (28)   winmgmt.exe   进程文件: winmgmt or winmgmt.exe   进程名称: Windows Management Service   描述: Windows Management Service透过Windows Management Instrumentation data (WMI)技术处理来自应用客户端的请求。   是否为系统进程: 是 编辑本段进程管理   操作系统的职能之一,主要是对处理机进行管理 。为了提高CPU的利用率而采用多道程序技术。通过进程管理来协调多道程序之间的关系,使CPU得到充分的利用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚者的utopia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值