Spring Cloud构建微服务架构: 消息总线

一张微服务地图的十年演进:Spring Cloud 核心、扩展与工具组件的定位、原理与选型 本文把 Spring Cloud 当作一张微服务地图来读:先说明它是由 Commons 抽象层、实现层和 Boot 装配层构成的发行版,再沿一次请求梳理注册发现、负载均衡、声明式调用、容错限流、网关、配置、追踪与总线七类核心组件的原理与换代,并扩展到云平台、服务网格、消息流、批处理、安全与周边生态。仿真实测:默认参数下实例宕机平均 242 秒才被感知,优雅下线降到 47 秒;一个慢实例让轮询的 P99 达 49 秒,P2C 仅 203 毫秒。最后给出退场替代表、技术栈组合、升级路线和决策树。 阅读详情

Spring Cloud Bus除了支持RabbitMQ的自动化配置之外,还支持现在被广泛应用的Kafka。在本文中,我们将搭建一个Kafka的本地环境,并通过它来尝试使用Spring Cloud Bus对Kafka的支持,实现消息总线的功能。由于本文会以之前Rabbit的实现作为基础来修改,所以先阅读《Spring Cloud构建微服务架构(七)消息总线》有助于理解本文。

Kafka简介

Kafka是一个由LinkedIn开发的分布式消息系统,它于2011年初开源,现在由著名的Apache基金会维护与开发。Kafka使用Scala实现,被用作LinkedIn的活动流和运营数据处理的管道,现在也被诸多互联网企业广泛地用作为数据流管道和消息系统。

Kafka是基于消息发布/订阅模式实现的消息系统,其主要设计目标如下:

  • 消息持久化:以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间复杂度的访问性能。
  • 高吞吐:在廉价的商用机器上也能支持单机每秒100K条以上的吞吐量
  • 分布式:支持消息分区以及分布式消费,并保证分区内的消息顺序
  • 跨平台:支持不同技术平台的客户端(如:Java、PHP、Python等)
  • 实时性:支持实时数据处理和离线数据处理
  • 伸缩性:支持水平扩展

Kafka中涉及的一些基本概念:

  • Broker:Kafka集群包含一个或多个服务器,这些服务器被称为Broker。
  • Topic:逻辑上同Rabbit的Queue队列相似,每条发布到Kafka集群的消息都必须有一个Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个Broker上,但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)
  • Partition:Partition是物理概念上的分区,为了提供系统吞吐率,在物理上每个Topic会分成一个或多个Partition,每个Partition对应一个文件夹(存储对应分区的消息内容和索引文件)。
  • Producer:消息生产者,负责生产消息并发送到Kafka Broker。
  • Consumer:消息消费者,向Kafka Broker读取消息并处理的客户端。
  • Consumer Group:每个Consumer属于一个特定的组(可为每个Consumer指定属于一个组,若不指定则属于默认组),组可以用来实现一条消息被组内多个成员消费等功能。

快速入门

在对Kafka有了一些基本了解之后,下面我们来尝试构建一个Kafka服务端,并体验一下基于Kafka的消息生产与消费。

环境安装

首先,我们需要从官网上下载安装介质。下载地址为:http://kafka.apache.org/downloads.html。本例中采用的版本为:Kafka-0.10.0.1

在解压Kafka的安装包之后,可以看到其目录结构如下:

kafka
  +-bin
    +-windows
  +-config
  +-libs
  +-logs
  +-site-docs

由于Kafka的设计中依赖了ZooKeeper,所以我们可以在bin和config目录中除了看到Kafka相关的内容之外,还有ZooKeeper相关的内容。其中bin目录存放了Kafka和ZooKeeper的命令行工具,bin根目录下是适用于Linux/Unix的shell,而bin/windows下的则是适用于windows下的bat。我们可以根据实际的系统来设置环境变量,以方便后续的使用和操作。而在config目录中,则是用来存放了关于Kafka与ZooKeeper的配置信息。

