VCS第三波-VCS的模块工作原理

前言

既然VCS用得多了,原理还是要了解下的,不然定位问题还是很吃力的,知道了集群知识和VCS的工作原理,对日常管理有很大的帮助 转自http://down.51cto.com/data/316764

小结

HAD用于是配合Agent做状态同步的工作进程
LLT用于心跳通讯
GAB用于Cluster内节点的广播,相互协同工作
Fencing用于Cluster管理,相当于脑部,搜集其他部件返回的信息作出决策判断

1、VCS模块简介

HAD: HighAvailability Daemon,是一个后台程序,VCS用来管理Cluster配置信息,响应用户命令,跟踪VCS AGENT传来的各种resource状态变化。
LLT: 是VCS底层的传输协议,同时也负责监控Cluster节点的心跳信息。
GAB: Groupmembership / Atomic Broadcast。 是一个用于管理Cluster成员组成,并在Cluster节点间进行原子广播的内核模块。
I/O Fencing: I/O Fencing 使用一种类似投票的机制,来防止Cluster分裂成两个或多个子群集,它保证在网络异常时,只有一个Cluster可以幸存下来。另外,I/O Fencing还利用SCSI-3协议的persistent reservation来保护共享磁盘上的数据。 

2、HAD

High Availability Daemon,是一个后台程序,VCS用来管理Cluster配置信息,响应用户命令,跟踪VCS AGENT传来的各种resource状态变化。并保持每个Node的状态同步,关于状态同步,有个专门的名称,叫做:“replicatedstate machine”,HAD就是这样一个machine。在每个Node上,都有一个HAD进程在运行。

……

阅读全文

VCS第二波-命令管理

声明:部分截图和命令的返回结果借用网络,因VCS是商业软件缺license,安装VCS后需要license,演示成本太大

环境和安装目录

VCS的安装和命令都在下列目录下:sbin, /usr/sbin, /opt/VRTS/bin,和/opt/VRTSvcs/bin
如果没有设置VCS的环境变量,请在/etc/profile文件中增加如下信息

1
PATH=/opt/VRTSvcs/bin:/sbin:/usr/sbin:/usr/local/sbin:$PATH

修改完毕后需执行

1
source /etc/profile

或者重新登录。

1、基本命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
使用/etc/rc.d/llt {start|stop|restart|condrestart|status}命令可以启动、停止、重启llt及查看llt状态;  
  
使用/etc/rc.d/gab{start|stop|restart|condrestart|status}命令可以启动、停止、重启gab及查看gab状态;  
  
使用/etc/rc.d/vcs {start|stop|restart|condrestart|status}命令可以启动、停止、重启VCS及查看VCS状态;也可使用hastart [-stale|-force]来启动VCS;  
  
使用下列命令来查看集群状态:hastatus或者hastat -sum  
  
启动单节点集群:hastart -onenode  
  
以时间共享方式启动VCS:hastart -ts  
  
关闭整个集群服务:hastop -all [-force]  
  
关闭集群中一台主机上的集群服务:hastop -local  
  
只关闭VCS相关进程,而不停止VCS所保护的服务进程:hastop -sys system [-force | -evacuate]  
  
使配置文件可读写:haconf -makerw  
  
使配置文件只读:haconf -dump -makero  

2、VCS的用户管理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
增加用户,用户默认有观察员身份:hauser -add user  
  
将用户提升为集群管理员:haclus -modify Administrators -add user  
  
将用户提升为集群操作员:haclus -modify Operators -add user;  
  
将用户提升为服务组管理员:hagrp  -modify service_group Adminstrators -add user  
  
将用户提升为服务组操作员:hagrp  -modify service_group Operators -add user  
  
删除用户:hauser -delete user  
  
显示所有用户:hauser -display  
  
查询集群日志:  
  
hamsg -list  
  
hamsg -info [-path path_name] LDF  

3、对服务组的基本管理操作:

 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
查询service_group状态:hagrp  -state [service_group] -sys [system];  
  
查询节点之间依赖关系:hagrp -dep [service_group]  
  
查询资源表:  
  
hagrp  -resources [service_group] ;  
  
hagrp -display [service_group] [-attribute attribute] [-sys system]  
  
hares -display [resource]  
  
hares -display -attribute [attribute]  
  
hares -display -group [service_group]  
  
hares -display -type [resource_type]  
  
hares -display -sys [system]  
  
启动服务组及使服务联机:hagrp  -online service_group -sys system  
  
