关于mysql集群主从服务器搭建
文件下载限速
首先,我们写一段使用php输出文件给浏览器下载的代码
在高并发流量下,数据库往往是服务端的瓶颈,由于数据库数据需要确保落地,同时保证数据同步,数据即时性,有效性的问题,导致数据库不能像平常后端程序一样负载均衡.
那么在大并发下,该如何缓解数据库的压力呢?
mysql读写分离
我们可以分析,程序对于mysql的操作无非就2种,写入数据/更新数据(数据变更),读取数据. 数据变更,因为要保证数据可靠以及数据同步问题,无法直接通过开多台服务器解决.
但是读取数据库,因为不涉及数据库变更,所以我们可以在程序中,将程序中涉及数据库数据变更的,和数据库查询的数据库区分,实现读写分离
数据变更时操作A服务器,A服务器数据变更后,立即传输给B服务器,使得B服务器进行更新数据.同时由于读数据库完全不涉及数据变更,可以开启多台读数据库,进行分散数据库压力
读写分离需要程序代码进行实现,而非数据库的功能,通过读写分离,能够极大的缓解数据库的压力.(虽然还是需要数据更新,并且还需要数据同步,但是写服务器只需要负责写入数据,读取的压力将分散到了读服务器上).
mysql集群
mysql集群除了为了解决数据库压力分散问题坏,同时为了实现数据库的高可用性,在一台数据库宕机的情况下,尽可能的降低业务的影响.
mysql集群有着以下几种方式:
1:mysql一主一从,mysql读写分离,使数据库压力分散,提高服务器性能
2:mysql一主多从,当主服务器出问题后,可以选择一台从服务器变更为主服务器,继续提供服务
3:mysql多主多从,一台主服务器出问题了,可立即切换另一台主服务器提供服务.
同时,mysql集群将带来相关的一些问题,例如:
1:主从同步数据延迟问题
2:一主多从虽然可以提高可用性,但在主服务器宕机的时候,可能会出现一些数据同步未完成,数据丢失的问题,需要在主服务器恢复后增量恢复
3:多主多从需要考虑主服务器都在使用时,id自增,主键冲突的问题,以及其中一台主服务器宕机时间至恢复时间内的数据丢失,增量同步的问题.
mysql一主一从搭建
当我们了解了mysql集群的实现原理,应用场景之后,就可以开始搭建主从集群环境了,我们需要准备:
1:2台服务器(虚拟机)
2:2台都需要安装mysql环境
目前我使用的是宝塔安装的mysql 5.6,可以自行安装mysql用于测试.
主服务器:192.168.192.131
从服务器:192.168.192.130
配置项
主服务器主要配置项:
log-bin = mysql-bin ##binlog文件存储路径,相对路径=datadir+log-bin.xxxxx 例如宝塔的binlog路径为;/www/server/data/mysql-bin.000001
server-id = 1 服务器标识id,通常主服务器id比从服务器小.
复制
从服务器配置项:
log-bin = mysql-bin
server-id = 1
复制
检查配置项命令:
[root@localhost www]# egrep "log-bin|server-id" /etc/my.cnf
log-bin=mysql-bin
server-id = 1
###上面是主服务器
[root@localhost ~]# egrep "log-bin|server-id" /etc/my.cnf
log-bin=mysql-bin
server-id = 2
###这里是从服务器
复制
查看binlog开启情况:
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
1 row in set (0.00 sec)
### 上面是主服务器
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
1 row in set (0.00 sec)
### 这里是从服务器
复制
主库新建从库访问账号,并给予相关的权限
## 新增主库访问账号rep,密码为123456,只允许通过192.168.*.* ip连接
mysql> grant replication slave on *.* to 'rep'@'192.168.%.%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
## 刷新系统用户权限
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
## 查询当前数据库账号
mysql> select user,host from mysql.user;
+------+-----------------------+
| user | host |
+------+-----------------------+
| root | 127.0.0.1 |
| rep | 192.168.%.% |
| root | ::1 |
| | localhost |
| root | localhost |
| | localhost.localdomain |
| root | localhost.localdomain |
+------+-----------------------+
7 rows in set (0.00 sec)
复制
服务器数据同步,保证2台服务器数据一致
主服务器加只读锁,防止在数据同步时,主服务器新增数据
## 数据库加锁,只允许读取数据
mysql> flush table with read lock;
Query OK, 0 rows affected (0.01 sec)
## 这个时候新建数据库,或者数据更新都会报错
mysql> create database test;
ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock
复制
备份数据
## 记录当前binlog位置,
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000013 | 120 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
复制
使用mysqldump命令进行备份数据库
mysqldump -uroot -p -A -F --master-data=2|gzip > /root/rep.sql.gz
-A 备份全部数据库
-F 刷新二进制日志
--master-data=2 追加二进制位置和文件输出到sql中
gzip 将mysqldump的数据压缩,节省存储空间
复制
将压缩文件上传到从服务器.并导入到从库中
gunzip < /root/rep.sql.gz |mysql -uroot -p
复制
配置从库与服务器通信参数:
mysql> CHANGE MASTER TO
-> MASTER_HOST='192.168.192.131',
-> MASTER_PORT=3306,
-> MASTER_USER='rep',
-> MASTER_PASSWORD='123456',
-> MASTER_LOG_FILE='mysql-bin.000013',
-> MASTER_LOG_POS=120;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
复制
验证修改的参数:
[root@localhost ~]# cd /www/server/data/
[root@localhost data]# cat master.info
23
mysql-bin.000012
120
192.168.192.131
rep
123456
3306
60
0
0
1800.000
0
86400
0
[root@localhost data]#
复制
启动从服务器节点,正式开始同步主服务器:
## 启动从服务器
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
## 查看从服务器运行状态
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.192.131
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000013
Read_Master_Log_Pos: 120
Relay_Log_File: localhost-relay-bin.000003
Relay_Log_Pos: 283
Relay_Master_Log_File: mysql-bin.000013
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 120
Relay_Log_Space: 460
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: f730887e-1e5c-11ea-ae2e-000c29fc65d1
Master_Info_File: /www/server/data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
1 row in set (0.00 sec)
复制
注意,只有当 slave_io_running和slave_sql_running 都为yes时,才算是启动成功.
如果你的mysql服务器是直接克隆的,需要注意删除mysql数据目录下的auto.cnf文件,并重启一次服务器,改文件记录了数据库的uuid,如果重复则会出错.
主服务器恢复可写:
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
复制
这步可以直接放到备份数据,并记录binlog文件名和位置时去做.
测试
主服务器当前数据库:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
4 rows in set (0.00 sec)
mysql>
复制
从服务器当前数据库:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
4 rows in set (0.00 sec)
mysql>
复制
主服务器新增数据库:
mysql> create database test666;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
| test666 |
+--------------------+
5 rows in set (0.00 sec)
mysql>
复制
从服务器查看:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
| test666 |
+--------------------+
5 rows in set (0.00 sec)
mysql>
复制
主从服务器搭建成功!
复制可以看出,php实现浏览器下载文件,主要是靠header头的支持以及echo 文件数据,那么,该如何限制速度呢?可以通过限制输出频率吗?例如每次读取1024之后,就进行一次sleep?
<?php
/**
* Created by PhpStorm.
* User: tioncico
* Date: 19-2-4
* Time: 下午4:30
*/
$filePath = './hyxd.zip';//文件
$fp=fopen($filePath,"r");
//取得文件大小
$fileSize=filesize($filePath);
header("Content-type:application/octet-stream");//设定header头为下载
header("Accept-Ranges:bytes");
header("Accept-Length:".$fileSize);//响应大小
header("Content-Disposition: attachment; filename=testName");//文件名
$buffer=1024;
$bufferCount=0;
while(!feof($fp)&&$fileSize-$bufferCount>0){//循环读取文件数据
$data=fread($fp,$buffer);
$bufferCount+=$buffer;
echo $data;//输出文件
sleep(1);//增加了一个sleep
}
fclose($fp);
复制
但是通过浏览器访问,我们发现是不行的,甚至造成了浏览器只有在n秒之后才会出现下载确认框,是哪里出了问题呢?
其实,这是因为php的buffer引起的,php buffer缓冲区,会使php不会马上输出数据,而是需要等缓冲区满之后才会响应到web服务器,通过web服务器再响应到浏览器中,详细请看:关于php的buffer(缓冲区)
那该怎么改呢?其实很简单,只需要使用ob系列函数就可解决:
<?php
/**
* Created by PhpStorm.
* User: tioncico
* Date: 19-2-4
* Time: 下午4:30
*/
$filePath = './hyxd.zip';//文件
$fp=fopen($filePath,"r");
//取得文件大小
$fileSize=filesize($filePath);
header("Content-type:application/octet-stream");//设定header头为下载
header("Accept-Ranges:bytes");
header("Accept-Length:".$fileSize);//响应大小
header("Content-Disposition: attachment; filename=testName");//文件名
ob_end_clean();//缓冲区结束
ob_implicit_flush();//强制每当有输出的时候,即刻把输出发送到浏览器
header('X-Accel-Buffering: no'); // 不缓冲数据
$buffer=1024;
$bufferCount=0;
while(!feof($fp)&&$fileSize-$bufferCount>0){//循环读取文件数据
$data=fread($fp,$buffer);
$bufferCount+=$buffer;
echo $data;//输出文件
sleep(1);
}
fclose($fp);
复制
这样,我们就已经实现了,每秒只输出1024字节的数据:
我们可以增加下载速度,把buffer改成更大的值,例如102400,那么就会变成每秒下载100kb:
文件断点续传
那么,我们该如何实现文件断点续传呢?首先,我们要了解http协议中,关于请求头的几个参数:
content-range和range,
在文件断点续传中,必须包含一个断点续传的参数,例如:
请求下载头:
Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头
响应文件头:
Content-Range: bytes 0-800/801 //801:文件总大小
正常下载文件时,不需要使用range头,而当断点续传时,由于再之前已经获得了n字节数据,所以可以直接请求
Range: bytes=n字节-总文件大小,代表着n字节之前的数据不再下载
响应头也是如此,那么,我们通过之前的限速下载,进行暂停,然后继续下载试试吧:
可看到,我们下载到600kb之后暂停了,然后我们代码记录下下次请求的请求数据:
<?php
/**
* Created by PhpStorm.
* User: tioncico
* Date: 19-2-4
* Time: 下午4:30
*/
$filePath = './hyxd.zip';//文件
$fp=fopen($filePath,"r");
set_time_limit(1);
//取得文件大小
$fileSize=filesize($filePath);
file_put_contents('1.txt',json_encode($_SERVER));
//下面的代码直接忽略了,主要看server
复制
当我点击继续下载时,浏览器会报出下载失败,原因是我们没有正确的响应它需要的数据,然后我们看下1.txt并打印成数组:
可看到,浏览器增加了一个range的请求头参数,想请求61400字节-文件尾的文件数据,那么,我们后端该如何处理呢?
我们只需要输出61400之后的文件内容即可
为了方便测试查看,我将文件改为了2.txt,内容如下:
编写可断点续传代码:
<?php
/**
* Created by PhpStorm.
* User: tioncico
* Date: 19-2-4
* Time: 下午4:30
*/
$filePath = './2.txt';//文件
$fp=fopen($filePath,"r");
//set_time_limit(1);
//取得文件大小
$fileSize=filesize($filePath);
$buffer=5000;
$bufferCount=0;
header("Content-type:application/octet-stream");//设定header头为下载
header("Content-Disposition: attachment; filename=2.txt");//文件名
if (!empty($_SERVER['HTTP_RANGE'])){
//切割字符串
$range = explode('-',substr($_SERVER['HTTP_RANGE'],6));
fseek($fp,$range[0]);//移动文件指针到range上
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $range[0]-$fileSize/$fileSize");
header("content-length:".$fileSize-$range[0]);
}else{
header("Accept-Length:".$fileSize);//响应大小
}
ob_end_clean();//缓冲区结束
ob_implicit_flush();//强制每当有输出的时候,即刻把输出发送到浏览器
header('X-Accel-Buffering: no'); // 不缓冲数据
while(!feof($fp)&&$fileSize-$bufferCount>0){//循环读取文件数据
$data=fread($fp,$buffer);
$bufferCount+=$buffer;
echo $data;//输出文件
sleep(1);
}
fclose($fp);
复制
使用谷歌浏览器进行下载并暂停
查看当前下载内容:
可看到,最后下载到的字符串为13517x,恢复浏览器下载,继续暂停
成功对接,并看到现在断点在51017x中,继续下载直到完成:
使用代码验证:
$txt = file_get_contents('/home/tioncico/Downloads/2.txt');
$arr = explode('x',$txt);
var_dump(count($arr));
var_dump($arr[count($arr)-2]);
复制
成功下载
多线程下载
通过前面,我们或许发现了什么:
1:限速是限制当前连接的数量
2:可以通过range来实现文件分片下载
那么,我们能不能使用多个连接,每个连接只下载x个字节,到最后进行拼装成一个文件呢?答案是可以的
下面,我们就使用php的curl_multi进行多线程下载
<?php
$filePath = '127.0.0.1/2.txt';
//查看文件大小
$ch = curl_init();
//$headerData = [
// "Range: bytes=0-1"
//];
//curl_setopt($ch, CURLOPT_HTTPHEADER, $headerData);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "HEAD");
curl_setopt($ch, CURLOPT_URL, $filePath);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return don't print
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
curl_setopt($ch, CURLOPT_MAXREDIRS, 7);
curl_setopt($ch, CURLOPT_HEADER, true);//需要获取header头
curl_setopt($ch, CURLOPT_NOBODY, 1); //不需要body,只需要获取header头的文件大小
$sContent = curl_exec($ch);
// 获得响应结果里的:头大小
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);//获取header头大小
// 根据头大小去获取头信息内容
$header = substr($sContent, 0, $headerSize);//获取真实的header头
curl_close($ch);
$headerArr = explode("\r\n", $header);
foreach ($headerArr as $item) {
$value = explode(':', $item);
if ($value[0] == 'Content-Length') {
$fileSize = (int)$value[1];//文件大小
break;
}
}
//开启多线程下载
$mh = curl_multi_init();
$count = 5;//n个线程
$handle = [];//n线程数组
$data = [];//数据分段数组
$fileData = ceil($fileSize / $count);
for ($i = 0; $i < $count; $i++) {
$ch = curl_init();
//判断是否读取数量大于剩余数量
if ($fileData > ($fileSize-($i * $fileData))) {
$headerData = [
"Range:bytes=" . $i * $fileData . "-" . ($fileSize)
];
}else{
$headerData = [
"Range:bytes=" . $i * $fileData . "-" .(($i+1)*$fileData)
];
}
echo PHP_EOL;
curl_setopt($ch, CURLOPT_URL, $filePath);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return don't print
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
curl_setopt($ch, CURLOPT_HTTPHEADER, $headerData);
curl_setopt($ch, CURLOPT_MAXREDIRS, 7);
curl_multi_add_handle($mh, $ch); // 把 curl resource 放进 multi curl handler 里
$handle[$i] = $ch;
}
$active = null;
do {
//同时执行多线程,直到全部完成或超时
$mrc = curl_multi_exec($mh, $active);
} while ($active);
for ($i = 0; $i < $count; $i++) {
$data[$i] = curl_multi_getcontent($handle[$i]);
curl_multi_remove_handle($mh, $handle[$i]);
}
curl_multi_close($mh);
$file = implode('',$data);//组合成一个文件
$arr = explode('x',$file);
var_dump($data);
var_dump(count($arr));
var_dump($arr[count($arr)-2]);
//测试文件是否正确
复制
运行截图:
该代码将会开出5个线程,按照不同的文件段去同时下载,再最后组装成一个字符串,即实现了多线程下载
以上代码是访问nginx直接测试的,之前的代码不支持head http头,我们需要修改一下才可以支持(但这是标准http写法)
我们需要修改下之前的代码,使其支持range的结束位置:
<?php
/**
* Created by PhpStorm.
* User: tioncico
* Date: 19-2-4
* Time: 下午4:30
*/
$filePath = './2.txt';//文件
$fp = fopen($filePath, "r");
//set_time_limit(1);
//取得文件大小
$fileSize = filesize($filePath);
$buffer = 50000;
$bufferCount = 0;
header("Content-type:application/octet-stream");//设定header头为下载
header("Content-Disposition: attachment; filename=2.txt");//文件名
if (!empty($_SERVER['HTTP_RANGE'])) {
//切割字符串
$range = explode('-', substr($_SERVER['HTTP_RANGE'], 6));
fseek($fp, $range[0]);//移动文件指针到range上
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $range[0]-$range[1]/$fileSize");
$range[1]>0&&$fileSize=$range[1];//只获取range[1]的数量
header("content-length:" . $fileSize - $range[0]);
} else {
header("Accept-Length:" . $fileSize);//响应大小
}
ob_end_clean();//缓冲区结束
ob_implicit_flush();//强制每当有输出的时候,即刻把输出发送到浏览器
header('X-Accel-Buffering: no'); // 不缓冲数据
while (!feof($fp) && $fileSize-$range[0] - $bufferCount > 0) {//循环读取文件数据
//避免多读取
$buffer>($fileSize-$range[0]-$bufferCount)&&$buffer=$fileSize-$range[0]-$bufferCount;
$data = fread($fp, $buffer);
$bufferCount += $buffer;
echo $data;//输出文件
sleep(1);
}
fclose($fp);
复制
修改下多线程下载代码:
<?php
$filePath = '127.0.0.1';
//查看文件大小
$ch = curl_init();
$headerData = [
"Range: bytes=0-1"
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headerData);
//curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "HEAD");
curl_setopt($ch, CURLOPT_URL, $filePath);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return don't print
curl_setopt($ch, CURLOPT_TIMEOUT, 0);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
curl_setopt($ch, CURLOPT_MAXREDIRS, 7);
curl_setopt($ch, CURLOPT_HEADER, true);//需要获取header头
curl_setopt($ch, CURLOPT_NOBODY, 1); //不需要body,只需要获取header头的文件大小
$sContent = curl_exec($ch);
// 获得响应结果里的:头大小
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);//获取header头大小
// 根据头大小去获取头信息内容
$header = substr($sContent, 0, $headerSize);//获取真实的header头
curl_close($ch);
$headerArr = explode("\r\n", $header);
foreach ($headerArr as $item) {
$value = explode(':', $item);
if ($value[0] == 'Content-Range') {//通过分段,获取到文件大小
$fileSize = explode('/',$value[1])[1];//文件大小
break;
}
}
var_dump($fileSize);
//开启多线程下载
$mh = curl_multi_init();
$count = 5;//n个线程
$handle = [];//n线程数组
$data = [];//数据分段数组
$fileData = ceil($fileSize / $count);
for ($i = 0; $i < $count; $i++) {
$ch = curl_init();
//判断是否读取数量大于剩余数量
if ($fileData > ($fileSize-($i * $fileData))) {
$headerData = [
"Range:bytes=" . $i * $fileData . "-" . ($fileSize)
];
}else{
$headerData = [
"Range:bytes=" . $i * $fileData . "-" .(($i+1)*$fileData)
];
}
echo PHP_EOL;
curl_setopt($ch, CURLOPT_URL, $filePath);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return don't print
curl_setopt($ch, CURLOPT_TIMEOUT, 0);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
curl_setopt($ch, CURLOPT_HTTPHEADER, $headerData);
curl_setopt($ch, CURLOPT_MAXREDIRS, 7);
curl_multi_add_handle($mh, $ch); // 把 curl resource 放进 multi curl handler 里
$handle[$i] = $ch;
}
$active = null;
do {
//同时执行多线程,直到全部完成或超时
$mrc = curl_multi_exec($mh, $active);
} while ($active);
for ($i = 0; $i < $count; $i++) {
$data[$i] = curl_multi_getcontent($handle[$i]);
curl_multi_remove_handle($mh, $handle[$i]);
}
curl_multi_close($mh);
$file = implode('',$data);//组合成一个文件
$arr = explode('x',$file);
var_dump($data);
var_dump(count($arr));
var_dump($arr[count($arr)-2]);
//测试文件是否正确
复制
成功下载,测试耗时结果为:5个线程4秒左右完成,1个线程花费13秒完成
- 点赞
- 收藏
- 关注作者
评论(0)