Memcache查看列出所有key方法及memkeys实时查看key使用情况

 更新时间:2016年11月25日 16:18  点击:1864
本文我们来分享关于Memcache查看列出所有key方法及利用memkeys实时查看memcached key使用情况,smemkeys是tumblr开源的类似top的工具,可用于实时查看memcached的key使用情况。

Memcache 查看列出所有key方法

今天在做一个Memcache的session测试,但是在测试的过程中,发现Memcache没有一个比较简单的方法可以直接象redis那样keys *列出所有的Session key,并根据key get对应的session内容,于是,我开始查找资料,翻出来的大部分是一些memcache常用命令等,但是对列出key的办法,讲解却不多,于是来到google,找到了一个国外的资料


具体的内容我套用我的测试环境中,操作如下

1. cmd上登录memcache

> telnet 127.0.0.1 11211

2. 列出所有keys

stats items // 这条是命令
STAT items:7:number 1
 STAT items:7:age 188
 END

3. 通过itemid获取key

接下来基于列出的items id,本例中为7,第2个参数为列出的长度,0为全部列出
stats cachedump 7 0 // 这条是命令
ITEM Sess_sidsvpc1473t1np08qnkvhf6j2 [183 b; 1394527347 s]
END

4. 通过get获取key值

上面的stats cachedump命令列出了我的session key,接下来就用get命令查找对应的session值
get Sess_sidsvpc1473t1np08qnkvhf6j2 //这条是命令
VALUE
Sess_sidsvpc1473t1np08qnkvhf6j2 1440 1
 83
 Sess_|a:5:{s:6:"verify";s:32:"e70981fd305170c41a5632b2a24bbcaa";s:3:"uid";s:1:"1
 ";s:8:"username";s:5:"admin";s:9:"logintime";s:19:"2014-03-11 16:24:25";s:7:"log
 inip";s:9:"127.0.0.1";}


memkeys实时查看memcached key使用情况

memkeys

memkeys是tumblr开源的类似top的工具,可用于实时查看memcached的key使用情况.

memkeys安装

安装autoconf(要求版本2.68以上):
# wget -c http://ftp.gnu.org/gnu/autoconf/autoconf-latest.tar.gz
# tar zxvf autoconf-latest.tar.gz
# cd autoconf-2.69
# ./configure
# make && make install

安装其它依赖:
# yum install libpcap-devel pcre-devel ncurses-devel

安装memkeys:
# git clone https://github.com/tumblr/memkeys.git
# cd memkeys
# ./autogen.sh
# ./configure
# make && make install

memkeys使用

# memkeys -h
Usage: memkeys -i NIC [options]
    -d, --discard=THRESH        Discard keys where req/s rate is below THRESH
    -i, --interface=NIC         Network interface to capture traffic on (required)
    -p, --port=PORT             Network port to capture memcache traffic on (default 11211)
    -r, --refresh=INTERVAL      Refresh the stats display every INTERVAL ms (default 500)
    -l, --logfile=FILE          Output logs to FILE
    -R, --report=REPORT         Output data in REPORT format (CSV or curses, default curses)
 
    -h, --help                  This help
    -v, --verbose               Increase verbosity. May be used multiple times.
    -V, --version               Show program info and exit.

例子1:

# memkeys -i eth0 -l /tmp/memkeys.log

例子2:

# memkeys -i eth0 -d 10.0 -l /tmp/memkeys.log

php memcache和php memcached是php的memcache分布式的高速缓存系统的两个客户端,php memcache是老客户端,php memcached是功能更加完善的新的代替php memcached的。

php memcache独立用php实现,是老客户端,从我们实践中已发现有多个问题,而且功能少,属性也可设置的少;
php memcached是基于原生的c的libmemcached的扩展,更加完善,建议替换为php memcached。

1. Php memcache的问题

1.1 分布式问题
php memcache默认会自动切换实例,所以有时取到老数据,并且value飘忽不定。
网友分享的问题:
这几天做某个产品的时候遇到一个小问题,现象比较诡异,产品用了两台分布式的memcached服务器。某一个计数器取回来的数偶尔会不对,最后定位在php memcache client的failover机制上面。
我们知道,在memcached分布式环境下,某一个key是通过hash计算,分配到某一个memcached上面的。
如果php.ini里面 memcache.allow_failover = 1的时候,在分布式环境下,某一台memcached出问题的话,会自动到其他的memcached尝试,就会出现上面的问题。所以要设置 allow_failover = 0 那么取不到时就直接返回失败而不会从其它mc上取,这样以避免网络异常或server端异常时,经常切换实例,会取到老数据。

1.2 高并发下稳定性问题
新浪微博提到的教训:
php memcache换成php memcached,在高并发下稳定下极大提高;
另外功能更多,出错码更精确。

Twitter的缓存经验
多层次Cache,减轻某些cache节点宕掉后的影响,读写都cache;
将memcached api统一换为libmemcached(方便多语言访问memcached,让分布式等各种规则都一致。)

1.3 1秒超时间隔没法修改问题
php memcache客户端有个1秒超时间隔没法修改问题:
bool Memcache::connect ( string $host [, int $port [, int $timeout ]] )
第三个参数本来可设置timeout,单位秒,但无法修改。
测试了以下三种修改timeout的方法都无效:
1.3.1. 用memcache api Memcache::setServerParams不能修改;
1.3.2. 改memcache 源代码vi php_memcache.h宏定义不能修改;
1.3.3. php.ini内这个配置:default_socket_timeout = 60对本timeout无效。

2. memcache和memcached对比

Php memcache这个老客户端在属性设置方面可设置的很少;
出错码粒度很粗,出错后难以定位;
而且功能欠缺一些:
There are primarily two clients used with PHP. One is the older, more widespread pecl/memcache and the other is the newer, less used, more feature rich pecl/memcached.
Both support the basics such as multiple servers, setting vaules, getting values, increment, decrement and getting stats.

Here are some more advanced features and information.
项目              pecl/memcache       pecl/memcached
First Release Date      2004-06-08      2009-01-29 (beta)
Actively Developed      Yes             Yes
External Dependency     None            libmemcached
Automatic Key Fixup1    Yes             No
Append/Prepend          No              Yes
Automatic Serialzation2 Yes             Yes
Binary Protocol         No              Optional
CAS                     No              Yes
Compression             Yes             Yes
Communication Timeout   Connect Only    Various Options
Consistent Hashing      Yes             Yes
Delayed Get             No              Yes
Multi-Get               Yes             Yes
Session Support         Yes             Yes
Set/Get to a specific server    No          Yes
Stores Numerics         Converted to Strings    Yes

注释:

1 pecl/memcache will convert an invalid key into a valid key for you. pecl/memcached will return false when trying to set/get a key that is not valid.
2 You do not have to serialize your objects or arrays before sending them to the set commands. Both clients will do

ajax聊天室就是定时不定的刷新框架页面了,下面来给各位整理了一个PHP+mysql+ajax轻量级聊天室实例(兼容Chrome和IE)的例子,希望对大家有帮助.


做了一个QQ聊天交友网站,想加个聊天的功能,于是做完用PHP做了简单又强大的聊天室

1. 创建mysql数据库表:create table chat( id bigint AUTO_INCREMENT,username varchar(20), chatdate datetime,msg varchar(500), primary key(id));

2.编写建议连接数据库函数:

dbconnect.php

<?php


function db_connect()
{

  date_default_timezone_set("Asia/Shanghai");

  $link = mysql_connect("xxx.xxx.xxx.xxx", "databasename", "password")
            or die('无法连接: ' . mysql_error());
  mysql_select_db("databasename") or die('没有你找到指定数据库');
  return true;
}

 

function quote($strText)
{
    $Mstr = addslashes($strText);
    return "'" . $Mstr . "'";
}


function isdate($d)
{
   $ret = true;
   try
   {
       $x = date("d",$d);
   }
   catch (Exception $e)
   {
       $ret = false;
   }
   echo $x;
   return $ret;
}

 
?>
3. 编写ajax发送和接收函数:

ajax发送函数chat_send_ajax.php

<?php
     require_once('dbconnect.php');

     db_connect();

     $msg = iconv("UTF-8","GB2312",$_GET["msg"]);
     $dt = date("Y-m-d H:i:s");
     $user = iconv("UTF-8","GB2312",$_GET["name"]);

     $sql="INSERT INTO chat(USERNAME,CHATDATE,MSG) " .
          "values(" . quote($user) . "," . quote($dt) . "," . quote($msg) . ");";

          echo $sql;

     $result = mysql_query($sql);
     if(!$result)
     {
        throw new Exception('Query failed: ' . mysql_error());
        exit();
     }

?>

 


ajax接收函数chat_recv_ajax.php

<?php
header("Content-Type:text/html;charset=gb2312");
header("Expires: Thu, 01 Jan 1970 00:00:01 GMT");
   header("Cache-Control: no-cache, must-revalidate");
   header("Pragma: no-cache");
     require_once('dbconnect.php');

     db_connect();
    
     $sql = "SELECT *, date_format(chatdate,'%Y年%m月%d日 %r') as cdt from chat order by ID desc limit 200";
     $sql = "SELECT * FROM (" . $sql . ") as ch order by ID";
     $result = mysql_query($sql) or die('Query failed: ' . mysql_error());
    
     // Update Row Information
     $msg="<table border='0' style='font-size: 10pt; color: white; font-family: verdana, arial;'>";
     while ($line = mysql_fetch_array($result, MYSQL_ASSOC))
     {
           $msg = $msg . "<tr><td>" . $line["cdt"] . " </td>" .
                "<td>" . $line["username"] . ": </td>" .
                "<td>" . $line["msg"] . "</td></tr>";
     }
     $msg=$msg . "</table>";
    
     echo $msg;

?>

 


4.聊天室页面:

chat.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
    <title>聊天页面</title>
  
<script type="text/javascript">

var t = setInterval(function(){get_chat_msg()},5000);


//
// General Ajax Call
//
     
var oxmlHttp;
var oxmlHttpSend;
     
function get_chat_msg()
{

    if(typeof XMLHttpRequest != "undefined")
    {
        oxmlHttp = new XMLHttpRequest();
    }
    else if (window.ActiveXObject)
    {
       oxmlHttp = new ActiveXObject("Microsoft.XMLHttp");
    }
    if(oxmlHttp == null)
    {
        alert("浏览器不支持XML Http Request!");
       return;
    }
   
    oxmlHttp.onreadystatechange = get_chat_msg_result;
    oxmlHttp.open("GET",encodeURI("chat_recv_ajax.php"),true);
    oxmlHttp.send(null);
}
    
function get_chat_msg_result()
{
    if(oxmlHttp.readyState==4 || oxmlHttp.readyState=="complete")
    {
        if (document.getElementById("DIV_CHAT") != null)
        {
            document.getElementById("DIV_CHAT").innerHTML =  oxmlHttp.responseText;
            oxmlHttp = null;
        }
        var scrollDiv = document.getElementById("DIV_CHAT");
        scrollDiv.scrollTop = scrollDiv.scrollHeight;
    }
}

     
function set_chat_msg()
{

    if(typeof XMLHttpRequest != "undefined")
    {
        oxmlHttpSend = new XMLHttpRequest();
    }
    else if (window.ActiveXObject)
    {
       oxmlHttpSend = new ActiveXObject("Microsoft.XMLHttp");
    }
    if(oxmlHttpSend == null)
    {
       alert("浏览器不支持XML Http Request!");
       return;
    }
   
    var url = "chat_send_ajax.php";
    var strname="noname";
    var strmsg="";
    if (document.getElementById("txtname") != null)
    {
        strname = document.getElementById("txtname").value;
        document.getElementById("txtname").readOnly=true;
    }
    if (document.getElementById("txtmsg") != null)
    {
        strmsg = document.getElementById("txtmsg").value;
        document.getElementById("txtmsg").value = "";
    }
   
    url += "?name=" + strname + "&msg=" + strmsg;
    oxmlHttpSend.open("GET",encodeURI(url),true);
    oxmlHttpSend.send(null);
}
function clickBtn(e)
  {
   if(window.event.keyCode==13)
   {
    var id=e.id;
    switch(id)
    {
     case "txtmsg":
      document.getElementById("Submit2").click();
      window.event.returnValue=false;
      break;
     }
   }
}
function fRandomBy(under, over){
switch(arguments.length){
case 1: return parseInt(Math.random()*under+1);
case 2: return parseInt(Math.random()*(over-under+1) + under);
default: return 0;
}
}
function SetTxtName(){
var i=fRandomBy(10);
if(i==0)document.getElementById('txtname').value='无敌战神';
if(i==1)document.getElementById('txtname').value='令狐冲';
if(i==2)document.getElementById('txtname').value='西门吹雪';
if(i==3)document.getElementById('txtname').value='超级玛丽';
if(i==4)document.getElementById('txtname').value='奥巴马';
if(i==5)document.getElementById('txtname').value='恐怖分子';
if(i==6)document.getElementById('txtname').value='聊斋奇女子';
if(i==7)document.getElementById('txtname').value='天朝?潘?;
if(i==8)document.getElementById('txtname').value='中500万了';
if(i==9)document.getElementById('txtname').value='神级奇葩';
if(i==10)document.getElementById('txtname').value='爱你不是两三天';
}
</script>

</head>
<body onload="SetTxtName();">
    
    <div style="border-right: black thin solid; border-top: black thin solid;
        border-left: black thin solid; border-bottom: black thin solid;
        background:#fff url('http://www.ihaonet.com/chat/blue.jpg') repeat-x left top;
        height: 450px;width: 500px; ">
        <table style="width:100%; height:100%">
            <tr>
                <td colspan="2" style="font-weight: bold; font-size: 16pt; color: white; font-family: verdana, arial;
                    text-align: center">
                    聊天窗口--全球最大QQ聊天交友网站</td>
            </tr>
            <tr>
                <td colspan="2" style="font-weight: bold; font-size: 16pt; color: white; font-family: verdana, arial;
                    text-align: left">
                    <table style="font-size: 12pt; color: white; font-family: Verdana, Arial;border: white thin solid; ">
                        <tr>
                            <td style="width: 100px">
                                名字:</td>
                            <td style="width: 100px"><input id="txtname" style="width: 150px" type="text" name="name" maxlength="15" value="匿名" /></td>
                        </tr>
                    </table>
                </td>
            </tr>
            <tr>
                <td style="vertical-align: middle;" valign="middle" colspan="2">
                    <div style="width: 480px; height: 300px; border-right: white thin solid; border-top: white thin solid; font-size: 10pt; border-left: white thin solid; border-bottom: white thin solid; font-family: verdana, arial; overflow:scroll; text-align: left;" id="DIV_CHAT">
                    </div>
                </td>
            </tr>
            <tr>
                <td style="width: 310px">
                    <input id="txtmsg" style="width: 350px" type="text" name="msg" onkeydown="return clickBtn(this)"/></td>
                <td style="width: 85px">
                    <input id="Submit2" style="font-family: verdana, arial" type="button" value="发送" onclick="set_chat_msg()"/></td>
            </tr>
            <tr>
                <td colspan="1" style="font-family: verdana, arial; text-align: center; width: 350px;">
                    </td>
                <td colspan="1" style="width: 85px; font-family: verdana, arial; text-align: center">
                </td>
            </tr>
        </table>
    </div>
</body>
</html>

 

效果:

 

php扩展开发对于很多朋友来讲都不太可能实现现因为php扩展开发是需要懂c的,下面我来为各位介绍一个 php扩展开发例子吧.

h1. 一、自动化建立扩展框架

到源码ext目录下

帮助
1
./ext_skel --extname=xiami_ext
生成如下几个文件文件列表:
* CREDITS
* EXPERIMENTAL
* config.m4
* config.w32
* php_xiami_ext.h
* tests
* xiami_ext.c
* xiami_ext.php

.c文件就是C语言系列的源文件,而.h文件则是C语言的头文件,即C系列中存放函数和全局变量的文件。子程序不要定义在*.h中。函数定义要放在*.c中,而*.h只做声明.否则多引用几次,就会发生函数重复定义的错误。

h1. 二、编写函数xiami_hello

h3. 1、不带参数

php_xiami_ext.h

 
PHP_MINIT_FUNCTION(xiami_ext);
PHP_MSHUTDOWN_FUNCTION(xiami_ext);
PHP_RINIT_FUNCTION(xiami_ext);
PHP_RSHUTDOWN_FUNCTION(xiami_ext);
PHP_MINFO_FUNCTION(xiami_ext);
 
PHP_FUNCTION(xiami_hello);
xiami_ext.c

 
const zend_function_entry xiami_ext_functions[] = {
    ZEND_FE(confirm_xiami_ext_compiled, NULL)
    ZEND_FE(xiami_hello,        NULL)
    PHP_FE_END
};
 
ZEND_FUNCTION(xiami_hello)
{
    php_printf("Hello World!n");
}
h3. 2、接收外来参数

xiami_ext.c
 
ZEND_BEGIN_ARG_INFO(arg_xiami_hello, 0)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()
 
ZEND_FUNCTION(xiami_hello)
{
    char *name = NULL;
    int argc = ZEND_NUM_ARGS();
    int name_len;
 
    if (zend_parse_parameters(argc TSRMLS_CC, "s", &name, &name_len) == FAILURE)
        return;
    php_printf("hello %s",name);
}
 
const zend_function_entry xiami_ext_functions[] = {
    ZEND_FE(confirm_xiami_ext_compiled, NULL)
    ZEND_FE(xiami_hello,        arg_xiami_hello)
    PHP_FE_END
};
以ZEND_BEGIN_ARG_INFO宏定义开始,以ZEND_END_ARG_INFO()结束,这两个宏定义解释如下:

ZEND_BEGIN_ARG_INFO(name, pass_rest_by_reference):
开始参数块定义,pass_rest_by_reference为1时,强制所有参数为引用类型
ZEND_END_ARG_INFO()

ZEND_NUM_ARGS()代表着参数的个数:

 
参数   代表着的类型
b   Boolean
l   Integer 整型
d   Floating point 浮点型
s   String 字符串
r   Resource 资源
a   Array 数组
o   Object instance 对象
O   Object instance of a specified type 特定类型的对象
z   Non-specific zval 任意类型~
Z   zval**类型
h1. 三、编写类XiamiClass

h3. 1、步骤

# 创建一个全局的zend_class_entry变量,用于存储类的入口。
# 创建一个zend_function_entry结构体数组,用于存储类中包含的方法。
# 在扩展的MINIT方法中注册类。

h3. 2、空类

xiami_ext.c
首先,我们创建一个名为php_xiamiclass_entry的zend_class_entry结构体变量,该结构体变量实际存储了我们创建的类的入口。

 
zend_class_entry *php_xiamiclass_entry;
这里的php_xiamiclass_entry在扩展源文件中是一个全局变量,为了使其它扩展可以使用我们创建的类,
这个全局变量应该在头文件中定义。

接下来,我们创建zend_function_entry结构体数组,这个数组与函数定义时的数组是一样的。
 
const zend_function_entry xiami_ext_methods[] = {
    PHP_FE_END
};
在MINIT函数中,首先创建了一个xiami_ce变量用于存储临时的类入口,接下来使用INIT_CLASS_ENTRY
宏初始化该变量,之后使用zend_register_internal_class()将该类注册到Zend引擎,
该函数会返回一个最终的类入口,将其赋值给前面创建的全局变量。

 
PHP_MINIT_FUNCTION(xiami_ext)
{
    zend_class_entry xiami_ce;
    INIT_CLASS_ENTRY(xiami_ce, "XiamiClass", xiami_ext_methods);
 
    php_xiamiclass_entry = zend_register_internal_class(&xiami_ce TSRMLS_CC);
    return SUCCESS;
}
h3. 3、类方法

php_xiami_ext.h
 
PHP_MINIT_FUNCTION(xiami_ext);
PHP_MSHUTDOWN_FUNCTION(xiami_ext);
PHP_RINIT_FUNCTION(xiami_ext);
PHP_RSHUTDOWN_FUNCTION(xiami_ext);
PHP_MINFO_FUNCTION(xiami_ext);
 
PHP_METHOD(XiamiClass,__construct);
PHP_METHOD(XiamiClass, set_xiami_age);
xiami_ext.c
 
ZEND_BEGIN_ARG_INFO_EX(arg_construct, 0, 0, 1)
    ZEND_ARG_INFO(0, age)
ZEND_END_ARG_INFO();
 
ZEND_BEGIN_ARG_INFO(arg_xiami_age, 0)
    ZEND_ARG_INFO(0, age)
ZEND_END_ARG_INFO()
 
PHP_METHOD(XiamiClass, __construct)
{
    long age;
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &age) == FAILURE){
        WRONG_PARAM_COUNT;
    }
    if( age <= 0 ) {
        age = 1;
    }
 
    zend_update_property_long(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_age"), age TSRMLS_CC);
    RETURN_TRUE;
}
 