关闭服务组及使服务脱机:hagrp  -offline service_group -sys system  
  
将服务组切换到另一个系统上:(只对failover组有效,且当服务组中服务部分或全部联机时):  
  
hagrp  -switch service_group -to system  
  
冻结服务组(disable onlining, offlining, and failover),option -persistent 是使cluster重启时freeze:  
  
hagrp  -freeze service_group [-persistent]  
  
解冻被冻结的服务组 (reenable onlining, offlining, and failover)  
  
hagrp  -unfreeze service_group [-persistent]  
  
激活服务组:(服务组激活后才能进行联机操作)hagrp  -enable service_group [-sys system]  
  
禁用服务组:(服务组禁用后不能进行联机或切换操作)hagrp  -disable service_group [-sys system]  
  
激活服务组中资源:hagrp  -enableresources service_group  
  
禁用服务组中资源:(如资源为disable时agents不监控资源组)hagrp  -disableresources service_group  
  
清除故障状态:hagrp  -clear [service_group] -sys [system]  

4、管理资源操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
查询资源代理列表:  
  
hatype -list  
  
hatype -resources resource_type  
  
hatype -display [resource_type]  
  
haagent -display [agent]  
  
使资源联机:hares -online resource -sys system  
  
使资源脱机:hares -offline resource -sys system  
  
使资源脱机并将命令传播给子节点:hares -offprop resource -sys system  
  
提示资源代理(agent)监控指定系统上的资源:(命令可能立即返回,但监控的进程可能还没结束)hares -probe resource -sys system  
  
清洗资源,使状态从RESOURCE_FAULTED 转为RESOURCE_OFFLINE:  
  
hares -clear resource [-sys system]  

5、管理主机:

 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
查询主机列表:  
  
hasys -list  
  
hasys -display [system]  
  
haclus -value attribute  
  
haclus -display  
  
在ADMIN_WAIT状态下强制主机加载集群,此命令会覆盖正在使用的集群配置,使用前请确认准备使用的主机的集群配置文件是否有效:  
  
hacf -verify /etc/VRTSvcs/conf/concig  
  
hasys -force system  
  
修改主机的属性,一些属性是VCS的内部属性,不能修改:  
  
hasys -modify modify_options  
  
查询主机ID,主机ID定义在文件/etc/llttab中:hasys -nodeid node_ID  
  
冻结主机 (防止主机进行联机或切换操作):hasys -freeze [-persistent] [-evacuate] system  
  
解冻被冻结的主机 (使主机可以进行联机或切换操作):hasys -unfreeze [-persistent] system  

6、VCS的license的管理操作:

1
2
3
安装VCS的License(root用户下执行):halic <key>或者vxlicinst -k  <key>  
  
查询license:vxlicrep  

7、双机日常管理类:

  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
#安装license,可以先用演示license-3个月 商用license-永久 测试license-长期和短期都有,但是veritas不提供维保服务  
vxlicense -c  
  
#查询license  
vxlicrep  
  
Symantec License Manager vxlicrep utility version 3.02.5010    
Copyright (C) 1996-2010 Symantec Corporation. All rights reserved.    
Creating a report on all VERITAS products installed on this system    
 -----------------***********************-----------------    
   License Key                         = XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-X    
   Product Name                        = VERITAS Cluster Server    
   Serial Number                       = XXXX    
   License Type                        = PERMANENT    
   OEM ID                              = 2006    
   Site License                        = YES    
   Editions Product                    = YES    
 Features :=     
   Platform                            = Unused                                 
  Version                             = 5.1                                    
   Tier                                = Unused                                 
   Reserved                            = 0     
   Mode                                = VCS                                    
   CPU_TIER                            = 2    
   VXKEYLESS                           = Enabled    
  
# 添加VCS控制台用户  
haconf -makerw  
hauser -add 用户名 -priv Administrator  
haconf -dump -makero  
  
#启动VCS, 主备机都执行  
hastart  
  
#停止VCS  
hastop -all   应用会offline,需要等待一定时间才能完全停止  
  
# 强制停止VCS  
hastop -all -force  应用不会offline,仅仅是把vcs进程停止  
  
#查看资源组状态  
hagrp -state  
  
#查看资源状态  
hares -state  
  
#查看双机状态  
hastatus -sum  
  
-- SYSTEM STATE    
-- System               State                Frozen                  
    
