OpenFeign、GateWay网关

我知道努力的尽头 都是 桃花源

OpenFeign使用

引入依赖

消费者方添加依赖

<!--    <dependency>-->
<!--        <groupId>org.springframework.cloud</groupId>-->
<!--        <artifactId>spring-cloud-starter-openfeign</artifactId>-->
<!--    </dependency>-->

<!--httpClient的依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

配置连接池

消费者方添加

feign:
  client:
    config:
      default: # default全局的配置
        loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
  httpclient:
    enabled: true # 开启 feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

Feign的优化:

  1. 日志级别尽量用basic

  2. 使用HttpClient或OKHttp 代替URLConnection

① 引入feign-httpClient依赖

② 配置文件开启httpClient功能,设置连接池参数

添加注解

消费者方添加注解 @EnableFeignClients,后面要修改!!!

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;


@SpringBootApplication
@EnableFeignClients//要修改
public class EurekaClientApplication {

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


}

Maven项目结构图

在这里插入图片描述

新建feign-api模块

  1. 添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>
  1. 新建接口UserClient
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient("eureka-server")
public interface UserClient {
    @GetMapping("/test")
    String test();
}

服务提供者pro

代码

@RestController
public class TestController {
    @GetMapping("test")
    public String test() {
        return "调用成功";
    }
    
}

服务调用者con

  1. 添加 fegin-api接口
<!-- fegin-api接口-->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>feign-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
  1. 代码
import com.example.feignapi.UserClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestCon {
    @Autowired
    private UserClient userClient;
    @GetMapping("hello")
    public  String hello(){
        return userClient.test();
    }
}

  1. 修改注解

@EnableFeignClients(basePackages = “com.example.feignapi”)—>不然 找不到会报错

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;


@SpringBootApplication
@EnableFeignClients(basePackages = "com.example.feignapi")
public class EurekaClientApplication {

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


}

解决扫描包问题

报错:

在这里插入图片描述
解决

方式一:

指定Feign应该扫描的包:

@EnableFeignClients(basePackages = "com.example.feignapi")

方式二:

指定需要加载的Client接口:

@EnableFeignClients(clients = {UserClient.class})

启动服务测试

eureka-server为应用名spring.application.name

流程:

  1. 本机的8080端口下的 hello 请求调用 userClient的test方法
  2. userClient的test方法会去请求eureka-server 下的test请求
  3. eureka-server下的test请求返回结果给 userClient的test方法
  4. 渲染页面
    在这里插入图片描述

OpenFeign自定义配置

Feign 可以支持很多的自定义配置,如下表所示:

类型作用说明
feign.Logger.Level修改日志级别包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder响应结果的解析器http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder请求参数编码将请求参数编码,便于通过http请求发送
feign. Contract支持的注解格式默认是SpringMVC的注解
feign. Retryer失败重试机制请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可

下面以日志为例来演示如何自定义配置

配置文件方式

基于配置文件修改 feign的日志级别可以针对单个服务

feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

也可以针对所有服务

feign:  
  client:
    config: 
      default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

而日志的级别分为四种:

  • NONE:不记录任何日志信息,这是默认值。
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

Java代码方式

也可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象:

public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}

配置生效

如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class) 

如果是局部生效,则把它放到对应的@FeignClient这个注解中:

//value 是对哪个服务生效
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class) 

Gateway服务网关

为什么需要网关

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

网关的核心功能特性

  • 请求路由
  • 权限控制
  • 限流

架构图:
在这里插入图片描述
权限控制:网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。

路由和负载均衡一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。

限流:当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大

gateway快速入门

  1. 依赖
<!--网关-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!-- eureka 服务发现依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<!-- nacos 服务发现依赖-->
<!--<dependency>-->
<!--     <groupId>com.alibaba.cloud</groupId>-->
<!--     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>-->
<!--</dependency>-->

<!--  web-->
<!--<dependency>-->
<!--    <groupId>org.springframework.boot</groupId>-->
<!--    <artifactId>spring-boot-starter-web</artifactId>-->
<!--</dependency>-->

spring-boot-start-web!!!不能有,因为这里面有tomcat!!

  1. yaml
server:
  port: 9000 # 网关端口
spring:
  application:
    name: gateway # 服务名称
  cloud:    
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

我们将符合Path 规则的一切请求,都代理uri参数指定的地址

解释:访问该网关的9000端口,路径为 /user/test,会被负载均衡地转发userservice 服务的 /user/test ,返回调用结果

userservice为注册中心的服务,为spring.application.name

  1. 测试
    在这里插入图片描述

网关路由

网关路由的流程图

整个访问的流程如下:

在这里插入图片描述
路由配置包括:

  1. 路由id:路由的唯一标示

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

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

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

断言工厂

我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件

