MySQL 读写分离方案-Amoeba环境部署记录

Mysql的读写分离可以使用MySQL Proxy,也可以使用Amoeba。Amoeba(变形虫)项目是一个类似MySQL Proxy的分布式数据库中间代理层软件,是由陈思儒开发的一个开源的java项目。其主要功能包括读写分离,垂直分库,水平分库等,经过测试,发现其功能和稳定性都非常的不错,如果需要构架分布式数据库环境,采用Amoeba是一个不错的方案。目前Amoeba一共包括For aladdin,For MySQL和For Oracle三个版本,以下介绍主要关注For MySQL版本的一个读写分离实现。实际上垂直切分和水平切分的架构也相差不大,改动几个配置就可以轻松实现。下图是一个采用Amoeba的读写分离技术结合MySQL的Master-Slave Replication的一个分布式系统的架构:

Amoeba处于在应用和数据库之间,扮演一个中介的角色,将应用传递过来的SQL语句经过分析后,将写的语句交给Master库执行,将读的语句路由到Slave库执行(当然也可以到Master读,这个完全看配置)。Amoeba实现了简单的负载均衡(采用轮询算法,在配置文件里设置)和Failover。如果配置了多个读的库,则任何一个读的库出现宕机,不会导致整个系统故障,Amoeba能自动将读请求路由到其他可用的库上,当然,写还是单点的依赖于Master数据库的,这个需要通过数据库的切换,或者水平分割等技术来提升Master库的可用性。

Amoeba可以在不同机器上启动多个,并且做同样的配置来进行水平扩展,以分担压力和提升可用性,可以将Amoeba和MySQL装在同一台机器,也可以装在不同的机器上,Amoeba本身不做数据缓存,所以对于内存消耗很少,主要是CPU占用。对于应用来说,图中的三个Amoeba就是三台一模一样的MySQL数据库,连接其中任何一台都是可以的,所以需要在应用端有一个Load balance和Failover的机制,需要连接数据库时从三台中随机挑选一台即可,如果其他任何一台出现故障,则可以自动Failover到剩余的可用机器上。MySQL的JDBC驱动从connector-j 3.17版本起已经提供了这样的负载均衡和故障切换的功能,那么剩下的事情对于应用来说就很简单了,不需要做太多的改动就能搭建一套高可用的MySQL分布式数据库环境,何乐而不为?

Amoeba专注分布式数据库proxy开发。Amoeba身处在Client、DB Server(s)之间,对客户端透明,具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库、可并发请求多台数据库合并结果。
Amoeba主要解决:
1)降低 数据切分带来的复杂多数据库结构
2)提供切分规则并降低 数据切分规则 给应用带来的影响
3)降低db 与客户端的连接数
4)读写分离

为什么要用Amoeba
目前要实现mysql的主从读写分离,主要有以下几种方案:
1)通过程序实现,网上很多现成的代码,比较复杂,如果添加从服务器要更改多台服务器的代码。
2)通过mysql-proxy来实现,由于mysql-proxy的主从读写分离是通过lua脚本来实现,目前lua的脚本的开发跟不上节奏,而写没有完美的现成的脚本,因此导致用于生产环境的话风险比较大,据网上很多人说mysql-proxy的性能不高。
3)自己开发接口实现,这种方案门槛高,开发成本高,不是一般的小公司能承担得起。
4)利用阿里巴巴的开源项目Amoeba来实现,具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库,并且安装配置非常简单。经测试,性能相比mysql-proxy较高。

下面就基于Amoeba的读写分离环节部署做一记录:

1)环境准备

1

2

3

4

5

6

7

182.48.115.236     master-node

182.48.115.238     slave-node

182.48.115.237     amoeba-node

182.48.115.236和182.48.115.238做成mysql主从复制。关闭三台机器的iptables防火墙和selinux

mysql安装参考:http://www.cnblogs.com/kevingrace/p/6109679.html

mysql主从部署参考:http://www.cnblogs.com/kevingrace/p/6256603.html

2)amoeba安装

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

Amoeba框架是居于JDK1.5开发的,采用了JDK1.5的特性,所以还需要安装java环境,建议使用javaSE1.5以上的JDK版本.

1)安装java环境

安装参考:http://www.cnblogs.com/kevingrace/p/5870814.html

[root@amoeba-node ~]# yum -y install java-1.7.0-openjdk*

设置java的环境变量

[root@amoeba-node ~]# vim /etc/profile