A  NodeA              RUNNING              0                        
A  NodeB              RUNNING              0                        
    
-- GROUP STATE    
-- Group           System               Probed     AutoDisabled    State              
    
B  RCS_DB_SG       NodeA                 Y          N               ONLINE             
B  RCS_DB_SG       NodeB                 Y          N               OFFLINE  
  
#查看动态的双机状态  
hastatus   一般集群刚启动时用得比较多,监控资源启动运行情况,稳定后使用上一个命令较多  
  
#某一节点应用上线  
hagrp -online 资源组名 -sys 节点名  
  
#某一节点应用下线  
hagrp -offline 资源组名 -sys 节点名  
  
#冻结资源组  
hagrp -freeze 资源组名 -persistent  就是冻结资源组,暂时不委托给vcs集群管理软件管理  
  
-- SYSTEM STATE    
-- System               State                Frozen                  
    
A  NodeA                RUNNING              0                        
A  NodeB                RUNNING              0                        
    
-- GROUP STATE    
-- Group           System               Probed     AutoDisabled    State              
    
B  RCS_DB_SG       NodeA                Y          N               ONLINE             
B  RCS_DB_SG       NodeB                Y          N               OFFLINE            
    
-- GROUPS FROZEN    
-- Group              
    
C  RCS_DB_SG          
    
-- RESOURCES DISABLED    
-- Group           Type            Resource                
    
H  RCS_DB_SG       Application     ora_raw           oracle11gR1的方案,用了raw,从oracle11gR2开始用grid管理      
H  RCS_DB_SG       Application     ora_vg                  
H  RCS_DB_SG       IP              ora_ip                  
H  RCS_DB_SG       NIC             ora_nic                 
H  RCS_DB_SG       Netlsnr         ora_listener            
H  RCS_DB_SG       Oracle          ora_ora11g   
  
#解冻资源组  
hagrp -unfreeze 资源组名   
  
#切换双机资源组至另外一节点  
hagrp -switch 资源组名 -to 节点名  
  
#VCS默认安装目录  
/opt/VRTSvcs/bin  
  
# VCS配置文件名  
/etc/VRTSvcs/conf/config/main.cf  
  
#检查配置文件是否正确  
hacf -verify config  
  
# 启动VCS控制台  
hagui &  
  
#修改vcs控制台(gui)管理员密码  
vcsencrypt -vcs   (然后输入2次密码,并将生成的加密后的密码粘到main.cf中,重启vcs生效)  
  
#修改vcs监控资源密码,如db2或Oracle的登录密码  
vcsencrypt -agent  (然后输入2次密码,并将生成的加密后的密码粘到main.cf中,重启vcs生效)  
  
2、故障检查类  
#如果有系统出现ADMIN_WAIT  
cd /etc/VRTSvcs/conf/config  
rm .stale  
  
#vcs日志  
/var/VRTSvcs/log/engine_A.log  
  
#检查llt心跳网卡  
lltstat -l     
  
LLT link information:    
link 0  eth10 on ether hipri    
        mtu 1500, sap 0xcafe, broadcast FF:FF:FF:FF:FF:FF, addrlen 6    
        txpkts 67688788  txbytes 1843243673    
        rxpkts 76679165  rxbytes 1360585331    
        latehb 0  badcksum 0  errors 0    
link 1  eth9 on ether hipri    
        mtu 1500, sap 0xcafe, broadcast FF:FF:FF:FF:FF:FF, addrlen 6    
        txpkts 67689522  txbytes 1843879628    
        rxpkts 276962155  rxbytes 1467872503    
        latehb 0  badcksum 0  errors 0    
link 2  eth8 on ether lowpri    
        mtu 1500, sap 0xcafe, broadcast FF:FF:FF:FF:FF:FF, addrlen 6    
        txpkts 21897078  txbytes 1072847300    
        rxpkts 222319811  rxbytes 1191109452    
        latehb 0  badcksum 0  errors 0    
          
#检查集群编号  
cat /etc/llttab  
  
set-node NodeA    
set-cluster 9521    
link eth10 eth-80:fb:06:35:b8:9f - ether - -    
link eth9 eth-80:fb:06:35:b8:a1 - ether - -    
link-lowpri eth8 eth-80:fb:06:35:b8:a0 - ether - -           
  
#检查心跳网卡工作状态  
lltstat -n  
    
LLT node information:    
    Node                 State    Links    
   * 0 NodeA            OPEN        3       *表示执行的节点,3表示llt和gab心跳链路工作均正常  
     1 NodeB            OPEN        3   
  
