架构设计:系统存储(11)——MySQL主从方案业务连接透明化(上)

MySQL5.7 多主一从(多源复制)同步配置 多主一从 阅读详情

转自http://blog.youkuaiyun.com/yinwenjie/article/details/52980883

1、MySQL主从方案业务层的问题

在之前的文章中,我们提到MySQL一主多从集群模式下,对上层业务系统的访问带来了一些问题。本编文章中我们将深入分析这个问题,并介绍如何对这个问题进行改进。MySQL一主多从集群对上层业务系统带来的主要问题是,上层业务系统需要自行控制本次MySQL数据操作需要访问MySQL集群中的哪个节点。产生这个问题的主要原因,是因为MySQL一主多从集群本身并没有提供现成功能,将集群中的节点打包成统一服务并向外提供。

这里写图片描述

在上图所示的MySQL集群中,有一个Master节点负责所有的写事务操作,还有两个Salve节点分别负责订单模块的读操作和用户模块的读操作,而这个架构方案中由于没有中间管理层,所以到底访问哪一个MySQL服务节点的判断工作全部需要由上层业务系统自行判断。那么解决这个问题的思路也就比较清晰了:我们需要通过一些手段自行为业务层的访问增加一个中间层,以减少业务开发人员的维护工作。
2、改进方式一:使用Spring套件屏蔽细节

如果您的工程使用了spring组件,那这个问题可以使用Spring配置问题进行改善。但这个方式只能算是改善问题,不能算作完全解决了问题。这是因为虽然通过Spring配置后,业务开发人员不需要再为“访问哪个数据库节点”操碎了心,但是Spring的配置文件依然是存在于业务系统中,当下层MySQL集群节点发生变化时,业务系统就需要改变配置信息并且重新部署;当MySQL集群现有节点发生故障时,上层业务系统也需要变更配置信息并重新部署。这种配置的方法并不能实现数据访问逻辑的完全脱耦。

下面我们给出一个示例,在这个示例中我们使用spring 3.X 版本 + hibernate 4.X 版本 + c3p0 + MySQL JDBC 实现在业务系统中访问数据库节点的规则配置。

这里写图片描述

如上图所示,我们在业务系统中建立了两个数据源:writeSessionFactory、readSessionFactory,分别负责业务数据的写操作和读操作。当下面的MySQL集群增加新的读节点或者集群中现有节点发生变化时,spring的配置文件也要做相应的配置变化:

写操作涉及的数据源和AOP点配置
<!-- 工程中和读写数据源分离无关的Spring配置信息,在这里就不进行赘述了 -->
......
<!-- 这个数据源连接到maseter节点, 作为写操作的数据源-->
<bean id="writedataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass"><value>${writejdbc.driver}</value></property>
    <property name="jdbcUrl"><value>${writejdbc.url}</value></property>
    <property name="user"><value>${writejdbc.username}</value></property>
    <property name="password"><value>${writejdbc.password}</value></property>
    <property name="minPoolSize"><value>${writec3p0.minPoolSize}</value></property>
    <property name="maxPoolSize"><value>${writec3p0.maxPoolSize}</value></property>
    <property name="initialPoolSize"><value>${writec3p0.initialPoolSize}</value></property>
    <property name="maxIdleTime"><value>${writec3p0.maxIdleTime}</value></property>
    <property name="acquireIncrement"><value>${writec3p0.acquireIncrement}</value></property>
</bean>