启动测试

下面我们来尝试启动ZooKeeper和Kafka来进行消息的生产和消费。示例中所有的命令均已配置了Kafka的环境变量为例。

  • 启动ZooKeeper,执行命令:zookeeper-server-start config/zookeeper.properties,该命令需要指定zookeeper的配置文件位置才能正确启动,kafka的压缩包中包含了其默认配置,开发与测试环境不需要修改。
[2016-09-28 08:05:34,849] INFO Reading configuration from: config\zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
[2016-09-28 08:05:34,850] INFO autopurge.snapRetainCount set to 3 (org.apache.zookeeper.server.DatadirCleanupManager)
[2016-09-28 08:05:34,851] INFO autopurge.purgeInterval set to 0 (org.apache.zookeeper.server.DatadirCleanupManager)
[2016-09-28 08:05:34,851] INFO Purge task is not scheduled. (org.apache.zookeeper.server.DatadirCleanupManager)
[2016-09-28 08:05:34,852] WARN Either no config or no quorum defined in config, running  in standalone mode (org.apache.zookeeper.server.quorum.QuorumPeerMain)
[2016-09-28 08:05:34,868] INFO Reading configuration from: config\zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
[2016-09-28 08:05:34,869] INFO Starting server (org.apache.zookeeper.server.ZooKeeperServerMain)
...
[2016-09-28 08:05:34,940] INFO binding to port 0.0.0.0/0.0.0.0:2181 (org.apache.zookeeper.server.NIOServerCnxnFactory)

从控制台信息中,我们可以看到ZooKeeper从指定的config/zookeeper.properties配置文件中读取信息并绑定2181端口启动服务。有时候启动失败,可查看一下端口是否被占用,可以杀掉占用进程或通过修改config/zookeeper.properties配置文件中的clientPort内容以绑定其他端口号来启动ZooKeeper。

  • 启动Kafka,执行命令:kafka-server-start config/server.properties,该命令也需要指定Kafka配置文件的正确位置,如上命令中指向了解压目录包含的默认配置。若在测试时,使用外部集中环境的ZooKeeper的话,我们可以在该配置文件中通过zookeeper.connect参数来设置ZooKeeper的地址和端口,它默认会连接本地2181端口的ZooKeeper;如果需要设置多个ZooKeeper节点,可以为这个参数配置多个ZooKeeper地址,并用逗号分割。比如:zookeeper.connect=127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002。

  • 创建Topic,执行命令:kafka-topics --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test,通过该命令,创建一个名为“test”的Topic,该Topic包含一个分区一个Replica。在创建完成后,可以使用kafka-topics --list --zookeeper localhost:2181命令来查看当前的Topic。

另外,如果我们不使用kafka-topics命令来手工创建,直接进行下面的内容进行消息创建时也会自动创建Topics来使用。

  • 创建消息生产者,执行命令:kafka-console-producer --broker-list localhost:9092 --topic test。kafka-console-producer命令可以启动Kafka基于命令行的消息生产客户端,启动后可以直接在控制台中输入消息来发送,控制台中的每一行数据都会被视为一条消息来发送。我们可以尝试输入几行消息,由于此时并没有消费者,所以这些输入的消息都会被阻塞在名为test的Topics中,直到有消费者将其消费掉位置。

  • 创建消息消费者,执行命令:kafka-console-consumer --zookeeper localhost:2181 --topic test --from-beginning。kafka-console-consumer命令启动的是Kafka基于命令行的消息消费客户端,在启动之后,我们马上可以在控制台中看到输出了之前我们在消息生产客户端中发送的消息。我们可以再次打开之前的消息生产客户端来发送消息,并观察消费者这边对消息的输出来体验Kafka对消息的基础处理。

整合Spring Cloud Bus

