今天在利用curl_multi函数来获取一些外网内容时发现只要一运行curl_multi函数我的cpu就占得非常的高,后来看一站长分享了此问题解决方法我也整理一下与各位分享一下,希望对大家有帮助。
简单的cURL处理如下:
代码如下 |
复制代码 |
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.111cn.net');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$con = curl_exec($ch);
curl_close($ch);
|
cURL还提供了批量处理会话,下面是cURL批量处理相关函数:
curl_multi_init — 返回一个新cURL批处理句柄
curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄
curl_multi_exec — 解析一个cURL批处理句柄
curl_multi_getcontent — 如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流
curl_multi_select — 等待所有cURL批处理中的活动连接
curl_multi_info_read — 获取当前解析的cURL的相关传输信息
curl_multi_remove_handle — 移除curl批处理句柄资源中的某个句柄资源
curl_multi_close — 关闭一组cURL句柄
看下面使用curl multi批处理的例子:
代码如下 |
复制代码 |
<?php
/**
* cURL multi批量处理
*
* @author mckee
* @link http://www.111cn.net
*
*/
$url_array = array(
'http://www.111cn.net/',
'http://www.111cn.net/php/627.html',
'http://www.111cn.net/php/258.html'
);
$handles = $contents = array();
//初始化curl multi对象
$mh = curl_multi_init();
//添加curl 批处理会话
foreach($url_array as $key => $url)
{
$handles[$key] = curl_init($url);
curl_setopt($handles[$key], CURLOPT_RETURNTRANSFER, 1);
curl_setopt($handles[$key], CURLOPT_TIMEOUT, 10);
curl_multi_add_handle($mh, $handles[$key]);
}
//======================执行批处理句柄=================================
$active = null;
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active and $mrc == CURLM_OK) {
if(curl_multi_select($mh) === -1){
usleep(100);
}
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
//====================================================================
//获取批处理内容
foreach($handles as $i => $ch)
{
$content = curl_multi_getcontent($ch);
$contents[$i] = curl_errno($ch) == 0 ? $content : '';
}
//移除批处理句柄
foreach($handles as $ch)
{
curl_multi_remove_handle($mh, $ch);
}
//关闭批处理句柄
curl_multi_close($mh);
print_r($contents); |
上面这段程序重点是执行批处理的那段,普通的处理:
代码如下 |
复制代码 |
do { $n=curl_multi_exec($mh,$active); } while ($active); |
会造成CPU Loading过高,因为$active要等全部url数据接受完毕才变成false,所以这里用到了curl_multi_exec的返回值判断是否还有数据,当有数据的时候就不停调用curl_multi_exec,没有执行数据就会sleep,如此就会避免CPU Loading 100%了。
发现一个前辈分析了PHP高并发高负载系统架构,觉得很全面很实用,收藏并分享,希望这个例子对各位朋友以后会有所帮助哦。
1.为什么要进行高并发与高负载的研究
2、高并发和高负载的约束条件
3、解决之道——硬件篇
4、解决之道——部署篇
5、解决之道——环境篇
6、解决之道——SiteEngine篇
7、解决之道——测试篇
1、为什么要进行高并发和高负载的研究
1.1、产品发展的需要
1.2、公司发展的需要
1.3、当前形式决定的
2、高并发和高负载的约束条件
2.1、硬件
2.2、部署
2.3、操作系统
2.4、Web 服务器
2.5、PHP
2.6、MySQL
2.7、测试
3、解决之道——硬件篇
处理能力的提升:部署多颗CPU,选择多核心、具备更高运算频率、更大高速缓存的CPU;
处理能力的提升最直接的反应在于Web请求的处理效率和PHP程序的执行效率。
内存带宽与容量:更大的内存带宽和容量;
内存带宽与容量的提升最直接的反应在于应对数据库大量的数据交换。
磁盘搜索与I/O能力:选择更高的转速、更大的硬盘缓存、组件磁盘阵列(RAID);
磁盘搜索与I/O能力的提升最直接反应在于数据库大量的查询和读写以及文件的读写。
网络带宽的提升可考虑的因素包括: 更大带宽、多线路接入、独享带宽;
服务器在大负载的情况下,对网络带宽的占用是十分可观的。
策略:硬件设施是应对大负载的基础,硬件设施的投入可根据实际压力和预算量力而行。
4、解决之道——部署篇
4.1、服务器分离
4.2、数据库集群和库表散列
4.3、镜像
4.4、负载均衡
分类:
1)、DNS轮循
2)代理服务器负载均衡
3)地址转换网关负载均衡
4)NAT负载均衡
5)反向代理负载均衡
6)混合型负载均衡
策略:根据硬件投入和业务需求,选择合理的部署方案。
部署方案1:
适用范围:静态内容为主体的网站和应用系统;对系统安全要求较高的网站和应用系统。
Main Server:主服务器
承载程序的主体运行压力,处理网站或应用系统中的动态请求;
将静态页面推送至多个发布服务器;
将附件文件推送至文件服务器;
安全要求较高,以静态为主的网站,可将服务器置于内网屏蔽外网的访问。
DB Server:数据库服务器
承载数据库读写压力;
只与主服务器进行数据量交换,屏蔽外网访问。
File/Video Server:文件/视频服务器
承载系统中占用系统资源和带宽资源较大的数据流;
作为大附件的存储和读写仓库;
作为视频服务器将具备视频自动处理能力。
发布服务器组:
只负责静态页面的发布,承载绝大多数的Web请求;
通过Nginx进行负载均衡部署。
部署方案2:
适用范围:以动态交互内容为主体的网站或应用系统;负载压力较大,且预算比较充足的网站或应用系统;
Web服务器组:
Web服务无主从关系,属平行冗余设计;
通过前端负载均衡设备或Nginx反向代理实现负载均衡;
划分专用文件服务器/视频服务器有效分离轻/重总线;
每台Web服务器可通过DEC可实现连接所有数据库,同时划分主从。
数据库服务器组:
相对均衡的承载数据库读写压力;
通过数据库物理文件的映射实现多数据库的数据同步。
共享磁盘/磁盘阵列
将用于数据物理文件的统一读写
用于大型附件的存储仓库
通过自身物理磁盘的均衡和冗余,确保整体系统的IO效率和数据安全;
方案特性:
通过前端负载均衡,合理分配Web压力;
通过文件/视频服务器与常规Web服务器的分离,合理分配轻重数据流;
通过数据库服务器组,合理分配数据库IO压力;
每台Web服务器通常只连接一台数据库服务器,通过DEC的心跳检测,可在极短时间内自动切换至冗余数据库服务器;
磁盘阵列的引入,大幅提升系统IO效率的同时,极大增强了数据安全性。
5、解决之道——环境篇
5.1、操作系统
操作系统的选择,关注点在于
•是否适应于搭建SiteEngine所需要的环境程序?
•系统本身占用的资源比;
•系统安全性;
•系统是否易于操作?
策略:我们选择FreeBSD,而且是最小化安装以后的FreeBSD。
5.2、Web服务器
Web服务器很大一部分资源占用来自于处理Web请求,通常情况下这也就是Apache产生的压力,Apache是世界使用排名第一的Web服务器软件。它可以运行在几乎所有广泛使用的计算机平台上。
在高并发连接的情况下,Nginx是Apache服务器不错的替代品。Nginx (“engine x”) 是俄罗斯人编写的一款高性能的 HTTP 和反向代理服务器。在国内,已经有新浪、搜狐通行证、网易新闻、网易博客、金山逍遥网、金山爱词霸、校内网、YUPOO相册、豆瓣、迅雷看看等多家网站、频道使用 Nginx 服务器。
Nginx的优势:
高并发连接:官方测试能够支撑5万并发连接,在实际生产环境中跑到2~3万并发连接数。
内存消耗少:在3万并发连接下,开启的10个Nginx 进程才消耗150M内存(15M*10=150M)。
内置的健康检查功能:如果 Nginx Proxy 后端的某台 Web 服务器宕机了,不会影响前端访问。
策略:相对于老牌的Apache,我们选择Lighttpd和Nginx这些具有更小的资源占用率和更高的负载能力的web服务器。
5.3、Mysql
MySQL本身具备了很强的负载能力,MySQL优化是一项很复杂的工作,因为这最终需要对系统优化的很好理解。大家都知道数据库工作就是大量的、短时的查询和读写,除了程序开发时需要注意建立索引、提高查询效率等软件开发技巧之外,从硬件设施的角度影响MySQL执行效率最主要来自于磁盘搜索、磁盘IO水平、CPU周期、内存带宽。
根据服务器上的硬件和软件条件进行MySQl优化。MySQL优化的核心在于系统资源的分配,这不等于无限制的给MySQL分配更多的资源。在MySQL配置文件中我们介绍几个最值得关注的参数:
改变索引缓冲区长度(key_buffer)
改变表长(read_buffer_size)
设定打开表的数目的最大值(table_cache)
对缓长查询设定一个时间限制(long_query_time)
如果条件允许 ,一般MySQL服务器最好安装在Linux操作系统中,而不是安装在FreeBSD中。
策略: MySQL优化需要根据业务系统的数据库读写特性和服务器硬件配置,制定不同的优化方案,并且可以根据需要部署MySQL的主从结构。
5.4、PHP
1、加载尽可能少的模块;
2、如果是在windows平台下,尽可能使用IIS或者Nginx来替代我们平常用的Apache;
3、安装加速器(都是通过缓存php代码预编译的结果和数据库结果来提高php代码的执行速度)
eAccelerator
eAccelerator是一个自由开放源码php加速器,优化和动态内容缓存,提高了性能php脚本的缓存性能,使得PHP脚本在编译的状态下,对服务器的开销几乎完全消除。
Apc
Alternative PHP Cache(APC)是 PHP 的一个免费公开的优化代码缓存。它用来提供免费,公开并且强健的架构来缓存和优化 PHP 的中间代码。
memcache
memcache是由Danga Interactive开发的,高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度。主要机制是通过在内存里维护一个统一的巨大的hash表,Memcache能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等
Xcache
国人开发的缓存器,
策略: 为PHP安装加速器。
5.5、代理服务器(缓存服务器)
Squid Cache(简称为Squid)是一个流行的自由软件(GNU通用公共许可证)的代理服务器和Web缓存服务器。Squid有广泛的用途,从作为网页服务器的前置cache服务器缓存相关请求来提高Web服务器的速度,到为一组人共享网络资源而缓存万维网,域名系统和其他网络搜索,到通过过滤流量帮助网络安全,到局域网通过代理上网。Squid主要设计用于在Unix一类系统运行。
策略:安装Squid 反向代理服务器,能够大幅度提高服务器效率。
6、解决之道——SiteEngine篇
7、解决之道——测试篇
7.1、测试方法
7.2、测试用例
7.3、压力测试
压力测试是一种基本的质量保证行为,它是每个重要软件测试工作的一部分。压力测试的基本思路很简单:不是在常规条件下运行手动或自动测试,而是在计算机数量较少或系统资源匮乏的条件下运行测试。通常要进行压力测试的资源包括内部内存、CPU 可用性、磁盘空间和网络带宽等。一般用并发来做压力测试。
压力测试工具:webbench,ApacheBench等
7.4、漏洞测试
在我们的系统中漏洞主要包括:sql注入漏洞,xss跨站脚本攻击等。安全方面还包括系统软件,如操作系统漏洞,mysql、apache等的漏洞,一般可以通过升级来解决。
漏洞测试工具:Acunetix Web Vulnerability Scanner
SAE域名绑定之后,一般是用CNAME方式将域名绑定到应用中。但是有时候我们需要用到A记录(比如说根域名,虽然在DNSPOD上可以设置CNAME记录,但很可能会影响到MX记录),而SAE的IP地址经常改变,ping应用二级域名得到的IP没多久就失效了(前些天网站因此几天打不开都没发现,我用的是教育网,自己能打开,但是电信线路变了)。还好DNSPOD有个功能叫D监控,可以帮你监控网站能否正常打开。如果发现宕机,DNSPOD会用邮件、短信、微信等方式提醒你。这里用到的是它的另一个通知方式,那就是URL回调
“通过DNSPod 提供的D监控 URL 回调功能,您可以让宕机或恢复信息提交到您指定的 URL 上,从而更加灵活地处理各种通知信息。”
我们可以通过宕机之后的URL回调取得相关参数,并通过DNSPOD API实现自动修改记录的功能,再通过飞信发送宕机通知。
代码在后面,先说设置方法:
设置方法
1.点此下载代码,修改其中的参数为你自己的。
2.将代码上传到网站。
3.在DNSPOD开启D监控,在通知设置中回调URL一栏填入monitorCallback.php的地址,如http://blog.gimhoy.com/monitorCallback.php?rHost=hipic.sinaapp.com.其中rHost是SAE的二级域名。并设置回调密钥。
4.Enjoy~
dnspod-monitor-callback
代码
monitorCallback.php
代码如下 |
复制代码 |
/*
* Copyright 2007-2014 Gimhoy Studio.
*
* @author Gimhoy
* @email contact@gimhoy.com
* @version 1.0.0
*/
$rHost = $_GET['rHost']; // SAE二级域名
if(empty($rHost)) $rHost = 'sinaapp.com';
$logName = 'monitorLog.txt'; //log 文件名
$logStorDomain = 'log'; // Storage Domain
$FetionNum = ''; //飞信登陆号码
$FetionPwd = ''; //飞信登陆密码
$MobileNum = ''; //接收通知短信的号码
$callback_key = 'MYKEY'; // 添加监控时设置的密钥
$monitor_id = $_POST['monitor_id']; // 监控编号
$domain_id = $_POST['domain_id']; // 域名编号
$domain = $_POST['domain']; // 域名名称
$record_id = $_POST['record_id']; // 记录编号
$sub_domain = $_POST['sub_domain']; // 主机名称
$record_line = $_POST['record_line']; // 记录线路
$ip = $_POST['ip']; // 记录IP
$status = $_POST['status']; // 当前状态
$status_code = $_POST['status_code']; // 状态代码
$reason = $_POST['reason']; // 宕机原因
$created_at = $_POST['created_at']; // 发生时间
$checksum = $_POST['checksum']; // 校检代码
if (md5($monitor_id. $domain_id. $record_id. $callback_key. $created_at) != $checksum) {
// 非法请求
echo 'BAD REQUEST';
} else {
// 开始处理
if ($status == 'Warn' || $status == 'Ok') {
// 宕机恢复
$msg = date("Y-m-d H:i:s").' '.$sub_domain.'.'.$domain."(".$record_line." ".$ip.")宕机恢复";
} elseif ($status == 'Down') {
// 宕机
$msg = date("Y-m-d H:i:s").' '.$sub_domain.'.'.$domain."(".$record_line." ".$ip.")在".$created_at."宕机。宕机原因:".$reason."可用IP:";
$ips = @gethostbyname($rHost);
include_once 'dnspod.class.php';
$newIP = $ips;
$data = array(
'domain_id' => $domain_id,
'record_id' => $record_id,
'sub_domain' => $sub_domain,
'record_type' => 'A',
'record_line' => $record_line,
'ttl' => '600',
'value' => $newIP
);
$dnspod = new dnspod();
$response = $dnspod->api_call('Record.Modify', $data);
if(isset($response['status']['code']) && $response['status']['code'] == 1) {
$msg = $msg.$newIP.'(已切换)';
}
else {
$msg = $msg.$newIP.'(切换失败,错误代码'.$response['status']['code'].')';
}
}
//飞信通知
require_once 'Fetion.class.php';
$fetion = new PHPFetion($FetionNum, $FetionPwd);
$result = $fetion->send($MobileNum, $msg);
if(strpos($result, '短信发送成功!') || strpos($result, '发送消息成功!')) { $r = "成功。";}else{$r = "失败。";}
$s = new SaeStorage();
$content = $s -> read($logStorDomain, $logName);
$content = $content.$msg.'。飞信通知'.$r.'
';
$s -> write($logStorDomain, $logName, $content);
// 处理完成
echo 'DONE';
}
dnspod.class.php
/*
* DNSPod API PHP Web 示例
* http://www.111cn.net/
*
* Copyright 2011, Kexian Li
* Released under the MIT, BSD, and GPL Licenses.
*
*/
class dnspod {
public function api_call($api, $data) {
if ($api == '' || !is_array($data)) {
exit('内部错误:参数错误');
}
$api = 'https://dnsapi.cn/' . $api;
$data = array_merge($data, array('login_email' => 'DNSPOD登陆账号', 'login_password' => 'DNSPOD登陆密码', 'format' => 'json', 'lang' => 'cn', 'error_on_empty' => 'yes'));
$result = $this->post_data($api, $data);
if (!$result) {
exit('内部错误:调用失败');
}
$results = @json_decode($result, 1);
if (!is_array($results)) {
exit('内部错误:返回错误');
}
if ($results['status']['code'] != 1) {
exit($results['status']['message']);
}
return $results;
}
private function post_data($url, $data) {
if ($url == '' || !is_array($data)) {
return false;
}
$ch = @curl_init();
if (!$ch) {
exit('内部错误:服务器不支持CURL');
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_USERAGENT, 'Gimhoy Monitor/1.0 (contact@gimhoy.com)');
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
}
|
Fetion.class.php
代码如下 |
复制代码 |
header('Content-Type: text/html; charset=utf-8');
/**
* PHP飞信发送类
* @author quanhengzhuang
* @version 1.5.0
*/
class PHPFetion {
/**
* 发送者手机号
* @var string
*/
protected $_mobile;
/**
* 飞信密码
* @param string
*/
protected $_password;
/**
* Cookie字符串
* @param string
*/
protected $_cookie = '';
/**
* Uid缓存
* @var array
*/
protected $_uids = array();
/**
* csrfToken
* @param string
*/
protected $_csrfToten = null;
/**
* 构造函数
* @param string $mobile 手机号(登录者)
* @param string $password 飞信密码
*/
public function __construct($mobile, $password)
{
if ($mobile === '' || $password === '')
{
return;
}
$this->_mobile = $mobile;
$this->_password = $password;
$this->_login();
}
/**
* 析构函数
*/
public function __destruct() {
$this->_logout();
}
/**
* 登录
* @return string
*/
protected function _login()
{
$uri = '/huc/user/space/login.do?m=submit&fr=space';
$data = 'mobilenum='.$this->_mobile.'&password='.urlencode($this->_password);
$result = $this->_postWithCookie($uri, $data);
//解析Cookie
preg_match_all('/.*?rnSet-Cookie: (.*?);.*?/si', $result, $matches);
if (isset($matches[1]))
{
$this->_cookie = implode('; ', $matches[1]);
}
$result = $this->_postWithCookie('/im/login/cklogin.action', '');
return $result;
}
/**
* 获取csrfToken,给好友发飞信时需要这个字段
* @param string $uid 飞信ID
* @return string
*/
protected function _getCsrfToken($uid)
{
if ($this->_csrfToten === null)
{
$uri = '/im/chat/toinputMsg.action?touserid='.$uid;
$result = $this->_postWithCookie($uri, '');
preg_match('/name="csrfToken".*?value="(.*?)"/', $result, $matches);
$this->_csrfToten = isset($matches[1]) ? $matches[1] : '';
}
return $this->_csrfToten;
}
/**
* 向指定的手机号发送飞信
* @param string $mobile 手机号(接收者)
* @param string $message 短信内容
* @return string
*/
public function send($mobile, $message) {
if($message === '') {
return '';
}
// 判断是给自己发还是给好友发
if($mobile === $this->_mobile) {
return $this->_toMyself($message);
}
else if(strlen($mobile)===11){
$uid = $this->_getUid($mobile);
}
else {
$uid=$mobile;
}
return $uid === '' ? $this->_addFriend($mobile) : $this->_toUid($uid, $message);
}
protected function _getname() {
$uri = '/im/index/index.action';
$result = $this->_postWithCookie($uri, '#');
// 匹配
preg_match('/(.*?)</a>/si', $result, $matches);
return $matches[2];
}
/*
* 通过手机号增加好友
* @param string $number 手机号(要加的好友手机)
* @param string $nickname 你的名字,出现在对方的验证短信里
* @param string $buddylist 分组,默认为空
* @param string $localName 好友屏显名
* @return string
*/
protected function _addFriend($number) {
$uri = '/im/user/insertfriendsubmit.action';
$data = 'nickname='. urlencode($this->_getname()).'&buddylist=1&localName=&number='. $number .'&type=0';
$result = $this->_postWithCookie($uri, $data);
return $result;
}
/**
* 获取飞信ID
* @param string $mobile 手机号
* @return string
*/
protected function _getUid($mobile)
{
if (empty($this->_uids[$mobile]))
{
$uri = '/im/index/searchOtherInfoList.action';
$data = 'searchText='.$mobile;
$result = $this->_postWithCookie($uri, $data);
//匹配
preg_match('/toinputMsg.action?touserid=(d+)/si', $result, $matches);
$this->_uids[$mobile] = isset($matches[1]) ? $matches[1] : '';
}
return $this->_uids[$mobile];
}
/**
* 向好友发送飞信
* @param string $uid 飞信ID
* @param string $message 短信内容
* @return string
*/
protected function _toUid($uid, $message) {
$uri = '/im/chat/sendMsg.action?touserid='.$uid;
$csrfToken = $this->_getCsrfToken($uid);
$data = 'msg='.urlencode($message).'&csrfToken='.$csrfToken;
$result = $this->_postWithCookie($uri, $data);
return $result;
}
/**
* 给自己发飞信
* @param string $message
* @return string
*/
protected function _toMyself($message) {
$uri = '/im/user/sendMsgToMyselfs.action';
$result = $this->_postWithCookie($uri, 'msg='.urlencode($message));
return $result;
}
/**
* 退出飞信
* @return string
*/
protected function _logout() {
$uri = '/im/index/logoutsubmit.action';
$result = $this->_postWithCookie($uri, '');
return $result;
}
protected function getgroup() {
$uri = '/im/index/index.action';
$data = 'type=group';
$result = $this->_postWithCookie($uri, $data);
// 匹配
preg_match_all('/contactlistView.action?idContactList=(d+)/si', $result, $matches);
foreach($matches[1] as $k=>$v){
if( $k== 0 ){
$min = $v;
$max = $v;
}else if($v!=9998&&$v!=9999){
$min = min($min,$v);
$max = max($max,$v);
}
}
return $max;
}
public function getyou1() {
$list=$this->getgroup();
for($i=0;$i<=$list;$i++){
$uri = '/im/index/contactlistView.action';
$data = 'idContactList='.$i.'&type=group';
$result = $this->_postWithCookie($uri, $data);
preg_match('/(.*?)|(.*?)((.*?)/(.*?))/si', $result, $listn);
if(!$listn[2]){continue;}
$shuchu.=str_replace(" ","",$listn[2])."(".$listn[4].")n";
preg_match('/共(d+)页/si', $result, $zpage);
preg_match('/共(d+)</a>页/si', $result, $dpage);
isset($zpage[1]) ? $page=$zpage[1] : $page=$dpage[4];
for($j=1;$j<=$page;$j++){
$uri = '/im/index/contactlistView.action';
$data = 'idContactList='.$i.'&page='.$j;
$result = $this->_postWithCookie($uri, $data);
preg_match_all('/(.*?)</a>/si', $result, $matches);
if(!$matches[1][0]){break;}
for($x=0;$x<=9;$x++){
if(!$matches[1][$x]){continue;}
$shuchu.=$matches[1][$x]." ".str_replace(" ","",$matches[3][$x])."n";
}
}
}
return $shuchu;
}
public function getyou() {
$list=$this->getgroup();
for($i=0;$i<=$list;$i++){
$uri = '/im/index/contactlistView.action';
$data = 'idContactList='.$i.'&type=group';
$result = $this->_postWithCookie($uri, $data);
preg_match('/(.*?)|(.*?)((.*?)/(.*?))/si', $result, $listn);
if(!$listn[2]){continue;}
$shuchu.=str_replace(" ","",$listn[2])."(".$listn[4].")n";
preg_match('/共(d+)页/si', $result, $zpage);
preg_match('/共(d+)</a>页/si', $result, $dpage);
isset($zpage[1]) ? $page=$zpage[1] : $page=$dpage[4];
for($j=1;$j<=$page;$j++){
$uri = '/im/index/contactlistView.action';
$data = 'idContactList='.$i.'&page='.$j;
$result = $this->_postWithCookie($uri, $data);
preg_match_all('/(.*?)</a>/si', $result, $matches);
if(!$matches[1][0]){break;}
for($x=0;$x<=9;$x++){
if(!$matches[1][$x]){continue;}
$shuchu.=$matches[1][$x]." ".str_replace(" ","",$matches[3][$x])."n";
}
}
}
return $shuchu;
}
/**
* 携带Cookie向f.10086.cn发送POST请求
* @param string $uri
* @param string $data
*/
protected function _postWithCookie($uri, $data)
{
$fp = fsockopen('f.10086.cn', 80);
fputs($fp, "POST $uri HTTP/1.1rn");
fputs($fp, "Host: f.10086.cnrn");
fputs($fp, "Cookie: {$this->_cookie}rn");
fputs($fp, "Content-Type: application/x-www-form-urlencodedrn");
fputs($fp, "User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1rn");
fputs($fp, "Content-Length: ".strlen($data)."rn");
fputs($fp, "Connection: closernrn");
fputs($fp, $data);
$result = '';
while (!feof($fp))
{
$result .= fgets($fp);
}
fclose($fp);
return $result;
}
}
|
网站安装过程我们需要几处非常简单,一个是我们要让用户输入用户名密码然后连接数据库之后再把我们以前准备好.sql文件利用php读取并执行,最后简单配置一下站点,这样一个完整的php网站安装过程就完美的搞定了。
这次顺便做了一个install.php才发现难度其实并不大,还是文件写入操作而已,安装其实主要操作的还是数据库里的内容,先来看看文件里怎么写:(还是用的Codeigiter,对于使用其他框架或者手写而言,仅思路可参考,用了挺多CI自带的helper或者是library的)
代码如下 |
复制代码 |
$content = "<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');n";
$content .= '$active_group'. "= 'default';n";
$content .= '$active_record'." = TRUE;n";
$content .= '$db'."['default']['hostname'] = '".$this->input->post('hostname')."';n";
$content .= '$db'."['default']['username'] = '".$this->input->post('rootname')."';n";
$content .= '$db'."['default']['password'] = '".$this->input->post('pass')."';n";
$content .= '$db'."['default']['database'] = '".$this->input->post('book')."';n";
$content .= '$db'."['default']['dbdriver'] = 'mysql';n";
$content .= '$db'."['default']['dbprefix'] = '';n";
$content .= '$db'."['default']['pconnect'] = TRUE;n";
$content .= '$db'."['default']['db_debug'] = TRUE;n";
$content .= '$db'."['default']['cache_on'] = FALSE;n";
$content .= '$db'."['default']['cachedir'] = '';n";
$content .= '$db'."['default']['char_set'] = 'utf8';n";
$content .= '$db'."['default']['dbcollat'] = 'utf8_general_ci';n";
$content .= '$db'."['default']['swap_pre'] = '';n";
$content .= '$db'."['default']['autoinit'] = TRUE;n";
$content .= '$db'."['default']['stricton'] = FALSE;";
|
在文件里用n来换行,因为里面包括了PHP的代码,这导致了我们只能用双引号避免冲突(否则的话就得用了,感觉工作量更大),针对$db,直接显示必须要用单引号,于是就出现了这个。
写入文件之后,接着我们需要做的是执行一系列安装操作,也就是CREATE TABLE,以及创建一个新用户用于登陆,在model里,我这么写:
代码如下 |
复制代码 |
function install() {
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_category'")) == 0)
$this->db->query("CREATE TABLE pr_category(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, category VARCHAR(100) NOT NULL UNIQUE, deadline INT NOT NULL)");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_college'")) == 0)
$this->db->query("CREATE TABLE pr_college(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL UNIQUE)");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_level'")) == 0)
$this->db->query("CREATE TABLE pr_level(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, level INT NOT NULL, name VARCHAR(20) NOT NULL)");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_rates'")) == 0)
$this->db->query("CREATE TABLE pr_rates(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, pid INT NOT NULL, ip VARCHAR(40) NOT NULL, category INT NOT NULL)");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_users'")) == 0)
$this->db->query("CREATE TABLE pr_users(uid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(45) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, level INT NOT NULL )");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_works'")) == 0)
$this->db->query("CREATE TABLE pr_works(pid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, content TEXT, realname VARCHAR(20) NOT NULL, studentnum VARCHAR(20) NOT NULL, college INT NOT NULL, filename TEXT NOT NULL, category INT NOT NULL)");
$query1 = $this->db->get_where('pr_level',array('level' => 1, 'name' => '普通用户'));
$query2 = $this->db->get_where('pr_level',array('level' => 5, 'name' => '管理员'));
$query3 = $this->db->get_where('pr_level',array('level' => 99, 'name' => '超级管理员'));
if ($query1->num_rows() == 0)
$this->db->query("INSERT INTO pr_level(level, name) VALUES (1, '普通用户')");
if ($query2->num_rows() == 0)
$this->db->query("INSERT INTO pr_level(level, name) VALUES (5, '管理员')");
if ($query3->num_rows() == 0)
$this->db->query("INSERT INTO pr_level(level, name) VALUES (99, '超级管理员')");
$this->username = $this->input->post('username');
$this->password = $this->input->post('password');
$this->level = 99;
$query4 = $this->db->get_where('pr_users',array('username' => $this->input->post('username')));
if ($query4->num_rows() == 0) {
$this->db->insert('pr_users', $this);
return TRUE;
} else {
return FALSE;
}
}
|
其实这么写查询量很大效率又低,不过这有效的避免了上一次安装被打断之后重新安装遇到的麻烦,检测是否已经创建了某个表,是否已经有新用户了之类的。
代码如下 |
复制代码 |
mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_category'"))
|
这句可以查询数据库中是否存在这个表。
执行完如果顺利的话,将install.php改个名字:
代码如下 |
复制代码 |
rename('application/controllers/install.php', 'application/controllers/install.lock');
|
在其他文件里加入检测install.php是否存在,如果存在的话就跳转到install.php,这样就做好了简单的安装流程了(必须放在model前,否则会提示没有数据库而不会跳转)。
代码如下 |
复制代码 |
if (file_exists('application/controllers/install.php'))
redirect('install');
|
至于什么是否存在表之类的,因为CI会check而且优先级也高于我们自己写的错误提示,所以这里就不加了。
完整源码(MVC):
Controller:
代码如下 |
复制代码 |
<?php
class Install extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->helper('file');
}
function index() {
$data['title'] = '安装向导';
$this->load->helper('form');
$this->load->library('form_validation');
$this->form_validation->set_rules('hostname', '主机名', 'trim|required|xss_clean');
$this->form_validation->set_rules('rootname', '数据库用户名', 'trim|required|xss_clean');
$this->form_validation->set_rules('username', '用户名', 'trim|required|xss_clean');
$this->form_validation->set_rules('password', '密码', 'trim|required|xss_clean|md5');
$data['error'] = '';
if ($this->form_validation->run() == FALSE) {
$this->load->view('install', $data);
} else {
$config['hostname'] = $this->input->post('hostname');
$config['username'] = $this->input->post('rootname');
$config['password'] = $this->input->post('pass');
$config['database'] = $this->input->post('book');
$config['dbdriver'] = 'mysql';
$config['dbprefix'] = '';
$config['pconnect'] = TRUE;
$config['db_debug'] = TRUE;
$config['cache_on'] = FALSE;
$config['cachedir'] = '';
$config['char_set'] = 'utf8';
$config['dbcollat'] = 'utf8_general_ci';
$config['swap_pre'] = '';
$config['autoinit'] = TRUE;
$config['stricton'] = FALSE;
if ($this->load->database($config, TRUE)) {
$content = "<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');n";
$content .= '$active_group'. "= 'default';n";
$content .= '$active_record'." = TRUE;n";
$content .= '$db'."['default']['hostname'] = '".$this->input->post('hostname')."';n";
$content .= '$db'."['default']['username'] = '".$this->input->post('rootname')."';n";
$content .= '$db'."['default']['password'] = '".$this->input->post('pass')."';n";
$content .= '$db'."['default']['database'] = '".$this->input->post('book')."';n";
$content .= '$db'."['default']['dbdriver'] = 'mysql';n";
$content .= '$db'."['default']['dbprefix'] = '';n";
$content .= '$db'."['default']['pconnect'] = TRUE;n";
$content .= '$db'."['default']['db_debug'] = TRUE;n";
$content .= '$db'."['default']['cache_on'] = FALSE;n";
$content .= '$db'."['default']['cachedir'] = '';n";
$content .= '$db'."['default']['char_set'] = 'utf8';n";
$content .= '$db'."['default']['dbcollat'] = 'utf8_general_ci';n";
$content .= '$db'."['default']['swap_pre'] = '';n";
$content .= '$db'."['default']['autoinit'] = TRUE;n";
$content .= '$db'."['default']['stricton'] = FALSE;";
if (write_file('application/config/database.php', $content)) {
$this->load->model('install_model');
if ($this->install_model->install()) {
rename('application/controllers/install.php', 'application/controllers/install.lock');
$this->load->library('session');
if (!empty($this->session->userdata['login'])) {
$this->session->sess_destroy(); // destroy the session
}
redirect('poster_admin/login', 'refresh');
} else {
$data['error'] = '超级管理员用户名已存在';
$this->load->view('install', $data);
}
} else {
$data['error'] = '安装失败,无法写入文件';
$this->load->view('install', $data);
}
}
}
}
}
?>
Model:
class Install_model extends CI_Model {
function __construct() {
parent::__construct();
$this->load->database();
$this->load->dbforge();
}
function install() {
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_category'")) == 0)
$this->db->query("CREATE TABLE pr_category(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, category VARCHAR(100) NOT NULL UNIQUE, deadline INT NOT NULL)");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_college'")) == 0)
$this->db->query("CREATE TABLE pr_college(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL UNIQUE)");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_level'")) == 0)
$this->db->query("CREATE TABLE pr_level(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, level INT NOT NULL, name VARCHAR(20) NOT NULL)");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_rates'")) == 0)
$this->db->query("CREATE TABLE pr_rates(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, pid INT NOT NULL, ip VARCHAR(40) NOT NULL, category INT NOT NULL)");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_users'")) == 0)
$this->db->query("CREATE TABLE pr_users(uid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(45) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, level INT NOT NULL )");
if (mysql_num_rows(mysql_query("SHOW TABLES LIKE 'pr_works'")) == 0)
$this->db->query("CREATE TABLE pr_works(pid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, content TEXT, realname VARCHAR(20) NOT NULL, studentnum VARCHAR(20) NOT NULL, college INT NOT NULL, filename TEXT NOT NULL, category INT NOT NULL)");
$query1 = $this->db->get_where('pr_level',array('level' => 1, 'name' => '普通用户'));
$query2 = $this->db->get_where('pr_level',array('level' => 5, 'name' => '管理员'));
$query3 = $this->db->get_where('pr_level',array('level' => 99, 'name' => '超级管理员'));
if ($query1->num_rows() == 0)
$this->db->query("INSERT INTO pr_level(level, name) VALUES (1, '普通用户')");
if ($query2->num_rows() == 0)
$this->db->query("INSERT INTO pr_level(level, name) VALUES (5, '管理员')");
if ($query3->num_rows() == 0)
$this->db->query("INSERT INTO pr_level(level, name) VALUES (99, '超级管理员')");
$this->username = $this->input->post('username');
$this->password = $this->input->post('password');
$this->level = 99;
$query4 = $this->db->get_where('pr_users',array('username' => $this->input->post('username')));
if ($query4->num_rows() == 0) {
$this->db->insert('pr_users', $this);
return TRUE;
} else {
return FALSE;
}
}
}
View:
<!DOCTYPE html>
<html lang="Zh-CN">
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="<?php echo base_url() . 'css/bootstrap.css' ?>">
<link rel="stylesheet" href="<?php echo base_url() . 'css/login.css' ?>">
<link rel="icon" type="image/x-icon" href="<?php echo base_url() . 'favicon.ico' ?>"/>
<script src="<?php echo base_url() . 'js/jquery-1.9.0.js' ?>"></script>
<script src="<?php echo base_url() . 'js/bootstrap.js' ?>"></script>
<title><?php echo $title; ?> - SMU Poster</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-sm-4 col-sm-offset-4">
<div <?php $err = validation_errors(); echo !(empty($error) && empty($err)) ? 'class="alert alert-danger text-center"' : '' ?>><?php echo validation_errors(); ?>
<?php echo $error ?></div>
<?php echo form_open('install', array('class'=>'form-horizontal','role'=>'form')); ?>
<div class="col-sm-sm-4 col-sm-offset-4"><h2>安装向导</h2></div>
<div class="form-group">
<div class="col-sm-9 col-sm-offset-2">
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-cloud"></span></span>
<input type="text" class="form-control" onMouseOver="$(this).tooltip('show')" data-toggle="tooltip" title="一般为localhost" id="hostname" name="hostname" placeholder="主机名" value="localhost"/>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-9 col-sm-offset-2">
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-book"></span></span>
<input type="text" class="form-control" onMouseOver="$(this).tooltip('show')" data-toggle="tooltip" title="数据库名如poster,请自行创建" id="book" name="book" placeholder="数据库名"/>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-9 col-sm-offset-2">
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
<input type="text" class="form-control" onMouseOver="$(this).tooltip('show')" data-toggle="tooltip" title="连接数据库的账号" id="rootname" name="rootname" placeholder="数据库账号"/>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-9 col-sm-offset-2">
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
<input type="password" class="form-control" onMouseOver="$(this).tooltip('show')" data-toggle="tooltip" title="连接数据库的密码" id="pass" name="pass" placeholder="数据库密码"/>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-9 col-sm-offset-2">
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
<input type="text" onMouseOver="$(this).tooltip('show')" data-toggle="tooltip" title="注册一个后台超级管理员账号" class="form-control" id="username" name="username" placeholder="后台登录账号"/>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-9 col-sm-offset-2">
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
<input type="password" onMouseOver="$(this).tooltip('show')" data-toggle="tooltip" title="后台超级管理员密码" class="form-control" id="password" name="password" placeholder="后台登陆密码"/>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-default">开始安装</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div id="footer">
<div class="container">
<p class="text-center copyright text-muted">Designed By <a href="http://www.111cn.net/">微软技术俱乐部</a></p>
</div>
</div>
</body>
</html>
|
仔细的研究了一下,原来用php写的解压程序效率比想象的还是高很多的,既然这么好,干脆再优化一下后用到自己后台中,虽然现在大部分空间的控制面板中有压缩和解压这个功能,但是毕竟有时候有些麻烦。
做这个之前,没有接触过php压缩这一块,网上搜了一些,大多数都是php压缩类、压缩函数,少则几百行,多的就几千行代码。这对于我这种新手来说很摸不到头脑,再说我也不用这么复杂的功能。最后参考函数手册,理清楚了几个相关的函数后,就明白了怎么去整了。
PHP Zip File 函数
记得要开启 zip ,把 php.ini 中的 extension=php_zip.dll 前面的分号去掉。
源码范例:
代码如下 |
复制代码 |
<?php
//需开启配置 php_zip.dll
//phpinfo();
header("Content-type:text/html;charset=utf-8");
function get_zip_originalsize($filename, $path) {
//先判断待解压的文件是否存在
if(!file_exists($filename)){
die("文件 $filename 不存在!");
}
$starttime = explode(' ',microtime()); //解压开始的时间
//将文件名和路径转成windows系统默认的gb2312编码,否则将会读取不到
$filename = iconv("utf-8","gb2312",$filename);
$path = iconv("utf-8","gb2312",$path);
//打开压缩包
$resource = zip_open($filename);
$i = 1;
//遍历读取压缩包里面的一个个文件
while ($dir_resource = zip_read($resource)) {
//如果能打开则继续
if (zip_entry_open($resource,$dir_resource)) {
//获取当前项目的名称,即压缩包里面当前对应的文件名
$file_name = $path.zip_entry_name($dir_resource);
//以最后一个“/”分割,再用字符串截取出路径部分
$file_path = substr($file_name,0,strrpos($file_name, "/"));
//如果路径不存在,则创建一个目录,true表示可以创建多级目录
if(!is_dir($file_path)){
mkdir($file_path,0777,true);
}
//如果不是目录,则写入文件
if(!is_dir($file_name)){
//读取这个文件
$file_size = zip_entry_filesize($dir_resource);
//最大读取6M,如果文件过大,跳过解压,继续下一个
if($file_size<(1024*1024*6)){
$file_content = zip_entry_read($dir_resource,$file_size);
file_put_contents($file_name,$file_content);
}else{ www.111cn.net
echo "<p> ".$i++." 此文件已被跳过,原因:文件过大, -> ".iconv("gb2312","utf-8",$file_name)." </p>";
}
}
//关闭当前
zip_entry_close($dir_resource);
}
}
//关闭压缩包
zip_close($resource);
$endtime = explode(' ',microtime()); //解压结束的时间
$thistime = $endtime[0]+$endtime[1]-($starttime[0]+$starttime[1]);
$thistime = round($thistime,3); //保留3为小数
echo "<p>解压完毕!,本次解压花费:$thistime 秒。</p>";
}
$size = get_zip_originalsize('20131101.zip','1代潇瑞/');
?>
|
测试解压了一个300多KB的小文件,花了0.115秒,测试解压了一个30多MB的(网页文件,小文件比较多),花了20多秒。
php解压程序
跟系统比起来确实慢了一些,但是这也很不错了!刚刚入门,代码还不优良,但是实现了解压,而且代码也比网上的简介易懂,如果你看到这篇文章,相信对你是有帮助的!程序中用到了程序运行的时间,具体可以看看: 。
最后,我觉得:
代码如下 |
复制代码 |
//最大读取6M,如果文件过大,跳过解压,继续下一个
if($file_size<(1024*1024*6)){
$file_content = zip_entry_read($dir_resource,$file_size);
file_put_contents($file_name,$file_content);
}
|
这一块做的不太好,这样对大文件解压就没办法了,等会再优化一下。