sparkStream+kafka实现exactlyOne第三种--Hbase

Kafka 中“精确一次(exactly-once)”语义解析 阅读详情

前边我们已经有了两种解决方式《sparkStream+kafka实现exactlyOne第三种--Mysql-事务》、《sparkStream+kafka实现exactlyOne第二种--Redis-pipeline》

这是第三种《sparkStream+kafka实现exactlyOne第二种--Hbase-行内事务 phoenix》,habse一行的数据 是有事务的,这一行要么都成功,要么都失败,

因此,我们可以为一个hbase表添加一个offset列族,每次写入一条数据就将offset一并写入,这样就能保证数据的消费和偏移量的记录是同时成功同时失败的

另外一点,利用hbase不需要把数据收集到driver端再写入,前两种都需要把数据收集到driver端,再通过事务、pipeline控制。利用hbase可以再executor端执行,

所以数据量大的话,hbase比较适合,mysql redis只适合每个批次中数据量小的。

 主方法

package com.ws.sparkstreaming.kafkahbase

import java.util

import com.google.gson.Gson
import com.ws.sparkstreaming.utils.{HbaseUtils, OffsetUtils}
import org.apache.hadoop.hbase.TableName
import org.apache.hadoop.hbase.client.{Connection, Put, Table}
import org.apache.hadoop.hbase.util.Bytes
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka010._
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, TaskContext}
import org.slf4j.{Logger, LoggerFactory}

object WordCountJoinKafkaHbaseManagerOffset {
  private val logger: Logger = LoggerFactory.getLogger(this.getClass.getName)

  def main(args: Array[String]): Unit = {
    val appname = "ssc-kafka-hbase"
    val group = "group1"
    val topics = Array("orders").toIterable
    val conf = new SparkConf().setAppName(appname).setMaster("local[*]")
    val ssc = new StreamingContext(conf, Seconds(5))
    ssc.sparkContext.setLogLevel("WARN")
    val kafkaParams: Map[String, Object] = Map[String, Object](
      "bootstrap.servers" -> "dream1:9092,dream2:9092,dream3:9092", // kafka地址
      "key.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer", // 设置反序列化组件
      "value.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer",
      "group.id" -> group, // 消费者组
      "auto.offset.reset" -> "earliest", // 指定消费者从哪开始消费[latest,earliest]
      "enable.auto.commit" -> "false" // 是否自动提交偏移量,默认是true
    )
    val lastoffset = OffsetUtils.selectOffsetFromHbase(appname, group)
    val dstream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream(
      ssc,
      LocationStrategies.PreferConsistent,
      ConsumerStrategies.Subscribe[String, String](topics, kafkaParams, lastoffset)
    )
    dstream.foreachRDD((kafkaRdd: RDD[ConsumerRecord[String, String]]) => {
      println("------------------------------------" + System.currentTimeMillis() + "------------------------------------")
      if (!kafkaRdd.isEmpty()) {
        // 每条数据都是一个json,第一步解析数据
        val ranges: Array[OffsetRange] = kafkaRdd.asInstanceOf[HasOffsetRanges].offsetRanges
        val lines: RDD[String] = kafkaRdd.map(_.value())
        val ordersAndErro: RDD[MyOrder] = lines.mapPartitions((pt: Iterator[String]) => {
          val gson = new Gson()
          var order: MyOrder = null
          pt.map(line => {
            try {
              order = gson.fromJson(line, classOf[MyOrder])
            } catch {
              case _: Exception =>
                logger.error(line + "-----------解析异常")
            }
            order
          })
        })
        // 过滤异常数据
        val orders: RDD[MyOrder] = ordersAndErro.filter(_ != null)

        orders.foreachPartition(pt1 => {
          if (pt1.nonEmpty) {
            // ****************** 根据分区id获取,offset传入executor端 ******************
            val range: OffsetRange = ranges(TaskContext.get.partitionId())
            var conn: Connection = null
            var table: Table = null
            try {
              // 获取Hbase连接
              conn = HbaseUtils.getConnection()
              table = conn.getTable(TableName.valueOf("STREAMING"))
              val puts: util.ArrayList[Put] = new util.ArrayList[Put]()
              pt1.foreach(order => {
                val put = new Put(Bytes.toBytes(order.gid))
                put.addColumn(Bytes.toBytes("DATA"), Bytes.toBytes("ORDER"), Bytes.toBytes(order.money))
                // create view STREAMING (pk varchar primary key,"OFFSET"."OFFSET" varchar,"OFFSET"."topic_partition" varchar);
                // 如果是最后一条数据将偏移量写入和数据一块写入 hbase,我们利用单条数据的一致性(要成功都成功要失败都失败)包子ExactlyOnce
                if (!pt1.hasNext) {
                  put.addColumn(Bytes.toBytes("OFFSET"), Bytes.toBytes("APP_GID"), Bytes.toBytes(appname + "_" + group))
                  put.addColumn(Bytes.toBytes("OFFSET"), Bytes.toBytes("TOPIC_PARTITION"), Bytes.toBytes(range.topic + "_" + range.partition))
                  put.addColumn(Bytes.toBytes("OFFSET"), Bytes.toBytes("OFFSET"), Bytes.toBytes(range.untilOffset))
                }
                puts.add(put)
                if (puts.size() % 10 == 0) {
                  table.put(puts)
                  puts.clear()
                }
              })
              table.put(puts)
            } catch {
              case e: Exception =>
                e.printStackTrace()
            } finally {
              if (table != null) {
                table.close()
              }
              if (conn != null) {
                conn.close()
              }
            }
          }
        })
      }
    })
    ssc.start()
    ssc.awaitTermination()
  }
}

 获取历史偏移量