PHP_METHOD(XiamiClass, set_xiami_age)
{
    long age;
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &age) == FAILURE){
        WRONG_PARAM_COUNT;
    }
    if( age <= 0 ) {
        age = 1;
    }
    zend_update_property_long(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_age"), age TSRMLS_CC);
    RETURN_TRUE;
}
 
PHP_MINIT_FUNCTION(xiami_ext)
{
    zend_class_entry xiami_ce;
    INIT_CLASS_ENTRY(xiami_ce, "XiamiClass", xiami_ext_methods);
 
    php_xiamiclass_entry = zend_register_internal_class(&xiami_ce TSRMLS_CC);
 
    zend_declare_property_null(php_xiamiclass_entry, ZEND_STRL("_age"), ZEND_ACC_PRIVATE TSRMLS_CC);
    return SUCCESS;
}
zend_declare_property_*系列函数:

 
ZEND_API int zend_declare_property_null(zend_class_entry *ce, char *name, int name_length, int access_type TSRMLS_DC);
用zend_read_property()和zend_update_property()函数:

 
ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, char *name, int name_length, zend_bool silent TSRMLS_DC);
 
ZEND_API void zend_update_property(zend_class_entry *scope, zval *object, char *name, int name_length, zval *value TSRMLS_DC);
h1. 四、读取ini文件