在上一篇使用Rabbit实现消息总线的案例中,我们已经通过引入spring-cloud-starter-bus-amqp模块,完成了使用RabbitMQ来实现的消息总线。若我们要使用Kafka来实现消息总线时,只需要把spring-cloud-starter-bus-amqp替换成spring-cloud-starter-bus-kafka模块,在pom.xml的dependenies节点中进行修改,具体如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>

如果我们在启动Kafka时均采用了默认配置,那么我们不需要再做任何其他配置就能在本地实现从RabbitMQ到Kafka的切换。我们可以尝试把刚刚搭建的ZooKeeper、Kafka启动起来,并将修改为spring-cloud-starter-bus-kafka模块的config-server和config-client启动起来。

在config-server启动时,我们可以在控制台中看到如下输出:

2016-09-28 22:11:29.627  INFO 15144 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder    : Using kafka topic for outbound: springCloudBus
2016-09-28 22:11:29.642  INFO 15144 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread        : Starting ZkClient event thread.
...
016-09-28 22:11:30.290  INFO 15144 --- [           main] o.s.i.kafka.support.ProducerFactoryBean  : Using producer properties => {bootstrap.servers=localhost:9092, linger.ms=0, acks=1, compression.type=none, batch.size=16384}
2016-09-28 22:11:30.298  INFO 15144 --- [           main] o.a.k.clients.producer.ProducerConfig    : ProducerConfig values:
...
2016-09-28 22:11:30.322  INFO 15144 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder$1  : Adding {message-handler:outbound.springCloudBus} as a subscriber to the 'springCloudBusOutput' channel
2016-09-28 22:11:30.322  INFO 15144 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'config-server:7001.springCloudBusOutput' has 1 subscriber(s).
2016-09-28 22:11:30.322  INFO 15144 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder$1  : started outbound.springCloudBus
...
2016-09-28 22:11:31.465  INFO 15144 --- [           main] s.i.k.i.KafkaMessageDrivenChannelAdapter : started org.springframework.integration.kafka.inbound.KafkaMessageDrivenChannelAdapter@4178cb34
2016-09-28 22:11:31.467  INFO 15144 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder$7  : Adding {message-handler:inbound.springCloudBus.anonymous.8b9e6c7b-6a50-48c5-b981-8282a0d5a30b} as a subscriber to the 'bridge.springCloudBus' channel
2016-09-28 22:11:31.467  INFO 15144 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder$7  : started inbound.springCloudBus.anonymous.8b9e6c7b-6a50-48c5-b981-8282a0d5a30b

从控制台的输出内容,我们可以看到config-server连接到了Kafka中,并使用了名为springCloudBus的Topic。

此时,我们可以使用kafka-topics --list --zookeeper localhost:2181命令来查看当前Kafka中的Topic,若已成功启动了config-server并配置正确,我们就可以在Kafka中看到已经多了一个名为springCloudBus的Topic。

我们再启动配置了spring-cloud-starter-bus-kafka模块的config-client,可以看到控制台中输出如下内容:

2016-09-28 22:43:55.067  INFO 6136 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder    : Using kafka topic for outbound: springCloudBus
2016-09-28 22:43:55.078  INFO 6136 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread        : Starting ZkClient event thread.
...
2016-09-28 22:50:38.584  INFO 828 --- [           main] o.s.i.kafka.support.ProducerFactoryBean  : Using producer properties => {bootstrap.servers=localhost:9092, linger.ms=0, acks=1, compression.type=none, batch.size=16384}
2016-09-28 22:50:38.592  INFO 828 --- [           main] o.a.k.clients.producer.ProducerConfig    : ProducerConfig values: 
...
2016-09-28 22:50:38.615  INFO 828 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder$1  : Adding {message-handler:outbound.springCloudBus} as a subscriber to the 'springCloudBusOutput' channel
2016-09-28 22:50:38.616  INFO 828 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'didispace:7002.springCloudBusOutput' has 1 subscriber(s).
2016-09-28 22:50:38.616  INFO 828 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder$1  : started outbound.springCloudBus
...
2016-09-28 22:50:39.162  INFO 828 --- [           main] s.i.k.i.KafkaMessageDrivenChannelAdapter : started org.springframework.integration.kafka.inbound.KafkaMessageDrivenChannelAdapter@60cf855e
2016-09-28 22:50:39.162  INFO 828 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder$7  : Adding {message-handler:inbound.springCloudBus.anonymous.f8fc9c0c-ccd3-46dd-9537-07198f4ee216} as a subscriber to the 'bridge.springCloudBus' channel
2016-09-28 22:50:39.163  INFO 828 --- [           main] o.s.c.s.b.k.KafkaMessageChannelBinder$7  : started inbound.springCloudBus.anonymous.f8fc9c0c-ccd3-46dd-9537-07198f4ee216