.......

export JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk.x86_64

export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

export PATH=$PATH:$JAVA_HOME/bin

使之生效

[root@amoeba-node ~]# source /etc/profile

[root@amoeba-node ~]# java -version

java version "1.7.0_141"

OpenJDK Runtime Environment (rhel-2.6.10.1.el6_9-x86_64 u141-b02)

OpenJDK 64-Bit Server VM (build 24.141-b02, mixed mode)

2)安装Amoeba

下载地址:https://sourceforge.net/projects/amoeba/

百度云盘下载:https://pan.baidu.com/s/1c1FRsbe    提取密码:xav2

Amoeba安装非常简单,直接解压即可使用,这里将Amoeba解压到/usr/local/amoeba目录下,这样就安装完成了

[root@amoeba-node ~]# unzip amoeba-mysql-3.0.5-RC-distribution.zip

[root@amoeba-node ~]# mv amoeba-mysql-3.0.5-RC /usr/local/amoeba

[root@amoeba-node ~]# cd /usr/local/amoeba

[root@amoeba-node amoeba]# ll

总用量 20

drwxrwxrwx. 2 root root 4096 7月   5 2013 benchmark

drwxrwxrwx. 2 root root 4096 7月   5 2013 bin

drwxrwxrwx. 2 root root 4096 7月   5 2013 conf

-rwxrwxrwx. 1 root root  728 7月   5 2013 jvm.properties

drwxrwxrwx. 2 root root 4096 7月   5 2013 lib

3)配置Amoeba

Amoeba的配置文件位于/usr/local/amoeba/conf目录下。配置文件比较多,但是仅仅使用读写分离功能,只需配置两个文件即可,分别是dbServers.xml和amoeba.xml,

如果需要配置ip访问控制,还需要修改access_list.conf文件,下面首先介绍dbServers.xml的配置:

[root@amoeba-node amoeba]# cat conf/dbServers.xml

<?xml version="1.0" encoding="gbk"?>

<!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd">

<amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/">

    <!--

      Each dbServer needs to be configured into a Pool,

      If you need to configure multiple dbServer with load balancing that can be simplified by the following configuration:

       add attribute with name virtual = "true" in dbServer, but the configuration does not allow the element with name factoryConfig

       such as 'multiPool' dbServer  

    -->

     

  <dbServer name="abstractServer" abstractive="true">

    <factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">

      <property name="connectionManager">${defaultManager}</property>

      <property name="sendBufferSize">64</property>

      <property name="receiveBufferSize">128</property>

         

      <!-- mysql port -->

      <property name="port">3306</property>            //设置Amoeba要连接的mysql数据库的端口,默认是3306

       

      <!-- mysql schema -->

      <property name="schema">huanqiutest</property>         //设置缺省的数据库,当连接amoeba时,操作表必须显式的指定数据库名,即采用dbname.tablename的方式,不支持 use dbname指定缺省库,因为操作会调度到各个后端dbserver

       

      <!-- mysql user -->

      <property name="user">wang</property>        //设置amoeba连接后端数据库服务器的账号,因此需要在所有后端数据库上创建该用户,并授权amoeba服务器可连接

       

      <property name="password">wang123456</property>     //设置amoeba连接后端数据库服务器的密码

    </factoryConfig>

    <poolConfig class="com.meidusa.toolkit.common.poolable.PoolableObjectPool">

      <property name="maxActive">500</property>      //最大连接数,默认500

      <property name="maxIdle">500</property>        //最大空闲连接数

      <property name="minIdle">1</property>          //最新空闲连接数

      <property name="minEvictableIdleTimeMillis">600000</property>

      <property name="timeBetweenEvictionRunsMillis">600000</property>

      <property name="testOnBorrow">true</property>

      <property name="testOnReturn">true</property>

      <property name="testWhileIdle">true</property>

    </poolConfig>

  </dbServer>

  <dbServer name="masterdb"  parent="abstractServer">     //设置一个后端可写的dbServer,这里定义为masterdb,这个名字可以任意命名,后面在amoeba.xml文件里会用到

    <factoryConfig>

      <!-- mysql ip -->

      <property name="ipAddress">182.48.115.236</property>    //设置后端可写dbserver的ip

    </factoryConfig>

  </dbServer>

   

  <dbServer name="slavedb"  parent="abstractServer">        //设置后端可读dbserver(如果是多个slave从节点,这里就配置多个<dbServer ... </dbServer>,然后加入到后面第一的可读的组内)

    <factoryConfig>

      <!-- mysql ip -->

      <property name="ipAddress">182.48.115.238</property>     //设置后端可读dbserver的ip

    </factoryConfig>

  </dbServer>

   

  <dbServer name="myslave" virtual="true">              //设置定义一个虚拟的dbserver,实际上相当于一个dbserver组,这里将可读的数据库ip统一放到一个组中,将这个组的名字命名为myslave

    <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">

      <!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->

      <property name="loadbalance">1</property>          //选择调度算法,1表示复制均衡,2表示权重,3表示HA, 这里选择1

       

      <!-- Separated by commas,such as: server1,server2,server1 -->

      <property name="poolNames">slavedb</property>                  //myslave组成员

    </poolConfig>

  </dbServer>

     