<!-- 数据层会话工厂和数据源的映射关系 -->
<bean id="writeSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="writedataSource" />
    <property name="namingStrategy">
        <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">${writehibernate.dialect}</prop>
            <prop key="hibernate.show_sql">${writehibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${writehibernate.format_sql}</prop>
            <prop key="hibernate.hbm2ddl.auto">${writehibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
        </props>
    </property>
</bean>
<bean id="writetransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="writeSessionFactory" />
</bean>

<!-- 
AOP设置,在templateSSHProject.dao.writeop包或者子包中
涉及以下名字的方法,都要开启事务托管。
并且在抛出任何异常的情况下,spring都要回滚事务
-->
<aop:config> 
    <aop:pointcut id="writedao" expression="execution(* templateSSHProject.dao.writeop..*.* (..))" />
    <aop:advisor advice-ref="writetxAdvice" pointcut-ref="writedao" />
</aop:config>
<tx:advice id="writetxAdvice" transaction-manager="writetransactionManager">
    <tx:attributes>
        <tx:method name="save*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="update*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="delete*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="modify*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="create*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="remove*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="*" read-only="true" />
    </tx:attributes>
</tx:advice>
......
读操作涉及的数据源和AOP点配置,和写操作数据源配置类似,各位读者只需要注意不同点
......
<!-- 这个数据源连接到salve节点, 作为读操作的数据源 -->
<bean id="readdataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass"><value>${readjdbc.driver}</value></property>
    <property name="jdbcUrl"><value>${readjdbc.url}</value></property>
    <property name="user"><value>${readjdbc.username}</value></property>
    <property name="password"><value>${readjdbc.password}</value></property>
    <property name="minPoolSize"><value>${readc3p0.minPoolSize}</value></property>
    <property name="maxPoolSize"><value>${readc3p0.maxPoolSize}</value></property>
    <property name="initialPoolSize"><value>${readc3p0.initialPoolSize}</value></property>
    <property name="maxIdleTime"><value>${readc3p0.maxIdleTime}</value></property>
    <property name="acquireIncrement"><value>${readc3p0.acquireIncrement}</value></property>
</bean>

<!-- 数据层会话工厂和数据源的映射关系,基本上和write的设置一致 -->
<bean id="readSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="readdataSource" />
    <property name="namingStrategy">
        <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">${readhibernate.dialect}</prop>
            <prop key="hibernate.show_sql">${readhibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${readhibernate.format_sql}</prop>
            <prop key="hibernate.hbm2ddl.auto">${readhibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
        </props>
    </property>
</bean>
<bean id="readtransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="readSessionFactory" />
</bean>

<!-- 
AOP设置,和写操作事务不同的点也在这里
这里不需要设置任何方法方法开启事务托管
甚至不需要配置这段AOP切面
-->
<aop:config> 
    <aop:pointcut id="readdao" expression="execution(* templateSSHProject.dao.readop..*.* (..))" />
    <aop:advisor advice-ref="readtxAdvice" pointcut-ref="readdao" />
</aop:config>
<tx:advice id="readtxAdvice" transaction-manager="readtransactionManager">
    <tx:attributes>
        <tx:method name="*" read-only="true" />
    </tx:attributes>
</tx:advice>
......

要看懂以上的Spring配置信息,首先请确定您接触过Spring组件。以上配置主要使用XML文件定义的方式指定需要使用“写数据源”的方法名,使用AOP切面为这些指定的方法名开启事务托管。在实际使用过程中,各位读者也可以使用Java代码注解的方式在指定的包范围内标记需要开启事务托管的方法。只需要在配置文件中增加一段说明信息“”

这样的读写数据源分离的方式,只会影响业务开发人员在数据层的操作,对显示层和业务逻辑层没有任何影响。这里再给出工程中数据层部分的包结构,这个结构和AOP配置中的切面扫描点“expression”有关。从以下给出的结构图可以看出,在数据层进行的数据读操作和数据写操作是分离的,这样可以避免业务开发人员在编写代码时发生混淆(例如在负责读操作的工程包中进行写操作)

这里写图片描述

当然针对各位读者自身的业务形态,您也可以将两个数据源合并在一起混合使用,不过这可能会加重业务开发人员的维护工作。将读写操作数据源合并的方式也很简单,基本上不需要更改任何配置,只需要将两个sessionFactory注入到同一个AbstractRelationalDBDAO——为DAO层设置的公共父类。

......
public abstract class AbstractReadRelationalDBDAO ...... {

    // AbstractRelationalDBDAO类的其它部分都省略了
    ......
    @Autowired
    @Qualifier("readSessionFactory")
    private SessionFactory readSessionFactory;

    @Autowired
    @Qualifier("writeSessionFactory")
    private SessionFactory sessionFactory;
    ......
}
......

本示例实现的Spring配置中只有一个负责写操作数据源和一个负责读操作数据源。那么从理论上讲这种Spring配置方式只能适应一主一从的MySQL集群。当MySQL集群中的节点发生,例如增加了一个Salve从节点,业务工程就会增加一个从节点的数据源配置信息,并且在工程的数据层(DAO层)增加新的代码包。这显然是很有问题的,甚至可以说整个业务工程基本无法维护,这是因为再稳定的MySQL集群也只能保证5个9的系统可用性(即99.999%),另外现在主流的集群思想中本来就是假设集群中的节点随时可能出现问题,而业务系统显然不可能在无法预知的情况下随时改变配置信息并重新部署。

3、改进方式二:透明化中间层

那么有没有什么办法能够解决以上的问题呢?既突破以上Spring配置方式只适应一主一从MySQL集群的瓶颈,又不增加业务开发人员的维护难度,还能适应下层数据集群随时发生的节点故障。当然是有办法的,使用我们已经在负载均衡专题介绍过的LVS,我们可以为MySQL集群中的多个读节点构造一个透明层,使得它们可以作为一个整体,并使用一个统一的访问地址向上层业务系统提供数据读取服务。

这里写图片描述

如果您还不清楚LVS配置方式,可以参见我另外一专题中,专门介绍LVS的几篇文章:《架构设计:负载均衡层设计方案(4)——LVS原理》、《架构设计:负载均衡层设计方案(5)——LVS单节点安装》。需要注意的是,这里选择的负载方案应该工作在网络协议的下层,例如OSI七层模型的链路层或者传输层。这是因为上层系统连接MySQL服务节点主要基于TCP/IP协议而不是基于HTTP协议,例如MySQL的多数客户端软件(MySQL-Front、Navicat等)都使用MySQL原生连接协议,这个协议就是基于TCP/IP协议的,再例如绝大多数Java应用程序连接和调用MySQL操作所基于的JDBC API,也是基于TCP/IP协议。所以这里使用的负载均衡方案不能使用Nginx这样只支持Http协议的组件,而LVS组件可以很好的适应技术需求。

以上的改进方案中,我们只对MySQL集群中的读操作节点进行了改进,但是整个集群还是没有足够的稳定保证。这是因为MySQL集群中写操作节点目前还只有单个节点承载工作,新加入的LVS负载节点也只有单个节点承载工作。如果在生产环境下,以上这些节点出现故障无法工作将导致整个MySQL集群崩溃。进一步的改进方式,就是为集群中的写操作节点和LVS负载节点增加热备方案,如下图所示:

这里写图片描述

上图中我们使用Heartbeat + DRBD第三方组件的方式,为MySQL Master节点复制了一个可以即时切换的处于“准备”状态的备用节点。Heartbeat组件的作用和之前我们介绍过的keepalived组件类似,它用于监控两个(或多个)节点的工作状态,并在满足宕机的判断条件时完成浮动IP的切换和备用服务的启动工作。DRBD组件是一个工作在Linunx系统下的,可以完成实时文件差异化同步的磁盘块映射软件,类似的软件还有RSync。

有了Heartbeat + DRBD第三方组件的支持,就可以保证当MySQL集群中的写操作节点不能提供服务时,另一个等待工作的备份写操作节点能够及时的接过工作任务,并且这个备份节点上的数据库表数据和之前崩溃的写操作节点上的数据库表数据是一致的。这个方案还可以更换成Keepalived + RSync的第三方组件方案。

LVS节点的高可用方案,在之前的文章中已经介绍过了。不清楚的读者可以参考我另一篇文章《架构设计:负载均衡层设计方案(7)——LVS + Keepalived + Nginx安装及配置》,只不过文章中的需要被保证高可用性的组件由Nginx替换成了MySQL服务。不过这样的读节点组织方式,也有一些问题:虽然这些读节点通过负载均衡的方式可以分担各自的工作压力,但是这些读操作节点不能按照上层业务的不同,分模块提供独立的、有个性的查询操作服务。

Java应用层数据加密存储:基于JDBC与AES-GCM的透明化实现方案 数据加密是保障敏感信息安全的核心技术手段,其原理是通过特定算法将明文转换为无法直接识别的密文,确保即使数据存储介质被非法访问,信息内容也不会泄露。在Java企业级应用中,JDBC作为数据库访问的标准接口,是实现数据安全落地的关键环节。通过在JDBC层集成加密逻辑,可以实现从应用到数据库的端到端加密,有效防范拖库、内鬼等安全风险,尤其适用于处理身份证号、手机号等个人敏感信息。本文聚焦于利用AES-GCM对称加密算法,结合MyBatis等ORM框架的类型处理器机制,实现一套对业务代码透明的数据加密存储方案,并深 阅读详情

相关推荐

MyBatis-Plus字段级数据加密实战:基于TypeHandler与MetaObjectHandler的透明化方案

数据安全是软件开发的基石,尤其在处理用户隐私和金融信息时,数据库层面的敏感数据保护至关重要。其核心原理在于,在数据持久化过程中,对特定字段进行加密转换,确保存储的密文即使泄露也无法直接识别。这一技术能有效防范“拖库”风险,为业务数据构建底层安全防线。在Java生态中,MyBatis-Plus作为广泛使用的ORM框架,其提供的**类型处理器(TypeHandler)** 与**元对象处理器(MetaObjectHandler)** 两大扩展机制,为实现非侵入式的字段级加密提供了优雅的工程实践路径。通过自定义T

weixin_33928137的博客 390

springcloud集成mysql一主多从配置

主从复制mysql

V_1920的博客 1016

MySQL 8.0 vs MariaDB 10.11:5个关键性能指标与3个迁移决策点

本文深入对比MySQL 8.0与MariaDB 10.11在性能指标、存储引擎、功能特性等方面的核心区别,提供5个关键性能指标实测数据和3个迁移决策框架,帮助架构师根据业务需求做出最优数据库选型。

weixin_33853827的博客 412

mysql5.6读写分离原理_Mysql主从 读写分离(一主一从或一主多从),双机热备(互为主从)...

什么是主从复制当mysql数据库的数据量太大的时候,查询数据就很吃力了,无论怎么优化都会产生瓶颈,这时我们需要增加服务器设备来实现分布式数据库,首先要了解主从数据库服务器的版本的需求,主从mysql的安装运行版本需一致。因此,我们利用mysql自带的REPLICATION来实现mysql多机主从同步的功能,mysql版本为5.7进行演示。 什么是读写分离就是把对数据库的读操作和写操作分离开,将读写...

weixin_39936403的博客 351

11.15 分析finrl 的框架

11.15 分析finrl 的框架

yueguoqing88的博客 1083

RoboMaster电控入门:手把手教你用STM32CubeMX配置大疆C板CAN总线驱动GM6020电机

本文详细介绍了如何使用STM32CubeMX配置大疆C板CAN总线驱动GM6020电机,适用于RoboMaster电控入门。从硬件准备到CubeMX工程初始化,再到CAN过滤器配置和电机控制协议解析,手把手教你避开常见配置陷阱,实现精准控制。

weixin_42524762的博客 267

mysql 多主一从_mysql 多主一从

一、主服务器准备1.1、环境准备两台主机器ip分别为100.100.100.105 (主1)100.100.100.106(主2)安装 mysql[root@centos ~]# yum install mysql-server mysql-client -y[root@centos ~]# service mysqld start[root@centos ~]# /usr/bin/mysqlad...

weixin_36280338的博客 957

别再浪费HDRI了!D5渲染器环境光设置避坑指南(附天空球调试参数)

本文深入解析D5渲染器中HDRI环境光的高阶调试技巧,揭示90%渲染师常犯的错误认知。通过参数联动黄金法则、特殊材质应对方案及动态场景优化策略,帮助用户精准控制光子能量分布,实现建筑玻璃反射云层过渡等专业级效果。附赠天空球调试参数和商业项目验证案例,助您彻底释放HDRI潜力。

weixin_29216049的博客 494

架构设计:系统存储(12)——MySQL主从方案业务连接透明化(中)

接上文《架构设计:系统存储(11)——MySQL主从方案业务连接透明化(上)》 4、改进方式三:MyCat数据库中间件...

113

Ubuntu 24.04 部署 EMQX 5.8 开源版MQTT服务器:从零搭建到实战应用

本文详细指导如何在Ubuntu 24.04系统上部署EMQX 5.8开源版MQTT服务器。内容涵盖从环境准备、安装Erlang、下载安装包到启动服务的完整步骤,并提供了验证服务、配置网络防火墙以及通过WebSocket工具进行基础消息收发的实战方法,帮助用户快速搭建稳定、免费的物联网消息中间件。

weixin_29209825的博客 293

nvidia系列教程-AGX-Orin usb3.0接口调试

NVIDIA Jetson AGX Orin 是一款高性能的嵌入式计算平台,适用于 AI 推理、机器人、自动驾驶等应用场景。本文将介绍如何对 AGX Orin 平台上的 USB 3.0 接口进行调试,包括硬件检查、软件配置以及常见问题的排查。本文主要介绍在AGX-Orin平台开启两个usb3.0口的方法。通过以上步骤,您可以有效地调试和优化 NVIDIA Jetson AGX Orin 上的 USB 3.0 接口。

flypig has a dream! 1500

Apache ShardingSphere数据脱敏全解决方案详解(下)

ApacheShardingSphere数据脱敏全解决方案详解(下)作者介绍潘娟,京东数科高级DBA,主要负责京东数科数据库中间件开发、数据库运维自动化平台开发、生产数据...

ShardingSphere官微 3494

在 SAP 中实现“租赁资产”管理,核心是把 IFRS16/ASC842 等新租赁准则的要求落地到系统

摘要:本文详细介绍了在SAP S/4HANA系统中实现IFRS16/ASC842租赁准则的具体操作流程。主要内容包括:1) 系统前置配置,如激活租赁会计组件、定义租赁类型和会计科目;2) 租赁合同创建与主数据维护;3) 初始确认的会计处理;4) 日常折旧计提和租金支付流程;5) 合同变更与终止管理。通过RE-FX-LM模块与固定资产模块的集成,系统可自动生成使用权资产和租赁负债,支持从合同创建到报表披露的全生命周期管理,确保符合新租赁准则要求。