php_xiami_ext.h

 
PHP_MINIT_FUNCTION(xiami_ext);
PHP_MSHUTDOWN_FUNCTION(xiami_ext);
PHP_RINIT_FUNCTION(xiami_ext);
PHP_RSHUTDOWN_FUNCTION(xiami_ext);
PHP_MINFO_FUNCTION(xiami_ext);
 
PHP_FUNCTION(xiami_hello);
ZEND_BEGIN_MODULE_GLOBALS(xiami_ext)
    long  age;
ZEND_END_MODULE_GLOBALS(xiami_ext)
 
#ifdef ZTS
#define XIAMI_EXT_G(v) TSRMG(xiami_ext_globals_id, zend_xiami_ext_globals *, v)
#else
#define XIAMI_EXT_G(v) (xiami_ext_globals.v)
#endif
xiami_ext.c

 
ZEND_DECLARE_MODULE_GLOBALS(xiami_ext)
 
PHP_INI_BEGIN()
   STD_PHP_INI_ENTRY("xiami_ext.age",      "42", PHP_INI_ALL, OnUpdateLong, age, zend_xiami_ext_globals, xiami_ext_globals)
PHP_INI_END()
 
static void php_xiami_ext_init_globals(zend_xiami_ext_globals *xiami_ext_globals)
{
    xiami_ext_globals->age = 10;
}
 