</amoeba:dbServers>

另一个配置文件amoeba.xml

[root@amoeba-node amoeba]# cat conf/amoeba.xml

<?xml version="1.0" encoding="gbk"?>

<!DOCTYPE amoeba:configuration SYSTEM "amoeba.dtd">

<amoeba:configuration xmlns:amoeba="http://amoeba.meidusa.com/">

  <proxy>

   

    <!-- service class must implements com.meidusa.amoeba.service.Service -->

    <service name="Amoeba for Mysql" class="com.meidusa.amoeba.mysql.server.MySQLService">

      <!-- port -->

      <property name="port">8066</property>             //设置amoeba监听的端口,默认是8066

       

      <!-- bind ipAddress -->                        //下面配置监听的接口,如果不设置,默认监听所以的IP

      <!--

      <property name="ipAddress">127.0.0.1</property>   

       -->

       

      <property name="connectionFactory">

        <bean class="com.meidusa.amoeba.mysql.net.MysqlClientConnectionFactory">

          <property name="sendBufferSize">128</property>

          <property name="receiveBufferSize">64</property>

        </bean>

      </property>

       

      <property name="authenticateProvider">

        <bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator">

           

          <property name="user">root</property>     //提供客户端连接amoeba时需要使用这里设定的账号 (这里的账号密码和amoeba连接后端数据库服务器的密码无关)

           

          <property name="password">123456</property>

           

          <property name="filter">

            <bean class="com.meidusa.toolkit.net.authenticate.server.IPAccessController">

              <property name="ipFile">${amoeba.home}/conf/access_list.conf</property>

            </bean>

          </property>

        </bean>

      </property>

       

    </service>

     

    <runtime class="com.meidusa.amoeba.mysql.context.MysqlRuntimeContext">

       

      <!-- proxy server client process thread size -->

      <property name="executeThreadSize">128</property>

       

      <!-- per connection cache prepared statement size  -->

      <property name="statementCacheSize">500</property>

       

      <!-- default charset -->

      <property name="serverCharset">utf8</property>

       

      <!-- query timeout( default: 60 second , TimeUnit:second) -->

      <property name="queryTimeout">60</property>

    </runtime>

     

  </proxy>

   

  <!--

    Each ConnectionManager will start as thread

    manager responsible for the Connection IO read , Death Detection

  -->

  <connectionManagerList>

    <connectionManager name="defaultManager" class="com.meidusa.toolkit.net.MultiConnectionManagerWrapper">

      <property name="subManagerClassName">com.meidusa.toolkit.net.AuthingableConnectionManager</property>

    </connectionManager>

  </connectionManagerList>

   

    <!-- default using file loader -->

  <dbServerLoader class="com.meidusa.amoeba.context.DBServerConfigFileLoader">

    <property name="configFile">${amoeba.home}/conf/dbServers.xml</property>

  </dbServerLoader>

   

  <queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter">

    <property name="ruleLoader">

      <bean class="com.meidusa.amoeba.route.TableRuleFileLoader">

        <property name="ruleFile">${amoeba.home}/conf/rule.xml</property>

        <property name="functionFile">${amoeba.home}/conf/ruleFunctionMap.xml</property>

      </bean>

    </property>

    <property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xml</property>

    <property name="LRUMapSize">1500</property>

    <property name="defaultPool">masterdb</property>         //设置amoeba默认的池,这里设置为masterdb(这个是在dbServers.xml文件里定义的)

     

    <property name="writePool">masterdb</property>        //这两个选项默认是注销掉的,一定要取消注释!否则读写分离无效,这里用来指定前面定义好的写池

    <property name="readPool">myslave</property>          //取消注释,这个是前面在dbServers.xml文件里定义的读池

   

    <property name="needParse">true</property>

  </queryRouter>