//offset好像是关键字,需要加“”

package com.ws.sparkstreaming.utils

import java.sql.{Connection, DriverManager}
import java.util

import org.apache.kafka.common.TopicPartition
import org.apache.spark.streaming.kafka010.OffsetRange

import scala.collection.mutable

object OffsetUtils {

  def selectOffsetFromHbase(appname: String, group: String): Map[TopicPartition, Long] = {
    val offsets = new mutable.HashMap[TopicPartition, Long]()
    val conn = DriverManager.getConnection("jdbc:phoenix:dream1,dream2,dream3:2181")
    // 必须加引号才行
    val ps = conn.prepareStatement("select TOPIC_PARTITION,max(\"OFFSET\") as \"OFFSET\" from streaming WHERE APP_GID =?  GROUP BY TOPIC_PARTITION")
    ps.setString(1,appname+"_"+group)
    val resultSet = ps.executeQuery()
    while (resultSet.next()) {
      val TOPIC_PARTITION = resultSet.getString("TOPIC_PARTITION").split("_")
      val OFFSET = resultSet.getInt("OFFSET")
      offsets(new TopicPartition(TOPIC_PARTITION(0),TOPIC_PARTITION(1).toInt)) = OFFSET.toLong
    }
    offsets.toMap
  }
}

hbase连接

// hbase 没有连接池

package com.ws.sparkstreaming.utils

import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.hbase.{HBaseConfiguration}
import org.apache.hadoop.hbase.client.{Connection, ConnectionFactory}

object HbaseUtils {

  def getConnection(): Connection = synchronized {
    val conf: Configuration = HBaseConfiguration.create()
    conf.set("hbase.zookeeper.quorum", "dream1:2181,dream2:2181,dream3:2181")
    ConnectionFactory.createConnection(conf)
  }

}

HBASE表

