手写RateLimiter

互联网高并发解决方案-高并发服务限流-纯手写打造RateLimiter注解框架 使用RateLimiter实现令牌桶限流 RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。 通常可应用于抢购限流防止冲垮系统;限制某接口、服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制;限制网速,单位时间内只允许上传下载多少字节等。 下面来看一些简单的实践,需要先引入guava的maven依赖。 <parent> <groupId>org.springfram 阅读详情

手写RateLimiter

自定义注解 封装

如果需要让接口实现限流RateLimiter使用

网关:一般拦截所有的接口 实现限流 秒杀 抢购 或者大流量的接口才会实现限流。灵活

不是所有接口都需要限流  秒杀等接口需要限流

设计: 加注解的才可以实现限流 

注解形式而不是网关形式 只有需要限流的才加这个注解

 

传统的方式整合RateLimiter有很大缺点:代码重复量特别大,而且本身不支持注解方式

限流代码可以写在网关,相当于针对所有接口实现限流,维护性不强

不是所有的接口都需要限流 一般限流主要针对大流量,比如秒杀抢购 

 

分析案例:

 定义一个自定义注解

 Spring Boot整合 spring aop

 

使用环绕通知

   判断请求方法上是否有 注解

   如果有  使用反射获取注解方法上的参数

   调用原生RateLImiter方法创建令牌

   如果获取令牌超时  直接调用服务降级(自己定义)

   如果能够获取令牌 直接进入实际请求方法

 

本案例没有用到 扫包   直接请求过来走方法的

 

 

首先自定义注解:

 

 

引入maven依赖:

<!-- springboot 整合AOP -->
	         <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>pom 

pom:

<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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.toov5</groupId>
  <artifactId>springboot-guava</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>25.1-jre</version>
		</dependency>
		<!-- springboot 整合AOP -->
	   <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
	</dependencies>
</project>

  

封装注解:

package com.toov5.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtRateLimiter {
    //以秒为单位 固定的速录往桶中添加
   double permitsPerSecond();
   
   //在规定的时间内,如果没有获取到令牌的话,直接走降级处理
   long timeout();
}

aop封装:

package com.toov5.aop;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.google.common.util.concurrent.RateLimiter;
import com.toov5.annotation.ExtRateLimiter;

//aop环绕通知 判断拦截所有springmvc请求,判断请求方法上是否存在ExtRateLimiter

@Aspect  //aop两种方式 注解 和 xml方式
@Component
public class RateLimiterAop {
    // 存放接口是否已经存在
        private static ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<String, RateLimiter>();

     //定义切入点 拦截
        @Pointcut("execution(public * com.toov5.controller.*.*(..))")  //所有类 所有方法 任意参数
        public void rlAop() {
        }
        
        //使用aop环绕通知判断拦截所有springmvc请求,判断方法上是否存在ExRanteLimiter
        @Around("rlAop()")
        public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            //1、判断请求方法上是否存在@RxtRateLimiter注解
            //2、如果请求方法上存在此注解@RxtRateLimiter注解 比如加上了RequestMapping表示请求方法
         Method sinatureMethod = getSinatureMethod(proceedingJoinPoint);
         if (sinatureMethod == null) {
            //直接报错  
             return null;
        }    
        
            //3、使用Java的反射机制获取拦截方法上自定义注解的参数
        ExtRateLimiter extRateLimiter = sinatureMethod.getDeclaredAnnotation(ExtRateLimiter.class);
        if (extRateLimiter==null) {  //方法上没有注解
            //直接放行代码 进入实际请求方法中
            proceedingJoinPoint.proceed();
        }
            //4、调用原生RateLimiter创建令牌
            double permitsPerSecond = extRateLimiter.permitsPerSecond();  //获取参数
            long timeout = extRateLimiter.timeout();
            //调用原生的RateLimiter创建令牌 保证每个请求对应的是单例的RateLimiter 一个请求一个RateLimiter 使用hashMap
            RateLimiter.create(permitsPerSecond);
            String requestURI = getRequestUrl();
            RateLimiter rateLimiter = null;
            if (rateLimiterMap.containsKey(requestURI)) {
                //如果检测到 
              rateLimiter = rateLimiterMap.get(requestURI);
            }else {
                //如果没有 则添加
                rateLimiter = RateLimiter.create(permitsPerSecond);
                rateLimiterMap.put(requestURI, rateLimiter);
            }
         