# 检查双机心跳网卡状态。  
lltstat -nvv       
  
#心跳通迅模块重启  
/etc/init.d/gab stop  
/etc/init.d/llt stop  
  
/etc/init.d/llt start  
/etc/init.d/gab start
……

阅读全文

VCS第一波-HA-VCS

前言

换到互联网电商行业了,以前电信行业的知识积累做下笔记。
谈到VCS,就会想到HA-高可用方案,做高可用方案的厂商比较多,其中工作接触到Veritas的VCS和IBM的HACMP,这里先对VCS做下笔记整理,互联网行业都喜欢用免费方案和热备,VCS的冷备方案几乎没见到厂商使用,但是VCS的简单易学习还是对高可用方案的知识积累很有帮助。

……

阅读全文

H2内存数据库

前言

H2数据库介绍
之前项目中用到了H2内存数据库,常用的开源数据库:H2,Derby,HSQLDB,MySQL,PostgreSQL。其中H2,HSQLDB类似,十分适合作为嵌入式数据库使用,其它的数据库大部分都需要安装独立的客户端和服务器端。

H2的优势

1
2
3
1、h2采用纯Java编写,因此不受平台的限制。
2、h2只有一个jar文件,十分适合作为嵌入式数据库试用。
3、性能和功能的优势

H2比HSQLDB的最大的优势就是h2提供了一个十分方便的web控制台用于操作和管理数据库内容,这点比起HSQLDB的swing和awt控制台实在好用多了。

……

阅读全文

利用网上的Caeser算法写了一个唯一id生成工具

前言

工作中需要用到,不需要用uuid这种太长的,但是需要有自己特色标识的,于是晚上搜集了部分算法和思路,加工出来一个唯一随机id生成类

思路

改造过的Caesar算法,利用随机数加密,生成前12位 + 后4位随机数,经测试大量数据和实际使用验证,无重复值出现,可以放心使用

1、代码

  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
// key  
private static String KEYS = "0123456789qwertyuiopasdfghjklzxcvbnm";  
  
/** 
 * 唯一id生成工具类,12位字母+数字 
 *  
 * @param id 随机整数即可 
 */  
public static String getRandomId(int id)  
{  
    String ret = null;  
    String num = String.format("%09d", id);  
    Random random = new Random();  
    int key = random.nextInt(10);  
    int seed = random.nextInt(100);  
    Caesar caesar = new Caesar(KEYS, seed);  
    num = caesar.encode(key, num);  
    ret = num + String.format("%01d", key) + String.format("%02x", seed);  
  
    return ret;  
}  
  
/** 
 * id唯一值生成工具类<br> 
 * 格式:test_xxxx,总共21位随机码  
 * @return test_+16位随机码 
 */  
public static String getID()  
{  
    // 前缀  
    String prefix = "test_";  
    // 随机码  
    String main = TestRandomUUID.getRandomId(new Random().nextInt(1000000));  
    String end = RandomUtils.getRandomAlphamericStrNew(4);  
      
    return prefix + main + end;  
}  

public class RandomUtils  
{  
     /** 
     * 获得特定长度的一串随机数字 
     *  
     * @param length  
     * @return 
     */  
    public static String getRandomNumberStr(int length)  
    {  
        StringBuilder builder = new StringBuilder();  
        Random random = new Random();  
        for (int i = 0; i < length; i++)  
        {  
            builder.append(random.nextInt(10));  
        }  
        return builder.toString();  
    }  
  
    /** 
     * 获得特定长度的一个随机字母数字混编字符串(所包含的字符包括0-9A-Z) 
     *  
     * @param length 
     * @return 
     */  
    public static String getRandomAlphamericStr(int length)  
    {  
        StringBuilder builder = new StringBuilder();  
        Random random = new Random();  
        for (int i = 0; i < length; i++)  
        {  
            int n = random.nextInt(36);  
            if (n < 10)  
            {  
                builder.append(n);  
            }  
            else  
            {  
                builder.append((char) (n + 55));  
            }  
        }  
        return builder.toString();  
    }  
  
