看了HTTPSQS,自己尝试开发了队列服务器。原理是借鉴HTTPSQS,只是通信协议层没有使用HTTP协议,而是自己的定的协议规则。
一是为了练手,练习检测内存泄露同时巩固指针的使用。
二是为了理解通信协议。
目前只支持Windows下的编译,后面会支持Linux。(现已支持Linux)
我每次都是先在windows下开发,之后再到调试支持linux下的编译。因为Linux的桌面版真心蛋疼,还不如直接windows下开发,然后通过虚拟机下的调试来支持Linux快。
现有支持的功能:
1.创建队列
2.获取队列
3.插入队列
4.删除队列
5.清空队列
6.队列状态
后续还会支持事务,事务与队列的关系可以参考Redis的设计实现:http://www.redisbook.com/en/latest/feature/transaction.html
一、开发用到的库
1、libevent:通信框架
2、glib:跨平台的基础库
3、Tokyo Cabinet:高效的数据存储
二、服务器端通信协议及队列原理
通信协议图:
单个队列图:
多个队列间的关系图直接借用HTTPSQS的:
三、PHP客户端的请求
<?php
define('QUEUE_MAX_LENGTH', 10000);
class QueueSocket{
private $socket;
private $errmsg;
public function __construct($host, $port){
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($this->socket, $host);
if(!socket_connect($this->socket, $host, $port)){
$this->set_errmsg("Unable to connect<pre>".socket_strerror(socket_last_error())."</pre>");
return false;
}
return true;
}
public function send($cmd, $content){
if(!is_array($content) || !$content) {
return false;
}
$params = false;
foreach($content as $key=>$value){
$params[] = strtoupper($key)."={$value}";
}
$params = join('&', $params)."\r\n";
$binary = pack("a20i1a*", strtoupper($cmd), strlen($params), $params);
socket_write($this->socket, $binary, strlen($binary));
}
public function read(){
$response = socket_read($this->socket, 256, PHP_BINARY_READ);
if($response) {
$response = json_decode($response, true);
}else{
return false;
}
if(isset($response['errno']) && $response['errno'] > 0)
$this->set_errmsg($response['errmsg']);
return $response;
}
public function set_errmsg($msg){
$this->errmsg = $msg;
}
public function get_errmsg(){
return $this->errmsg;
}
public function __destory(){
socket_close($this->socket);
}
}
class QueueClient extends QueueSocket{
private $appkey;
public function __construct($host, $port, $appkey){
$this->appkey = $appkey;
parent::__construct($host, $port);
}
public function create_new_queue($queue_name, $max_length = QUEUE_MAX_LENGTH){
$this->send("CREATE", array('queue_name'=>$queue_name, 'max_length'=>$max_length, 'appkey'=>$this->appkey));
return $this->read();
}
public function delete_queue($queue_name){
$this->send("DELETE", array('queue_name'=>$queue_name, 'appkey'=>$this->appkey));
return $this->read();
}
public function clear_queue($queue_name){
$this->send("CLEAR", array('queue_name'=>$queue_name, 'appkey'=>$this->appkey));
return $this->read();
}
public function put_queue($queue_name, $data){
if(is_array($data) || is_object($data)) $data = serialize($data);
$this->send("PUT", array('queue_name'=>$queue_name, 'data'=>$data, 'appkey'=>$this->appkey));
return $this->read();
}
public function get_queue($queue_name){
$this->send("GET", array('queue_name'=>$queue_name, 'appkey'=>$this->appkey));
return $this->read();
}
public function state($queue_name){
$this->send("STATE", array('queue_name'=>$queue_name, 'appkey'=>$this->appkey));
return $this->read();
}
}
$queue_client = new QueueClient("localhost", 8080, 'testkey');
//test create queue
$result = $queue_client->create_new_queue("test_queue", 10000);
print_r($result);
//test put
for($i=0; $i<100; $i++){
$result = $queue_client->put_queue("test_queue", "jinyong");
print_r($result);
}
// test get_queue
for($i=0; $i<100; $i++){
$result = $queue_client->get_queue("test_queue");
print_r($result);
}
// test state_queue
$result = $queue_client->state("test_queue");
print_r($result);
// test clear_queue
$queue_client->clear_queue("test_queue");
下面是客户端请求结果的部分截图:
队列服务器软件下载(Windows):http://yunpan.cn/QWPWu4kk6N36D
队列服务器源码、PHP客户端源码下载:http://yunpan.cn/QW563d8CJhzaM
Linux下的源码安装:
tar -zxf queue_server.tar.gz
cd queue_server/libevent
make
在安装之前需要先安装Kyoto Cabinet.
wget http://fallabs.com/tokyocabinet/tokyocabinet-1.4.48.tar.gz
tar -zxf tokyocabinet-1.4.48.tar.gz
cd tokyocabinet-1.4.48
./configure
make && make install