            //5、获取桶中的令牌,如果没有有效期获取到令牌,直接调用降级方法。
            boolean tryAcquire = rateLimiter.tryAcquire(timeout,TimeUnit.MILLISECONDS);
            if (!tryAcquire) {
                //服务降级
                fallback();
                return null;
            }
            //6、否则 直接进入到实际请求方法中
            
            return proceedingJoinPoint.proceed();
        } 
        private void fallback() throws IOException {
            //在aop编程中获取响应
             ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
             HttpServletResponse response = attributes.getResponse();
             //防止乱码
             response.setHeader("Content-type", "text/html;charset=UTF-8");
             PrintWriter writer = response.getWriter();
             try {
                 writer.println("亲,别抢了");
            } catch (Exception e) {
                writer.close();
            }
            
        }
     private String getRequestUrl() {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            return request.getRequestURI();
     }    
     private Method getSinatureMethod(ProceedingJoinPoint proceedingJoinPoint) {
         //获取到目标代理对象
            MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
            //获取当前aop拦截的方法        
         Method method = signature.getMethod();
         return method;
     }
}

 

controller

package com.toov5.controller;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.google.common.util.concurrent.RateLimiter;
import com.toov5.annotation.ExtRateLimiter;
import com.toov5.service.OrderService;

@RestController
public class IndexController {
  @Autowired
  private OrderService orderService;
 //create方法中传入一个参数 以秒为单位固定的速率值 1r/s 往桶中存入一个令牌
  RateLimiter rateLimiter = RateLimiter.create(1);  //独立线程!它自己是个线程
   
  //相当于接口每秒只能接受一个客户端请求
  @RequestMapping("/addOrder")
    public String addOrder() {
        //限流放在网关 获取到当前 客户端从桶中获取对应的令牌 结果表示从桶中拿到令牌等待时间
        //如果获取不到令牌 就一直等待
        double acquire = rateLimiter.acquire();
        System.out.println("从桶中获取令牌等待时间"+acquire);
      boolean   tryAcquire=rateLimiter.tryAcquire(500,TimeUnit.MILLISECONDS);  //如果在500sms没有获取到令牌 直接走降级
      if (!tryAcquire) {
        System.out.println("别抢了,等等吧!");
        return "别抢了,等等吧!";
    }
        //业务逻辑处理
        boolean addOrderResult = orderService.addOrder();
        if (addOrderResult) {
            System.out.println("恭喜抢购成功!等待时间");
            return "恭喜抢购成功!";
        }
        
        return "抢购失败!";
    }
  //以每秒1个的速度往桶中添加令牌 
  @RequestMapping("/findIndex")
  @ExtRateLimiter(permitsPerSecond=1.0,timeout=500)
  public void findIndex() {
      System.out.println("findIndex"+System.currentTimeMillis());
  }
  
}

service

package com.toov5.service;

import org.springframework.stereotype.Service;

@Service
public class OrderService {
    
    public boolean addOrder() {
      System.out.println("db...正在操作订单表数据库");    
      return true;
    }
}

启动类

package com.toov5;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

 疯狂点击:

 

 

posted @ 2018-11-20 23:49 toov5 阅读( ...) 评论( ...) 编辑 收藏
一次性搞定分布式限流————手写分布式限流框架 目录一、目标和需求分析二、初步设计三、客户端的实现1)限流器参数定义(1)限流规则定义(2)客户端配置定义2)限流器实现(1)接口定义(2)限流器的抽象实现(2)访问策略实现(3)单机限流器的实现(4)分布式限流器的实现3)分发服务器管理类实现4)限流器注册实现(1)限流器构造工厂(2)限流注册器5)注解实现四、服务器实现 gitee:https://gitee.com/qiaodaimadewangcai/flood-myth 一、目标和需求分析 为了框架能满足当代互联网的基本需求,和使用的便利,优先实现 阅读详情