金牌架构师 984

sqllyog可以连接oracle_sqlyog连接不上数据库

MYSQL备份还原问题用mysql,原有数据库shopdb,想新增一个数据库shopdb2,表结构保持和shopdb一样在ecs服务器上用sqlyog连接rds数据库,创建shopdb2后右键shopdb数据库导出,导出shopdb的表结构保持,然后在shopdb2上右键导入问题来了,shopdb里的数据删除了,因...文章niunan2020-04-17321浏览量visual studio中运...

weixin_30709043的博客 2088

AFSim2.9.0学习笔记 —— 1、AFSim及完整工具介绍(文末附:完整afsim2.9.0源码、编译好的完整工具包、中文教材等)

AFSim2.9.0学习笔记 —— 1、AFSim及完整工具介绍(附:完整afsim2.9.0源码、编译好的完整工具包、中文教材等)

5170

解锁PyCharm+Deepseek:本地部署AI编程超能力

在本次探索中,我们成功地在 PyCharm 中实现了 DeepSeek 的本地化部署,这一过程虽然涉及多个步骤,但每一步都为我们开启 AI 编程大门奠定了基础。从安装最新版 PyCharm,确保我们拥有一个功能强大且兼容性良好的开发环境,到注册 DeepSeek 账号并获取 API Key,这是连接我们与强大 AI 能力的关键纽带。接着,在 PyCharm 中安装 Continue 插件并进行精细配置,让 DeepSeek 能够深度融入我们的编程流程。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 5548