create VIEW "STREAMING"(PK VARCHAR PRIMARY KEY,"DATA"."ORDER" UNSIGNED_DOUBLE,"OFFSET"."APP_GID" VARCHAR,"OFFSET"."TOPIC_PARTITION" VARCHAR,"OFFSET"."OFFSET" UNSIGNED_LONG);
0: jdbc:phoenix:> select * from streaming;
+------+--------+-------------------------+------------------+---------+
|  PK  | ORDER  |         APP_GID         | TOPIC_PARTITION  | OFFSET  |
+------+--------+-------------------------+------------------+---------+
| g11  | 666.3  | ssc-kafka-hbase_group1  | orders_2         | 1       |
| g12  | 335.3  | ssc-kafka-hbase_group1  | orders_1         | 1       |
| g13  | 615.3  | ssc-kafka-hbase_group1  | orders_0         | 1       |
| g14  | 211.3  | ssc-kafka-hbase_group1  | orders_2         | 2       |
| g15  | 366.3  | ssc-kafka-hbase_group1  | orders_1         | 2       |
| g16  | 566.3  | ssc-kafka-hbase_group1  | orders_0         | 2       |
| g17  | 66.3   | ssc-kafka-hbase_group1  | orders_2         | 4       |
| g18  | 166.3  | ssc-kafka-hbase_group1  | orders_1         | 3       |
| g19  | 16.3   | ssc-kafka-hbase_group1  | orders_0         | 3       |
+------+--------+-------------------------+------------------+---------+
9 rows selected (0.151 seconds)
0: jdbc:phoenix:> 

kafka

Last login: Thu Feb 25 10:10:25 2021 from 192.168.33.1
[root@dream1 ~]# kafka-console-producer.sh --broker-list dream1:9092,dream2:9092,dream3:9092 --topic orders        
>{"gid": "g01","money": 19.99}
{"gid": "g02","money": 29.99}
{"gid": "g03","money": 39.99}>>
>

数据

{"gid": "g01","money": 19.99}
{"gid": "g02","money": 29.99}
{"gid": "g03","money": 39.99}
{"gid": "g04","money": 39.99}
{"gid": "g05","money": 49.99}
{"gid": "g06","money": 59.99}
{"gid": "g07","money": 89.99}

 

【中间件设计 Kafka】Kafka Exactly-Once语义,Kafka如何保证消息不重不漏 本文深入解析Kafka如何实现消息传递的三种语义(At most once、At least once、Exactly once),重点剖析Exactly-Once语义的实现机制。Kafka从0.11.0版本开始通过幂等性生产者和事务性生产者来确保消息不丢失不重复。文章采用"问题→解决方案"的结构,首先分析如何保证消息不丢失(At Least Once),包括生产者确认机制、Broker多副本配置和消费者手动提交偏移量;然后详细阐述如何避免消息重复(Exactly-Once核心),分别从 阅读详情

相关推荐

Kafka Exactly-Once语义实现原理深度解读

在分布式系统中,消息中间件就像"数据公交车",负责在不同服务间传递信息。但这辆"公交车"经常出问题:有时消息"坐过站"(丢失),有时"重复上车"(重复传递)。Kafka作为主流消息中间件,从0.11.0.0版本开始支持Exactly-Once语义,承诺"消息既不丢失也不重复,只被处理一次"。解释什么是Exactly-Once语义,以及它与另外两种语义(At-Least-Once、At-Most-Once)的区别;

架构师的AI之路,分享AI应用开发架构的学习与实践。 1123

Kafka 是如何实现exactly once语义的?

一、消息语义概述 在分布式系统中,构成系统的任何节点都是被定义为可以彼此独立失败的。比如在 Kafka中,broker可能会crash,在producer推送数据至topic的过程中也可能会遇到网络问题。根据producer处理此类故障所采取的提交策略类型,我们可以获得不同的语义: 至少一次(at-least-once):消息不会丢失,但有可能被重复发送。如果producer收到来自Kafka broker的确认(ack)或者acks = all,则表示该消息已经写入到Kafka。但如果producer

大数据 3246

【Kafka】Kafka 实现 Exactly-once (ack机制、高水位)

这个acks参数在kafka的使用中,是非常核心以及关键的一个参数,决定了很多东西。Kafka producer有三种ack机制 初始化producer时在config中进行配置ack等于0:意味着producer不等待broker同步完成的确认,继续发送下一条(批)信息提供了最低的延迟。但是最弱的持久性,当服务器发生故障时,就很可能发生数据丢失。例如leader已经死亡,producer不知情,还会继续发送消息broker接收不到数据就会数据丢失。ack等于1(默认)

九师兄 1448

Kafka系列之:对源连接器的的Exactly-Once支持

