用CBrother脚本代替redis做PHP缓存
PHP的短板就是无法进行缓存,所有要保存的内容都要写进文件里,之前一直用readis做缓存,但是readis对于PHP来说也是每次建立链接获取数据,而且在数据灵活性比较大的时候,readis用起来总是很麻烦。
我一直想如果php可以提供一种常驻内存进程,自己提供http接口来做数据缓存就好了,身边的同事我建议我去学java,或者python,但是java太庞大,python的语法还适应不了,最后我找到了一种叫CBrother的脚本,它的语法和js非常类似,我几乎没怎么看教程就直接上手了。
1.安装Cbrother
从官网下载最新的包,绿色的免安装,我是在windows上跑的,做缓存需要大内存,我用的64位的。http://www.cbrother.net
是个zip压缩包,我解压的D盘
具体目录可以去官网看文档,文档全是中文,里面有介绍,我就不多说了。
然后我在D盘创建一个自己的工作目录D:\cachetest,建议不要建到cbrother目录里,这样官方有新版本了直接覆盖就好了,不用挪代码目录。
从官网下载http://www.cbrother.net/down/cbrother.xml,这个是Notepad++的代码变色配置,导入后用Notepad++打开.cb后缀的脚本就会变色了
2.创建CBrother脚本
创建一个cache.cb的空文件,开始敲代码
import CBHttp.code //导入Http扩展
import lib/mysqlpool //导入官方提供的一个mysql链接池
function main(parm)//这个是主函数,程序启动的入口
{
InitCatch();//初始化数据库,把数据库里的数据都加载进内存
var httpServer = new HttpServer();
httpServer.setThreadCount(50);
httpServer.setOutTime(5);
httpServer.addAction("query.cb",new QueryAction()); //添加一个接口响应,QueryAction我写到下面了
httpServer.startServer(); //启动服务默认监听8000端口
OpenSaveTime(); //开启一个定时器,定时回写数据
while(1) //这里我是看官方例子才知道的,主线程不能退出
{
Sleep(1000);
}
}
下面来看下数据库初始化,我这里是建立了一张测试表说明问题,实际应用是多张表的数据组合
var g_mysqlpool = null; //全局变量表示mysql连接池
var g_TestTable = new Map(); //存储表格的map
var g_Lock = new Lock(); //因为是多线程的,所有访问数据要加锁
//先定义一个跟表格相关的类,方便在内存里查询
class TestTableRow
{
var _id;
var _type;
var _auth = new Array();
}
function InitCatch()
{
g_mysqlpool = new MySQLPool(100,"127.0.0.1",3306,"root","root","test");//创建连接池,这个类的源码在cbrother的lib文件夹里
var mysql = g_mysqlpool.GetFreeMySqlConnect(); //获取一个闲暇链接
if(mysql == null)
{
print "mysql connect err!!";
return;
}
var res = mysql.query("select * from test");//查询数据
if(!res)
{
print "mysql query err!!";
g_mysqlpool.ReleaseSqlConnect(mysql);//查询出错了要把链接还回去
return;
}
//依次获取数据保存在内存中
while(mysql.next())
{
var newRow = new TestTableRow();
newRow._id = mysql.getInt(0);
newRow._type = mysql.getString(1);
var auth = mysql.getString(2);
newRow._auth = strsplit(auth,",");//这一列按逗号拆开
g_TestTable.add(newRow._id,newRow);//添加进全局的map,初始化阶段是单线程的,所以不用加锁
}
g_mysqlpool.ReleaseSqlConnect(mysql);//还回去
}
这样我们就把表的数据插入到全局变量g_TestTable中了,后面可以直接访问了,再看看http接口的响应
class QueryAction
{
function DoAction(request,respon)//听做java的同事说这个接口跟servlet很相似,官方说这个接口是被多线程调用的
{
var id = request.getParmInt("id");//获取参数
var json = new Json();//创建一个json
g_Lock.lock();//加锁
var idRow = g_TestTable.get(id);//查询出来后放进json里
if(idRow != null)
{
json.add("id",idRow._id);
json.add("type",idRow._type);
var jsonarray = json.addArray("auth");
for(var i = 0 ; i < idRow._auth.size() ; i++)
{
jsonarray.push(idRow._auth[i]);
}
}
g_Lock.unlock();//解锁
respon.write(json.toJsonString());
respon.flush();//把数据返回
}
}
再开看看回写数据的定时器
var g_LastTime = time(); //上次保存时间
var g_thread; //定时器线程
function SaveTime()
{
if(time() - g_LastTime > 3600)//相差一个小时了回写数据
{
g_LastTime = time();
g_Lock.lock();
g_TestTable.begin();
do
{
var key = g_TestTable.getKey();
var idRow = g_TestTable.getValue();
var auth = "";
for(var i = 0 ; i < idRow._auth.size() ; i++)
{
if(i != 0)
{
auth += ",";
}
auth += idRow._auth[i];
}
var sql = "update test set type=" + idRow._type + "," + "auth='" + auth + "' where id=" + idRow._id;
var mysql = g_mysqlpool.GetFreeMySqlConnect();
if(mysql != null)
{
print sql;
mysql.upDate(sql);
g_mysqlpool.ReleaseSqlConnect(mysql);
}
} while(g_TestTable.next());
g_Lock.unlock();
}
}
function OpenSaveTime()
{
g_LastTime = time(); //保存时间先赋值为当前
g_thread = new Thread(); //创建线程
g_thread.addTimer(1000,SaveTime);//添加一个1秒执行一次的心跳
g_thread.start(); //开启线程
}
大功告成,我们在浏览器访问http://127.0.0.1:8000/query.cb?id=1
访问http://127.0.0.1:8000/query.cb?id=2
访问http://127.0.0.1:8000/query.cb?id=3
之后你就可以在PHP中调用这些接口了,你还可以根据自己的需求增加一些增删改查的接口,很多逻辑可以在CBrother端直接完成,返回结果,比以前用readis方便多了。