相关推荐

进阶数据结构——链式前向星

链式前向星是一种进阶的数据结构,主要用于图的存储。我希望大家先看这个视频(蓝色字体点进去) 不然你很难理解的。在图论中,图的存储方式主要有邻接矩阵和邻接表两种。邻接矩阵虽然建图简单方便,但占内存较大,节点数量稍微大点的题目就难以使用(适合稠密图);而邻接表虽然节省空间,但其指针形式难写易错,且vector形式的读取速度较慢,易导致大数据下的超时(TLE)。针对上述问题,链式前向星作为一种折中的方式应运而生。链式前向星本质上是一种静态链表,它将数据以链表形式连接在一起。

ycs66的博客 2137

最近学习了限流与RateLimiter

前言 分布式环境下应对高并发保证服务稳定几招,按照个人理解,优先级从高到低分别为缓存、限流、降级、熔断,每招都有它的作用,本文重点就讲讲限流这部分。 坦白讲,其实上面的说法也不准确,因为服务降级、熔断本身也是限流的一种,因为它们本质上也是阻断了流量进来,但是本文希望大家可以把限流当做一个单纯的名词来理解,看一下对请求做流控的几种算法及具体实现方式。 为什么要限流 其实很好理解的一个问题,为什么要限流,自然就流量过大了呗,一个对外服务有很多场景都会流量增大: 业务用户量不断攀升 各种促销 .

woaitingting1985的博客 793

SpringMvc 限流之 RateLimiter

概念 限流 限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理 常用限流算法 常用的限流算法有两种:漏桶算法和令牌桶算法 漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。 对于很多应用场景来说,除了要求能

Lili429的博客 4495

超频玩家必看:TestMem5配置文件详解与内存稳定性测试实战(含黄金参数包)

本文详细解析了TestMem5(TM5)在内存超频稳定性测试中的核心价值与实战应用,特别针对DRAM颗粒特性提供黄金参数配置。通过anta777等专业配置的深度剖析,指导超频玩家精准定位内存故障,优化时序参数,实现性能与稳定性的完美平衡。涵盖从初级验证到极限压测的全流程方案,是硬件发烧友提升系统稳定性的必备指南。

weixin_29198045的博客 1814

多智能体强化学习MASAC算法:从原理到PyTorch实战详解

深度强化学习通过智能体与环境的交互试错来学习最优策略,是解决复杂序列决策问题的核心技术。其核心原理在于利用价值函数或策略梯度来最大化累积奖励,在机器人控制、游戏AI等领域展现出巨大技术价值。当面对多个智能体需要协同或竞争的复杂动态环境时,单智能体算法因环境非平稳性而面临挑战,多智能体强化学习应运而生。Multi-Agent Soft Actor-Critic (MASAC) 算法融合了SAC的稳定高效与中心化训练去中心化执行的范式,通过维护中心化Critic网络和独立策略网络,有效解决了多智能体协作中的信用

weixin_30080745的博客 331

springmvc限流拦截器

springmvc限流拦截器限流器算法目前常用限流器算法为两种:令牌桶算法和漏桶算法,主要区别在于:漏桶算法能够强行限制请求速率,平滑突发请求,而令牌桶算法在限定平均速率的情况下,允许一定量的突发请求 下面是从网上找到的两张算法图示,就很容易区分这两种算法的特性了 漏桶算法 令牌桶算法 针对接口来说,一般会允许处理一定量突发请求,只要求限制平均速率,所以令牌桶算法更加常见。 令牌桶算法工具R

valleychen1111的博客 6437

【密码专栏】超强进阶:PLONK VS Groth16(上)

前言 前文《天冷了,干了这碗“零知识证明”鸡汤》对「零知识证明学习」作了一个形象化的比喻:炖鸡汤。那么本系列的主要内容可以简单概括为《论高压锅炖鸡汤的一百种方法》之方法二。在学会了“清炖鸡汤”之后,不如来一口“阿胶鸡汤”补补脑细胞吧! 正如鸡汤不同风味之间各具千秋,不同的zk-SNARK方案也各有所长。zk-SNARK方案可以被分为【通用】与【非通用】zk-SNARK,PLONK与Groth16分别是其中的典型代表。通过本系列,我们将对PLONK算法内容作简要介绍,并指出PLONK和Groth16算法思路上