    /** 
     * 获得特定长度的一个随机字母数字混编字符串(所包含的字符包括0-9a-z) 
     *  
     * @param length 
     * @return 
     */  
    public static String getRandomAlphamericStrNew(int length)  
    {  
        StringBuilder builder = new StringBuilder();  
        Random random = new Random();  
        for (int i = 0; i < length; i++)  
        {  
            int n = random.nextInt(36);  
            if (n < 10)  
            {  
                builder.append(n);  
            }  
            else  
            {  
                builder.append((char) (n + 87));  
            }  
        }  
        return builder.toString();  
    }  
  
    /** 
     * 获得特定长度的一个随机十六进制数字字母混编字符串(所包含的字符包括0-9A-F) 
     *  
     * @param length 
     * @return 
     */  
    public static String getRandomHexStr(int length)  
    {  
        StringBuilder builder = new StringBuilder();  
        Random random = new Random();  
        for (int i = 0; i < length; i++)  
        {  
            int n = random.nextInt(16);  
            if (n < 10)  
            {  
                builder.append(n);  
            }  
            else  
            {  
                builder.append((char) (n + 55));  
            }  
        }  
        return builder.toString();  
    }  
  
    public static void main(String[] args)  
    {  
        for (int i = 0; i < 100; i++)  
        {  
            System.out.println(RandomUtils.getRandomAlphamericStrNew(4));  
            // String num = String.format("%09d", new Random().nextInt(100000));  
            // System.out.println(num);  
        }  
  
    }  
}  

2、凯撒算法工具类

  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
public class Caesar  
{  
    private String table;  
    private int seedA = 1103515245;  
    private int seedB = 12345;  
  
    public Caesar(String table, int seed)  
    {  
        this.table = chaos(table, seed, table.length());  
    }  
  
    public Caesar(String table)  
    {  
        this(table, 11);  
    }  
  
    public Caesar()  
    {  
        this(11);  
    }  
  
    public Caesar(int seed)  
    {  
        this("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", seed);  
    }  
  
    public char dict(int i, boolean reverse)  
    {  
        int s = table.length(), index = reverse ? s - i : i;  
        return table.charAt(index);  
    }  
  
    public int dict(char c, boolean reverse)  
    {  
        int s = table.length(), index = table.indexOf(c);  
        return reverse ? s - index : index;  
    }  
  
    public int seed(int seed)  
    {  
        long temp = seed;  
        return (int) ((temp * seedA + seedB) & 0x7fffffffL);  
    }  
  
    public String chaos(String data, int seed, int cnt)  
    {  
        StringBuffer buf = new StringBuffer(data);  
        char tmp;  
        int a, b, r = data.length();  
        for (int i = 0; i < cnt; i += 1)  
        {  
            seed = seed(seed);  
            a = seed % r;  
            seed = seed(seed);  
            b = seed % r;  
            tmp = buf.charAt(a);  
            buf.setCharAt(a, buf.charAt(b));  
            buf.setCharAt(b, tmp);  
        }  
        return buf.toString();  
    }  
  
    public String crypto(boolean reverse, int key, String text)  
    {  
        String ret = null;  
        StringBuilder buf = new StringBuilder();  
        int m, s = table.length(), e = text.length();  
  
        for (int i = 0; i < e; i += 1)  
        {  
            m = dict(text.charAt(i), reverse);  
            if (m < 0)  
            {  
                break;  
            }  
            m = m + key + i;  
            buf.append(dict(m % s, reverse));  
        }  
        if (buf.length() == e)  
        {  
            ret = buf.toString();  
        }  
        return ret;  
    }  
  
    public String encode(int key, String text)  
    {  
        return crypto(false, key, text);  
  
    }  
  
    public String decode(int key, String text)  
    {  
        return crypto(true, key, text);  
    }  
  
    public static void main(String[] args)  
    {  
        Caesar caesar = new Caesar("0123456789qwertyuiopasdfghjklzxcvbnm");  
        String data = caesar.encode(32, "23423");  
        System.out.println(data);  
        String pass = caesar.decode(32, data);  
        System.out.println(pass);  
  
    }  
}
……

阅读全文

Java高防OCR和机器人图形验证码

前言

结合网上的图形验证码技术,不依赖第三方包纯java加工了一个比较复杂的图形验证码方案,防OCR、防机器人。
网上的图形验证码方案都是零星的,弄了一个随机字体、随机彩色字符、随机字体大小、随机扭曲、随机旋转等技术,能有效的防OCR、描边、深浅色等技术识别。
本意是想区分登录、领券、抽奖等一些场景操作的,后来一想,随机拼人品吧,于是就有了下面的工具类。

……

阅读全文

最近文章

分类

标签

其它