可以看到,config-client启动时输出了类似的内容,他们都订阅了名为springCloudBus的Topic。

在启动了config-server和config-client之后,为了更明显地观察消息总线刷新配置的效果,我们可以在本地启动多个不同端口的config-client。此时,我们的config-server以及多个config-client都已经连接到了由Kafka实现的消息总线上。我们可以先访问各个config-client上的/from请求,查看他获取到的配置内容。然后,修改Git中对应的参数内容,再访问各个config-client上的/from请求,可以看到配置内容并没有改变。最后,我们向config-server发送POST请求:/bus/refresh,此时我们再去访问各个config-client上的/from请求,就能获得到最新的配置信息,各客户端上的配置都已经加载为最新的Git配置内容。

从config-client的控制台中,我们可以看到如下内容:

2016-09-29 08:20:34.361  INFO 21256 --- [ kafka-binder-1] o.s.cloud.bus.event.RefreshListener      : Received remote refresh request. Keys refreshed [from]

RefreshListener监听类记录了收到远程刷新请求,并刷新了from属性的日志。

Kafka配置

在上面的例子中,由于Kafka、ZooKeeper均运行于本地,所以我们没有在测试程序中通过配置信息来指定Kafka和ZooKeeper的配置信息,就完成了本地消息总线的试验。但是我们实际应用中,Kafka和ZooKeeper一般都会独立部署,所以在应用中都需要来为Kafka和ZooKeeper配置一些连接信息等。Kafka的整合与RabbitMQ不同,在Spring Boot 1.3.7中并没有直接提供的Starter模块,而是采用了Spring Cloud Stream的Kafka模块,所以对于Kafka的配置均采用了spring.cloud.stream.kafka的前缀,比如:

属性名说明默认值
spring.cloud.stream.kafka.binder.brokersKafka的服务端列表localhost
spring.cloud.stream.kafka.binder.defaultBrokerPortKafka服务端的默认端口,当brokers属性中没有配置端口信息时,就会使用这个默认端口9092
spring.cloud.stream.kafka.binder.zkNodesKafka服务端连接的ZooKeeper节点列表localhost
spring.cloud.stream.kafka.binder.defaultZkPortZooKeeper节点的默认端口,当zkNodes属性中没有配置端口信息时,就会使用这个默认端口2181

更多配置参数请参考官方文档

本文完整示例:



作者:程序猿DD
链接:https://www.jianshu.com/p/730d86030a41
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
【python3.7】解决报错 error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ 解决pip安装py7zr遇到的报错问题。 阅读详情

相关推荐

modbus

从机地址 功能码 起始地址高位 起始地址低位 数量高位 数量低位 字节数 0001H高位 0001H低位 0002H高位 0002H低位 CRC高位 CRC低位。每个输入寄存器的长度为2个字节。其中,CDH对应线圈0013H到001AH的内容,01H对应线圈001B到001CH的内容,未使用位用0填充。从机地址 功能码 起始地址高位 起始地址低位 寄存器数量高位 寄存器数量低位 CRC高位 CRC低位。从机地址 功能码 起始地址高位 起始地址低位 寄存器数量高位 寄存器数量低位 CRC高位 CRC低位。

