有意栽花花不開,無心插柳柳成蔭。
在線人數大約在3000左右,用兩臺機器跑,在高峰時會出現連不了memcache的情況,以前只是11211端口佔用比較多,但昨天出現不能連memcache。老問題了,一直因為非常重要或者很重要的事情而放到出現問題才會解決(11211端口的問題在很久以前就提出)。
連接memcached的地方有兩處,session和DB Cache,我先試著改session:
- function open() {
- global $_memcache;
- $_memcache = memcache_pconnect(HOST,PORT);
- }
- // session close
- function close() {
- global $_memcache;
- memcache_close($_memcache);
- }
- // session gc
- function gc($key) {
- global $_memcache;
- memcache_close($_memcache);
- }
- // session destroy
- function destroy($key) {
- global $_memcache;
- memcache_close($_memcache);
- }
- // session read
- function read($key) {
- global $_memcache;
- $_memcache->get($key);
- }
- // session write
- function write($key,$val) {
- global $_memcache;
- memcache_set($_memcache,$key,$val,0,SESSION_MAX_LIFTTIME);
- }
- // 初始化
- function init()
- {
- ini_set(‘session.use_trans_sid’,0);
- ini_set(‘session.gc_maxlifetime’,SESSION_MAX_LIFTTIME);
- ini_set(‘session.use_cookies’, 1);
- ini_set(‘session.cookie_path’, ‘/’);
- ini_set(‘session.cookie_domain’,’.’.BASE_URL);
- session_module_name(‘user’);
- session_set_save_handler(
- ‘open’,
- ‘close’,
- ‘read’,
- ‘write’,
- ‘destroy’,
- ‘gc’
- );
- session_start();
- }
- init();
發現問題照舊,再改成:
- $_session_save_path = "tcp://192.XX.XX.XX:11211";
- ini_set(‘session.save_handler’, ‘memcache’);
- ini_set(‘session.save_path’, $_session_save_path);
- session_start();
還是一樣,眼看半天過去,問題都沒找到。看樣子問題不出在session,那就是db cache了,只有連接沒有斷開,不斷創建新的連接,才會有那麼多的端口被佔用,俺想到一個最簡單的辦法,設計模式的單例模式不就解決類似的問題嗎?
先看看單例模式的類圖:
- class Obj_Memcache {
- private static $_instance = null;
- private function __construct(){}
- private function __clone(){}
- public static function getInstance()
- {
- if (self::$_instance == null) {
- self::$_instance = new Memcache();
- self::$_instance->connect(CFG_DB_CACHE_HOST, CFG_DB_CACHE_PORT);
- }
- return self::$_instance;
- }
- }
找到實現cache的地方:
替換為:
- $this->Mem = Obj_Memcache::getInstance();
看看結果:
今天觀察看看,應該不是蓋的。。
雷死人的hosts和resolv.conf
今天算是碰到怪事了,做的儲值模塊,因為代碼去抓取遠程資料庫的數據,在Server端已寫好api,直接調用就可以了。所以就用fsockopen來建立連接,問題出來了,不管怎麼寫,死活抓不到資料。
- if (!$_fp) {
- throw new Exception($_error_msg.$_errorno."\n");
- }
一直提示
- Connection timed out 110
換成curl後還是不可以~在命令行下運行
- curl -G http://sub.domain.com/XX/xx/xx
出現couldn’t connect to host.
但是在內部的測試機卻是正常。就是說內部機訪問subdomain,dns可以正常解析,換服務器就不可以了。
在hosts中加入
- 192.X.X.X sub.domain.com
指定ip後再試,ok了。
整理的htc desire 大陆和香港间的差异
除了接口、htc 小工具、GPS,操作系统不一样,其他的都一样。
php5 iterator - 迭代器
上一篇我提到php的spl(php 标准库),今天刚好又用到iterators,觉得非常方便。于是从网上找了一些资料,结合我蹩脚的能力写了下来。
先看看iterators有哪些内容:
图中有数组、文件、目录、对象和相对应实现递归的迭代器。拿数组说事:
- $_demo_arr = array(‘name’ => ‘OGX’, ‘sex’ => ‘B’, ‘age’ => ’100′, ‘color’ => ‘yellow’, 300);
正常我们来遍历数组:
- foreach ($_demo_arr AS $_key => $_val) {
- echo $_key . ‘==’ . $_val;
- }
- // 或者
- while (list($_key,$_val) = @each($_demo_arr)) {
- echo sprintf("%s == %s",$_key,$_val);
- }
结果如下:
同样的数组,我改为迭代器遍历,代码如下:
- $_obj_iterator = new ArrayIterator($_demo_arr);
- while ($_obj_iterator->valid()) {
- echo "i’m iterator: \r\n";
- echo sprintf("%s == %s",$_obj_iterator->key(),$_obj_iterator->current()) . "\r\n";
- }
结果如下:
当然,你可以使用别的iterator来实现你需要的功能,例如遍历目录、遍历对象等等。具体就不介绍了,也没能力写得更好。如有遗漏或者错误之处还请见谅。
唠叨贴
上面说了php的异常处理,现在来骂一下吧!
php本来是弱类型的语言,所有的异常信息都要我们自己来定义,加上php5.2 和php5.3 对错误纪别处理也不一样,在php5.3下可以正常运行的代码,在php5.2下一直报错,或者白屏,毛都被qi没了。
更加弱质的是,参数前如果加了类型,php5.3会正常,php5.2就不行,况方法的参数数量不一样,也会报错。php5.2 接口的使用还不能完整的使用。
php异常处理
在开发中经常会碰到处理一些信息,提供给用户或者管理员。简单的直接输出就可以,如果遇到嵌套类中最内层的信息,就有些麻烦了,不得不直拉输出信息。
php5 引进异常处理,每当出的异常都会被catch语句块补获,并每一个 try必由一个catch来处理异常。
exception 是php标准的函数库,其定义如下:
- <?php
- class Exception
- {
- protected $message = ‘Unknown exception’; // 异常信息
- protected $code = 0; // 用户自定义异常代码
- protected $file; // 发生异常的文件名
- protected $line; // 发生异常的代码行号
- function __construct($message = null, $code = 0);
- final function getMessage(); // 返回异常信息
- final function getCode(); // 返回异常代码
- final function getFile(); // 返回发生异常的文件名
- final function getLine(); // 返回发生异常的代码行号
- final function getTrace(); // backtrace() 数组
- final function getTraceAsString(); // 已格成化成字符串的 getTrace() 信息
- /* 可重载的方法 */
- function __toString(); // 可输出的字符串
- }
- ?>
当我们要定义自己的异常处理类时,调用父类的构造函数,指定异常信息,如果要得到自己的字符串
信息,重载__toString()方法即可。因为默认输出的信息会比较全,我们不用让用户了解这些对他们来说没用的信息。
- class AppException extends Exception {
- public function __construct($r_message,$r_code)
- {
- parent::__construct($r_message,$r_code);
- }
- public function __toString()
- {
- return get_class($this).’XXXX’; // 这儿还可以定义信息
- }
- }
- class Demo {
- public function examplException()
- {
- try {
- $_variable = ‘abcd’;
- if (!is_numeric($_variable)) {
- throw new AppException("XXXXX");
- }
- } catch (AppException $e) {
- die($e->getMessage()); // 当然还可以做别的东东,比如打飞机
- }
- }
- }
但今天碰到一个情况,使用ajax调用,从内部类丢出的异常跟我想要的不一样。
- class AjaxException extends Exception {
- public function __construct($r_message,$r_code)
- {
- parent::__construct($r_message,$r_code);
- }
- public function getAjaxMessage()
- {
- return json_encode(array(‘status’ => $this->code,’error’ => $this->message));
- // 对,我就是闲的蛋疼,想输出json格式的信息。
- }
- }
- class Demo {
- public function examplException()
- {
- try {
- $_variable = ‘abcd’;
- if (!is_numeric($_variable)) {
- throw new AppException("XXXXX");
- }
- if (strlen($_variable) < 8) {
- throw new AjaxException("XXOO",400);
- }
- } catch (AppException $e) {
- die($e->getMessage()); // 当然还可以做别的东东,比如打飞机
- } catch (AjaxException $e) {
- echo $e->getAjaxMessage();
- }
- }
- }
这样就可以使用自己定义的异常了,当然,好的地方不止这些。详细请看php的exception
当你用一阵时间后,你会发现方便很多。
ubuntu不支持ll 命令的解决办法
习惯使用ll命令~但再ubuntu下不支持。其实ll 就是 ls -l的一个别名。解决办法有二:
1、再当前用户目录创建bin目录,然后创建一个名为ll 的shell脚本实现ls -l,然后设定该shell脚本路径到系统变量。
- $ cat > ~/bin/ll
- #! /bin/sh
- # alias ll = ‘ls -l’
- ls -l
- ^D
- chmod +x ~/bin/ll
- PATH=$PATH:$HOME/bin
现在试一下,搞定。
2、修改.bashrc文件,加入一句:alias ll=’ls -l’(或者把该句前面的注释去掉即可)。
omg~pdo 数据抽象层 实例
defined("DSN") OR define("DSN","mysql:dbname=text;127.0.0.1");
defined("DB_USER") OR define("DB_USER","root");
defined("DB_PASSWORD") OR define("DB_PASSWORD","");
/**
* 使用mysql的pdo驱动
*
* @author
* 单例模式
**/
class Db {
/**
* 类的实例
**/
private static $instance;
/**
* 构造函数,定义为私有防止类再外部被实例化
*
* @example
* $obj = new Db();
*
**/
private function __construct(){}
/**
* 定义为私有确保实例不被clone
**/
private function __clone(){}
/**
* 返回一个pdo的实例
*
**/
public static function getInstance()
{
try {
if (!self::$instance) {
self::$instance = new PDO(DSN,DB_USER,DB_PASSWORD);
self::$instance->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
}
} catch (EXCEPTION $e) {
echo "connection failed!" . $e->getMessage();
die();
}
return self::$instance;
}
/**
* 通过db的实例来调用pdo的方法
*
* @param $r_method
* @param $r_arr_argument
* @return mix
**/
final public static function __callStatic($r_method,$r_arr_argument)
{
$_obj_instance = self::getInstance();
return call_user_func_array(array($_obj_instance,$r_method),$r_arr_argument);
}
}
实例: ';
try {
$_sql = "SELECT * FROM posts limit 10";
foreach (Db::query($_sql) AS $row) {
echo '
';
print_r($row);
echo '
}
}catch (Exception $e) {
echo $e->getMessage();
}
终于搞定简单版~接下来就是数据关系映射,然后是数据持久层。
不关站升级数据库
下面是infoq的计划,我认为除了计划,每一条都是值得我借鉴的。
比如说:准备工作,要让服务提供商知道详细内容。呃,这一点我就不说了。安全可控的关闭mysql,停止web服务,保证所有事务已经提交,设置mysql为只读。innodb引擎的转换,这里提到mysql的Per-Table Tablespaces –innodb-file-per-table 设置项,是因为innodb默认把数据跟索引文件分成不同的表空间存放,这也就是为什么不能直接拷贝data目录下的文件来备份innodb的原因。至于备份这mysql的官网有详细的资料 innodb-file-pre-table
接着就是主从,除了主从还是主从。具体就要看怎么去想更好的方案。嗯,就这样,睡觉。
假想案例:不关站升级数据库
前段时间莫哥提到升级数据库失败问题,因为他所负责的项目会比较复杂,并且在安全性上要求比较高。并且还有其他的一些原因(开发新功能、修复bug之类),后来不知有没有升级成功。今天看到infoq升级数据库的文章,收获颇丰。借鉴infoq的案例和总结的经验,我觉得也非常适合莫哥负责的项目。并且也给新成长的站点有了借鉴的地方。
先来看看infoq的经验:
1、有详细的计划。(这点非常重要)要涉及到方方面面的问题:线路、机器、人工以及可能突发的状况和状况发生时的应急预案。
2、变化。记得在head first系列书里面有提到软件开发最普遍的就是变化(change),这尤其也是我后来一直拿来安慰别人和自我安慰的一个词。
3、适应变化。这点是我加上去的,因为我们在处理碰到的问题时,往往有些人还要比我们更没有耐心和浮躁。他们往往会在你紧绷神经时来找茬或者捣乱。
4、节省系统调试的时间。这点我苟且暂时认同,因为毕竟我还没达到那种保证事情99%正确的境界。如果调试(测试)不能发现问题,就别指望我们的其他工作人员找到问题。根据我自己的经历,某些大的问题往往都是用户直接反馈给我们的。
5、积极寻求反馈。这点可能再国内或者tw市场有点困难。因为亚洲人(至少是中国人)已经习惯了出钱就是爷的思维意识。我们获得反馈信息的途径也很有限,无非就是网站留言、电话、邮件、msn(少量)等几种,不像infoq上所说的google group、twitter等。国内少有的类似twitter的网站都具有阉割和自我阉割的功能,google group已经被墙,我估计以后的网民甚至不知道有google group这样一说。当然,这些只限在中国大陆。
接下来要讲一下infoq在升级方案,未完待续。