jsp保险业务管理系统utl94

jsp保险业务管理系统utl94本系统(程序源码数据库调试部署开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表项目功能开题报告内容。

ck3027的博客 1141

ssm框架搭建

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

java datasource详解_Apache ShardingSphere数据脱敏全解决方案详解

Apache ShardingSphere数据脱敏全解决方案详解作者简介潘娟,京东数科高级DBA,主要负责京东数科数据库中间件开发、数据库运维自动化平台开发、生产数据库运维工作。多次参与京东6.18、11.11等大促活动的护航工作。曾负责京东数科数据库自动化平台设计与开发项目,现专注于Apache ShardingSphere分布式数据库中间件开发。乐于在数据库、自动化、分布式、中间件等相关领域进...

weixin_39697096的博客 182

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

廖雪峰Python教程[完整版]

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

KingbaseES权限隔离深度解析:从MySQL的遗憾到安全增强的革新之路

本文深入探讨数据库权限管理的安全问题,对比分析MySQL与国产金仓KingbaseES的权限体系差异。MySQL存在超级权限过度集中、列级权限难实现等缺陷,而金仓通过"三权分立"架构实现系统管理员、安全管理员和审计管理员的权限隔离,提供了更精细的权限控制。文章详细介绍金仓的系统特权设置、角色权限授予机制,展示其从粗放式管理到精准制衡的进化过程,为关键行业数据安全提供可靠解决方案。

优快云博客专家,领域包括但不限于:AI、大数据、Python、架构师,有合作、课程、问题、疑惑请私信博主 1万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

jsp房地产公司业务管理系统rq7yh

在美国和欧洲等高度发达的房地产市场,数字化和信息化的管理系统应用十分普遍,如Zillow等在线房地产平台,通过强大的前后台系统,实现了房产信息的透明化、高效化管理。JSP的引入主要是为了简化Web应用程序的开发过程,并实现动态内容的展示。本研究旨在开发一套基于JSP的房地产公司业务管理系统,涵盖员工信息管理、部门信息管理、岗位信息管理、申请模板管理、员工请假管理以及员工帖子管理等功能模块。在JSP文件中,可以使用HTML标签来定义页面的结构,并使用JSP标签(如<% %>、<%= %>、<%!

cccc_cz的博客 957

【中间件】UDAL(统一数据访问层):一种分布式数据库中间件解决方案深度解析

UDAL(统一数据访问层)是一种分布式数据库中间件解决方案,由数据存储层(TeleDB)和数据访问层(TeleUDAL)两部分组成。其核心设计目标是解决分布式系统中数据库分库分表带来的数据透明访问难题,同时提供高性能、高可用的数据库集群服务。

一起探索IT的世界 1591
上一篇: 架构设计:系统存储(10)——MySQL简单主从方案及暴露的问题
下一篇: C++细节——static类成员使用注意
lm_123_456
博客等级 码龄11年 35粉丝 86原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值