尽管在这种情况下,用户不一定需要控制每个连接器的偏移主题的名称(例如,工作人员可以简单地在连接器覆盖的 Kafka 集群上创建一个具有相同名称的偏移主题),但公开此级别一旦意识到每个连接器偏移主题的必要性,控制的增加就不会增加此设计的复杂性。如果工作线程启动了事务性生产者,然后在完成对配置主题末尾的读取之前被阻塞,则领导者发起的这一轮生产者围栏应该隔离该生产者,并且任何后续的重新启动尝试都将阻塞,直到/除非工作线程能够完成对配置主题末尾的读取(并处理在此过程中从主题读取的新信息所需的任何重新平衡)。

zhengzaifeidelushang的博客 1620

深入理解Kafka—如何保证Exactly Once语义

Kafka 是一种高吞吐量、分布式、可分区、多副本的消息系统。它在使用上非常灵活,可以作为 Pulsar、RabbitMQ 的替代品。但同时也带来了一些复杂性和问题,比如Exactly Once 语义。从本质上说,Exactly Once 就是对消费者读取的数据只要不丢失,就一定能得到一次完整的处理,而且不会被重复处理。确保 Exactly Once 语义一直是企业级应用中必须考虑的问题。

AI天才研究院 1597

京东礼品卡绑定技术解析与安全验证实践

电商系统开发中,卡券绑定是常见的业务场景,其核心在于安全验证机制的设计与实现。从技术原理来看,现代验证系统通常采用多层防护,包括前端数据加密、行为验证(如滑块验证)和风控校验等关键技术。这些机制通过设备指纹采集、请求签名验证等方式,有效防范自动化攻击。在实际工程实践中,开发者需要特别关注接口鉴权流程,正确处理安全令牌(如st/vt令牌)和请求签名。京东礼品卡绑定作为典型案例,展示了如何平衡用户体验与系统安全,其验证系统会持续升级,需要开发者跟踪接口变化。对于企业级应用,建议采用混合模式技术方案,结合浏览器自

weixin_28607671的博客 235

kafka中的端到端一致性(exactly-one)

端到端一致性: exactly-once传输过程要保证exactly-once, 需要内部能够实现状态保存, 当下游保存失败时, 能够更加保存的历史状态再次提交数据.而下游接受数据, 需要具备去重能力. 去重有两种方式:幂等写入, 事务写入内部: 支持checkpoint或者状态保存下一级: 数据不会被重复写入幂等写入根据key去重, 比如k-v数据库: hbase, redis事务写入...

bitbitbyte的博客 1171

华为CCA架构软件栈VehicleStack解析:3层服务化设计与AOS/VOS双核OS

本文深入解析华为CCA架构下的VehicleStack软件平台,重点介绍其三层服务化设计(硬件抽象层、服务框架层、应用层)与AOS/VOS双核操作系统协同机制。该架构通过计算与通信的深度融合,推动汽车电子电气架构向集中式发展,为智能汽车提供灵活、高效的软件定义汽车解决方案。

weixin_33491377的博客 324

Stream Processing: Apache Kafka的Exactly-once的定义 原理和实现

2018年,Apache Kafka以一种特殊的设计和方法实现了强语义的exactly-once和事务性。热泪盈眶啊! 这篇文章将讲解kafka中exactly-once和事务操作的原理,具体为(1)exactly-once在kafka中的定义。(2)数据生产者的幂等操作和kafka的事务性支持。(3)exactly-once的流处理。 1. 什么是恰好一次exactly-once exact...

ChainingBlocks 5321

天祥TX-1C单片机开发板-秒表程序

郭天祥TX-1C开发板实现秒表程序 附开发板图片和原理图 代码 #include <reg52.h> sbit key1=P3^4; //按键S2 sbit key2=P3^5; //按键S3 sbit U1=P2^6; //LED位选 sbit U2=P2^7; //LED片选 unsigned char code ledchar[]={ /...

Run_long_road 3514

抖音短连接v.douyin.com/xxx 如何生成?