Hyperchain的博客 1805

面试官:说说你了解几种限流算法,手写个demo?

在流量突增的场景下,为了保证后端服务在整体上一个稳定性,我们需要对请求进行限流,来避免系统崩溃。 不过限流会对少部分用户的请求直接进行拒绝或者延迟处理,影响这些用户的体验。 本文会介绍一些常见的限流算法,并在最后附上对分布式限流的一些思考。

阳阳的博客 1万+

利用Cesium引擎实现大疆智图B3DM三维模型的高效加载与可视化

本文详细介绍了如何利用Cesium引擎高效加载与可视化大疆智图B3DM三维模型。通过性能优化实战和典型应用场景分析,帮助开发者掌握模型预处理、渲染参数调优及高级功能实现技巧,提升大规模倾斜摄影数据的网页端展示效率。

weixin_29231725的博客 219

C# .NET6 TesseractOCR 图片文字识别

在你的项目中的tessdata-main文件夹找到这几个文件,并右键->属性设置为【如果较新则复制】有几个需要用到的模型,需要生成的时候复制到输出目录中,在项目中设置即可。TesseractOCR兼容 x86 与 x64。附 :ImageUtils.RegionCropping()源码。Visual Studio 2022 社区版。下载完成后解压在你的项目中,如图所示。从官方项目中获取模型数据, 网址为。

qq_44121554的博客 3854

限流的那些事:限流算法和RateLimiter初体验

目录前言漏桶算法(Leaky Bucket)令牌桶算法(Token Bucket)RateLimiter简介RateLimiter的常用方法解析 前言 在开发高并发系统时,有三把利器用来保护系统:缓存、降级和限流。那么何为限流呢?顾名思义,限流就是限制流量,对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机。 为什么要限流? 很简单,说白了就是流量太大了,服务器承受不了,所以需要限制流量来保证系统的正常运行。 多大的流量是大呢?没有标准答案,这需要根据你系统

qq_36551991的博客 1483

扫地机器人设计与实现——基于嵌入式系统的智能家居解决方案

本文介绍了基于嵌入式技术的扫地机器人设计与实现。通过利用STM32单片机、传感器、驱动系统等硬件组件,并结合路径规划与控制算法、远程控制等软件技术,设计出了一款具有智能避障、路径规划、自动充电等功能的扫地机器人。系统的开发不仅提高了清洁效率,也为进一步研究和优化扫地机器人提供了实践基础。

2201_76125393的博客 1639

高并发限流纯手写RateLimiter注解版本

RateLimiter实现令牌桶限流 什么是RateLimiter? RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。 通常可应用于抢购限流防止冲垮系统;限制某接口、服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制;限制网速...

791

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

互联网高并发解决方案03-高并发服务限流特技-纯手写打造RateLimiter注解框架

封装RateLimiter 自定义注解封装RateLimiter.实例: @RequestMapping("/myOrder") @ExtRateLimiter(value = 10.0, timeOut = 500) public String myOrder() throws InterruptedException { Sys...

qq_35524586的博客 258

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

之前手写的两个好用开源组件优化升级

这两个手写开源好用的组件是在日常项目实践中发现了一些问题,后面优化了迭代了,依赖包都推送到maven中央仓库上了,项目中直接引入对应的maven依赖包安装教程配置一下,就可以方便轻松快捷的使用我给大家提供的开源组件了,解决对应的业务问题还是一把利器的,简化代码,纵享丝滑,希望我的分享对你有所启发和帮助,请一键三连,么么么哒!

qq_34905631的博客 654

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

超详细的Guava RateLimiter限流原理解析

限流是保护高并发系统的三把利器之一,另外两个是缓存和降级。限流在很多场景中用来限制并发和请求量,比如说秒杀抢购,保护自身系统和下游系统不被巨型流量冲垮等。限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务或进行流量整形。

2301_81922209的博客 588

Spring Boot 中 RateLimiter 的使用详解(原理 + 实战 + 踩坑)

本文系统介绍了RateLimiter限流器的核心原理与Spring Boot应用实践。主要内容包括:1)RateLimiter基于令牌桶算法实现平滑限流,支持突发流量;2)Spring Boot中通过AOP+注解方式优雅实现接口限流;3)常见使用场景如接口防刷、第三方调用控制等;4)生产环境需注意单机限流、线程阻塞等坑点;5)推荐采用分层限流架构,结合网关层与本地限流器。文章强调限流的核心是保护系统而非限制用户,并提供了完整的代码示例和实践建议。