</amoeba:configuration>

4)在masterdb上(即master节点机182.48.115.236上)创建数据库huanqiutest

mysql> create database huanqiutest;

Query OK, 1 row affected (0.00 sec)

mysql> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| huanqiutest        |

| mysql              |

| performance_schema |

test               |

+--------------------+

5 rows in set (0.00 sec)

然后在slavedb上(即slave节点182.48.115.238上)查看是否复制成功

mysql> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| huanqiutest        |

| mysql              |

| performance_schema |

test               |

+--------------------+

5 rows in set (0.00 sec)

分别在masterdb和slavedb上为amoedb授权

mysql> GRANT ALL ON huanqiutest.* TO 'wang'@'182.48.115.237' IDENTIFIED BY 'wang123456';

Query OK, 0 rows affected (0.00 sec)

mysql> flush privileges;

Query OK, 0 rows affected (0.00 sec)

启动amoeba

[root@amoeba-node ~]# /usr/local/amoeba/bin/launcher

....................................................................................

报错1:

The stack size specified is too small, Specify at least 228k

Error: Could not create the Java Virtual Machine.

Error: A fatal exception has occurred. Program will exit.

解决办法:

从错误文字上看,应该是由于stack size太小,导致JVM启动失败,要如何修改呢?

其实Amoeba已经考虑到这个问题,并将JVM参数配置写在属性文件里,可以通过该属性文件修改JVM参数。

修改jvm.properties文件JVM_OPTIONS参数。

[root@amoeba-node ~]# vim /usr/local/amoeba/jvm.properties

将内容

JVM_OPTIONS="-server -Xms256m -Xmx1024m -Xss196k -XX:PermSize=16m -XX:MaxPermSize=96m"

修改为

JVM_OPTIONS="-server -Xms1024m -Xmx1024m -Xss256k -XX:PermSize=16m -XX:MaxPermSize=96m"

再次启动Amoeba就ok了

[root@amoeba-node ~]# nohup /usr/local/amoeba/bin/launcher &      //将amoeba放在后台执行。该命令执行后,按ctrl+c

[root@amoeba-node ~]# ps -ef|grep amoeba

root     19079     1  1 15:01 pts/0    00:00:02 /usr/lib/jvm/java-1.7.0-openjdk.x86_64/bin/java -server -Xms1024m -Xmx1024m -Xss256k -XX:PermSize=16m -XX:MaxPermSize=96m -Dproject.home=/usr/local/amoeba -Damoeba.home=/usr/local/amoeba -Dproject.name=Amoeba-MySQL -Dproject.output=/usr/local/amoeba/logs -Dignore.signals=1,2 -Dclassworlds.conf=/usr/local/amoeba/bin/launcher.classpath -classpath /usr/local/amoeba/lib/plexus-classworlds-2.4.2-HEXNOVA.jar org.codehaus.classworlds.Launcher

root     19103 19009  0 15:02 pts/0    00:00:00 /bin/bash /usr/local/amoeba/bin/launcher

root     19109 19103  0 15:02 pts/0    00:00:00 tail -f /usr/local/amoeba/logs/console.log

root     19172 19009  0 15:04 pts/0    00:00:00 grep amoeba

[root@amoeba-node ~]# lsof -i:8066

COMMAND   PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME

java    19079 root   64u  IPv6 2705157      0t0  TCP *:8066 (LISTEN)

..................................................................................

3)amoeba读写分离测试

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

注意:上面在amoeba.xml中指定的连接amoba的帐号和密码(即root/123456)要提前在master和slave两台节点机上授权

mysql> grant all on *.* to root@'182.58.115.%' identified by "123456";

Query OK, 0 rows affected (0.00 sec)

mysql> flush privileges;

Query OK, 0 rows affected (0.00 sec)

然后在mysql客户端通过amoeba配置文件amoeba.xml中指定的用户名、密码、和端口以及amoeba服务器ip地址远程登陆mysql数据库

[root@localhost ~]# mysql -h182.48.115.237 -uroot -p123456 -P8066

Welcome to the MySQL monitor.  Commands end with ; or \g.

Your MySQL connection id is 2052322366