最近公司有个需求,在抖音里如何宣传自己的网站,并且不要有拦截域名的提示,截图如下。 通过ida反编译抖音的app后,将下载后的ipa文件用解压缩软件(如7zip等)解压,从解压后的目录Payload/Aweme.app/找到Aweme可执行文件,拖入ida进行反编译,ida可以检测objective-c语法。 然后进入主题,既然是聊天发消息,就尝试在ida里搜索"message",看看能否找到对...

kakuapi的博客 3万+

kafka消息零丢失方案

exactly one = least-once(-1、全写) + 幂等性(去重) exactly-one是要保证 生产者只生产一次、kafka只存储一份数据、消费者只消费一次数据 我们在切片的场景下,kafka分区全写保证least-once,消费者通过业主唯一标识保证这个幂等性。这样生产者可以重复生产,kafka可以数据重复,消费者可以重复消费。 kafka消息丢失怎么解决? exit-one 生产者到mq 消息发出去了,等到消息的ack,再向下进行。消息发送成功。 消息发出去了,应用死了,ack也回.

古笑来的博客 668

通信系统原理[郭宇春]——模拟调制系统——课后习题答案

通信系统原理[郭宇春]——第三章课后习题答案,出错望指正!

尧呱呱的博客 3844

Kafka消息队列怎么保证exactlyOnce以及顺序消费

一个Topic可以有多个Paritition,相当于把一个Topic里面的N个消息数据进行一个分片存储。消费端去消费的时候会从指定的Paritition里面去获取。在同一个消费组中,一个消费者可以消费多个Partition中的数据。当我们向某个Topic发送消息的时候,在Kafka的Broker上会通过Partition分区的机制来实现消息的物理存储。在Kafka里面,每个Paritition分区的消息本身就是按顺序来存储的。而消费者这边只需要去消费这个分区,就可以实现消息的顺序处理。

qq_46130027的博客 576

Calibre LVS错误排查实战:从Innovus到GDS的完整调试流程(附常见错误解析)

本文详细介绍了数字IC后端设计中,从Innovus工具输出数据到Calibre LVS物理验证的完整调试流程。文章系统性地讲解了LVS错误排查的方法论,包括报告解读、图形化定位以及针对器件不匹配、电源网络错误、短路开路等常见问题的深度解析与解决方案,旨在帮助工程师高效完成版图与原理图的一致性检查。

weixin_29218963的博客 776

实战指南:利用 LangGraph 的状态管理实现多轮对话的上下文隔离与持久化

本文详细介绍了如何利用LangGraph的状态管理机制,高效实现多轮对话中的上下文隔离与持久化。通过实战代码演示,从基础的消息历史管理到自定义状态扩展,再到使用SQLite进行生产级持久化,系统讲解了如何借助LangGraph的State和Checkpointer自动化处理对话记忆、用户隔离及数据存储,显著提升开发效率。

weixin_28396629的博客 489

Spark Streaming实时写入HBase(十分稳定,不会导致宕机)

文章目录Spark Streaming实时写入HBase0--适用框架1-代码解析1-1 Kafka部分1-2 Hbase部分2-代码源码3-pom.xml Spark Streaming实时写入HBase 0–适用框架 1-代码解析 1-1 Kafka部分 //定义一个主题数组,内可包含多个主题,此处只有一个 val kafkaTopic = Array("mytopic") //bootstrap.servers kafka的服务节点和端口号,用于连接kafka //key.deserializer

咸鱼的博客 861

单片机设计具备断电记忆功能的计时器

本文介绍了如何设计一款具备断电记忆功能的计时器,利用单片机和EEPROM存储器实现了计时器值的保存和恢复。当然,在实际的设计中还需要考虑其他因素,如电源管理、时钟精度等,以确保计时器的正常运行和准确性。选择合适的单片机:在设计计时器时,我们需要选择一款具备存储功能的单片机,以便在断电后能够保存计时器的状态。可以优化显示方式,使用更加直观的界面来展示计时器的值;函数中,我们首先从 EEPROM 中读取计时器的初始值,然后在一个无限循环中每秒钟增加计时器的值,并将新的值写入 EEPROM 中。

RlDart的博客 1094

Kafka实现精确一次(exactly once)发送消息的原理