PHP_MINIT_FUNCTION(xiami_ext)
{
    ZEND_INIT_MODULE_GLOBALS(xiami_ext, php_xiami_ext_init_globals, NULL);
    REGISTER_INI_ENTRIES();
 
    return SUCCESS;
}
 
ZEND_FUNCTION(xiami_hello)
{
    RETURN_LONG(XIAMI_EXT_G(age));
}
 
const zend_function_entry xiami_ext_functions[] = {
    ZEND_FE(xiami_hello,        NULL)
    PHP_FE_END
};
STD_PHP_INI_ENTRY的最后三个参数是来告诉PHP修改哪个全局变量,我们扩展的全局变量的数据结构,以及这些全局变量被保存到的全局容器的名称。
在php_xiami_ext.h添加的内容中,使用了一对宏ZEND_BEGIN_MODULE_GLOBALS()和ZEND_END_MODULE_GLOBALS() — 用来创建一个包含一个age类型,名为zend_xiami_ext_globals的结构体。然后继续声明了XIAMI_EXT_G()来从一个线程池中获取值,或者只是从全局空间中获取 - 如果你为一个非线程环境编译的话。

在php_xiami_ext.c中你用了ZEND_DECLARE_MODULE_GLOBALS()宏来真正实例化zend_xiami_ext_globals结构体为一个真正的全局变量.最后,在MINIT中,你使用了ZEND_INIT_MODULE_GLOBALS()来分配一个线程安全的资源id.