lunhuicnm的博客 9748

Spring Cloud构建微服务架构(七)消息总线(续:Kafka)

Spring Cloud Bus除了支持RabbitMQ的自动化配置之外,还支持现在被广泛应用的Kafka。在本文中,我们将搭建一个Kafka的本地环境,并通过它来尝试使用Spring Cloud Bus对Kafka的支持,实现消息总线的功能。由于本文会以之前Rabbit的实现作为基础来修改,所以先阅读《Spring Cloud构建微...

weixin_33674976的博客 531

从零开始:使用Mask2Former训练你的专属图像分割数据集

本文详细介绍了如何使用Mask2Former训练自定义图像分割数据集,从环境配置、数据集准备到模型训练与优化。通过实战技巧和问题排查指南,帮助开发者高效完成从零开始的图像分割任务,特别适合需要处理自定义数据集的AI研究人员和工程师。

weixin_30566063的博客 642

Spring Cloud构建微服务架构--基于kafka构建消息总线BUS

一、环境准备: centos环境下,借助docker安装kafka,安装kafka之前我们需要安装Zookeeper; 一个典型的Kafka集群中包含以下: 若干Produce; 若干broker(broker数量越多,集群吞吐率越高); 若干Consumer Group,以及一个Zookeeper集群。 Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,C.

撸起袖子加油干 1656

智能座舱音频系统集成与调试:从芯片选型到音质优化实战

在智能座舱域控制器架构中,音频系统已从简单的功放加扬声器演进为SoC、数字音频总线、外部DSP与多通道功放协同的复杂子系统。TDM总线的时隙分配、PDM麦克风阵列的信号完整性,以及AEC回声消除算法如何与硬件协同,直接决定多音区体验和语音交互的可靠性。工程实践中,音频调试的关键在于先理顺供电、接地、时钟与数据路由,再针对功放削波、采样率失配、TDM slot错位等典型问题进行系统性排查。从芯片选型到系统级指标验收,只有将音频链路视为整车电子电气架构的一个协同整体,才能真正实现从“响”到“好听”的跨越。本文结

weixin_30162121的博客 364

黑金ZYNQ7010开发板开箱实测:从接线到串口通信的保姆级指南

本文提供黑金ZYNQ7010开发板从开箱到串口通信的完整实操指南。详细介绍了硬件连接、驱动安装、启动模式设置及串口终端配置等关键步骤,并针对常见问题提供排查思路,帮助FPGA开发者快速上手ZYNQ平台,建立稳定的调试通道。

weixin_29266749的博客 466

利用kafka实现消息总线Spring Cloud Bus

相信大多数读者之前都使用过各种各样的消息队列,例如RabbitMQ、kafka等等,消息总线和他的概念差不多,在微服务系统的架构中,我们通常会使用轻量级的消息代理来构建一个共用的消息主题让系统中所有的微服务都连接上来,由于该主题中产生的消息会被所有实例监听和消费,所以我们称他们为消息总线。在总线上的各个实例都可以方便的广播一些需要让其他连接到该主题上的实例都知道的消息,例如配置的变更或者其他一些管理操作等。......

TPH_BETTER.的博客 1319

MATLAB演示元胞自动机算法

一、元胞自动机理论 元胞自动机与格子理论是一个非常好的模型,许多复杂的问题都可以通过它来建立模型,下面就简要介绍一下。 元胞自动机 实质上是定义在一个具有离散、有限状态的元胞组成的元胞空间上,并按照一定的局部规则,在离散的时间维度上演化的动力学系统。 元胞 元胞又可称为单元、细胞,是元胞自动机的最基本的组成部分。 元胞具有以下特点: 元胞自动机最基本的单元。 元胞有记忆贮存状态的功...

frange zone 2万+