qq_72793376的博客 1660

手写常用限流算法

1、令牌桶算法 这是关于令牌桶的定义,我也不用去解释了,直接百度就OK。平时我们可能会使用guava的RateLimiter。我个人的理解是感觉该接口每秒可以请求的次数。 那么我们根据该解释来手动去实现一个所谓的令牌桶算法。令牌桶首先要有一个桶对吧,还要有令牌、还要实现平均发送令牌,桶满了丢弃等,那么你们有思路了吗,开始diy吧。 import java.util.concurrent.LinkedBlockingQueue; /** * 简单实现令牌桶算法 */ public class MyRat

lovely960823的博客 1950

Resilience4j RateLimiter实战:从原理到应用的限流策略解析

本文深入解析了Resilience4j RateLimiter的限流策略,从令牌桶算法、漏斗算法等核心原理出发,详细阐述了其在微服务架构中防止系统过载、避免雪崩效应的关键作用。通过Spring Boot实战示例,展示了如何配置与集成RateLimiter,实现方法级限流、差异化用户策略,并与熔断器等组件协同工作,为构建高韧性服务提供可靠保障。

weixin_29207533的博客 285

手写服务限流6种实现方式

总的来说,要保证系统的抗压能力,限流是一个必不可少的环节,虽然可能会造成某些用户的请求被丢弃,但相比于突发流量造成的系统宕机来说,这些损失一般都在可以接受的范围之内。前面也说过,限流可以结合熔断、降级一起使用,多管齐下,保证服务的可用性与健壮性。

qq_38730338的博客 1010

分布式限流组件-基于Redis的注解支持的Ratelimiter

原文:https://juejin.im/entry/5bd491c85188255ac2629bef?utm_source=coffeephp.com 在分布式领域,我们难免会遇到并发量突增,对后端服务造成高压力,严重甚至会导致系统宕机。为避免这种问题,我们通常会为接口添加限流、降级、熔断等能力,从而使接口更为健壮。Java领域常见的开源组件有Netflix的hystrix,阿...

dgqvhtlwq472235338的博客 506

手写了一个好用的限流、分布式锁和手动控制事务相结合的starter启动器

由于之前写如下两篇文章:自定义注解实现Redis分布式锁、手动控制事务和根据异常名字或内容限流的三合一的功能自定义redission装配和集成分布式开源限流业务组件ratelimiter-spring-boot-starter的正确姿势在项目中使用下来就觉得能不能把这两个结合起来使用?

qq_34905631的博客 683

Java限流神器:手写一个通用限流任务执行器,支持重试和指数退避!

本文介绍了一个自制的限流任务执行器RateLimitedExecutor,用于解决调用第三方API时的限流问题。该工具具有限流执行、顺序保证、自动重试(支持指数退避)和异步返回等核心功能,基于Guava的RateLimiter实现流量控制。文章提供了完整的Java代码实现,包括构造参数说明和使用方法,可直接集成到项目中,帮助开发者轻松控制API调用频率,避免触发限流机制。

Claymore0720的博客 268

面试官当场让我手写Java线程安全工具类,我写完直接拿到了35K offer

本文我将完整复刻这场面试的全部手写代码、解题思路、底层原理、面试话术和避坑指南,全文万字干货,从0到1手把手教你手写5大核心高频Java线程安全工具类,吃透并发核心,彻底搞定大厂并发面试。

小筱在线博客 310
上一篇: 互联网高并发之限流
下一篇: 关于CDN
Art&Code
博客等级 码龄7年 77粉丝 554原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值