Server version: 5.1.45-mysql-amoeba-proxy-3.0.4-BETA Source distribution

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| huanqiutest        |

test               |

+--------------------+

3 rows in set (0.00 sec)

在huanqiutest库创建haha表,并插入数据

mysql> use huanqiutest;

Database changed

mysql> create table if not exists haha (id int(10) PRIMARY KEY AUTO_INCREMENT,name varchar(50) NOT NULL);

Query OK, 0 rows affected (0.20 sec)

mysql> insert into haha values(1,"wangshibo"),(2,"guohuihui");

Query OK, 2 rows affected (0.01 sec)

Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from haha;

+----+-----------+

id | name      |

+----+-----------+

|  1 | wangshibo |

|  2 | guohuihui |

+----+-----------+

2 rows in set (0.01 sec)

分别登陆masterdb(即master-node节点)和slavedb(slave-node节点)查看数据

master-node数据库

mysql> select * from huanqiutest.haha;

+----+-----------+

id | name      |

+----+-----------+

|  1 | wangshibo |

|  2 | guohuihui |

+----+-----------+

2 rows in set (0.00 sec)

slave-node数据库

mysql> select * from huanqiutest.haha;

+----+-----------+

id | name      |

+----+-----------+

|  1 | wangshibo |

|  2 | guohuihui |

+----+-----------+

2 rows in set (0.00 sec)

-------------------------------------------------------------------------

停掉masterdb,然后在客户端分别执行插入和查询功能

[root@master-node ~]# /etc/init.d/mysql stop

Shutting down MySQL............ SUCCESS!

客户端连接amoeba后插入新数据

mysql> insert into huanqiutest.haha values(3,"zhangmin");

ERROR 1044 (42000): Amoeba could not connect to MySQL server[182.48.115.236:3306],拒绝连接

mysql> select * from huanqiutest.haha;

+----+-----------+

id | name      |

+----+-----------+

|  1 | wangshibo |

|  2 | guohuihui |

+----+-----------+

2 rows in set (0.01 sec)

可以看到,关掉masterdb后,写入报错,读正常

------------------------------------------------------------------------

开启masterdb上的msyql 关闭slavedb上的mysql

masterdb

[root@master-node ~]# /etc/init.d/mysql start

Starting MySQL.. SUCCESS!

slavedb

[root@slave-node ~]# /etc/init.d/mysql stop

Shutting down MySQL....                                    [确定]

客户端再次尝试

mysql> insert into huanqiutest.haha values(3,"zhangmin");

Query OK, 1 row affected (0.01 sec)

mysql> select * from huanqiutest.haha;

ERROR 1044 (42000): poolName=myslave, no valid pools

可以看到插入成功,读取失败

------------------------------------------------------------------------

开启slavedb上的mysql,查看数据是否自动同步

[root@slave-node ~]# /etc/init.d/mysql start

Starting MySQL..                                           [确定]

客户端:

mysql> select * from huanqiutest.haha;

+----+-----------+

id | name      |

+----+-----------+

|  1 | wangshibo |

|  2 | guohuihui |

|  3 | zhangmin  |

+----+-----------+

3 rows in set (0.00 sec)

由此可见,amoeba的读写分离的效果已经很明显了!上面是amoeba针对一个库的读写分离配置,如果是多个库的读写分离,可以部署多个amoeba实例,amoeba端口不一样,然后启动多个实例即可。

....................................................................................................................
Amoeba的有关配置文件说明

1

2

3

4

5

6

7

主配置文件:amoeba.xml                  用来配置Amoeba服务的基本参数,如Amoeba主机地址、端口、认证方式、用于连接的用户名、密码、线程数、超时时间、其他配置文件的位置等。

数据库服务器配置文件:dbServers.xml       用来存储和配置Amoeba所代理的数据库服务器的信息,如:主机IP、端口、用户名、密码等。

切分规则配置文件rule.xml                 用来配置切分规则。

数据库函数配置文件:functionMap.xml       用来配置数据库函数的处理方法,Amoeba将使用该配置文件中的方法解析数据库函数。

切分规则函数配置文件ruleFunctionMap.xml   用来配置切分规则中使用的用户自定义函数的处理方法。

访问规则配置文件:access_list.conf        用来授权或禁止某些服务器IP访问Amoeba。

日志规格配置文件log4j.xml                用来配置Amoeba输出日志的级别和方式。

*************** 当你发现自己的才华撑不起野心时,就请安静下来学习吧!***************

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值