phpinfo扩展信息中,显示ini信息

 
PHP_MINFO_FUNCTION(xiami_ext)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "xiami_ext support", "enabled");
    php_info_print_table_end();
 
    DISPLAY_INI_ENTRIES();
}
h1. 五、设置常量
 
PHP_MINIT_FUNCTION(ggg)
{
    zend_constant c;
    char *trim_key = "xiami";
    char *trim_val = "hello";
    int trim_val_len,trim_key_len;
 
    trim_key_len = strlen(trim_key);
    trim_val_len = strlen(trim_val);
 
    c.value.type = IS_STRING;
    c.value.value.str.val = pestrdup(trim_val, trim_val_len+1);
    c.value.value.str.len = trim_val_len;
    c.flags = CONST_PERSISTENT | CONST_CS;
    c.name = pestrdup(trim_key, trim_key_len+1);
    c.name_len = trim_key_len+1;
    c.module_number = module_number;
    zend_register_constant(&c TSRMLS_CC);
 
    return SUCCESS;
}
h1. 六、资源处理

PHP中的资源类型在内核中是通过一个zend_rsrc_list_entry结构体来实现:
 
typedef struct _zend_rsrc_list_entry {
    void *ptr;
    int type;
    int refcount;
} zend_rsrc_list_entry;
其中,ptr是一个指向资源的最终实现的指针,例如一个文件句柄,或者一个数据库连接结构。type是一个类型标记,用于区分不同的资源类型。refcount用于资源的引用计数。
资源类型可分为普通的资源,以及持久型的资源。例如mysql普通连接与持久连接。均保存在_zend_executor_globals结构体当中,其中包含如下两个HashTable
 