例如Path=/user/**是按照路径匹配,这个规则是由

org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来

处理的,像这样的断言工厂在SpringCloudGateway还有十几个:

名称说明示例
After是某个时间点后的请求- After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before是某个时间点之前的请求- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between是某两个时间点之前的请求- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie请求必须包含某些cookie- Cookie=chocolate, ch.p —>名称chocolate,值ch.p
Header请求必须包含某些header- Header=X-Request-Id, \d+ —>名称X-Request-Id,值为数字
Host请求必须是访问某个host(域名)- Host=.somehost.org,.anotherhost.org 或者的关系
Method请求方式必须是指定方式- Method=GET,POST
Path请求路径必须符合指定规则- Path=/red/{segment},/blue/** ,如/red/1 或 /red/bar 、 /blue/hello
Query请求参数必须包含指定参数- Query=smile,\d+ —>请求参数中包含smile参数,并且smile参数必须符合逗号后边的正则表达式的值,只有匹配成功才可以路由
RemoteAddr请求者的ip必须是指定范围- RemoteAddr=192.168.1.1/24—>#请求必须是192.168.1.3,并且子网掩码为255.255.255.0的地址发出的才可以路由
Weight权重处理如下
# 这里只给出了局部配置,其他配置保持不变
spring:
  cloud:
    gateway:
      routes:
        - id: weight_high
          uri: https://a.org
          predicates:
            - Weight=group1, 8
        - id: weight_low
          uri: https://b.org
          predicates:
            - Weight=group1, 2
# 这条路线会将大约80%的流量转发到a.org,将大约20%的流量转发到b.org。

多个断言以-分隔:

 predicates:
            #匹配所有以/consumer/product/开头的请求
            - Path=/consumer/product/**
            #你的请求必须带有Header,并且其中有Header的名称叫Authorization,Header的值会跟你自己定义的Java正则匹配,这里的\d+,代表值必须为数字
            - Header=Authorization,\d+

我们只需要掌握Path这种路由工程就可以

过滤器工厂

说明

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理
在这里插入图片描述

路由过滤器的种类

Spring提供了31种不同的路由过滤器工厂。例如:

名称说明
AddRequestHeader给当前请求添加一个请求头
RemoveRequestHeader移除请求中的一个请求头
AddResponseHeader给响应结果中添加一个响应头
RemoveResponseHeader从响应结果中移除有一个响应头
RequestRateLimiter限制请求的流量

请求头过滤器

下面我们以AddRequestHeader 为例来讲解。

需求:给所有进入userservice的请求添加一个请求头:Truth=Xiaod is handsome!

只需要 修改gateway服务的application.yml文件,添加路由过滤即可:

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/** 
        filters: # 过滤器
        - AddRequestHeader=Truth,Xiaod  is  handsome! # 添加请求头

当前 过滤器写在userservice路由下,因此仅仅对访问userservice的请求有效

默认过滤器

如果要对所有的路由都生效,则可以将过滤器工厂写到default下。格式如下:

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/**
      default-filters: # 默认过滤项
      - AddRequestHeader=Truth,Xiaod  is  handsome!

总结

过滤器的作用是什么?

① 对路由的请求或响应做加工处理,比如添加请求头
② 配置在路由下的过滤器只对当前路由的请求生效

defaultFilters的作用是什么?

① 对所有路由都生效的过滤器

全局过滤器

全局过滤器作用

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现

定义方式是实现GlobalFilter接口

public interface GlobalFilter {
    /**
     *  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文,里面可以获取Request、Response等信息
     * @param chain 用来把请求委托给下一个过滤器 
     * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

在filter中编写自定义逻辑,可以实现下列功能:

  • 登录状态判断
  • 权限校验
  • 请求限流等

自定义全局过滤器

需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:

  • 参数中是否有authorization,
  • authorization 参数值是否为admin

如果同时满足则放行,否则拦截

实现:

在 gateway中定义一个过滤器:

package cn.itcast.gateway.filters;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Order(-1) // 默认 2147483647
@Component
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取请求参数
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        // 2.获取authorization参数
        String auth = params.getFirst("authorization");
        // 3.校验
        if ("admin".equals(auth)) {
            // 放行, 需要传入上下文
            return chain.filter(exchange);
        }
        // 4.拦截
        // 4.1.禁止访问,设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        // 4.2.结束处理
        return exchange.getResponse().setComplete();
    }
}

过滤器执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:
在这里插入图片描述

排序的规则是什么呢?

  • 每一个过滤器都必须指定一个int类型的order值order值越小,优先级越高,执行顺序越靠前
  • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
  • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
  • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

跨域、熔断、限流

跨域问题

localhost:8090访问localhost:10010,端口不同,显然是跨域的请求。

解决

在gateway服务的application.yml 文件中,添加下面的配置:

spring:
  cloud:
    gateway:
      # 。。。
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求 
              - "http://localhost:8090"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

网关熔断降级

熔断降级含义

熔断降级:在分布式系统中,网关作为流量的入口,大量请求进入网关,向后端远程系统或服务发起调用,后端服务不可避免的会产生调用失败(超时或者异常),失败时 不能让请求堆积在网关上,需要快速失败并返回回去,这就需要在网关上做熔断、降级操作

配置使用

在网关配置

  1. pom依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. yaml
spring:
  application:
  	# 应用名
    name: gateway
  cloud:
    gateway:
      routes: # 网关路由配置
        - id: eureka-server # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://eureka-server # 路由的目标地址 lb 就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/** # 这个是按照路径匹配,只要以 / 开头就符合要求
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd
                fallbackUri: forward:/defaultFallback #服务降级转发到/defaultFallback

# hystrix 信号量隔离,2秒后自动超时
hystrix:
  shareSecurityContext: true
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE
          thread:
            timeoutInMilliseconds: 2000
#注册中心            
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
#网关端口      
server:
  port: 9000


  1. 服务 提供者方
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {
    @GetMapping("/test")
    public String test(int id) throws InterruptedException {
        if (id < 0) {
            throw new RuntimeException("不能为负数");
        }
        if (id > 0) {
            Thread.sleep(3000);
            return "3秒后业务正常!";
        }
        return "调用成功";
    }

    @RequestMapping("/defaultFallback")
    public String defaultFallback() {
        return "Your Access is dined!";
    }

}


解释:
例子:访问网关的9000端口,路径为 test会被转到eureka-server 的 test请求,即为 http://eureka-server/test

超过2秒没有返回结果,会被转发到 服务者所在的 /defaultFallback接口,进行降级的处理

在这里插入图片描述

网关限流

网关限流含义

网关上有大量请求,对指定服务进行限流,可以很大程度上提高服务的可用性与稳定性,限流的目的是通过对并发访问/请求进行限速,或对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级

使用

在网关配置

  1. pom 依赖
<!-- gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>

<!-- hystrix熔断器-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

<!-- redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

<!-- 连接池-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
  1. yaml
spring:
  redis:
    host: 192.168.111.101
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 1024  #最大连接数
        max-wait: 10000   #最大等待时间,单位毫秒
        max-idle: 256     #最大空闲连接
        min-idle: 8       #最小空闲连接
  application:
    name: gateway #应用名
  cloud:
    gateway:
      routes: # 网关路由配置
        - id: eureka-server # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://eureka-server # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/** # 这个是按照路径匹配,只要以 / 开头就符合要求
          filters:
            # 服务的熔断和降级配置(内置的过滤器)
            - name: Hystrix
              args:
                name: fallbackcmd       
                fallbackUri: forward:/defaultFallback # 服务降级转发到/defaultFallback
            # 网关的限流过滤器配置(内置的过滤器)
            - name: RequestRateLimiter
              args:
                key-resolver: '#{@pathKeyResolver}' #使用 SpEL 表达式按名称引用bean,一共有三种,名称请查看配置对象
                redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
                redis-rate-limiter.burstCapacity: 2 #令牌桶总的容量大小,写2方便测试

# hystrix 信号量隔离,2秒后自动超时
hystrix:
  shareSecurityContext: true
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE
          thread:
            timeoutInMilliseconds: 2000

# 注册中心
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
server:
  # 网关端口
  port: 9000

**新建Java类PathKeyResolver **
在这里插入图片描述

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class PathKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // 根据ip限流
        return Mono.just (exchange.getRequest ().getRemoteAddress ().getAddress ().getHostName ());

    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nacos、OpenFeignGateway和Sentinel都是微服务架构中常用的开源工具。 Nacos(Naming and Configuration Service)是一个动态服务发现、配置管理和服务治理平台。它提供了服务注册和发现、配置管理和动态路由等功能,使得微服务架构中的各个服务能够自动注册并通过服务名进行发现。通过Nacos,我们可以方便地进行服务的注册与发现,以及实时更新配置信息。 OpenFeign是一个基于Java的声明式服务调用客户端。它内置了Ribbon和Hystrix,可以自动处理服务的负载均衡和容错。通过使用注解和接口代理,OpenFeign可以简化服务间的调用,在代码中只需要定义接口和方法的声明,而无需手动实现具体的服务调用逻辑。 Gateway是一个高性能的API网关,用于将外部客户端的请求路由到后端的不同服务。通过配置路由规则和过滤器链,Gateway可以完成请求的转发并进行相应的处理。它具有动态路由、请求限流、熔断降级、安全认证等功能,可以提高系统的可靠性和稳定性。 Sentinel是一个流量控制和流量治理框架,用于实现对微服务架构中各个服务的流量控制和实时监控。它提供了实时的监控、熔断降级、系统保护和流量统计等功能,并且具备高度可扩展性。通过使用Sentinel,我们可以对服务的流量进行实时监控和管理,保证系统的稳定性和可靠性。 综上所述,Nacos提供了服务发现和配置管理,OpenFeign简化了微服务间的调用,Gateway实现了高性能的API网关,Sentinel用于流量控制和治理。这些工具的结合使用可以帮助我们构建可靠、稳定的微服务架构,并提升系统的性能和可扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值