从零学SpringCloud系列(九):利用kafka实现消息总线Spring Cloud Bus

一、什么是消息总线 相信大多数读者之前都使用过各种各样的消息队列,例如RabbitMQ、kafka等等,消息总线和他的概念差不多,在微服务系统的架构中,我们通常会使用轻量级的消息代理来 构建一个共用的消息主题让系统中所有的微服务都连接上来,由于该主题中产生的消息会被所有实例监听和消费,所以 我们称他们为消息总线。在总线上的各个实例都可以方便的广播一些需要让其他连接到该主题上的实例都知道的消息,例如配置的变更或者其他一些管理操作等。 二、整合消息总线实现配置自动刷新 在上一篇博客中spring cloud c

m0_52789121的博客 2595

ANSYS FLUENT 超临界流体物性分段线性插值数据批量导入

使用fluent自带数据库进行超临界流体仿真时,时不时会出现计算错误,考虑是数据库不稳定引起,而使用自定义的分段线性插值物性则不会出现上述情况。 超临界流体在拟临界点附近物性变化剧烈,需要使用较多的数据点才能更准确的表述物性,如果是在对话框中修改,一次只能输入一个点,人工输入极其费时费力且容易失误。 数据批量导入方法为: 1.以CO2为例,使用REFREPRO等物性查询软件或数据资料,获得所需物性数据表。 2.在FLUENT材料Materia...

wind_rider的博客 1万+

TC3XX - MCAL知识点(二十四):UART MCAL配置及代码实战

UART驱动程序负责按照UART协议提供通信服务。ASCLIN模块为异步通信提供硬件支持,实现UART协议。UART驱动程序提供了配置、初始化、数据传输、接收等功能,还提供了中止传输和中止接收 abort等可选特性。软硬件匹配关系如下:UART驱动程序使用ASCLIN进行数据的传输和接收。驱动程序使用的关键硬件功能特性是:1、全双工异步工作模式2、支持半双工工作模式3、16字节的TXFIFO4、16字节的RXFIFO5、2 ~ 16位数据帧6、校验位生成/检查7、一个或两个停止位。

梦想技术家 1831

Spring Cloud构建微服务架构:消息总线

💡 方案教程: Spring Cloud构建微服务架构:分布式配置中心🔧 核心组件:🚀 流程:✅ 优点:❌ 缺点:🔧 核心组件:🚀 流程:✅ 优点:❌ 缺点:使用Spring Cloud Bus与Spring Cloud Config的整合,并以RabbitMQ作为消息代理,实现了应用配置的动态更新。整个方案的架构如上图所示,包括Git仓库、Config Server、以及微服务“Service A”的三个实例。准备工作:扩展 应用:启动应用: 2️⃣ 添加配置信息 3️⃣ Java代码

yueerba126的博客 394

3.4 ISIS和OSPF之间路由引入

通过以上输出可以看到OSPF的路由引入到了IS-IS中。通过以上输出可以看到ISIS的路由没有传递给OSPF。通过以上输出可以看到OSPF的路由没有传递给ISIS。通过以上输出可以看到ISIS的路由被引入进了OSPF。掌握在IS-IS中引入OSPF路由的方法。掌握在OSPF中引入IS-IS路由的方法。图3-3:ISIS和OSPF之间路由引入。在R3上把ISIS的路由引入到OSPF。OSPF引入外部路由的缺省度量值为1。在R3上把OSPF的路由引入到ISIS。引入的外部路由类型为Type2。

lwljh134的博客 1802

【SpringCloud】Gateway网关、SpringCloud Config配置中心、消息总线BUS以及Spring Cloud Stream

在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。...

m0_67390788的博客 424

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

上一篇: Unix.Trojan.DDoS_XOR-1 清理办法
下一篇: ThreadPoolExecutor 是如何做到线程重用的
大树叶
博客等级 码龄12年 1171粉丝 664原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值