语义介绍 At-least-once(最少一次):如果生产者从Kafka broker接收到一个确认(ack)并且ack = all,这意味着消息已经被写入Kafka topic一次。然而,如果生产者ack超时或收到一个错误,它可能会重试发送消息,假设消息没有写入Kafka topic,如果broker在它发送ack之前失败,但在消息被成功写入Kafka topic后,此重试将导致消息被写入两次,因此将不止一次地传递给最终使用者。这种方法可能会导致消息重复和错误的结果。 At-most-once(最多一

xzh_blog 5071

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

VC中sqlite的使用

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

Kafka 消息队列怎么保证 exactlyOnce,怎么实现顺序消费

目前市面上的 MQ 产品,基本上都没有提供 Exactly Once 语义的实现。我们只能通过一些其他手段来达到 Exactly Once 的效果。也就是确保生产者只发送一次,消费端只接受一次。生产者可以采用事物消息的方式,事务可以支持多分区的数据完整性,原子性。并且支持跨会话的 exactly once 处理语义,即使 producer 宕机重启,依旧能保证数据只处理一次开启事务首先需要开启幂等性,即设置 enable.idempotence 为true。然后对producer。

qq_63431773的博客 249

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Kafka 实战 - Kafka Exactly Once 语义与事务机制原理

Kafka Exactly Once(简称EO)语义是指在分布式系统中,确保每条消息只被处理一次,无论发生何种故障或重试,都不会出现重复处理或遗漏处理的情况。这对于许多关键业务场景至关重要,如金融交易、计费系统等。Kafka从0.11版本开始引入了事务机制,以支持Exactly Once语义。

qq_33240556的博客 765

efm32 入门.pdf

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

kafka精准一次、事务、幂等性

kafka精准一次、事务、幂等性

weixin_47467016的博客 2814

Kafka保证exactly once原理

Kafka保证exactly once原理 1 从producer角度考虑幂等性 Kafka producer新增了幂等性的传递选项,该选项保证重传不会在 log 中产生重复条目。 为实现这个目的, broker 给每个 producer 都分配了一个 ID ,并且 producer 给每条被发送的消息分配了一个序列号来避免产生重复的消息。 同样也是从 0.11.0.0 版本开始, producer 新增了使用类似事务性的语义将消息发送到多个 topic partition 的功能: 也就是说,要么所有的消

weixin_42989986的博客 2001

如何实现Kafka的Exactly-Once语义?

这里模式有个问题,会导致性能下降,并且即使使用了该种模式,生产者和消费者该做的重试和幂等都需要做,只是重复数据会下降(比如业务处理成功了,但是提交offset失败了,会导致broker重发,这种场景严格意义来说不算成功,但是真实场景下网络是有波动的,可能会导致失败)。对于金融级业务,推荐使用生产者事务+数据库事务的混合方案。对于日志处理等场景,可采用消费者手动提交偏移量+处理结果去重的简化方案。在实现Exactly-Once之前,需要了解kafka三语义以及2PC实现。:协调器持久化事务元数据到。

weixin_45762066的博客 1544

Kafka Producer 如何实现Exactly Once消息传递语义

Kafka中的Exactly-Once语义确保消息既不会丢失也不会重复,仅幂等性无法保证完整的Exactly-Once语义,必须结合事务机制和消费者隔离级别才能实现。事务机制增强了Kafka的故障恢复能力,确保即使在Producer崩溃重启的情况下,仍能维持Exactly-Once语义。

SengMay的专栏 982

kafka:Kafka Exactly-Once语义实现与分布式事务异常处理深度解析

Kafka的Exactly-Once语义(EOS)通过事务协调器(Transaction Coordinator)和幂等生产者(Idempotent Producer)机制实现:fill:#333;返回PID(Producer ID)和Epoch发送消息(带PID和Sequence)序列号校验(去重)loop写入事务标记(COMMIT/ABORT)仅对已提交消息可见。

weixin_43290370的博客 1196
上一篇: phoenix 处理hbase存的double类型数据显示为负数
下一篇: sqoop mysql导入hdfs 需要注意的点
JNWsong
博客等级 码龄9年 287粉丝 204原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值