struct _zend_executor_globals {
    ...
    HashTable regular_list;
    HashTable persistent_list;
    ...
}
regular_list保存普通资源,persistent_list则保存持久型资源。
要使用资源,先要注册一个资源类型,使用如下的API函数:
 
ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, const char *type_name, int module_number);
此函数返回一个资源类型id,zend_rsrc_list_entry结构体当中的type成员即对应此值。在扩展当中,此id应作为一个全局变量保存,以传递给其它资源API。
函数的第一及第二个参数,分别对应普通资源及持久资源的析构函数,第三个参数为资源类型的简短名称描述,一般用于错误提示,最后一个参数module_number为引擎内部使用,当我们调用这个函数时,只需要传递一个已经定义好的module_number变量。

 
static void myfile_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC){
     FILE *fp = (FILE *) rsrc->ptr;
     fclose(fp);
}
 
PHP_MINIT_FUNCTION(myfile) {
 
//le_myfile是一个用于保存资源类型id的全局变量
     le_myfile = zend_register_list_destructors_ex(myfile_dtor,NULL,"standard-c-file", module_number);
     return SUCCESS;
要创建一个资源,通过ZEND_REGISTER_RESOURCE()函数:

 
ZEND_API int zend_register_resource(zval *rsrc_result, void *rsrc_pointer, int rsrc_type TSRMLS_DC);
#define ZEND_REGISTER_RESOURCE(rsrc_result, rsrc_pointer, rsrc_type)  zend_register_resource(rsrc_result, rsrc_pointer, rsrc_type TSRMLS_CC);
其第一个参数rsrc_result是一个指向zval的指针,很显然其作用是将PHP变量和资源进行绑定。
第二个参数rsrc_pointer是一个指向资源数据的指针。
第三个参数rsrc_type,很显然,是上面通过zend_register_list_destructors_ex()函数注册所返回的资源类型id。

 
PHP_FUNCTION(file_open){
     char *filename = NULL;
     char *mode = NULL;
     int argc = ZEND_NUM_ARGS();
     int filename_len;
     int mode_len;
     FILE *fp;
 
     if (zend_parse_parameters(argc TSRMLS_CC, "ss", &filename,&filename_len, &mode, &mode_len) == FAILURE) {
          return;
     }
 
     fp = fopen(filename, mode);
 
     if (fp == NULL) {
          RETURN_FALSE;
     }
 
     ZEND_REGISTER_RESOURCE(return_value, fp, le_myfile);
}
要访问一个资源,是通过ZEND_FETCH_RESOURCE()函数来进行的:

 
ZEND_API void *zend_fetch_resource(zval **passed_id TSRMLS_DC, int default_id, const char *resource_type_name, int *found_resource_type, int num_resource_types, ...);
#define ZEND_VERIFY_RESOURCE(rsrc)  if (!rsrc) { RETURN_FALSE; }
#define ZEND_FETCH_RESOURCE(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type)  rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC, default_id, resource_type_name, NULL, 1, resource_type);    ZEND_VERIFY_RESOURCE(rsrc);
第一个参数rsrc,是要保存资源值所对应的变量名。
第二个参数,是一个指针转换的定义,用于内部将资源转换为正确的类型。
第三个参数,是一个对应的资源值
第四个参数,用于实现资源的默认值
第五个参数,同zend_register_list_destructors_ex()的第三个参数
第六个参数,对应zend_register_list_destructors_ex()的返回值

 
PHP_FUNCTION(file_write){
     char *buffer = NULL;
     int argc = ZEND_NUM_ARGS();
     int buffer_len;
     zval *filehandle = NULL;
     FILE *fp;
 
     if (zend_parse_parameters(argc TSRMLS_CC, "rs", &filehandle,&buffer, &buffer_len) == FAILURE) {
          return;
     }
 
     ZEND_FETCH_RESOURCE(fp, FILE *, &filehandle, -1, "standard-cfile", le_myfile);
 
     if (fwrite(buffer, 1, buffer_len, fp) != buffer_len) {
          RETURN_FALSE;
     }
 
     RETURN_TRUE;
}
要删除一个资源,则使用zend_list_delete()函数:

 
ZEND_API int _zend_list_delete(int id TSRMLS_DC);
#define zend_list_delete(id)  _zend_list_delete(id TSRMLS_CC)
这个函数仅有一个资源id的参数,返回SUCCESS或者FAILURE。
 
 
PHP_FUNCTION(file_close){
     int argc = ZEND_NUM_ARGS();
     zval *filehandle = NULL;
 
     if (zend_parse_parameters(argc TSRMLS_CC, "r", &filehandle) == FAILURE) {
          return;
     }
 
     if (zend_list_delete(Z_RESVAL_P(filehandle)) == FAILURE) {
          RETURN_FALSE;
     }
 
     RETURN_TRUE;
}
参考:

PHPMailer是一款邮件发送插件了,我们只要简单的配置就可以实现发邮件了,但小编在配置使用ssl时发现无法连接服务器了,下面我们来看问题解决办法.

使用PHPMailer发送邮件都是使用未加密的smtp服务器,这次使用的smtp服务器要用ssl(Secure Sockets Layer,安全套接层)方式连接。

好在PHPMailer支持ssl的smtp服务器,网上查了资料,以为改下端口,加上SMTPSecure设置就可以了,但是事实并非如此。

代码中端口改成了465,加了$mail->SMTPSecure = "ssl";设置,结果PHPMailer一直提示不能连接主机。

网上各种查资料,各种咨询,百般折腾后终于找到了原因,PHP没有开启openssl扩展,汗……

windows开启openssl扩展

在php.ini文件中找到;extension=php_openssl.dll,把前边的“分号”去掉,重启一下Apache即可。

linux开启openssl扩展

cd php-5.2.14/ext/openssl/
mv config0.m4 config.m4
phpize
./configure
make
make install
 
修改php.ini文件, 在适当的位置添加

extension=openssl.so

给php-fpm master进程发一个USR2信号,ssl扩展添加成功。

[!--infotagslink--]

相关文章

  • php 中file_get_contents超时问题的解决方法

    file_get_contents超时我知道最多的原因就是你机器访问远程机器过慢,导致php脚本超时了,但也有其它很多原因,下面我来总结file_get_contents超时问题的解决方法总结。...2016-11-25
  • 图解PHP使用Zend Guard 6.0加密方法教程

    有时为了网站安全和版权问题,会对自己写的php源码进行加密,在php加密技术上最常用的是zend公司的zend guard 加密软件,现在我们来图文讲解一下。 下面就简单说说如何...2016-11-25
  • HTTP 408错误是什么 HTTP 408错误解决方法

    相信很多站长都遇到过这样一个问题,访问页面时出现408错误,下面一聚教程网将为大家介绍408错误出现的原因以及408错误的解决办法。 HTTP 408错误出现原因: HTT...2017-01-22
  • php抓取网站图片并保存的实现方法

    php如何实现抓取网页图片,相较于手动的粘贴复制,使用小程序要方便快捷多了,喜欢编程的人总会喜欢制作一些简单有用的小软件,最近就参考了网上一个php抓取图片代码,封装了一个php远程抓取图片的类,测试了一下,效果还不错分享...2015-10-30
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • ps怎么使用HSL面板

    ps软件是现在很多人都会使用到的,HSL面板在ps软件中又有着非常独特的作用。这次文章就给大家介绍下ps怎么使用HSL面板,还不知道使用方法的下面一起来看看。 &#8195;...2017-07-06
  • ps把文字背景变透明的操作方法

    ps软件是现在非常受大家喜欢的一款软件,有着非常不错的使用功能。这次文章就给大家介绍下ps把文字背景变透明的操作方法,喜欢的一起来看看。 1、使用Photoshop软件...2017-07-06
  • intellij idea快速查看当前类中的所有方法(推荐)

    这篇文章主要介绍了intellij idea快速查看当前类中的所有方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-09-02
  • Mysql select语句设置默认值的方法

    1.在没有设置默认值的情况下: 复制代码 代码如下:SELECT userinfo.id, user_name, role, adm_regionid, region_name , create_timeFROM userinfoLEFT JOIN region ON userinfo.adm_regionid = region.id 结果:...2014-05-31
  • js导出table数据到excel即导出为EXCEL文档的方法

    复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta ht...2013-10-13
  • mysql 批量更新与批量更新多条记录的不同值实现方法

    批量更新mysql更新语句很简单,更新一条数据的某个字段,一般这样写:复制代码 代码如下:UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_value';如果更新同一字段为同一个值,mysql也很简单,修改下where即...2013-10-04
  • Plesk控制面板新手使用手册总结

    许多的朋友对于Plesk控制面板应用不是非常的了解特别是英文版的Plesk控制面板,在这里小编整理了一些关于Plesk控制面板常用的使用方案整理,具体如下。 本文基于Linu...2016-10-10
  • js基础知识(公有方法、私有方法、特权方法)

    本文涉及的主题虽然很基础,在许多人看来属于小伎俩,但在JavaScript基础知识中属于一个综合性的话题。这里会涉及到对象属性的封装、原型、构造函数、闭包以及立即执行表达式等知识。公有方法 公有方法就是能被外部访问...2015-11-08
  • ps怎么制作倒影 ps设计倒影的方法

    ps软件是一款非常不错的图片处理软件,有着非常不错的使用效果。这次文章要给大家介绍的是ps怎么制作倒影,一起来看看设计倒影的方法。 用ps怎么做倒影最终效果&#819...2017-07-06
  • 使用insertAfter()方法在现有元素后添加一个新元素

    复制代码 代码如下: //在现有元素后添加一个新元素 function insertAfter(newElement, targetElement){ var parent = targetElement.parentNode; if (parent.lastChild == targetElement){ parent.appendChild(newEl...2014-05-31
  • PHP 验证码不显示只有一个小红叉的解决方法

    最近想自学PHP ,做了个验证码,但不知道怎么搞的,总出现一个如下图的小红叉,但验证码就是显示不出来,原因如下 未修改之前,出现如下错误; (1)修改步骤如下,原因如下,原因是apache权限没开, (2)点击打开php.int., 搜索extension=ph...2013-10-04
  • c#中分割字符串的几种方法

    单个字符分割 string s="abcdeabcdeabcde"; string[] sArray=s.Split('c'); foreach(string i in sArray) Console.WriteLine(i.ToString()); 输出下面的结果: ab de...2020-06-25
  • 安卓手机wifi打不开修复教程,安卓手机wifi打不开解决方法

    手机wifi打不开?让小编来告诉你如何解决。还不知道的朋友快来看看。 手机wifi是现在生活中最常用的手机功能,但是遇到手机wifi打不开的情况该怎么办呢?如果手机wifi...2016-12-21
  • 使用percona-toolkit操作MySQL的实用命令小结

    1.pt-archiver 功能介绍: 将mysql数据库中表的记录归档到另外一个表或者文件 用法介绍: pt-archiver [OPTION...] --source DSN --where WHERE 这个工具只是归档旧的数据,不会对线上数据的OLTP查询造成太大影响,你可以将...2015-11-24
  • 使用GruntJS构建Web程序之构建篇

    大概有如下步骤 新建项目Bejs 新建文件package.json 新建文件Gruntfile.js 命令行执行grunt任务 一、新建项目Bejs源码放在src下,该目录有两个js文件,selector.js和ajax.js。编译后代码放在dest,这个grunt会...2014-06-07