浏览模式: 标准 | 列表 | 订阅

用PHP对memcache的数据(key-value)进行遍历操作

作者:ciniao 时间:2011-08-27 分类:php No Comments

    目前,用到memcache的公司和网站也越来越多。Memcache的客户端操作一般都只提供了get,set等简单的操作,这些操作都是非常高效的。  虽然memcache是个key-value存储的系统,但是在某些时候,我们可能需要遍历memcache的数据。     

    PHP代码实现
$host='127.0.0.1';
$port=11211;
$mem=new Memcache();
$mem->connect($host,$port);

$items=$mem->getExtendedStats('items');
$items=$items["$host:$port"]['items'];
foreach($items as $key=>$values){
    $number=$key;
    $str=$mem->getExtendedStats ("cachedump",$number,0);
    $line=$str["$host:$port"];
    if( is_array($line) && count($line)>0){
        foreach($line as $key=>$value){
            echo $key.'=>';
            print_r($mem->get($key));
            echo "\r\n";
        }
    }

    由此可以实现查找memcache某个前缀的key的数据,或者查询某些value的key。甚至实现数据库的like功能。请注意:遍历memcache的操作并没有memcache的get操作那么高效。

    转自:http://blog.csdn.net/sunli1223/article/details/3193146     

用python来开发webgame服务端(3)

作者:ciniao 时间:2011-08-27 分类:python 6 Comments

    刺鸟原创文章,转载请注明出处
    在之前的准备工作中,我们已经建立了一个socket服务器,并且经过了简单的测试,得到的结论是python可以满足我们的需求,那么接下来,我们要解决的是身为webgame服务端必须的几个功能模块:

    一、记录和维护所有客户机的状态
    更新:按照jinmin_lan同学的建议,这里直接用self.transport.sessionno即可,查twisted文档的时候我忽略了这个东西。因此无需按照我下面的方法来手动维护,好吧,我造了一次轮子……    

    为了实现这个功能,我们先回过头来分析下之前的服务端代码:
    1、每个客户端连接会有一个gameSocket对象被创建,然后触发connectionMade事件。
    2、客户端数据到达的时候触发dataReceived事件
    3、连接断开的时候触发connectionLost事件,然后对象被析构

    根据通常的处理思路,我们需要为每个客户端建立一个编号,即传说中的sockid,然后维护一个client和sockid之间的双向字典,以便我们能够简单的互相反查。我决定维护2份数据,以空间换时间,新建一个sockMana类来实现该功能:
# sockmana.py
class SockMana:
    def __init__ (self):
        self.sockNum = 0 #记录当前的在线总数
        self.sockIndex = 1 #累加sockid
        self.client2id = {} #保存client->sockid字典
        self.id2client = {} #保存sockid->client字典

    def addClient(self,client):
        #增加一个客户端
        print '** add client **'
        self.sockNum = self.sockNum + 1
        self.client2id[client] = self.sockIndex
        self.id2client[self.sockIndex] = client
        self.sockIndex = self.sockIndex + 1

        print self.sockNum
        print self.client2id
        print self.id2client
    
    def delClient(self,client):
        #删除一个客户端
        print '** del client **'
        if client in self.client2id:
            self.sockNum = self.sockNum - 1
            _sockid = self.client2id[client]
            del self.client2id[client]
            del self.id2client[_sockid]

            print self.client2id
            print self.id2client
    
    def getSockid(self,client):
        #通过client获取sockid
        if client in self.client2id:
            return self.client2id[client]
        else:
            return None
        
    def getClient(self,sockid):
        #通过sockid获取client
        if sockid in self.id2client:
            return self.id2client[sockid]
        else:
            return None    

#初始化连接管理器
sockMana = SockMana()

    接下来在我们的socket服务端代码中import它,并增加调用事件,然后略修改dataReceived事件,当收到客户端数据的时候,我们向客户端返回它的sockid,完整的服务端代码调整为:

import os
if os.name!='nt':
    from twisted.internet import epollreactor
    epollreactor.install()    
else:
    from twisted.internet import iocpreactor
    iocpreactor.install()
from twisted.internet.protocol import Factory,Protocol
from twisted.internet import reactor
from sockmana import sockMana

class gameSocket(Protocol):
    #有新用户连接至服务器
    def connectionMade(self):
        sockMana.addClient(self)
        print 'New Client'
    
    #客户端断开连接
    def connectionLost(self,reason):
        sockMana.delClient(self)
        print 'Lost Client'
    
    #收到客户端发送数据
    def dataReceived(self, data):
        print 'Get data:' + str(data)
        #向该客户端发送数据
        self.transport.write('your sockid is:'+ str(sockMana.getSockid(self)))
    
if __name__=='__main__':
    f = Factory()
    f.protocol = gameSocket
    reactor.listenTCP(5200,f)
    print 'server started...'
    reactor.run()

    然后我们依然用telnet,来建立2个连接试试。
   

    可以看到,每增加一个客户端,我们的sockMana类中就会分别增加2个 key->val的键值对,通过sockMana.getSockid方法即可获取客户端的sockid,这样我们就为每个客户端建立了一个唯一且可用于传递和储存的数值编号,在以后的逻辑处理中,这将作为客户端的唯一标识。

    好了,我们断开其中一个客户端,看看我们的sockMana工作正常否?
    

    Yes!和预料中的一样,一切工作正常。我们又向前迈进了小小的一步,下面,我们得研究研究服务端如何和客户端之间高效的传输数据了。

#本文由刺鸟原创,欢迎转载,但请保留出处,也欢迎对本文感兴趣的同学多多交流。#

用python来开发webgame服务端(2)

作者:ciniao 时间:2011-08-30 分类:python 6 Comments

    刺鸟原创文章,转载请注明出处
    就在刚才,我们用基于python下的Twisted库写了一个简单的socket服务器,不知道它的性能和基本的承载到底如何呢?接下来,我们作一个简单的测试。

    说是简单的测试,一点也不假,因为这确实只是最基本的测试,流程是这样的:用python写一个客户端,设定连接数,然后向我们指定的端口发起socket连接,连接成功后,向服务端发送一个字符串并一直保持连接状态,服务端在收到客户端的内容后,向它回复:bingo!i got your msg:{content}。

    好啦,有了需求,开始动手吧!把我们之前的服务端代码略作修改:

from twisted.internet.protocol import Factory,Protocol
from twisted.internet import reactor

class gameSocket(Protocol):
    #有新用户连接至服务器
    def connectionMade(self):
        print 'New Client'
    
    #客户端断开连接
    def connectionLost(self,reason):
        print 'Lost Client'
    
    #收到客户端发送数据
    def dataReceived(self, data):
        print 'Get data:' + str(data)
        #向该客户端发送数据
        self.transport.write('bingo!i got your msg:'+ str(data))

if __name__=='__main__':
    f = Factory()
    f.protocol = gameSocket
    reactor.listenTCP(5200,f)
    print 'server started...'
    reactor.run()

    接下来,是要写一个客户端来连接它,然后用消息炸弹轰炸它!为了让客户端能高并发的测试,我选择了Stackless Python,来吧:

import socket,stackless
sockIndex = 1

def connToServer ():
    global sockIndex
    #创建一个socket连接到127.0.0.1:5200,并发送内容
    conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    conn.connect(("127.0.0.1", 5200))
    conn.send("hi,I'm NO."+ str(sockIndex))

    print sockIndex
    sockIndex = sockIndex + 1

    while True:
        #等待服务端返回数据,并输出
        rev = conn.recv(1024)
        print 'get server msg:' + str(rev)
        stackless.schedule()

#先来500个并发试试
for i in range(0,500):
    stackless.tasklet(connToServer)()

stackless.run()

准备就绪!开火!


    毫无压力,瞬间所有的连接全部都处理完成。那我们来加大火力,将客户端代码中的500修改为3000试试看。


    悲剧了,当客户端连接成功512个之后,服务端开始报错,难道python+Twisted如此的不堪一击?非也,经过查阅Twisted的官方文档,我们会发现,twisted默认用的是select()模式,而Windows的对文件描述符(file descriptor)有一定限制,这个限制值是512,在Linux的下这个限制为1024, 如果超过这个限制值,就会出现上面的异常。如果要在windows中有更好的表现,看来得用iocp,而linux下,用epoll则是更合适的方案,而Twisted自身就已经支持了这2种模式,看看如何启用:
windows:
from twisted.internet import iocpreactor
iocpreactor.install()

linux:
from twisted.internet import epollreactor
epollreactor.install()

    我的程序是在windows上开发的,最终部署到linux上,所以得写一个简单的判断来根据系统选择对应的模式,完整的服务端代码调整为:
import os
if os.name!='nt':
    from twisted.internet import epollreactor
    epollreactor.install()    
else:
    from twisted.internet import iocpreactor
    iocpreactor.install()
from twisted.internet.protocol import Factory,Protocol
from twisted.internet import reactor

class gameSocket(Protocol):
    #有新用户连接至服务器
    def connectionMade(self):
        print 'New Client'
    
    #客户端断开连接
    def connectionLost(self,reason):
        print 'Lost Client'
    
    #收到客户端发送数据
    def dataReceived(self, data):
        print 'Get data:' + str(data)
        #向该客户端发送数据
        self.transport.write('bingo!i got your msg:'+ str(data))

if __name__=='__main__':
    f = Factory()
    f.protocol = gameSocket
    reactor.listenTCP(5200,f)
    print 'server started...'
    reactor.run()

    我们再来试试刚才的3000个连接。


    OK,这次顺利的连接上了,在我用如上代码对工作机进行简单测试时,同时上万个连接无压力,而在实际的webgame应用环境中,达到上万个连接的可能性不大,当然,这里只是简单的连接,并未处理游戏逻辑,因此不能作为最终结论。
    不过就算单台服务器处理不过来,只要程序构架合理,我们也还可以将服务端分布到不同的服务器,所以,单从socket并发这点来看,python已经达到了我的需求。我将以以上服务端代码作为原型,来尝试开发一个webgame的服务端。

#本文由刺鸟原创,欢迎转载,但请保留出处,也欢迎对本文感兴趣的同学多多交流。#

用python来开发webgame服务端(1)

作者:ciniao 时间:2011-08-26 分类:python 8 Comments

    刺鸟原创文章,转载请注明出处
    在开始之前,先简单描述一下项目的特点:我要实现的是一个mmorpg的webgame,地图上需要看到其他的玩家,战斗系统采用半回合制的模式,所谓的半回合制,即是:采用回合制的画面布局,友方和敌方分列左右,但是战斗的中途,其他的玩家可以及时的随时插入这场战斗。当然,作为一款rpgGame,装备加工,NPC对话,任务逻辑等是必不可少的了。

    在分析了需求以后,我为这款game的程序结构定义如下:客户端,毋庸置疑肯定是flash,服务端分为两块:一是通讯不需要很及时,但是会涉及到很多逻辑处理的模块(如:各UI窗口内的逻辑,NPC对话逻辑,装备加工等),我为此选择了PHP作为服务端,二是需要及时的通讯,但是逻辑处理相对简单的模块(如:地图模块,战斗系统,聊天等),从性能上考虑,要实现这些模块,首选的当然是C、C++等,但若选择这样太底层的语言,再加上当前的人员配置,必将意味着开发周期的增加。经过讨论,在ZengRong的建议下,决定尝试使用python(我选择的版本是2.6,以下代码均基于该版本)。

    首先,要测试的是python的socket处理能力,我选择了Twisted作为网络处理库,在我的开发机(windows 2003)上安装Twisted。安装过程很easy,到pypi上找到Twisted的最新版本:http://pypi.python.org/pypi/Twisted/ 下载Twisted-11.0.0.winxp32-py2.6.msi安装即可。

测试是否安装成功:
from twisted.internet import reactor
在我的电脑上,运行该脚本后提示缺少模块:
ImportError: No module named zope.interface
http://pypi.python.org/pypi/zope.interface 下载并安装最新的zope.interface模块:zope.interface-3.7.0-py2.6-win32.egg。好吧,也许很多新手看到这个“蛋”一样的egg文件,不知如何安装,其实很简单,你需要的有以下几步:

1:先下载并运行一个叫做ez_setup.py的脚本,这个脚本会自动到网上下载相关的安装程序,提示完成后,会在python安装目录的的scripts文件下生成几个exe可执行文件。
2:将zope.interface-3.6.4-py2.6-win32.egg放到{python安装目录}\Scripts下
3:开始->运行->CMD

C:\>cd Python26\Scripts
C:\Python26\Scripts>easy_install.exe zope.interface-3.6.4-py2.6-win32.egg

安装完毕。接下来写一最简单的个socket的服务器:

from twisted.internet.protocol import Factory,Protocol
from twisted.internet import reactor
class gameSocket(Protocol):
    #有新用户连接至服务器
    def connectionMade(self):
        print 'New Client'
    
    #客户端断开连接
    def connectionLost(self,reason):
        print 'Lost Client'
    
    #收到客户端发送数据
    def dataReceived(self, data):
        print 'Get data:' + str(data)
if __name__=='__main__':
    f = Factory()
    f.protocol = gameSocket
    reactor.listenTCP(5200,f)
    print 'server started...'
    reactor.run()

    你没看错,在python的世界里,一切就是这个简单,这样就实现了一个socket服务器,监听5200端口,我们来运行它试试,看能否正常工作,作为现阶段的socket客户端,最简单的当然就是telnet了:
telnet 127.0.0.1 5200
   

   BingGo!一切正常,但是它的处理能力,并发连接等性能如何呢?一切都还需要进一步的测试,不过我们开了个好头,不是吗?

#本文由刺鸟原创,欢迎转载,但请保留出处,也欢迎对本文感兴趣的同学多多交流。#

python中列表(list)和字典(dict)数据排序

作者:ciniao 时间:2011-08-25 分类:python No Comments

对字符串列表进行排序
list_str = ['blue','allen','sophia','keen']
print list_str
list_str.sort()
print list_str

#执行结果:
['blue', 'allen', 'sophia', 'keen']
['allen', 'blue', 'keen', 'sophia']

对整型数进行排序
list_int = [34,23,2,2333,45]
print list_int
list_int.sort()
print list_int

#执行结果:
[34, 23, 2, 2333, 45]
[2, 23, 34, 45, 2333]

对字典数据进行排序
dict_str = {'blue':'5555@sina.com',
                'allen':'222@163.com',
                'sophia':'4444@gmail.com',
                'ceen':'blue@263.net'}
print dict_str
# 按照key进行排序
print sorted(dict_str.items(), key=lambda d: d[0])   
# 按照value进行排序
print sorted(dict_str.items(), key=lambda d: d[1])

#执行结果:
{'blue': '5555@sina.com', 'allen': '222@163.com', 'sophia': '4444@gmail.com', 'ceen': 'blue@263.net'}
[('allen', '222@163.com'), ('blue', '5555@sina.com'), ('ceen', 'blue@263.net'),('sophia', '4444@gmail.com')]
[('allen', '222@163.com'), ('sophia', '4444@gmail.com'), ('blue', '5555@sina.com'), ('ceen', 'blue@263.net')]