In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >
Share
Shulou(Shulou.com)05/31 Report--
This article introduces you how to master and slave MHA in MYSQL architecture, the content is very detailed, interested friends can refer to, hope to be helpful to you.
MHA-GTID mode
MasterHigh Availability Manager and Toolsfor MySQL. Is a script management tool written in PERL voice, which is suitable for MySQL Replication environment and is designed to maintain the high availability of the master main library.
MHA is an automatic master failover and slave promotion package based on standard MySQL replication (asynchronous, semi-synchronous)
MHA Manager management nodes can be deployed on a separate server to manage multiple master-slave clusters, or on a single slave. MHA Manager probes node nodes in a cluster
MHA has two components.
MHA Manager (Management Node)
MHA node (data node)
Download address:
Https://github.com/yoshinorim/mha4mysql-manager/releases
Https://github.com/yoshinorim/mha4mysql-node/releases
The MHA Manager management node can be deployed on a single server to manage multiple master-slave clusters, or it can be deployed on a single slave. MHA Manager detects the node nodes in the cluster. When it is found that the master fails, it can automatically upgrade the slave with the latest data to the new master, and then direct all other slave to the new master.
The whole failover process is transparent to the application, and the MHA node data node can run on each Mysql server
The purpose of MHA is to maintain the high availability of the master library in MYSQL Replication. Its most important feature is that it can fix the difference logs between multiple slave, finally make all slave keep data consistent, and then choose one of them to act as the new master and point the other slave to it. When the master fails, the position number of the master library binlog can be read by comparing the slave O thread, and the nearest slave can be selected as the alternative master library. Other slave libraries can generate different relay logs by comparing with the alternative master database, apply the binlog saved from the original master to the alternative master database, and promote the alternative master database to master. Finally, apply the corresponding differential relay logs on other slave and start replication from the new master.
Environment configuration
\
Primary server 1
From server 1
From Server 2
OS
Centos el7.x86_64
Centos el7.x86_64
Centos el7.x86_64
IP
192.168.31.79
192.168.31.188
192.168.31.90
HOSTNAME
Mysql
Mysql2
Mysql3
Mysql-version
5.7.23
5.7.23
5.7.23
Database
TEST
Remarks 1
Vip 192.168.31.100
Remark 2
Management node
Remark 3
Candidate node
Remark 4
Name of single network card: enp0s3
Name of single network card: enp0s3
Name of single network card: enp0s3
1. Install Mysql
Install mysql on three nodes
At the same time, several tables are established under the main node database TEST and TEST database for testing purposes.
two。 Set up an account
All three nodes are running
Set up a replication account
Mysql > create user 'rep1'@'192.168.31.%' identified by' Oracle123'
Mysql > grant replication slave on *. * to 'rep1'@'192.168.31.%'
Mysql > flush privileges
Set up a management account
Mysql > create user 'dba'@'192.168.31.%' identified by' Oracle123'
Mysql > grant all privileges on *. * to 'dba'@'192.168.31.%'
Mysql > flush privileges
3. Edit configuration file
Primary server 1
[root@mysql ~] # vi / etc/my.cnf
Add the following content
Gtid_mode=on
Enforce_gtid_consistency=on
Log_bin=on
Binlog_format=row
Server-id=79
Log-bin = master-log
Relay-log = relay-log
Skip_name_resolve
From server 1
[root@mysql2 ~] # vi / etc/my.cnf
Gtid_mode=on
Enforce_gtid_consistency=on
Log_slave_updates=1
Server-id=188
Relay-log = relay-log
Log-bin = master-log
Skip_name_resolve
Relay_log_purge = 0
# # read_only = ON the slave library will be selected as the candidate master library, so cancel read_only
From Server 2
[root@mysql3 ~] # vi / etc/my.cnf
Gtid_mode=on
Enforce_gtid_consistency=on
Log_slave_updates=1
Server-id=90
Relay-log = relay-log
Log-bin = master-log
Skip_name_resolve
Relay_log_purge = 0
Read_only = ON
4. Backup and restore database
Primary server 1
Back up the TEST database
[root@mysql] # mysqldump-- single-transaction-uroot-pOracle123 TEST > TEST_20200316.sql
From server 1
[root@mysql2] # scp root@192.168.31.79:/root/TEST_20200316.sql.
Mysql > create database TEST
[root@mysql2] # mysql-uroot-pOracle123 TEST
< TEST_20200316.sql 从服务器2 [root@mysql3 ~]# scp root@192.168.31.79:/root/TEST_20200316.sql . mysql>Create database TEST
[root@mysql3] # mysql-uroot-pOracle123 TEST
< TEST_20200316.sql 5.重启数据库 三个节点都重启 [root@mysql ~]# service mysql stop [root@mysql ~]# service mysql start 6.从服务器配置 从服务器两个节点都执行 [root@mysql2 bin]# mysql -uroot -p mysql>Change master to master_host='192.168.31.79',master_user='rep1',master_password='Oracle123',master_port=3306,master_auto_position=1
Mysql > start slave
Mysql > show slave status\ G
Note: if there is an error in the above execution of change master, Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.'
The solution is as follows:
Mysql > change master to master_auto_position=0
Mysql > start slave
Note: if an error occurs in the execution of the above
Last_Error: Error 'Table' test4' already exists' on query. Default database: 'TEST'. Query: 'create table test4 (id integer,name varchar)'
The solution is as follows:
Main library: mysql > SHOW MASTER STATUS\ g-- get File Position
Execute from the library
Mysql > change master to master_host='192.168.31.79',master_user='rep1', master_password='Oracle123',master_port=3306,master_log_file='master-log.000001',master_log_pos=154
Primary server 1
[root@mysql] # mysql-uroot-p
Mysql > show master status
7. Verify master-slave synchronization
Primary server 1
Mysql > use TEST
Mysql > insert into test2 values (1)
Mysql > commit
From server 1
Mysql > use TEST
Mysql > select * from test2
From Server 2
Mysql > use TEST
Mysql > select * from test2
8. Mutual trust in the configuration of installing MHA
All three nodes are executed (including management nodes)
[root@mysql ~] # cd .ssh /
[root@mysql .ssh] # ssh-keygen-t dsa-P'- f id_dsa
[root@mysql .ssh] # cat id_dsa.pub > > authorized_keys
The master server receives the secret key
Master server operation
[root@mysql .ssh] # scp 192.168.31.188:/root/.ssh/id_dsa.pub. / id_dsa.pub_188
[root@mysql .ssh] # scp 192.168.31.90:/root/.ssh/id_dsa.pub. / id_dsa.pub_90
[root@mysql .ssh] # cat id_dsa.pub_188 > > authorized_keys
[root@mysql .ssh] # cat id_dsa.pub_90 > > authorized_keys
[root@mysql .ssh] # scp authorized_keys 192.168.31.188:/root/.ssh/
[root@mysql .ssh] # scp authorized_keys 192.168.31.90:/root/.ssh/
Three nodes are configured with host
All three nodes execute
[root@mysql ~] # vi / etc/hosts
Add as follows:
192.168.31.79 mysql
192.168.31.188 mysql2
192.168.31.90 mysql3
Verification
All three nodes test each other and succeed without entering a password.
[root@mysql .ssh] # ssh mysql2
[root@mysql .ssh] # ssh mysql3
9. Install MHA related rpm
NODE node installation
[root@mysql soft] # yum install-y perl-CPAN
[root@mysql soft] # yum install-y mha4mysql-node-0.58-0.el7.centos.noarch.rpm
Manage node installation (i.e. from server 1)
If the package is missing, you can download it and install it.
Https://centos.pkgs.org/
[root@mysql2 ~] # yum install perl-Config-Tiny perl-Email-Date-Format perl-File-Remove perl-Log-Dispatch perl-Mail-Sender perl-Mail-Sendmail perl-MIME-Lite perl-MIME-Types perl-Module-Install perl-Module-ScanDeps perl-Parallel-ForkManager perl-YAML-y
[root@mysql2 soft] # yum install mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
10. Configure MHA script
Every node
[root@mysql ~] # cd / usr/local/
[root@mysql local] # mkdir mha
Management node (slave server 1)
[root@mysql2 soft] # mkdir / usr/local/mha
[root@mysql2 mha] # vi mha.conf
The content is added as follows:
[server default]
User = dba
Password = Oracle123
Ssh_user = root
Repl_user = rep1
Repl_password = Oracle123
Ping_interval = 1
Ping_type = SELECT
Manager_workdir=/usr/local/mha
Manager_log=/usr/local/mha/manager.log
Remote_workdir=/usr/local/mha
Master_ip_failover_script= "/ usr/local/mha/master_ip_failover"
Master_ip_online_change_script= "/ usr/local/mha/master_ip_online_change"
Shutdown_script= ""
Report_script= ""
# check_repl_delay=0
[server1]
Hostname=mysql
Port=3306
Master_binlog_dir= "/ u01/data"
Candidate_master=1 # # give priority to selecting this node as a candidate node
[server2]
Hostname=mysql2
Port=3306
Master_binlog_dir= "/ u01/data"
Candidate_master=1
[server3]
Hostname=mysql3
Port=3306
Master_binlog_dir= "/ u01/data"
No_master = 1 # # do not select this node as a candidate node
[root@mysql2 mha] # vi master_ip_failover
Add the following content
#! / usr/bin/env perl
Use strict
Use warnings FATAL = > 'all'
Use Getopt::Long
My (
$command, $ssh_user, $orig_master_host, $orig_master_ip
$orig_master_port, $new_master_host, $new_master_ip, $new_master_port
);
My $vip = '192.168.31.100 IP 32 seconds; # Virtual IP, the ip details refer to step 12 to add IP, showing the result
My $key = "0"
My $int = "enp0s3"
# # my $ssh_start_vip = "/ sbin/ifconfig $int:$key $vip";-- this environment is centos7, so note this directive
# # my $ssh_stop_vip = "/ sbin/ifconfig $int:$key down";-- this instruction is invalid because the environment is centos7
My $ssh_start_vip = "/ sbin/ip addr add $vip dev $int"
My $ssh_stop_vip = "/ sbin/ip addr del $vip dev $int"
My $arp_effect = "/ sbin/arping-Uq-s192.168.31.100-I $int 192.168.31.255-c 3"; # Virtual IP and gatway
# my $test = "echo successfull > / tmp/test.txt"
$ssh_user = "root"
GetOptions (
'command=s' = >\ $command
'ssh_user=s' = >\ $ssh_user
'orig_master_host=s' = >\ $orig_master_host
'orig_master_ip=s' = >\ $orig_master_ip
'orig_master_port=i' = >\ $orig_master_port
'new_master_host=s' = >\ $new_master_host
'new_master_ip=s' = >\ $new_master_ip
'new_master_port=i' = >\ $new_master_port
);
Exit & main ()
Sub main {
Print "\ n\ nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\ n\ n"
If ($command eq "stop" | | $command eq "stopssh") {
# $orig_master_host, $orig_master_ip, $orig_master_port are passed.
# If you manage master ip address at global catalog database
# invalidate orig_master_ip here.
My $exit_code = 1
Eval {
Print "Disabling the VIP on old master: $orig_master_host\ n"
& stop_vip ()
$exit_code = 0
}
If ($@) {
Warn "Got Error: $@\ n"
Exit $exit_code
}
Exit $exit_code
}
Elsif ($command eq "start") {
# all arguments are passed.
# If you manage master ip address at global catalog database
# activate new_master_ip here.
# You can also grant write access (create user, set read_only=0, etc) here.
My $exit_code = 10
Eval {
Print "Enabling the VIP-$vip on the new master-$new_master_host\ n"
& start_vip ()
$exit_code = 0
}
If ($@) {
Warn $@
Exit $exit_code
}
Exit $exit_code
}
Elsif ($command eq "status") {
Print "Checking the Status of the script.. OK\ n"
# `ssh $ssh_user\ @ cluster1\ "$ssh_start_vip\" `
& status ()
Exit 0
}
Else {
& usage ()
Exit 1
}
}
# A simple system call that enable the VIP on the new master
Sub start_vip () {
`ssh $ssh_user\ @ $new_master_host\ "$ssh_start_vip\" `
`ssh $ssh_user\ @ $new_master_host\ "$arp_effect\" `
# `ssh $ssh_user\ @ $new_master_host\ "$test\" `
}
# A simple system call that disable the VIP on the old_master
Sub stop_vip () {
`ssh $ssh_user\ @ $orig_master_host\ "$ssh_stop_vip\" `
}
Sub status () {
Print `ssh $ssh_user\ @ $orig_master_host\ "ip add show $int\" `
}
Sub usage {
"Usage: master_ip_failover-- command=start | stop | stopssh | status-- orig_master_host=host-- orig_maste
R_ip=ip-orig_master_port=port-new_master_host=host-new_master_ip=ip-new_master_port=port\ n "
}
[root@mysql2 mha] # vi master_ip_online_change
The content is added as follows:
#! / usr/bin/env perl
Use strict
Use warnings FATAL = > 'all'
Use Getopt::Long
Use MHA::DBHelper
Use MHA::NodeUtil
Use Time::HiRes qw (sleep gettimeofday tv_interval)
Use Data::Dumper
My $_ tstart
My $_ running_interval = 0.1
My $vip = "192.168.31.100gam32"
My $if = "enp0s3"
My (
$command, $orig_master_is_new_slave, $orig_master_host
$orig_master_ip, $orig_master_port, $orig_master_user
$orig_master_password, $orig_master_ssh_user, $new_master_host
$new_master_ip, $new_master_port, $new_master_user
$new_master_password, $new_master_ssh_user
);
GetOptions (
'command=s' = >\ $command
'orig_master_is_new_slave' = >\ $orig_master_is_new_slave
'orig_master_host=s' = >\ $orig_master_host
'orig_master_ip=s' = >\ $orig_master_ip
'orig_master_port=i' = >\ $orig_master_port
'orig_master_user=s' = >\ $orig_master_user
'orig_master_password=s' = >\ $orig_master_password
'orig_master_ssh_user=s' = >\ $orig_master_ssh_user
'new_master_host=s' = >\ $new_master_host
'new_master_ip=s' = >\ $new_master_ip
'new_master_port=i' = >\ $new_master_port
'new_master_user=s' = >\ $new_master_user
'new_master_password=s' = >\ $new_master_password
'new_master_ssh_user=s' = >\ $new_master_ssh_user
);
Exit & main ()
Sub drop_vip {
My $output = `ssh-o ConnectTimeout=15-o ConnectionAttempts=3 $orig_master_host / sbin/ip addr del $vip/32 dev $if`
}
Sub add_vip {
My $output = `ssh-o ConnectTimeout=15-o ConnectionAttempts=3 $new_master_host / sbin/ip addr add $vip/32 dev $if`
}
Sub current_time_us {
My ($sec, $microsec) = gettimeofday ()
My $curdate = localtime ($sec)
Return $curdate. "". Sprintf ("d", $microsec)
}
Sub sleep_until {
My $elapsed = tv_interval ($_ tstart)
If ($_ running_interval > $elapsed) {
Sleep ($_ running_interval-$elapsed)
}
}
Sub get_threads_util {
My $dbh = shift
My $my_connection_id = shift
My $running_time_threshold = shift
My $type = shift
$running_time_threshold = 0 unless ($running_time_threshold)
$type = 0 unless ($type)
My @ threads
My $sth = $dbh- > prepare ("SHOW PROCESSLIST")
$sth- > execute ()
While (my $ref = $sth- > fetchrow_hashref ()) {
My $id = $ref- > {Id}
My $user = $ref- > {User}
My $host = $ref- > {Host}
My $command = $ref- > {Command}
My $state = $ref- > {State}
My $query_time = $ref- > {Time}
My $info = $ref- > {Info}
$info = ~ s / ^\ s * (. *?)\ if defined ($info)
Next if ($my_connection_id = = $id)
Next if (defined ($query_time) & & $query_time
< $running_time_threshold ); next if ( defined($command) && $command eq "Binlog Dump" ); next if ( defined($user) && $user eq "system user" ); next if ( defined($command) && $command eq "Sleep" && defined($query_time) && $query_time >= 1)
If ($type > = 1) {
Next if (defined ($command) & & $command eq "Sleep")
Next if (defined ($command) & & $command eq "Connect")
}
If ($type > = 2) {
Next if (defined ($info) & & $info = ~ m / ^ select / I)
Next if (defined ($info) & & $info = ~ m / ^ show / I)
}
Push @ threads, $ref
}
Return @ threads
}
Sub main {
If ($command eq "stop") {
# # Gracefully killing connections on the current master
# 1. Set read_only= 1 on the new master
# 2. DROP USER so that no app user can establish new connections
# 3. Set read_only= 1 on the current master
# 4. Kill current queries
# * Any database access failure will result in script die.
My $exit_code = 1
Eval {
# # Setting read_only=1 on the new master (to avoid accident)
My $new_master_handler = new MHA::DBHelper ()
# args: hostname, port, user, password, raise_error (die_on_error) _ or_not
$new_master_handler- > connect ($new_master_ip, $new_master_port
$new_master_user, $new_master_password, 1)
Print current_time_us (). "Set read_only on the new master.."
$new_master_handler- > enable_read_only ()
If ($new_master_handler- > is_read_only ()) {
Print "ok.\ n"
}
Else {
Die "Failed!\ n"
}
$new_master_handler- > disconnect ()
# Connecting to the orig master, die if any database error happens
My $orig_master_handler = new MHA::DBHelper ()
$orig_master_handler- > connect ($orig_master_ip, $orig_master_port
$orig_master_user, $orig_master_password, 1)
# # Drop application user so that nobody can connect. Disabling per-session binlog beforehand
$orig_master_handler- > disable_log_bin_local ()
# print current_time_us (). "Drpping app user on the orig master..\ n"
Print current_time_us (). "drop vip $vip..\ n"
# drop_app_user ($orig_master_handler)
& drop_vip ()
# # Waiting for N * 100milliseconds so that current connections can exit
My $time_until_read_only = 15
$_ tstart = [gettimeofday]
My @ threads = get_threads_util ($orig_master_handler- > {dbh})
$orig_master_handler- > {connection_id})
While ($time_until_read_only > 0 & & $# threads > = 0) {
If ($time_until_read_only% 5 = = 0) {
Printf
"% s Waiting all running d threads are disconnected.. (max% d milliseconds)\ n"
Current_time_us (), $# threads + 1, $time_until_read_only * 100
If ($# threads
< 5 ) { print Data::Dumper->New ([$_])-> Indent (0)-> Terse (1)-> Dump. "\ n"
Foreach (@ threads)
}
}
Sleep_until ()
$_ tstart = [gettimeofday]
$time_until_read_only--
@ threads = get_threads_util ($orig_master_handler- > {dbh})
$orig_master_handler- > {connection_id})
}
# # Setting read_only=1 on the current master so that nobody (except SUPER) can write
Print current_time_us (). "Set read_only=1 on the orig master.."
$orig_master_handler- > enable_read_only ()
If ($orig_master_handler- > is_read_only ()) {
Print "ok.\ n"
}
Else {
Die "Failed!\ n"
}
# # Waiting for M * 100milliseconds so that current update queries can complete
My $time_until_kill_threads = 5
@ threads = get_threads_util ($orig_master_handler- > {dbh})
$orig_master_handler- > {connection_id})
While ($time_until_kill_threads > 0 & & $# threads > = 0) {
If ($time_until_kill_threads% 5 = = 0) {
Printf
"% s Waiting all running d queries are disconnected.. (max% d milliseconds)\ n"
Current_time_us (), $# threads + 1, $time_until_kill_threads * 100
If ($# threads
< 5 ) { print Data::Dumper->New ([$_])-> Indent (0)-> Terse (1)-> Dump. "\ n"
Foreach (@ threads)
}
}
Sleep_until ()
$_ tstart = [gettimeofday]
$time_until_kill_threads--
@ threads = get_threads_util ($orig_master_handler- > {dbh})
$orig_master_handler- > {connection_id})
}
# # Terminating all threads
Print current_time_us (). "Killing all application threads..\ n"
$orig_master_handler- > kill_threads (@ threads) if ($# threads > = 0)
Print current_time_us (). "done."
$orig_master_handler- > enable_log_bin_local ()
$orig_master_handler- > disconnect ()
# # After finishing the script, MHA executes FLUSH TABLES WITH READ LOCK
$exit_code = 0
}
If ($@) {
Warn "Got Error: $@\ n"
Exit $exit_code
}
Exit $exit_code
}
Elsif ($command eq "start") {
# # Activating master ip on the new master
# 1. Create app user with write privileges
# 2. Moving backup script if needed
# 3. Register new master's ip to the catalog database
# We don't return error even though activating updatable accounts/ip failed so that we don't interrupt slaves' recovery.
# If exit code is 0 or 10, MHA does not abort
My $exit_code = 10
Eval {
My $new_master_handler = new MHA::DBHelper ()
# args: hostname, port, user, password, raise_error_or_not
$new_master_handler- > connect ($new_master_ip, $new_master_port
$new_master_user, $new_master_password, 1)
# # Set read_only=0 on the new master
$new_master_handler- > disable_log_bin_local ()
Print current_time_us (). "Set read_only=0 on the new master."
$new_master_handler- > disable_read_only ()
# # Creating an app user on the new master
# print current_time_us (). "Creating app user on the new master..\ n"
Print current_time_us (). "Add vip $vip on $if..\ n"
# create_app_user ($new_master_handler)
& add_vip ()
$new_master_handler- > enable_log_bin_local ()
$new_master_handler- > disconnect ()
# # Update master ip on the catalog database, etc
$exit_code = 0
}
If ($@) {
Warn "Got Error: $@\ n"
Exit $exit_code
}
Exit $exit_code
}
Elsif ($command eq "status") {
# do nothing
Exit 0
}
Else {
& usage ()
Exit 1
}
}
Sub usage {
"Usage: master_ip_online_change-- command=start | stop | status-- orig_master_host=host-- orig_master_ip=ip-- orig_master_port=port-- new_master_host=host-- new_master_ip=ip-- new_master_port=port\ n"
Die
}
11. Check statu
Management node
[root@mysql2 mha] # which masterha_check_ssh
[root@mysql2 mha] # chmod + x master_ip_online_change
[root@mysql2 mha] # chmod + x master_ip_failover
[root@mysql2 mha] # masterha_check_ssh-- conf=/usr/local/mha/mha.conf
[root@mysql2 mha] # masterha_check_repl-- conf=/usr/local/mha/mha.conf
twelve。 Add VIP to the main library
Primary server 1
[root@mysql ~] # ip addr add 192.168.31.100 dev enp0s3
[root@mysql ~] # ip addr show
Notice that it is 100,32.
13. Start MHA
Management node (i.e. slave server 1)
[root@mysql2 mha] # nohup masterha_manager-- conf=/usr/local/mha/mha.conf > / tmp/mha_manager.log
< /dev/null 2>& 1 &
14. Verify MHA statu
Management node (i.e. slave server 1)
[root@mysql2 mha] # masterha_check_status-- conf=/usr/local/mha/mha.conf
15. Test verification
Primary server 1
[root@mysql ~] # service mysql stop
Note:
Every time you start a MHA switch, you need to delete the mha.failover.complete file in the management node, otherwise an error will be reported next time.
Slave server 1 (candidate node)
Check
[root@mysql2 mha] # tail-100f manager.log
[root@mysql2 mha] # ip addr show
Mysql > show slave status\ G
Mysql > show master status\ G
From Server 2
16. Subsequent operation
Restore the original master database and become the backup database for the current cluster environment
Now the main library operation
Mysql > show master status
Operation of the original main library
[root@mysql data] # service mysql start
[root@mysql data] # mysql-uroot-p
Mysql > change master to master_host='192.168.31.188',master_user='rep1', master_password='Oracle123',master_port=3306,master_log_file='master-log.000001',master_log_pos=409
Mysql > start slave
Mysql > show slave status\ G
On how to share the MYSQL architecture from the MHA here, I hope the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.